前几天解决了原生WebGL开发中的一个问题,就是在一个场景中绘制多个几何网格特征不同的模型,比如本文所做的绘制多个圆锥和圆柱在同一个场景中,今天抽空把解决的办法记录下来,同时也附上代码。首先声明,圆柱和圆锥的网格生成是我自己写的polyhedron.js模块,如果要加载其他模型,只需要把geometry换成其他几何体的网格即可,本文的重点不在于使用什么几何模型,而在于如何将各种不同的模型绘制到同一个场景中去。

第一件事,我们还是先把依赖的模型生成的js文件贴出来,以便参考者能够将代码组装起来。首先看工程目录结构,如下图

该工程实际用到的文件就是这4个红框框出的文件,其中除了jquery-2.1.4.min.js以外(请于cdn或百度或51cto站点自行下载),剩下的minMatrix.js,polyhedron.js,testCoordinates.html本作都将贴出完整的源码,以便参考者本地调试。

首先贴出minMatrix.js源码,minMatrix.js负责向量矩阵的运算,由于不是本文的中心论点,故不做赘述,如下所示

// ------------------------------------------------------------------------------------------------
// minMatrix.js
// version 0.0.1
// Copyright (c) doxas
// ------------------------------------------------------------------------------------------------ function matIV(){
this.create = function(){
return new Float32Array(16);
};
this.identity = function(dest){
dest[0] = 1; dest[1] = 0; dest[2] = 0; dest[3] = 0;
dest[4] = 0; dest[5] = 1; dest[6] = 0; dest[7] = 0;
dest[8] = 0; dest[9] = 0; dest[10] = 1; dest[11] = 0;
dest[12] = 0; dest[13] = 0; dest[14] = 0; dest[15] = 1;
return dest;
};
this.multiply = function(mat1, mat2, dest){
var a = mat1[0], b = mat1[1], c = mat1[2], d = mat1[3],
e = mat1[4], f = mat1[5], g = mat1[6], h = mat1[7],
i = mat1[8], j = mat1[9], k = mat1[10], l = mat1[11],
m = mat1[12], n = mat1[13], o = mat1[14], p = mat1[15],
A = mat2[0], B = mat2[1], C = mat2[2], D = mat2[3],
E = mat2[4], F = mat2[5], G = mat2[6], H = mat2[7],
I = mat2[8], J = mat2[9], K = mat2[10], L = mat2[11],
M = mat2[12], N = mat2[13], O = mat2[14], P = mat2[15];
dest[0] = A * a + B * e + C * i + D * m;
dest[1] = A * b + B * f + C * j + D * n;
dest[2] = A * c + B * g + C * k + D * o;
dest[3] = A * d + B * h + C * l + D * p;
dest[4] = E * a + F * e + G * i + H * m;
dest[5] = E * b + F * f + G * j + H * n;
dest[6] = E * c + F * g + G * k + H * o;
dest[7] = E * d + F * h + G * l + H * p;
dest[8] = I * a + J * e + K * i + L * m;
dest[9] = I * b + J * f + K * j + L * n;
dest[10] = I * c + J * g + K * k + L * o;
dest[11] = I * d + J * h + K * l + L * p;
dest[12] = M * a + N * e + O * i + P * m;
dest[13] = M * b + N * f + O * j + P * n;
dest[14] = M * c + N * g + O * k + P * o;
dest[15] = M * d + N * h + O * l + P * p;
return dest;
};
this.scale = function(mat, vec, dest){
dest[0] = mat[0] * vec[0];
dest[1] = mat[1] * vec[0];
dest[2] = mat[2] * vec[0];
dest[3] = mat[3] * vec[0];
dest[4] = mat[4] * vec[1];
dest[5] = mat[5] * vec[1];
dest[6] = mat[6] * vec[1];
dest[7] = mat[7] * vec[1];
dest[8] = mat[8] * vec[2];
dest[9] = mat[9] * vec[2];
dest[10] = mat[10] * vec[2];
dest[11] = mat[11] * vec[2];
dest[12] = mat[12];
dest[13] = mat[13];
dest[14] = mat[14];
dest[15] = mat[15];
return dest;
};
this.translate = function(mat, vec, dest){
dest[0] = mat[0]; dest[1] = mat[1]; dest[2] = mat[2]; dest[3] = mat[3];
dest[4] = mat[4]; dest[5] = mat[5]; dest[6] = mat[6]; dest[7] = mat[7];
dest[8] = mat[8]; dest[9] = mat[9]; dest[10] = mat[10]; dest[11] = mat[11];
dest[12] = mat[0] * vec[0] + mat[4] * vec[1] + mat[8] * vec[2] + mat[12];
dest[13] = mat[1] * vec[0] + mat[5] * vec[1] + mat[9] * vec[2] + mat[13];
dest[14] = mat[2] * vec[0] + mat[6] * vec[1] + mat[10] * vec[2] + mat[14];
dest[15] = mat[3] * vec[0] + mat[7] * vec[1] + mat[11] * vec[2] + mat[15];
return dest;
};
this.rotate = function(mat, angle, axis, dest){
var sq = Math.sqrt(axis[0] * axis[0] + axis[1] * axis[1] + axis[2] * axis[2]);
if(!sq){return null;}
var a = axis[0], b = axis[1], c = axis[2];
if(sq != 1){sq = 1 / sq; a *= sq; b *= sq; c *= sq;}
var d = Math.sin(angle), e = Math.cos(angle), f = 1 - e,
g = mat[0], h = mat[1], i = mat[2], j = mat[3],
k = mat[4], l = mat[5], m = mat[6], n = mat[7],
o = mat[8], p = mat[9], q = mat[10], r = mat[11],
s = a * a * f + e,
t = b * a * f + c * d,
u = c * a * f - b * d,
v = a * b * f - c * d,
w = b * b * f + e,
x = c * b * f + a * d,
y = a * c * f + b * d,
z = b * c * f - a * d,
A = c * c * f + e;
if(angle){
if(mat != dest){
dest[12] = mat[12]; dest[13] = mat[13];
dest[14] = mat[14]; dest[15] = mat[15];
}
} else {
dest = mat;
}
dest[0] = g * s + k * t + o * u;
dest[1] = h * s + l * t + p * u;
dest[2] = i * s + m * t + q * u;
dest[3] = j * s + n * t + r * u;
dest[4] = g * v + k * w + o * x;
dest[5] = h * v + l * w + p * x;
dest[6] = i * v + m * w + q * x;
dest[7] = j * v + n * w + r * x;
dest[8] = g * y + k * z + o * A;
dest[9] = h * y + l * z + p * A;
dest[10] = i * y + m * z + q * A;
dest[11] = j * y + n * z + r * A;
return dest;
}; this.lookAt = function(eye, center, up, dest){
var eyeX = eye[0], eyeY = eye[1], eyeZ = eye[2];
var centerX = center[0], centerY = center[1], centerZ = center[2];
var upX = up[0], upY = up[1], upZ = up[2]; if(eyeX == centerX && eyeY == centerY && eyeZ == centerZ){
return this.identity(dest);
} var x0, x1, x2, y0, y1, y2, z0, z1, z2, l; z0 = eyeX - centerX; z1 = eyeY - centerY; z2 = eyeZ - centerZ;
l = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
z0 *= l; z1 *= l; z2 *= l; x0 = upY * z2 - upZ * z1; x1 = upZ * z0 - upX * z2; x2 = upX * z1 - upY * z0;
l = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
if(!l){
x0 = 0; x1 = 0; x2 = 0;
} else {
l = 1 / l;
x0 *= l; x1 *= l; x2 *= l;
} y0 = z1 * x2 - z2 * x1; y1 = z2 * x0 - z0 * x2; y2 = z0 * x1 - z1 * x0;
l = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
if(!l){
y0 = 0; y1 = 0; y2 = 0;
} else {
l = 1 / l;
y0 *= l; y1 *= l; y2 *= l;
} dest[0] = x0; dest[1] = y0; dest[2] = z0; dest[3] = 0;
dest[4] = x1; dest[5] = y1; dest[6] = z1; dest[7] = 0;
dest[8] = x2; dest[9] = y2; dest[10] = z2; dest[11] = 0;
dest[12] = -(x0 * eyeX + x1 * eyeY + x2 * eyeZ);
dest[13] = -(y0 * eyeX + y1 * eyeY + y2 * eyeZ);
dest[14] = -(z0 * eyeX + z1 * eyeY + z2 * eyeZ);
dest[15] = 1;
return dest;
}; this.view = function(eye, at, up, dest){ var forward = [];
forward[0] = at[0] - eye[0];
forward[1] = at[1] - eye[1];
forward[2] = at[2] - eye[2];
var l = Math.sqrt(forward[0]*forward[0] + forward[1]*forward[1] + forward[2]*forward[2]);
forward[0] = forward[0]/l; forward[1] = forward[1]/l; forward[2] = forward[2]/l; var side = [];
side[0] = forward[2]*up[1] - up[2]*forward[1];
side[1] = forward[0]*up[2] - up[0]*forward[2];
side[2] = forward[1]*up[0] - up[1]*forward[0];
l = Math.sqrt(side[0]*side[0] + side[1]*side[1] + side[2]*side[2]);
side[0] = side[0]/l; side[1] = side[1]/l; side[2] = side[2]/l; up[0] = side[2]*forward[1] - forward[2]*side[1];
up[1] = side[0]*forward[2] - forward[0]*side[2];
up[2] = side[1]*forward[0] - forward[1]*side[0]; var dest = [];
dest[0] = side[0]; dest[1] = up[0]; dest[2] = -forward[0]; dest[3] = 0;
dest[4] = side[1]; dest[5] = up[1]; dest[6] = -forward[1]; dest[7] = 0;
dest[8] = side[2]; dest[9] = up[2]; dest[10]= -forward[2]; dest[11]= 0;
dest[12]= 0; dest[13]= 0; dest[14]= 0; dest[15]= 1; dest[0] = dest[0]; dest[1] = dest[1]; dest[2] = dest[2]; dest[3] = dest[3];
dest[4] = dest[4]; dest[5] = dest[5]; dest[6] = dest[6]; dest[7] = dest[7];
dest[8] = dest[8]; dest[9] = dest[9]; dest[10] = dest[10]; dest[11] = dest[11];
dest[12] = dest[0] * (-eye[0]) + dest[4] * (-eye[1]) + dest[8] * (-eye[2]) + dest[12];
dest[13] = dest[1] * (-eye[0]) + dest[5] * (-eye[1]) + dest[9] * (-eye[2]) + dest[13];
dest[14] = dest[2] * (-eye[0]) + dest[6] * (-eye[1]) + dest[10] * (-eye[2]) + dest[14];
dest[15] = dest[3] * (-eye[0]) + dest[7] * (-eye[1]) + dest[11] * (-eye[2]) + dest[15]; return dest;
} /**
* 正交投影
*/
this.ortho = function(left, right, bottom, top, near, far, dest){ var a = 2/(right-left);
var b = -(right+left)/(right-left);
var c = 2/(top-bottom);
var d = -(top+bottom)/(top-bottom);
var e = 2/(far-near);
var f = -(far+near)/(far-near); dest[0] = a; dest[1] = 0; dest[2] = 0; dest[3] = b;
dest[4] = 0; dest[5] = c; dest[6] = 0; dest[7] = d;
dest[8] = 0; dest[9] = 0; dest[10]= e; dest[11]= f;
dest[12]= 0; dest[13]= 0; dest[14]= 0; dest[15]= 1; return dest;
} /**
* 透视投影
*/
this.perspective = function(fovy, aspect, near, far, dest){
var t = near * Math.tan(fovy * Math.PI / 360);
var r = t * aspect;
var a = r * 2, b = t * 2, c = far - near;
dest[0] = near * 2 / a;
dest[1] = 0;
dest[2] = 0;
dest[3] = 0;
dest[4] = 0;
dest[5] = near * 2 / b;
dest[6] = 0;
dest[7] = 0;
dest[8] = 0;
dest[9] = 0;
dest[10] = -(far + near) / c;
dest[11] = -1;
dest[12] = 0;
dest[13] = 0;
dest[14] = -(far * near * 2) / c;
dest[15] = 0;
return dest;
};
this.transpose = function(mat, dest){
dest[0] = mat[0]; dest[1] = mat[4];
dest[2] = mat[8]; dest[3] = mat[12];
dest[4] = mat[1]; dest[5] = mat[5];
dest[6] = mat[9]; dest[7] = mat[13];
dest[8] = mat[2]; dest[9] = mat[6];
dest[10] = mat[10]; dest[11] = mat[14];
dest[12] = mat[3]; dest[13] = mat[7];
dest[14] = mat[11]; dest[15] = mat[15];
return dest;
};
this.inverse = function(mat, dest){
var a = mat[0], b = mat[1], c = mat[2], d = mat[3],
e = mat[4], f = mat[5], g = mat[6], h = mat[7],
i = mat[8], j = mat[9], k = mat[10], l = mat[11],
m = mat[12], n = mat[13], o = mat[14], p = mat[15],
q = a * f - b * e, r = a * g - c * e,
s = a * h - d * e, t = b * g - c * f,
u = b * h - d * f, v = c * h - d * g,
w = i * n - j * m, x = i * o - k * m,
y = i * p - l * m, z = j * o - k * n,
A = j * p - l * n, B = k * p - l * o,
ivd = 1 / (q * B - r * A + s * z + t * y - u * x + v * w);
dest[0] = ( f * B - g * A + h * z) * ivd;
dest[1] = (-b * B + c * A - d * z) * ivd;
dest[2] = ( n * v - o * u + p * t) * ivd;
dest[3] = (-j * v + k * u - l * t) * ivd;
dest[4] = (-e * B + g * y - h * x) * ivd;
dest[5] = ( a * B - c * y + d * x) * ivd;
dest[6] = (-m * v + o * s - p * r) * ivd;
dest[7] = ( i * v - k * s + l * r) * ivd;
dest[8] = ( e * A - f * y + h * w) * ivd;
dest[9] = (-a * A + b * y - d * w) * ivd;
dest[10] = ( m * u - n * s + p * q) * ivd;
dest[11] = (-i * u + j * s - l * q) * ivd;
dest[12] = (-e * z + f * x - g * w) * ivd;
dest[13] = ( a * z - b * x + c * w) * ivd;
dest[14] = (-m * t + n * r - o * q) * ivd;
dest[15] = ( i * t - j * r + k * q) * ivd;
return dest;
};
this.rotateByAnyAxis = function(RawMat, axis, rad, destMat){ };
}

