本篇笔记加强了上篇笔记示例代码的程序,实现了使用nodejs-websocket来广播每个玩家的坐标数据并在同一个世界模型中进行多人在线交互。

websocket服务端:

安装nodejs与npm,创建一个服务端目录

npm init

npm install nodejs-websocket

index.js,代码如下,创建好index.js后,执行 node index.js 开启websocket服务端

var ws = require("nodejs-websocket")

function broadcast(server, msg) {
server.connections.forEach(function (conn) {
conn.sendText(msg)
})
}

var server = ws.createServer(function (conn) {
console.log("New connection")
conn.on("text", function (str) {
console.log("Received "+str)
broadcast(server, str)
})
conn.on("close", function (code, reason) {
console.log("Connection closed")
})
conn.on("error", function (code, reason) {
console.log("异常关闭", reason)
});
}).listen(8001)

客户端测试代码如下,拷贝到您的浏览器打开,方向键控制镜头旋转与移动,多个用户(或浏览器窗口)使用相同的websocket服务地址可以看到彼此。

<!doctype html>
<html>
<head>
<style>
* {margin:0;padding:0;overflow:hidden;}
</style>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord;

uniform mat4 uPMatrix;
uniform mat4 uTranslateMatrix;
uniform mat4 uScaleMatrix;
uniform mat4 uRotateYMatrix;
uniform mat4 uRotateXMatrix;
uniform mat4 uRotateZMatrix;
varying vec2 vTextureCoord;

void main(void){
gl_Position = uPMatrix * uRotateZMatrix * uRotateYMatrix * uRotateXMatrix *uTranslateMatrix * uScaleMatrix * vec4(aVertexPosition, 1.0);
vTextureCoord = aTextureCoord;
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;

varying vec2 vTextureCoord;

uniform sampler2D uSampler;

void main(void) {
gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
}

</script>
<script>

var op = function op(){}

op.intCanvas = function(id) {
var canvas = document.getElementById(id)
if(!canvas) {
alert('Could not intialise Canvas, sorry :-(")')
}
op.canvas = canvas
}

op.intGL = function(canvas) {
if(!canvas){
canvas = op.canvas
}
try {
var gl = canvas.getContext("experimental-webgl")

gl.viewportWidth = canvas.width
gl.viewportHeight = canvas.height
op.gl = gl
} catch(e) {
}
if(!gl) {
alert('Could not intialise WebGL, sorry :-(")')
}
}
op.intShader = function(){
op.gl.vertexShader = op.getShader('vertex-shader')
op.gl.fragmentShader = op.getShader('fragment-shader')
}
op.getShader = function (scriptId) {

try {
if(!op.gl) {
throw new Error('找不到WebGL对象。');
}
var gl = op.gl

var shaderScript = document.getElementById(scriptId)
if( ! shaderScript) {
throw new Error('找不到着色器代码。');
}
var str = ''
const TEXT_NODE = 3
k = shaderScript.firstChild;

while(k) {
if(k.nodeType == TEXT_NODE) {
str += k.textContent
}
k = k.nextSibling;
}
var shader, shaderType;
if(shaderScript.type == "x-shader/x-fragment") {
shaderType = gl.FRAGMENT_SHADER
} else if( shaderScript.type == "x-shader/x-vertex") {
shaderType = gl.VERTEX_SHADER
} else {
throw new Error('着色器类型有误。');
}
shader = gl.createShader(shaderType);
gl.shaderSource(shader, str)
gl.compileShader(shader);

if(! gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
throw new Error('着色器编译出现问题:' + gl.getShaderInfoLog(shader));
}
} catch(e) {
alert('[Error]' + e)
}

return shader;
}

op.intProgram = function(){
var gl = op.gl
var fragmentShader = gl.fragmentShader
var vertexShader = gl.vertexShader
var program = gl.createProgram()
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
gl.linkProgram(program)
if(! gl.getProgramParameter(program, gl.LINK_STATUS) ) {
alert("Could not initialise shaders");
}
gl.useProgram(program)
gl.program = program

program.vertexPositionAttribute = gl.getAttribLocation(program, 'aVertexPosition');
gl.enableVertexAttribArray(program.vertexPositionAttribute)

program.pMatrixUniform = gl.getUniformLocation(program, 'uPMatrix')
program.uRotateXMatrixUniform = gl.getUniformLocation(program, 'uRotateXMatrix');
program.uRotateYMatrixUniform = gl.getUniformLocation(program, 'uRotateYMatrix');
program.uRotateZMatrixUniform = gl.getUniformLocation(program, 'uRotateZMatrix');
program.uScaleMatrixUniform = gl.getUniformLocation(program, 'uScaleMatrix');
program.uTranslateMatrixUniform = gl.getUniformLocation(program, 'uTranslateMatrix');

program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord");
gl.enableVertexAttribArray(program.textureCoordAttribute);

program.samplerUniform = gl.getUniformLocation(program, 'uSampler')
}

op.setBuffer = function(bufferName, bufferData, isIndexBuffer, itemSize){

var gl = op.gl
var buffer = gl.createBuffer()
gl[bufferName] = buffer

if(isIndexBuffer) {
var BUFFER_TYPE = gl.ELEMENT_ARRAY_BUFFER
} else {
var BUFFER_TYPE = gl.ARRAY_BUFFER
}
gl.bindBuffer(BUFFER_TYPE, buffer)
gl.bufferData(BUFFER_TYPE, bufferData, gl.STATIC_DRAW)
gl.bindBuffer(BUFFER_TYPE, null)

buffer.itemSize = itemSize
buffer.numItem = bufferData.length/itemSize
buffer.bufferType = BUFFER_TYPE

return buffer
}

op.intBuffer = function(setCallback){
setCallback()
}

op.projection = function(angle, a, zMin, zMax) {

var ang = Math.tan((angle*.5)*Math.PI/180);
var matrix = [
0.5/ang, 0 , 0, 0,
0, 0.5*a/ang, 0, 0,
0, 0, -(zMax+zMin)/(zMax-zMin), -1,
0, 0, (-2*zMax*zMin)/(zMax-zMin), 0
]

op.pMatrix = matrix
}

var matrixobj = function(mobj = null){

if(mobj == null) {
this.translateMatrix = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]

this.scaleMatrix = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]

this.rotateYMatrix = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]

