在 WebGL 中绘制物体时须要的顶点是以直角坐标表示的,
固然了,gl_Position 是一个四维的向量,通常将顶点赋值给 gl_Position 时,最后一维会设为 1,web
gl_Position = uMVPMatrix * vec4(aVertexPosition, 1.0);
这个时候的 aVertexPosition 三维向量就表明了顶点的直角坐标。
若是咱们计算出球面上的顶点,并以直角坐标的形式传入着色器中,用合适的方式绘制,就能画出球面了。canvas
可是,绘制球体须要用到顶点,若是直接用直角坐标,并很差计算,
这时候须要用到球坐标系将球面上的各个顶点表示出来,而后再将球坐标表示成直角坐标。数组
/** * 假设球心即为原点,将球面坐标系转换成平面直角坐标系 * @param theta 球心到顶点的连线与 Z 轴正方向的夹角为 theta,范围是 [0, 180] * @param beta 球心到顶点的连线在 xoy 平面上的投影与 X 轴正方向的夹角为 beta,范围是 [0, 360] * @param r 球半径 * @return 顶点的坐标,用三维数组表示 */ function calcVertex(theta, beta, r) { var st = Math.sin(Math.PI * theta / 180); var ct = Math.cos(Math.PI * theta / 180); var sb = Math.sin(Math.PI * beta / 180); var cb = Math.cos(Math.PI * beta / 180); var x = r * st * cb; var y = r * st * sb; var z = r * ct; return [x, y, z]; }
这个 calcVertex 函数就够把特定角度和半径的球坐标转换成相应的直角坐标了。
如今只须要从 0 - 180 遍历 theta,0 - 360 遍历 beta 角,就能够获得球面上的全部顶点了。app
var n = 48; var vetices = []; var r = 1; for( var j = 0; j < n; j++ ) { for( var i = 0; i < n: i++ ) { vertices.push.apply( vertices, calcVertex( i * 180 / n, j * 360 / n, r ) ); // 或者用数组的 concat 方法,效果是同样的,不过听说 concat 方法更高效 // vertices = vertices.concat( calcVertex( i * 180 / n, j * 360 / n, r ) ); } }
接下来把获得的顶点传入 gl 的缓冲区中,框架
var buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
若是只是利用这些顶点,还不能画出球面,借助索引缓冲区能够实现:函数
var index = []; for ( j = 0; j < n; j++ ) { for ( i = 0; i < n+1; i++ ) { index.push( i + j * (n+1), // 0 i+1 + j * (n+1), // 1 i+1 + (j+1) * (n+1) // n+1 ); index.push( i + j * (n+1), // 0 i+1 + (j+1) * (n+1) // n+1 i + (j+1) * (n+1) // n ); } } gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer); gl.drawElements(gl.TRIANGLES, index.length, gl.UNSIGNED_SHORT, 0);
最后就能在 canvas 画布上画出球面了,n 越大,球面越精细,画出的球越圆滑。webgl
因为有导入模型的需求,开始接触 ThreeJS,用这个框架只须要调用 SphereGeometry 的 API 生成一个球,最后把球添加到场景中并进行渲染便可。spa
sphere = new THREE.Mesh( new THREE.SphereGeometry(4, 36, 36), new THREE.MeshPhongMaterial( { opacity: 0.65, transparent: true, color: 0xeeeeee } ) ); scene.add(sphere);
尽管 ThreeJS 对底层的 WebGL 封装的很好,可是我只须要导入 OBJ 格式的模型就好了,ThreeJS 很强大,用起来也很方便。
而要从 WebGL 写的话,还须要去了解 GLSL 着色器语言,本身编写着色器代码,繁琐不少。code