接下来就要贴出几何体生成工具 polyhedron.js,我们用到的几何体是其中的圆锥 coneGeo和圆柱 cylinderGeo,这两个类都是PolyhedronGeometry的子类,几何体都包含顶点成员this.vertices,法向成员this.normals,索引成员this.faces。具体构造详见代码如下

/**
* Created by ccentry on 2018/10/15.
*/ /**
* 圆锥
* radius:底面半径
* height:圆锥高度
* meshDensity:网格密度
* */
var coneGeo = function(radius, height, segment){
//锥顶
var top = [0, height, 0];
//锥底,锥底半径radius
//根据segment来切割锥底圆
var sliceNum = segment || 3;
var rad = Math.PI*2/sliceNum;
var bottom = [];
for(var i=0; i<sliceNum; i++){
bottom[i*3] = radius*Math.cos(rad*i);
bottom[i*3 + 1] = 0;
bottom[i*3 + 2] = radius*Math.sin(rad*i);
}
//圆锥的顶点
this.vertices = [];
//顶点法向
this.normals = [];
//锥顶
for(var i=0; i<sliceNum; i++){
this.vertices[i*3] = top[0];
this.vertices[i*3+1] = top[1];
this.vertices[i*3+2] = top[2];
}
//锥面圆环
for(var i=0; i<bottom.length; i++){
this.vertices[3*sliceNum+i] = bottom[i];
}
//锥底圆环
for(var i=0; i<bottom.length; i++){
this.vertices[2*3*sliceNum+i] = bottom[i];
}
//锥底圆心
this.vertices.push(0, 0, 0);
//圆锥面索引
this.faces = [];
for(var i=sliceNum; i<2*sliceNum-1; i++){
//圆锥侧面
this.faces.push(i, i-sliceNum, i+1);
//圆锥底面
this.faces.push(3*sliceNum, sliceNum+i, sliceNum+i+1);
}
//补侧面
this.faces.push(2*sliceNum-1, sliceNum-1, sliceNum);
//补底面
this.faces.push(3*sliceNum, 3*sliceNum-1, 2*sliceNum);
//计算所有顶点法向
this.computeNormal4EachVertex();
}; /**
* 圆柱
* radius:底面半径
* height:圆柱高度
* meshDensity:网格密度
* */
var cylinderGeo = function(radius, height, segment){
radius = radius || 1;
height = height || 1;
//根据网格密度来切割圆柱体底圆
var sliceNum = segment || 3;
//底面圆弧度
var rad = Math.PI*2/sliceNum;
//底面圆环顶点
var bottomVertices = [];
//顶面顶点
var topVertices = [];
//切割底面圆和底面圆
for(var i=0; i<sliceNum; i++){
bottomVertices[3*i] = radius * Math.cos(rad * i);
bottomVertices[3*i+1] = 0;
bottomVertices[3*i+2] = radius * Math.sin(rad * i); topVertices[3*i] = radius * Math.cos(rad * i);
topVertices[3*i+1] = height;
topVertices[3*i+2] = radius * Math.sin(rad * i);
}
//圆柱的顶点
this.vertices = [];
for(var i=0; i<bottomVertices.length; i++){
//底面圆
this.vertices[i] = bottomVertices[i];
//顶面圆
this.vertices[bottomVertices.length + i] = topVertices[i];
//柱底圈
this.vertices[2*bottomVertices.length + i] = bottomVertices[i];
//柱顶圈
this.vertices[3*bottomVertices.length + i] = topVertices[i];
}
//底面圆心;
this.vertices.push(0, 0, 0);
//顶面圆心
this.vertices.push(0, height, 0);
//圆柱的面
this.faces = [];
for(var i=0; i<sliceNum-1; i++){
//底面圆
this.faces.push(4*sliceNum, i, i+1);
//顶面圆
this.faces.push(4*sliceNum+1, sliceNum+i+1, sliceNum+i);
//柱身
this.faces.push(2*sliceNum+i, 3*sliceNum+i, 2*sliceNum+i+1);
this.faces.push(3*sliceNum+i, 3*sliceNum+i+1, 2*sliceNum+i+1);
}
//补底面圆
this.faces.push(4*sliceNum, sliceNum-1, 0);
//补顶面圆
this.faces.push(4*sliceNum+1, sliceNum, 2*sliceNum-1);
//补柱身
this.faces.push(3*sliceNum-1, 4*sliceNum-1, 2*sliceNum);
this.faces.push(4*sliceNum-1, 3*sliceNum, 2*sliceNum);
//顶点法向
this.normals = [];
//计算所有顶点法向
this.computeNormal4EachVertex();
}; /**
* 球体
* radius:球体半径
* meshDensity:网格密度
* */
var sphereGeo = function(radius, widthSegments, heightSegments){
var phiStart = 0;
var phiLength = Math.PI * 2;
var thetaStart = 0;
var thetaLength = Math.PI;
var thetaEnd = thetaStart + thetaLength;
var ix, iy;
var index = 0;
var grid = [];
var vertex = {};
var normal = {};
//存储
var indices = [];
var vertices = [];
var normals = [];
var uvs = [];
//生成顶点,法向,uv
for ( iy = 0; iy <= heightSegments; iy ++ ) {
var verticesRow = [];
var v = iy / heightSegments;
for ( ix = 0; ix <= widthSegments; ix ++ ) {
var u = ix / widthSegments;
//顶点
vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
vertex.y = radius * Math.cos( thetaStart + v * thetaLength );
vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );
vertices.push( vertex.x, vertex.y, vertex.z );
//法向
normal.x = vertex.x; normal.y = vertex.y; normal.z = vertex.z;
//单位化
this.normalize([normal.x, normal.y, normal.z]);
normals.push( normal.x, normal.y, normal.z );
//uv
uvs.push( u, 1 - v );
verticesRow.push( index ++ );
}
grid.push( verticesRow );
}
//索引数组
for ( iy = 0; iy < heightSegments; iy ++ ) {
for ( ix = 0; ix < widthSegments; ix ++ ) {
var a = grid[ iy ][ ix + 1 ];
var b = grid[ iy ][ ix ];
var c = grid[ iy + 1 ][ ix ];
var d = grid[ iy + 1 ][ ix + 1 ];
if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d );
if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d );
}
}
//构造geometry
this.faces = indices;
this.vertices = vertices;
this.normals = normals;
//this.uv = uvs;
}; var cubeGeo = function(width, height, depth){
this.vertices = [];
this.faces = [];
var bottom = [];
bottom[0] = [width/2, 0, depth/2];
bottom[1] = [-width/2, 0, depth/2];
bottom[2] = [-width/2, 0, -depth/2];
bottom[3] = [width/2, 0, -depth/2];
var top = [];
top[0] = [width/2, height, depth/2];
top[1] = [-width/2, height, depth/2];
top[2] = [-width/2, height, -depth/2];
top[3] = [width/2, height, -depth/2];
for(var i=0; i<4; i++){
for(var j=0; j<3; j++){
this.vertices.push(bottom[i][j]);
}
}
for(var i=0; i<4; i++){
for(var j=0; j<3; j++){
this.vertices.push(top[i][j]);
}
}
for(var i=0; i<3; i++){
this.faces.push(i, i+4, i+1);
this.faces.push(i+4, i+5, i+1);
}
//补面
this.faces.push(3, 7, 0);
this.faces.push(7, 4, 0);
//补底面
this.faces.push(0, 1, 2, 2, 3, 0);
//补底面
this.faces.push(4, 7, 6, 6, 5, 4);
}; var PolyhedronGeometry = function(){
this.type = "PolyhedronGeometry";
}; PolyhedronGeometry.prototype = {
//坐标单位话
normalize : function(arr){
if(arr instanceof Array){
var sum = 0;
for(var i=0; i<arr.length; i++){
sum += arr[i]*arr[i];
}
for(var i=0; i<arr.length; i++){
arr[i] = arr[i]/sum;
}
}
},
//计算顶点法向
computeVertexNormal: function(face3){
//顶点逆时针绕向
//向量v1-v0
var v1_0 = {};
v1_0.x = face3[1][0] - face3[0][0];
v1_0.y = face3[1][1] - face3[0][1];
v1_0.z = face3[1][2] - face3[0][2];
//向量v2-v1
var v2_1 = {};
v2_1.x = face3[2][0] - face3[1][0];
v2_1.y = face3[2][1] - face3[1][1];
v2_1.z = face3[2][2] - face3[1][2];
//v1_0 叉乘 v2_1
var normal = {};
normal.x = v1_0.y * v2_1.z - v2_1.y * v1_0.z;
normal.y = v1_0.z * v2_1.x - v2_1.z * v1_0.x;
normal.z = v1_0.x * v2_1.y - v2_1.x * v1_0.y;
var normalArray = [normal.x, normal.y, normal.z];
this.normalize(normalArray);
return normalArray;
},
computeNormal4EachVertex:function(){
//遍历索引,通过hash插值构造normals数组
var normalList = [];
for(var i=0; i<this.faces.length; i=i+3){
//顶点索引
var vertex0 = this.faces[i];
var vertex1 = this.faces[i+1];
var vertex2 = this.faces[i+2];
//顶点
var v0 = [this.vertices[3*vertex0], this.vertices[3*vertex0+1], this.vertices[3*vertex0+2]];
var v1 = [this.vertices[3*vertex1], this.vertices[3*vertex1+1], this.vertices[3*vertex1+2]];
var v2 = [this.vertices[3*vertex2], this.vertices[3*vertex2+1], this.vertices[3*vertex2+2]];
//取出索引指向的顶点坐标
var face3 = [v0, v1, v2];
var normalArray = this.computeVertexNormal(face3);
var normal0 = {index:vertex0, normal:normalArray};
var normal1 = {index:vertex1, normal:normalArray};
var normal2 = {index:vertex2, normal:normalArray};
normalList.push(normal0, normal1, normal2);
}
//根据index属性排序
var sortedNormalList = [];
var total = normalList.length;
for(var i=0; i<total; i++){
for(var j=0; j<normalList.length; j++){
if(normalList[j].index === i){
sortedNormalList[i] = {index:normalList[j].index, normal:normalList[j].normal};
//删掉该normal节点
normalList.splice(j, 1);
break;
}
}
}
for(var i=0; i<sortedNormalList.length; i++){
var normal = sortedNormalList[i].normal;
this.normals.push(normal[0], normal[1], normal[2]);
}
}
}; coneGeo.prototype = new PolyhedronGeometry();
cylinderGeo.prototype = new PolyhedronGeometry();
sphereGeo.prototype = new PolyhedronGeometry();
cubeGeo.prototype = new PolyhedronGeometry();