this.rotateXMatrix = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]

this.rotateZMatrix = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]
} else {
this.translateMatrix = mobj.translateMatrix

this.scaleMatrix = mobj.scaleMatrix

this.rotateYMatrix = mobj.rotateYMatrix

this.rotateXMatrix = mobj.rotateXMatrix

this.rotateZMatrix = mobj.rotateZMatrix
}

}

matrixobj.prototype.translate = function(x,y,z){

this.translateMatrix[12]+=x
this.translateMatrix[13]+=y
this.translateMatrix[14]+=z

}

matrixobj.prototype.translateReverse = function(){

this.translateMatrix[12]=this.translateMatrix[12]*-1
this.translateMatrix[13]=this.translateMatrix[13]*-1
this.translateMatrix[14]=this.translateMatrix[14]*-1

}

matrixobj.prototype.translateRelative = function(x,y,z){

glmatrix = new matrixobj
glmatrix.translate(x,y,z)

this.translateMatrix = multiply([], this.translateMatrix, glmatrix.translateMatrix)
}

matrixobj.prototype.scale = function(scale){
var matrix = [
scale, 0, 0, 0,
0, scale, 0,0,
0,0,scale,0,
0,0,0,1
]
this.scaleMatrix = matrix
}

matrixobj.prototype.rotateX = function(deg){
var rad, mcos, msin;

rad=deg*Math.PI/180

msin=Math.sin(rad)
mcos=Math.cos(rad)

var matrix = [
1, 0,0,0,
0, mcos,msin,0,
0, -msin, mcos, 0,
0,0,0,1
]

this.rotateXMatrix = matrix

}

matrixobj.prototype.rotateY = function(deg){
var rad, mcos, msin;
rad=deg*Math.PI/180
mcos=Math.cos(rad)
msin=Math.sin(rad)
var matrix = [
mcos, 0, -msin, 0,
0, 1, 0, 0,
msin, 0 , mcos, 0,
0, 0, 0, 1
]
this.rotateYMatrix = matrix
}

matrixobj.prototype.rotateYRelative = function(deg){

var rad, mcos, msin;
rad=deg*Math.PI/180
mcos=Math.cos(rad)
msin=Math.sin(rad)
var matrix = [
mcos, 0, -msin, 0,
0, 1, 0, 0,
msin, 0 , mcos, 0,
0, 0, 0, 1
]
this.scaleMatrix = multiply([], this.scaleMatrix, matrix)
}