以上就是几何体的构造函数,我们的圆锥和圆柱即将由此生成。接下来进入我们今天的重点,如何将多个圆锥圆柱绘制进一个场景中去,直接上html代码,场景代码如下

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=gb2312">
<script type="text/JavaScript" src="minMatrix.js"></script>
<script type="text/JavaScript" src="jquery-2.1.4.min.js"></script>
<script type="text/javascript" src="polyhedron.js"></script> <script id="vs" type="x-shader/x-vertex">
attribute vec3 position;
attribute vec3 normal;
attribute vec4 color;
uniform mat4 mvpMatrix;
uniform mat4 invMatrix;
uniform vec3 lightDirection;
uniform vec4 ambientColor;
varying vec4 vColor;
uniform float lightS; void main(void){
vec3 invLight = normalize(invMatrix * vec4(lightDirection, 0)).xyz;
float diffuse = clamp(dot(normal, invLight), 0.0, 1.0) * lightS;
vColor = color * vec4(vec3(diffuse), 1.0) + ambientColor;
gl_Position = mvpMatrix * vec4(position, 1.0);
}
</script> <script id="fs" type="x-shader/x-fragment">
precision mediump float;
varying vec4 vColor;
void main(void){
gl_FragColor = vColor;
}
</script> <script>
onload = function(){
// canvas对象获取
var c = document.getElementById('canvas');
c.width = 1000;
c.height = 800; // webgl的context获取
var gl = c.getContext('webgl') || c.getContext('experimental-webgl');
//初始化gl
initGL(gl);
// 顶点着色器和片段着色器的生成
var v_shader = create_shader('vs');
var f_shader = create_shader('fs');
// 程序对象的生成和连接
var prg = create_program(v_shader, f_shader); // attributeLocation的获取
var attLocation = new Array(2);
attLocation[0] = gl.getAttribLocation(prg, 'position');
attLocation[1] = gl.getAttribLocation(prg, 'normal');
attLocation[2] = gl.getAttribLocation(prg, 'color'); // 将元素数attribute保存到数组中
var attStride = new Array(2);
attStride[0] = 3;
attStride[1] = 3;
attStride[2] = 4; /**
* 光
* */
// 环境光,漫反射光
var ambientColor = [0.2, 0.2, 0.2, 1.0];
// 光照强度
var lightS = 1.6;
// 平行光源的方向
var lightDirection = [1, 1, 1]; /**
* 视图矩阵
*/
// matIV对象生成
var m = new matIV();
// 画布的宽高比
var aspect = c.width / c.height;
var mMatrix = m.identity(m.create());
var invMatrix = m.identity(m.create());
var tmpMatrix = m.identity(m.create());
var mvpMatrix = m.identity(m.create());
// 将视图矩阵根据宽高比进行反比,避免X/Y平面内出现变形
tmpMatrix[0] = 1/aspect;
// 得到mvpMatrix定位坐标矩阵
//m.multiply(tmpMatrix, mMatrix, mvpMatrix);
// mMatrix的逆矩阵
//m.inverse(mMatrix, invMatrix); /**
* 绑定shader的uniform
* */
// 取得uniformLocation
var uniLocation = new Array();
uniLocation[0] = gl.getUniformLocation(prg, 'mvpMatrix');
uniLocation[1] = gl.getUniformLocation(prg, 'invMatrix');
uniLocation[2] = gl.getUniformLocation(prg, 'lightDirection');
uniLocation[3] = gl.getUniformLocation(prg, 'ambientColor');
uniLocation[4] = gl.getUniformLocation(prg, 'lightS');
// 向uniformLocation中传入坐标变换矩阵
gl.uniformMatrix4fv(uniLocation[0], false, mvpMatrix);
gl.uniformMatrix4fv(uniLocation[1], false, invMatrix);
gl.uniform3fv(uniLocation[2], lightDirection);
gl.uniform4fv(uniLocation[3], ambientColor);
gl.uniform1f(uniLocation[4], lightS); //绘制坐标系
drawCoordinates(0, 1, 0, 0); // 清理gl
gl.flush(); /**
* 交互
* */ //判断是否鼠标左键按下
var mouseLeftKeyDown = false;
//判断是否鼠标右键按下
var mouseRightKeyDown = false;
//标记鼠标x坐标
var mouseX;
//标记鼠标y坐标
var mouseY;
//标记鼠标z坐标
var mouseZ;
//旋转球的半径
var R = 250; $('#canvas').mousedown(function(e){ var inCircle = false; if(e.which == 1){
mouseLeftKeyDown = true;
mouseRightKeyDown = false; mouseX = e.clientX - 0.5*c.width;
mouseY = -(e.clientY - 0.5*c.height);
if(R*R - mouseX*mouseX - mouseY*mouseY > 0){
mouseZ = Math.sqrt(R*R - mouseX*mouseX - mouseY*mouseY);
} else {
mouseLeftKeyDown = false;
} //转动前向量坐标
$('#cordX').val(mouseX);
$('#cordY').val(mouseY);
$('#cordZ').val(mouseZ); } else if(e.which == 3){
mouseRightKeyDown = true;
mouseLeftKeyDown = false;
}
}); $('#canvas').mouseup(function(e){
if(e.which == 1){
mouseLeftKeyDown = false;
} else if(e.which == 3){
mouseRightKeyDown = false;
}
}); $('#canvas').mouseout(function(e){
mouseLeftKeyDown = false;
mouseRightKeyDown = false;
}); $('#canvas').mousemove(function(e){
if(mouseLeftKeyDown){//鼠标左键按下 var X_1 = e.clientX - 0.5*c.width;
var Y_1 = -(e.clientY - 0.5*c.height);
if(R*R - X_1*X_1 - Y_1*Y_1 > 0){
var Z_1 = Math.sqrt(R*R - X_1*X_1 - Y_1*Y_1);
} else {
mouseLeftKeyDown = false;
} //转动前向量坐标
$('#cordX').val(mouseX);
$('#cordY').val(mouseY);
$('#cordZ').val(mouseZ); //转动后向量坐标
$('#cordX1').val(X_1);
$('#cordY1').val(Y_1);
$('#cordZ1').val(Z_1); //先算出转动轴向量
var axisX = -(Z_1*mouseY-mouseZ*Y_1);
var axisY = -(X_1*mouseZ-mouseX*Z_1);
var axisZ = +(Y_1*mouseX-mouseY*X_1);
//轴向量单位化
var mod_axis = Math.sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);
axisX = axisX/mod_axis;
axisY = axisY/mod_axis;
axisZ = axisZ/mod_axis; var a1 = mMatrix[0]*axisX + mMatrix[1]*axisY + mMatrix[2]*axisZ + mMatrix[3]*0;
var a2 = mMatrix[4]*axisX + mMatrix[5]*axisY + mMatrix[6]*axisZ + mMatrix[7]*0;
var a3 = mMatrix[8]*axisX + mMatrix[9]*axisY + mMatrix[10]*axisZ + mMatrix[11]*0;
var a4 = mMatrix[12]*axisX + mMatrix[13]*axisY + mMatrix[14]*axisZ + mMatrix[15]*0; axisX = a1;
axisY = a2;
axisZ = a3;
//轴向量单位化
mod_axis = Math.sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);
axisX = axisX/mod_axis;
axisY = axisY/mod_axis;
axisZ = axisZ/mod_axis; //法向坐标
$('#axisX').val(axisX);
$('#axisY').val(axisY);
$('#axisZ').val(axisZ); //计算转轴向量和转前向量的点积
$('#00').val(axisX*mouseX + axisY*mouseY + axisZ*mouseZ);
//计算转轴向量和转后向量的点积
$('#01').val(axisX*X_1 + axisY*Y_1 + axisZ*Z_1); //再计算转动角弧度
//a=(x1,y1,z1),b=(x2,y2,z2) a*b=x1x2+y1y2+z1z2 |a|=√(x1^2+y1^2+z1^2).|b|=√(x2^2+y2^2+z2^2)
//cosθ=a*b/(|a|*|b|) 角θ=arccosθ
//Math.acos(x) 反余弦函数
var ab = X_1*mouseX + Y_1*mouseY + Z_1*mouseZ;
var mod_a = Math.sqrt(X_1*X_1 + Y_1*Y_1 + Z_1*Z_1);
var mod_b = Math.sqrt(mouseX*mouseX + mouseY*mouseY + mouseZ*mouseZ);
var cosθ = ab/(mod_a*mod_b);
var rad = Math.acos(cosθ); //转角弧度
$('#rad').val(rad); rotateModel(axisX, axisY, axisZ, rad); mouseX = X_1;
mouseY = Y_1;
mouseZ = Z_1; }
}); $('#rotate').click(function(){
var axisX = $('.axisX').val();
var axisY = $('.axisY').val();
var axisZ = $('.axisZ').val();
var rad = $('.rad').val();
rad = rad * Math.PI / 180; var a1 = mMatrix[0]*axisX + mMatrix[1]*axisY + mMatrix[2]*axisZ + mMatrix[3]*0;
var a2 = mMatrix[4]*axisX + mMatrix[5]*axisY + mMatrix[6]*axisZ + mMatrix[7]*0;
var a3 = mMatrix[8]*axisX + mMatrix[9]*axisY + mMatrix[10]*axisZ + mMatrix[11]*0;
var a4 = mMatrix[12]*axisX + mMatrix[13]*axisY + mMatrix[14]*axisZ + mMatrix[15]*0; axisX = a1;
axisY = a2;
axisZ = a3;
//轴向量单位化
mod_axis = Math.sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);
axisX = axisX/mod_axis;
axisY = axisY/mod_axis;
axisZ = axisZ/mod_axis; rotateModel(axisX, axisY, axisZ, rad);
}); /**
* 模型旋转函数
* x:旋转轴向量的x轴分量
* y:旋转轴向量的y轴分量
* z:旋转轴向量的z轴分量
* rad:绕旋转轴旋转的弧度
*/
function rotateModel(x, y, z, rad){
initGL(gl); // 模型旋转6度
//var rad = 1 * Math.PI / 180;
// 模型坐标变换矩阵的生成(沿着Y轴旋转)
//m.rotate(mMatrix, rad, [x, y, z], mMatrix);
//m.multiply(tmpMatrix, mMatrix, mvpMatrix);
// mMatrix的逆矩阵
//m.inverse(mMatrix, invMatrix);
// 向uniformLocation中传入坐标变换矩阵
//gl.uniformMatrix4fv(uniLocation[0], false, mvpMatrix);
//gl.uniformMatrix4fv(uniLocation[1], false, invMatrix);
// 使用索引进行绘图,画三角面
//gl.drawElements(gl.TRIANGLES, index.length, gl.UNSIGNED_SHORT, 0); drawCoordinates(x, y, z, rad); // context刷新
gl.flush();
} /**
* 生成着色器的函数
*/
function create_shader(id){
// 用来保存着色器的变量
var shader; // 根据id从HTML中获取指定的script标签
var scriptElement = document.getElementById(id); // 如果指定的script标签不存在,则返回
if(!scriptElement){return;} // 判断script标签的type属性
switch(scriptElement.type){ // 顶点着色器的时候
case 'x-shader/x-vertex':
shader = gl.createShader(gl.VERTEX_SHADER);
break; // 片段着色器的时候
case 'x-shader/x-fragment':
shader = gl.createShader(gl.FRAGMENT_SHADER);
break;
default :
return;
} // 将标签中的代码分配给生成的着色器
gl.shaderSource(shader, scriptElement.text); // 编译着色器
gl.compileShader(shader); // 判断一下着色器是否编译成功
if(gl.getShaderParameter(shader, gl.COMPILE_STATUS)){ // 编译成功,则返回着色器
return shader;
}else{ // 编译失败,弹出错误消息
alert(gl.getShaderInfoLog(shader));
}
} /**
* 程序对象的生成和着色器连接的函数
*/
function create_program(vs, fs){
// 程序对象的生成
var program = gl.createProgram(); // 向程序对象里分配着色器
gl.attachShader(program, vs);
gl.attachShader(program, fs); // 将着色器连接
gl.linkProgram(program); // 判断着色器的连接是否成功
if(gl.getProgramParameter(program, gl.LINK_STATUS)){ // 成功的话,将程序对象设置为有效
gl.useProgram(program); // 返回程序对象
return program;
}else{ // 如果失败,弹出错误信息
alert(gl.getProgramInfoLog(program));
}
} /**
* 生成VBO的函数
*/
function create_vbo(data){
// 生成缓存对象
var vbo = gl.createBuffer(); // 绑定缓存
gl.bindBuffer(gl.ARRAY_BUFFER, vbo); // 向缓存中写入数据
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW); // 将绑定的缓存设为无效
gl.bindBuffer(gl.ARRAY_BUFFER, null); // 返回生成的VBO
return vbo;
} /**
* 绑定VBO相关的函数
*/
function set_attribute(vbo, attL, attS){
// 处理从参数中得到的数组
for(var i in vbo){
// 绑定缓存
gl.bindBuffer(gl.ARRAY_BUFFER, vbo[i]); // 将attributeLocation设置为有效
gl.enableVertexAttribArray(attL[i]); //通知并添加attributeLocation
gl.vertexAttribPointer(attL[i], attS[i], gl.FLOAT, false, 0, 0);
}
} /**
* IBO的生成函数
*/
function create_ibo(data){
// 生成缓存对象
var ibo = gl.createBuffer(); // 绑定缓存
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo); // 向缓存中写入数据
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Int16Array(data), gl.STATIC_DRAW); // 将缓存的绑定无效化
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); // 返回生成的IBO
return ibo;
} /**
* 初始化gl
* */
function initGL(gl){
// 设定canvas初始化的颜色
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// 设定canvas初始化时候的深度
gl.clearDepth(1.0);
// canvas的初始化
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// 将深度测试设置为有效
gl.enable(gl.DEPTH_TEST);
// 指定一般深度测试的评价方法
gl.depthFunc(gl.LEQUAL);
// 将遮挡剔除设置为有效
gl.enable(gl.CULL_FACE);
} /**
* bindBuffer
* */
function bindBuffer(vertexArray, normalArray, colorArray, indices){
/**
* 三角面
*/
// 生成VBO
var position_vbo = create_vbo(vertexArray);
var normal_vbo = create_vbo(normalArray);
var color_vbo = create_vbo(colorArray);
// 将VBO进行绑定并添加
set_attribute([position_vbo, normal_vbo, color_vbo], attLocation, attStride);
// 生成IBO
var ibo = create_ibo(indices);
// IBO进行绑定并添加
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
} /**
* 画坐标系
**/
function drawCoordinates(x, y, z, rad){
/**
* y轴
* */
//空间变换
mMatrix = m.identity(m.create());
m.rotate(mMatrix, rad, [x, y, z], mMatrix);
// 得到mvpMatrix定位坐标矩阵
m.multiply(tmpMatrix, mMatrix, mvpMatrix);
// mMatrix的逆矩阵
m.inverse(mMatrix, invMatrix);
// 向uniformLocation中传入坐标变换矩阵
gl.uniformMatrix4fv(uniLocation[0], false, mvpMatrix);
gl.uniformMatrix4fv(uniLocation[1], false, invMatrix);
//圆柱模型
var cylinder = new cylinderGeo(0.03, 0.5, 15);
var cylinderPositionY = cylinder.vertices;
var cylinderNormalY = cylinder.normals;
var cylinderColor = [];
for(var i=0; i<cylinderPositionY.length/3; i++){
cylinderColor.push(0.6, 1.0, 0.0, 1.0);
}
var cylinderIndexY = cylinder.faces;
//写缓存绑定vertex-shader
bindBuffer(cylinderPositionY, cylinderNormalY, cylinderColor, cylinderIndexY);
// 使用索引进行绘图,画三角面
gl.drawElements(gl.TRIANGLES, cylinderIndexY.length, gl.UNSIGNED_SHORT, 0); //空间变换
mMatrix = m.identity(m.create());
m.translate(mMatrix, [0, 0.5, 0], mMatrix);
m.rotate(mMatrix, rad, [x, y, z], mMatrix);
// 得到mvpMatrix定位坐标矩阵
m.multiply(tmpMatrix, mMatrix, mvpMatrix);
// mMatrix的逆矩阵
m.inverse(mMatrix, invMatrix);
// 向uniformLocation中传入坐标变换矩阵
gl.uniformMatrix4fv(uniLocation[0], false, mvpMatrix);
gl.uniformMatrix4fv(uniLocation[1], false, invMatrix);
//圆锥模型
var cone = new coneGeo(0.05, 0.25, 15);
var conePositionY = cone.vertices;
var coneNormalY = cone.normals;
var coneColorY = [];
for(var i=0; i<conePositionY.length/3; i++){
coneColorY.push(0.6, 1.0, 0.0, 1.0);
}
var coneIndexY = cone.faces;
bindBuffer(conePositionY, coneNormalY, coneColorY, coneIndexY);
// 使用索引进行绘图,画三角面
gl.drawElements(gl.TRIANGLES, coneIndexY.length, gl.UNSIGNED_SHORT, 0); /**
* x轴
* */
//空间变换
mMatrix = m.identity(m.create());
m.rotate(mMatrix, -90*Math.PI/180, [0, 0, 1], mMatrix);
m.rotate(mMatrix, rad, [x, y, z], mMatrix);
// 得到mvpMatrix定位坐标矩阵
m.multiply(tmpMatrix, mMatrix, mvpMatrix);
// mMatrix的逆矩阵
m.inverse(mMatrix, invMatrix);
// 向uniformLocation中传入坐标变换矩阵
gl.uniformMatrix4fv(uniLocation[0], false, mvpMatrix);
gl.uniformMatrix4fv(uniLocation[1], false, invMatrix);
//圆柱模型
var cylinderPositionX = cylinder.vertices;
var cylinderNormalX = cylinder.normals;
var cylinderColorX = [];
for(var i=0; i<cylinderPositionX.length/3; i++){
cylinderColorX.push(0.6, 0.1, 0.0, 1.0);
}
var cylinderIndexX = cylinder.faces;
//写缓存绑定vertex-shader
bindBuffer(cylinderPositionX, cylinderNormalX, cylinderColorX, cylinderIndexX);
// 使用索引进行绘图,画三角面
gl.drawElements(gl.TRIANGLES, cylinderIndexX.length, gl.UNSIGNED_SHORT, 0); //空间变换
mMatrix = m.identity(m.create());
m.translate(mMatrix, [0.5, 0, 0], mMatrix);
m.rotate(mMatrix, -90*Math.PI/180, [0, 0, 1], mMatrix);
m.rotate(mMatrix, rad, [x, y, z], mMatrix);
// 得到mvpMatrix定位坐标矩阵
m.multiply(tmpMatrix, mMatrix, mvpMatrix);
// mMatrix的逆矩阵
m.inverse(mMatrix, invMatrix);
// 向uniformLocation中传入坐标变换矩阵
gl.uniformMatrix4fv(uniLocation[0], false, mvpMatrix);
gl.uniformMatrix4fv(uniLocation[1], false, invMatrix);
//圆锥模型
var conePositionX = cone.vertices;
var coneNormalX = cone.normals;
var coneColorX = [];
for(var i=0; i<conePositionX.length/3; i++){
coneColorX.push(0.6, 0.1, 0.0, 1.0);
}
var coneIndexX = cone.faces;
bindBuffer(conePositionX, coneNormalX, coneColorX, coneIndexX);
// 使用索引进行绘图,画三角面
gl.drawElements(gl.TRIANGLES, coneIndexX.length, gl.UNSIGNED_SHORT, 0); /**
* z轴
* */
//空间变换
mMatrix = m.identity(m.create());
m.rotate(mMatrix, 90*Math.PI/180, [1, 0, 0], mMatrix);
//圆锥姿态
m.rotate(mMatrix, rad, [x, y, z], mMatrix);
// 得到mvpMatrix定位坐标矩阵
m.multiply(tmpMatrix, mMatrix, mvpMatrix);
// mMatrix的逆矩阵
m.inverse(mMatrix, invMatrix);
// 向uniformLocation中传入坐标变换矩阵
gl.uniformMatrix4fv(uniLocation[0], false, mvpMatrix);
gl.uniformMatrix4fv(uniLocation[1], false, invMatrix);
//圆柱模型
var cylinderPositionZ = cylinder.vertices;
var cylinderNormalZ = cylinder.normals;
var cylinderColorZ = [];
for(var i=0; i<cylinderPositionZ.length/3; i++){
cylinderColorZ.push(0.0, 0.1, 0.6, 1.0);
}
var cylinderIndexZ = cylinder.faces;
//写缓存绑定vertex-shader
bindBuffer(cylinderPositionZ, cylinderNormalZ, cylinderColorZ, cylinderIndexZ);
// 使用索引进行绘图,画三角面
gl.drawElements(gl.TRIANGLES, cylinderIndexZ.length, gl.UNSIGNED_SHORT, 0); //空间变换
mMatrix = m.identity(m.create());
m.translate(mMatrix, [0, 0, 0.5], mMatrix);
m.rotate(mMatrix, 90*Math.PI/180, [1, 0, 0], mMatrix);
m.rotate(mMatrix, rad, [x, y, z], mMatrix);
// 得到mvpMatrix定位坐标矩阵
m.multiply(tmpMatrix, mMatrix, mvpMatrix);
// mMatrix的逆矩阵
m.inverse(mMatrix, invMatrix);
// 向uniformLocation中传入坐标变换矩阵
gl.uniformMatrix4fv(uniLocation[0], false, mvpMatrix);
gl.uniformMatrix4fv(uniLocation[1], false, invMatrix);
//圆锥模型
var conePositionZ = cone.vertices;
var coneNormalZ = cone.normals;
var coneColorZ = [];
for(var i=0; i<conePositionZ.length/3; i++){
coneColorZ.push(0.0, 0.1, 0.6, 1.0);
}
var coneIndexZ = cone.faces;
bindBuffer(conePositionZ, coneNormalZ, coneColorZ, coneIndexZ);
// 使用索引进行绘图,画三角面
gl.drawElements(gl.TRIANGLES, coneIndexZ.length, gl.UNSIGNED_SHORT, 0);
}
}; </script>
<script src="polyhedron.js"></script> </head>
<body>
<canvas id="canvas"></canvas>
<br/>
转动前X坐标:<input id="cordX">转动前Y坐标:<input id="cordY">转动前Z坐标:<input id="cordZ">和转轴的点积:<input id="00">
<br/>
转动后X坐标:<input id="cordX1">转动后Y坐标:<input id="cordY1">转动后Z坐标:<input id="cordZ1">和转轴的点积:<input id="01">
<br/>
旋转轴X坐标分量:<input id="axisX">旋转轴Y坐标分量:<input id="axisY">旋转轴Z坐标分量:<input id="axisZ">
<br/>
旋转角度:<input id="rad">
<br/>
===============================================旋转操作参数===============================================
<br/>
旋转轴X坐标分量:<input class='axisX'>旋转轴Y坐标分量:<input class="axisY">旋转轴Z坐标分量:<input class="axisZ">
旋转角度:<input class="rad">
<button id="rotate">旋转</button>
</body>
</html>