matrixobj.prototype.rotateZ = function(deg){
var rad, mcos, msin;
rad=deg*Math.PI/180
mcos=Math.cos(rad)
msin=Math.sin(rad)
var matrix = [
mcos, msin,0,0,
-msin, mcos,0,0,
0, 0 , 1, 0,
0,0,0,1
]
this.rotateZMatrix = matrix
}

matrixobj.prototype.equals = function(matrixobj2){

if( equals(this.translateMatrix, matrixobj2.translateMatrix) &&
equals(this.scaleMatrix, matrixobj2.scaleMatrix) &&
equals(this.rotateYMatrix, matrixobj2.rotateYMatrix) &&
equals(this.rotateXMatrix, matrixobj2.rotateXMatrix) &&
equals(this.rotateZMatrix, matrixobj2.rotateZMatrix)
) {
return true;
} else {
return false;
}

}

function multiply(out, a, b) {
let a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
let a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
let a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];

// Cache only the current line of the second matrix
let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
out[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
out[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
out[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
out[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33;

b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7];
out[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
out[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
out[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
out[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33;

b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11];
out[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
out[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
out[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
out[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33;

b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15];
out[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30;
out[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31;
out[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32;
out[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33;
return out;
}

function equals(a, b) {
glMatrix = {}
glMatrix.EPSILON = 0.000001
let a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
let a4 = a[4], a5 = a[5], a6 = a[6], a7 = a[7];
let a8 = a[8], a9 = a[9], a10 = a[10], a11 = a[11];
let a12 = a[12], a13 = a[13], a14 = a[14], a15 = a[15];

let b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
let b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7];
let b8 = b[8], b9 = b[9], b10 = b[10], b11 = b[11];
let b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15];

return (Math.abs(a0 - b0) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
Math.abs(a1 - b1) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
Math.abs(a2 - b2) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a2), Math.abs(b2)) &&
Math.abs(a3 - b3) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a3), Math.abs(b3)) &&
Math.abs(a4 - b4) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a4), Math.abs(b4)) &&
Math.abs(a5 - b5) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a5), Math.abs(b5)) &&
Math.abs(a6 - b6) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a6), Math.abs(b6)) &&
Math.abs(a7 - b7) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a7), Math.abs(b7)) &&
Math.abs(a8 - b8) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a8), Math.abs(b8)) &&
Math.abs(a9 - b9) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a9), Math.abs(b9)) &&
Math.abs(a10 - b10) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a10), Math.abs(b10)) &&
Math.abs(a11 - b11) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a11), Math.abs(b11)) &&
Math.abs(a12 - b12) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a12), Math.abs(b12)) &&
Math.abs(a13 - b13) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a13), Math.abs(b13)) &&
Math.abs(a14 - b14) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a14), Math.abs(b14)) &&
Math.abs(a15 - b15) <= glMatrix.EPSILON*Math.max(1.0, Math.abs(a15), Math.abs(b15)));
}

op.setPMVMatrixUniformToProgram = function(mobj){
var gl = op.gl
var program = gl.program
if(!op.pMatrix) {
var angle = 45
op.projection(angle, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0);
}

gl.uniformMatrix4fv(program.pMatrixUniform, false, new Float32Array(op.pMatrix))
gl.uniformMatrix4fv(program.uRotateZMatrixUniform, false, new Float32Array(mobj.rotateZMatrix))
gl.uniformMatrix4fv(program.uRotateXMatrixUniform, false, new Float32Array(mobj.rotateXMatrix))
gl.uniformMatrix4fv(program.uRotateYMatrixUniform, false, new Float32Array(mobj.rotateYMatrix))
gl.uniformMatrix4fv(program.uScaleMatrixUniform, false, new Float32Array(mobj.scaleMatrix))
gl.uniformMatrix4fv(program.uTranslateMatrixUniform, false, new Float32Array(mobj.translateMatrix))
}

op.setBufferAttribToProgram = function(buffer, attribute, isTexture = false, texture){
var gl = op.gl
var program = gl.program

gl.bindBuffer(buffer.bufferType, buffer)
gl.vertexAttribPointer(attribute, buffer.itemSize, gl.FLOAT, false, 0, 0)
if(isTexture) {
gl.activeTexture(gl.TEXTURE0)
gl.bindTexture(gl.TEXTURE_2D, texture)
gl.uniform1i(program.samplerUniform, 0)
}
}

op.handleLoadedTexture = function(texture){
var gl = op.gl
gl.bindTexture(gl.TEXTURE_2D, texture)
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.bindTexture(gl.TEXTURE_2D, null)
}

op.createTexture = function(src){
var gl = op.gl
var texture = gl.createTexture()
texture.image = new Image()
texture.image.onload = function(){
op.handleLoadedTexture(texture)
}
texture.image.src = src

return texture

}
var trans_x=0
var trans_y=0
var trans_z=0

var lastTime = 0;

mobj = new matrixobj
console.log(mobj)

var objx = 0
var objxt = false;
var objz = 0
var objzt = false;
var user = Math.random()

var last_self = {user:user, cp:[0,0,0]};
op.drawScene = function(){

var gl = op.gl
var program = gl.program

if(gl.worldVertexTextureCoordBuffer == null || ! gl.worldVertexPositionBuffer == null) {
return
}

gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);

gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

var angle = 90
op.projection(angle, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0);

mobj = new matrixobj
mobj.translate(-xPos, -0.4, -zPos);
mobj.rotateY(-yaw)

op.setBufferAttribToProgram(gl.worldVertexTextureCoordBuffer, program.textureCoordAttribute, true,op.mtexture)
op.setBufferAttribToProgram(gl.worldVertexPositionBuffer, program.vertexPositionAttribute)
op.setPMVMatrixUniformToProgram(mobj)

gl.drawArrays(gl.TRIANGLES, 0, gl.worldVertexPositionBuffer.numItems);

if(otheruser.length>0) {
for(var i in otheruser) {
if(otheruser[i].user !=user){

mobjuser = new matrixobj(otheruser[i].wobj)

mobjuser.translateRelative(-xPos, 0.1, -zPos);

mobjuser.rotateY(-yaw)

mobjuser.scale(0.1)

mobjuser.rotateYRelative(-otheruser[i].cp[3])

op.setPMVMatrixUniformToProgram(mobjuser)

op.setBufferAttribToProgram(gl.squareVertexTextureCoordBuffer, program.textureCoordAttribute, true,op.nehetexture)
op.setBufferAttribToProgram(gl.squareVertexPositionBuffer, program.vertexPositionAttribute)
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.squareVertexIndexBuffer)
gl.drawElements(gl.TRIANGLES, gl.squareVertexIndexBuffer.numItem, gl.UNSIGNED_SHORT, 0)

}

}
}

/*
mobj_self = new matrixobj
mobj_self.translate(0, -0.2, -0.2);
mobj_self.rotateZ(10)
mobj_self.rotateX(10)
mobj_self.scale(0.05)
op.setPMVMatrixUniformToProgram(mobj_self)
op.setBufferAttribToProgram(gl.squareVertexTextureCoordBuffer, program.textureCoordAttribute, true,op.nehetexture)
op.setBufferAttribToProgram(gl.squareVertexPositionBuffer, program.vertexPositionAttribute)
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.squareVertexIndexBuffer)
gl.drawElements(gl.TRIANGLES, gl.squareVertexIndexBuffer.numItem, gl.UNSIGNED_SHORT, 0)
*/
usercp = {}
usercp.wobj = mobj;
usercp.wobj.translateMatrix[12] = usercp.wobj.translateMatrix[12] *-1
usercp.wobj.translateMatrix[14] = usercp.wobj.translateMatrix[14] *-1

usercp.user = user
usercp.cp = [-xPos, -0.4, -zPos, -yaw]
if(last_self) {
if(last_self.cp[0] !=-xPos || last_self.cp[2] !=-zPos|| last_self.cp[3] !=-yaw ) {
console.log('start send', usercp)
ws.send(JSON.stringify(usercp))
}
}

last_self = JSON.parse(JSON.stringify(usercp))
}

</script>
</head>
<body>
<canvas id="my_Canvas" width="500" height="500" ></canvas>

<script>

var clearColor = [1.0, 1.0, 1.0, 1.0];

var setBuffers = function(){
op.setBuffer('squareVertexPositionBuffer', new Float32Array([
-1,-1,-1, 1,-1,-1, 1, 1,-1, -1, 1,-1,
-1,-1, 1, 1,-1, 1, 1, 1, 1, -1, 1, 1,
-1,-1,-1, -1, 1,-1, -1, 1, 1, -1,-1, 1,
1,-1,-1, 1, 1,-1, 1, 1, 1, 1,-1, 1,
-1,-1,-1, -1,-1, 1, 1,-1, 1, 1,-1,-1,
-1, 1,-1, -1, 1, 1, 1, 1, 1, 1, 1,-1,
]), false, 3)

op.setBuffer('squareVertexIndexBuffer', new Uint16Array([
0,1,2, 0,2,3, 4,5,6, 4,6,7,
8,9,10, 8,10,11, 12,13,14, 12,14,15,
16,17,18, 16,18,19, 20,21,22, 20,22,23
]), true, 1)

op.setBuffer('squareVertexTextureCoordBuffer', new Float32Array([
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
0.0, 1.0,
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
1.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
]), false, 2)

}