以上是渲染场景的html代码,我们先来看一下最终结果,如下图

如图可以看到,我们成功将3个圆柱,3个圆锥绘制到场景中去了,那么这个实现的核心部分在哪里呢,我们来分析一下,其中绘图是采用索引缓存的方式写入的,而和顶点着色器attribute类型的变量进行传值的数组每次绘制一个几何体对象都会被覆写,然后重新写入索引缓存,然后重新gl.drawElements绘制。这就是向一个场景中绘入多个模型的核心思想,每次要写入一个几何体,就重新向顶点着色器的attribute变量传值,重新写入索引缓存,重新绘图,而每次绘图都不会将之前绘制完成的几何体从场景中擦除(这就是增量渲染)。

通过记录这个工程案例,对gl的缓存机制又有新的认识。引用本文请注明出处https://www.cnblogs.com/ccentry/p/9864847.html

原生WebGL场景中绘制多个圆锥圆柱的更多相关文章

  1. 在WebGL场景中管理多个卡牌对象的实验

    这篇文章讨论如何在基于Babylon.js的WebGL场景中,实现多个简单卡牌类对象的显示.选择.分组.排序,同时建立一套实用的3D场景代码框架.由于作者美工能力有限,所以示例场景视觉效果可能欠佳,本 ...

  2. 在WebGL场景中进行棋盘操作的实验

    这篇文章讨论如何在基于Babylon.js的WebGL场景中,建立棋盘状的地块和多个可选择的棋子对象,在点选棋子时显示棋子的移动范围,并且在点击移动范围内的空白地块时向目标地块移动棋子.在这一过程中要 ...

  3. 在WebGL场景中使用2DA*寻路

      这篇文章将讨论如何在一个自定义的地面网格上进行简单的2D寻路,以及确定路径后如何使用基于物理引擎的运动方式使物体沿路径到达目标地点.读者需要预先对WebGL和Babylonjs知识有一些了解,可以 ...

  4. 在WebGL场景中建立游戏规则

    在前三篇文章的基础上,为基于Babylon.js的WebGL场景添加了类似战棋游戏的基本操作流程,包括从手中选择单位放入棋盘.显示单位具有的技能.选择技能.不同单位通过技能进行交互.处理交互结果以及进 ...

  5. osg 在场景中绘制坐标轴(xyz)

    //x y z font_size osg::Geode* makeCoordinate(float a_x,float a_y,float a_z,float font_size) { osg::r ...

  6. 如何在3D场景中在模型上面绘制摄取点

    有些时候,我们在屏幕上面绘制一个摄取点,在单屏玩游戏的模式下,我们并不能觉得有什么不妥.但是最近VR的热火朝天,我们带上眼镜看双屏的时候,总觉得这个摄取点看着很不舒服. 这个问题该怎么解决?在这里我首 ...

  7. 基于 HTML5 WebGL 的 3D 场景中的灯光效果

    构建 3D 的场景除了创建模型,对模型设置颜色和贴图外,还需要有灯光的效果才能更逼真的反映真实世界的场景.这个例子我觉得既美观又代表性很强,所以拿出来给大家分享一下. 本例地址:http://www. ...

  8. 使用着色器在WebGL3D场景中呈现行星表面地形

    实验目的:按照一定规律生成类地行星地表地形区块,并用合理的方式将地形块显示出来 涉及知识:Babylon.js引擎应用.着色器编程.正态分布.数据处理.canvas像素操作 github地址:http ...

  9. WebGL场景的两种地面构造方法

    总述:大部分3D编程都涉及到地面元素,在场景中我们使用地面作为其他物体的承载基础,同时也用地面限制场景使用者的移动范围,还可以在通过设置地块的属性为场景的不同位置设置对应的计算规则.本文在WebGL平 ...