op.loadWorld = function(){
//引用自hiwebgl.com的world.txt文件
op.handleLoadedWorld(`

NUMPOLLIES 36

// Floor 1
-3.0 0.0 -3.0 0.0 6.0
-3.0 0.0 3.0 0.0 0.0
3.0 0.0 3.0 6.0 0.0

-3.0 0.0 -3.0 0.0 6.0
3.0 0.0 -3.0 6.0 6.0
3.0 0.0 3.0 6.0 0.0

// Ceiling 1
-3.0 1.0 -3.0 0.0 6.0
-3.0 1.0 3.0 0.0 0.0
3.0 1.0 3.0 6.0 0.0
-3.0 1.0 -3.0 0.0 6.0
3.0 1.0 -3.0 6.0 6.0
3.0 1.0 3.0 6.0 0.0

// A1

-2.0 1.0 -2.0 0.0 1.0
-2.0 0.0 -2.0 0.0 0.0
-0.5 0.0 -2.0 1.5 0.0
-2.0 1.0 -2.0 0.0 1.0
-0.5 1.0 -2.0 1.5 1.0
-0.5 0.0 -2.0 1.5 0.0

// A2

2.0 1.0 -2.0 2.0 1.0
2.0 0.0 -2.0 2.0 0.0
0.5 0.0 -2.0 0.5 0.0
2.0 1.0 -2.0 2.0 1.0
0.5 1.0 -2.0 0.5 1.0
0.5 0.0 -2.0 0.5 0.0

// B1

-2.0 1.0 2.0 2.0 1.0
-2.0 0.0 2.0 2.0 0.0
-0.5 0.0 2.0 0.5 0.0
-2.0 1.0 2.0 2.0 1.0
-0.5 1.0 2.0 0.5 1.0
-0.5 0.0 2.0 0.5 0.0

// B2

2.0 1.0 2.0 2.0 1.0
2.0 0.0 2.0 2.0 0.0
0.5 0.0 2.0 0.5 0.0
2.0 1.0 2.0 2.0 1.0
0.5 1.0 2.0 0.5 1.0
0.5 0.0 2.0 0.5 0.0

// C1

-2.0 1.0 -2.0 0.0 1.0
-2.0 0.0 -2.0 0.0 0.0
-2.0 0.0 -0.5 1.5 0.0
-2.0 1.0 -2.0 0.0 1.0
-2.0 1.0 -0.5 1.5 1.0
-2.0 0.0 -0.5 1.5 0.0

// C2

-2.0 1.0 2.0 2.0 1.0
-2.0 0.0 2.0 2.0 0.0
-2.0 0.0 0.5 0.5 0.0
-2.0 1.0 2.0 2.0 1.0
-2.0 1.0 0.5 0.5 1.0
-2.0 0.0 0.5 0.5 0.0

// D1

2.0 1.0 -2.0 0.0 1.0
2.0 0.0 -2.0 0.0 0.0
2.0 0.0 -0.5 1.5 0.0
2.0 1.0 -2.0 0.0 1.0
2.0 1.0 -0.5 1.5 1.0
2.0 0.0 -0.5 1.5 0.0

// D2

2.0 1.0 2.0 2.0 1.0
2.0 0.0 2.0 2.0 0.0
2.0 0.0 0.5 0.5 0.0
2.0 1.0 2.0 2.0 1.0
2.0 1.0 0.5 0.5 1.0
2.0 0.0 0.5 0.5 0.0

// Upper hallway - L
-0.5 1.0 -3.0 0.0 1.0
-0.5 0.0 -3.0 0.0 0.0
-0.5 0.0 -2.0 1.0 0.0
-0.5 1.0 -3.0 0.0 1.0
-0.5 1.0 -2.0 1.0 1.0
-0.5 0.0 -2.0 1.0 0.0

// Upper hallway - R
0.5 1.0 -3.0 0.0 1.0
0.5 0.0 -3.0 0.0 0.0
0.5 0.0 -2.0 1.0 0.0
0.5 1.0 -3.0 0.0 1.0
0.5 1.0 -2.0 1.0 1.0
0.5 0.0 -2.0 1.0 0.0

// Lower hallway - L
-0.5 1.0 3.0 0.0 1.0
-0.5 0.0 3.0 0.0 0.0
-0.5 0.0 2.0 1.0 0.0
-0.5 1.0 3.0 0.0 1.0
-0.5 1.0 2.0 1.0 1.0
-0.5 0.0 2.0 1.0 0.0

// Lower hallway - R
0.5 1.0 3.0 0.0 1.0
0.5 0.0 3.0 0.0 0.0
0.5 0.0 2.0 1.0 0.0
0.5 1.0 3.0 0.0 1.0
0.5 1.0 2.0 1.0 1.0
0.5 0.0 2.0 1.0 0.0

// Left hallway - Lw

-3.0 1.0 0.5 1.0 1.0
-3.0 0.0 0.5 1.0 0.0
-2.0 0.0 0.5 0.0 0.0
-3.0 1.0 0.5 1.0 1.0
-2.0 1.0 0.5 0.0 1.0
-2.0 0.0 0.5 0.0 0.0

// Left hallway - Hi

-3.0 1.0 -0.5 1.0 1.0
-3.0 0.0 -0.5 1.0 0.0
-2.0 0.0 -0.5 0.0 0.0
-3.0 1.0 -0.5 1.0 1.0
-2.0 1.0 -0.5 0.0 1.0
-2.0 0.0 -0.5 0.0 0.0

// Right hallway - Lw

3.0 1.0 0.5 1.0 1.0
3.0 0.0 0.5 1.0 0.0
2.0 0.0 0.5 0.0 0.0
3.0 1.0 0.5 1.0 1.0
2.0 1.0 0.5 0.0 1.0
2.0 0.0 0.5 0.0 0.0

// Right hallway - Hi

3.0 1.0 -0.5 1.0 1.0
3.0 0.0 -0.5 1.0 0.0
2.0 0.0 -0.5 0.0 0.0
3.0 1.0 -0.5 1.0 1.0
2.0 1.0 -0.5 0.0 1.0
2.0 0.0 -0.5 0.0 0.0
`)
/*
var request = new XMLHttpRequest();
request.open("GET", "world.txt");
request.onreadystatechange = function () {
if (request.readyState == 4) {
op.handleLoadedWorld(request.responseText);
}
}
request.send();*/
}

op.handleLoadedWorld = function (data) {
var worldVertexPositionBuffer = null;
var worldVertexTextureCoordBuffer = null;

var gl = op.gl
var lines = data.split("\n");
var vertexCount = 0;
var vertexPositions = [];
var vertexTextureCoords = [];
for (var i in lines) {
var vals = lines[i].replace(/^\s+/, "").split(/\s+/);

if (vals.length == 5 && vals[0] != "//") {
// It is a line describing a vertex; get X, Y and Z first
vertexPositions.push(parseFloat(vals[0]));
vertexPositions.push(parseFloat(vals[1]));
vertexPositions.push(parseFloat(vals[2]));
// And then the texture coords
vertexTextureCoords.push(parseFloat(vals[3]));
vertexTextureCoords.push(parseFloat(vals[4]));
vertexCount += 1;
}
}

worldVertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, worldVertexPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexPositions), gl.STATIC_DRAW);
worldVertexPositionBuffer.itemSize = 3;
worldVertexPositionBuffer.numItems = vertexCount;
worldVertexPositionBuffer.bufferType = gl.ARRAY_BUFFER

worldVertexTextureCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, worldVertexTextureCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexTextureCoords), gl.STATIC_DRAW);
worldVertexTextureCoordBuffer.itemSize = 2;
worldVertexTextureCoordBuffer.numItems = vertexCount;
worldVertexTextureCoordBuffer.bufferType = gl.ARRAY_BUFFER

gl.worldVertexTextureCoordBuffer = worldVertexTextureCoordBuffer
gl.worldVertexPositionBuffer = worldVertexPositionBuffer
}

op.intCanvas('my_Canvas')
op.intGL()
op.intShader()
op.intProgram()
op.intBuffer(setBuffers)
//引用自hiwebgl.com的纹理
textimgdata = "data:image/gif;base64,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"

op.nehetexture = op.createTexture(textimgdata)
op.mtexture = op.createTexture(textimgdata)
op.loadWorld()

op.gl.clearColor(...clearColor)
op.gl.enable(op.gl.DEPTH_TEST)