随机推荐

  1. windows下搭建nginx+php开发环境

    windows下搭建nginx+php开发环境 1.前言 windows下大多我们都是下载使用集成环境,但是本地已经存在一个集成环境,但不适合项目的需求.因此准备再自己搭建一个环境. 2.准备 工具: ...

  2. Xcode 错误收集及解决办法

    1.An unknown error occurred. 如果仅仅提示“An unknown error occurred.” 而没有别的提示,很有可能是设备内存已满,没有足够的空间来安装这个应用. ...

  3. nRF5 SDK for Mesh( 七 ) BLE MESH 的 架构(rchitecture)

    The mesh architecture   The mesh stack consists of a number of subsystems that are interfaced throug ...

  4. 【Javascript-基础-getOwnPropertyNames】Object.getOwnPropertyNames() 获取对象自身可枚举属性

    可枚举属性和不可枚举属性 在JavaScript中,对象的属性分为可枚举和不可枚举之分,它们是由属性的enumerable值决定的.可枚举性决定了这个属性能否被for-in查找遍历到. 可枚举属性 e ...

  5. II、Python HelloWorld

    大家都不是小孩子了,直接上 IDE 现在有个大问题!!没有解析器啊 这样 解析器地址比他多个 e OJBK

  6. 1006.Sign in and Sign out(25)—PAT 甲级

    At the beginning of every day, the first person who signs in the computer room will unlock the door, ...

  7. JAVA WEB 前台实时监控后台程序运行

    基本思路: 1. 操作状态在类中以静态变量方式(或公共类存储公共变量方式,SESSION方式.COOKIE方式)存在 2. 前台采用AJAX方式激发后台进行业务逻辑操作,并实时更新操作状态信息 3.  ...

  8. [笔记][SQL] 连接join

    在学习菜鸟教程里的MySQL教程时,对左右连接的结果有点不解. 其中有如下两个表: runoob_tbl: +-----------+--------------+---------------+-- ...

  9. C语言入门学习和整理 1-3

    1. int main() { char c = 'A'; printf("c=%c", c); } int main() { ; printf("c=%c", ...

  10. MySQL->复制表[20180509]

    MySQL复制表     通常复制表所采用CREATE TABLE .... SELECT 方式将资料复制,但无法将旧表中的索引,约束(除非空以外的)也复制.   完整复制MySQL数据表所需步骤: ...