var speed = 0
var yawRate = 0
var yaw = 0;
var xPos = 0;
var yPos = 0.4;
var zPos = 0;

op.handleUserOp = function(){
if(key_sw['ArrowUp']) {
speed= 0.1
}
else if(key_sw['ArrowDown']) {
speed= -0.1
} else {
speed = 0
}

if(key_sw['ArrowLeft']) {
yawRate = 0.1;
mov_x_length+=2;
}else if(key_sw['ArrowRight']) {
yawRate = -0.1;
mov_x_length-=2;
} else {
yawRate = 0
}

if(key_sw['PageUp']) {
trans_y+=0.05;
}

if(key_sw['PageDown']) {
trans_y-=0.05;
}
}

var xjd=0,yjd=0,zjd=0

var moving = false
var mov_x_length = 0
var mov_y_length = 0
var page_x=0;
var page_y=0;

document.onmousedown = function(e){
moving = true
page_x = e.x
page_y = e.y
}

document.onmouseup = function(e) {
moving = false
}

document.onmousemove = function(e) {
if(moving) {
mov_x_length+=page_x-e.x
mov_y_length+=page_y-e.y
page_x = e.x
page_y = e.y
}
}

var key_sw = []
var zoom=0;

document.onkeydown = function(e) {
key_sw[e.key] = true
}

document.onkeyup = function(e) {
key_sw[e.key] = false
}

var lastTime = 0;

var otheruser = []

var server = prompt('请您输入Websocket服务地址','ws://127.0.0.1:8001')

var ws = new WebSocket(server);
try {
ws.onopen = function(e){
console.log("连接服务器成功");

animate = function(){
canvas = op.canvas
gl = op.gl
gl.viewportWidth = canvas.width = innerWidth
gl.viewportHeight = canvas.height = innerHeight

op.handleUserOp()
op.drawScene()

var timeNow = new Date().getTime();
if (lastTime != 0) {
var elapsed = timeNow - lastTime;

if (speed != 0) { //是否前进/后退(平移z,需要重新计算model x,y ,用当前x减去 移动过的比值)
xPos-= Math.sin(yaw * Math.PI/180) * speed * 1;
zPos-= Math.cos(yaw * Math.PI/180) * speed * 1;

//joggingAngle += elapsed * 0.6; // 0.6 "fiddle factor" - makes it feel more realistic :-)
//yPos = Math.sin(degToRad(joggingAngle)) / 20 + 0.4
}

yaw += yawRate * 5;
//console.log('yaw',yaw)
}
lastTime = timeNow;

window.requestAnimationFrame(animate)

}

animate()
}
ws.onclose = function(e){
alert("服务器关闭");
}
ws.onerror = function(){
alert("连接出错");
}

ws.onmessage = function(e){

usermodel = JSON.parse(e.data)

var time = new Date();
if(usermodel.user != user) {

if(otheruser.length<=0) {
console.log('push user')
otheruser.push(usermodel)
} else {
var f = false
for(var i in otheruser) {
if(otheruser[i].user == usermodel.user) {
f=true

otheruser[i] = usermodel
break
}
}
if(!f){
otheruser.push(usermodel)
}

}
console.log(otheruser)
//console.log(time+"的消息:"+usermodel.user)
}

}

} catch(e) {
alert('出现异常')
}

</script>
</body>
</html>

WebGL学习笔记(4)的更多相关文章

  1. webgl学习笔记五-纹理

    写在前面 建议先阅读下前面我的三篇文章. webgl学习笔记一-绘图单点 webgl学习笔记二-绘图多点 webgl学习笔记三-平移旋转缩放 术语 : 纹理 :图像 图形装配区域 :顶点着色器顶点坐标 ...

  2. webgl学习笔记四-动画

    写在前面 建议先阅读下前面我的三篇文章. webgl学习笔记一-绘图单点 webgl学习笔记二-绘图多点 webgl学习笔记三-平移旋转缩放   下面我们将讲解下如何让一个正方形动起来~不断擦除和重绘 ...

  3. webgl学习笔记三-平移旋转缩放

    写在前面 建议先阅读下前面我的两篇文章. webgl学习笔记一-绘图单点 webgl学习笔记二-绘图多点 平移 1.关键点说明 顶点着色器需要加上 uniform vec4 u_Translation ...

  4. webgl学习笔记二-绘图多点

    写在前面 建议先看下第一篇webgl学习笔记一-绘图单点 第一篇文章,介绍了如何用webgl绘图一个点.接下来本文介绍的是如何绘制多个点.形成一个面. webgl提供了一种很方便的机制,即缓冲区对象, ...

  5. WebGL学习笔记二——绘制基本图元

    webGL的基本图元点.线.三角形 gl.drawArrays(mode, first,count) first,代表从第几个点开始绘制即顶点的起始位置 count,代表绘制的点的数量. mode,代 ...

  6. WebGL学习笔记(2)

    根据上一篇学习笔记,理解WebGL的工作原理及基本调用代码后,可以开始研究3D顶点对象的缩放.旋转.以及对对象进行可交互(鼠标或键盘控制)的旋转或者缩放. 要理解对象的旋转/缩放需要首先学习矩阵的计算 ...

  7. WEBGL学习笔记(七):实践练手1-飞行类小游戏之游戏控制

    接上一节,游戏控制首先要解决的就是碰撞检测了 这里用到了学习笔记(三)射线检测的内容了 以鸟为射线原点,向前.上.下分别发射3个射线,射线的长度较短大概为10~30. 根据上一节场景的建设,我把y轴设 ...

  8. WebGL学习笔记(3)

    根据上篇笔记,在对3D对象可进行普通的控制后,以及学习了http://hiwebgl.com的教程第10章内容:世界模型的载入以及控制镜头移动,经过多次调试矩阵代码,已经可以实现在世界中旋转镜头/控制 ...

  9. WebGL学习笔记七点一

    第六章讲的是一些GL的一些语法,前面已经涉及,学习时直接跳过,来看第七章,第七章是真正意义的三维立体的出现,其实图形绘制方法是差不多的,就是Z坐标此时不再为0,所以很容易能构造出一些立体图形,但是立体 ...

  10. WebGL学习笔记一

    学习用来做web3D的,从第一页开始学起先做2D的,接下来的程序是一个入门级的程序,可以通过在画板上的不同位置点击而获取不同颜色的点,以画板中心为坐标原点四个象限有不同的颜色,访问地址  http:/ ...

随机推荐

  1. 使用vue-router切换页面时,获取上一页url以及当前页面url

    今天在实现一个小功能的时候,遇到一个问题,使用vue-router获取上一页面的url信息,我尝试了多种方式,发现使用vue-router的canDeactivate钩子实现这个功能最为方便,现在将我 ...

  2. jQuery二——属性操作、文档操作、位置属性

    一.jquery的属性操作 jquery对象有它自己的属性和方法. 其中jquery的属性操作模块分为四个部分:html属性操作,dom属性操作,类样式操作和值操作. 1.html属性操作 是对htm ...

  3. vue loading组件

    <!-- * * loadingGif组件--"数据请求中" * * 使用方法: * <loading-gif :show-loading="showLoad ...

  4. JavaWeb_01_html基本学习

    *:first-child { margin-top: 0 !important; } .markdown-body>*:last-child { margin-bottom: 0 !impor ...

  5. Office 365系列(三) -Office 365 Pro plus 安装

    这一篇博客主要是说Office 365 Pro plus安装. 1. 当登陆到Office 365以后,点击右边链接“下载软件” 2. 安装最新Office 软件 3. 采用点对点安装,当安装成功以后 ...

  6. Android Studio笔记之快捷键

    Android Studio h2{ color: #4abcde; } pre{ background-color: #f8f8f8; border: solid 1px #ccc; border- ...

  7. 多线程(八)~ThreadLocal、InheritableThreadLocal的使用

    通过前面的学习,我们了解了在多线程+成员变量等情况下会出现线程安全的问题.那么解决线程安全问题除了使用synchronize关键字之外,还有另一种常用的解决思路,那就是使用ThreadLocal类,下 ...

  8. 通过 Powershell 来替换 ARM 模式下虚拟机的网络接口

    需求描述 客户在部署完 ARM 模式的虚拟机以后,由于误操作在虚拟机内部禁用了网卡导致远程访问虚拟机受到限制,以下是通过 Powershell 命令来替换原有虚拟网络接口实现虚拟网卡重置功能. Not ...

  9. awk使用实例一则

    $META_DB -N -e "use web_boss_rainbow; select iDsId, sDbname, sHost, sPort, sNameServiceKey,sDri ...

  10. thinkphp5设置403 404等http状态页面

    在thinkphp5中如何抛出异常状态码(比如401,403,404等),因为这些能极大的给用户以良好的体验. 因为在上线阶段,任何的系统错误信息都不能让浏览用户给看到,比如404(Not Found ...