WebGL 踩坑系列-3

WebGL 踩坑系列-3

绘制球体

在 WebGL 中绘制物体时需要的顶点是以直角坐标表示的,
当然了,gl_Position 是一个四维的向量,一般将顶点赋值给 gl_Position 时,最后一维会设为 1,

gl_Position = uMVPMatrix * vec4(aVertexPosition, 1.0);

这个时候的 aVertexPosition 三维向量就代表了顶点的直角坐标。
如果我们计算出球面上的顶点,并以直角坐标的形式传入着色器中,用合适的方式绘制,就能画出球面了。

但是,绘制球体需要用到顶点,如果直接用直角坐标,并不好计算,
这时候需要用到球坐标系将球面上的各个顶点表示出来,然后再将球坐标表示成直角坐标。

/**
 * 假设球心即为原点,将球面坐标系转换成平面直角坐标系
 * @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 角,就可以得到球面上的所有顶点了。

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 越大,球面越精细,画出的球越圆滑。

ThreeJS 绘制球形

由于有导入模型的需求,开始接触 ThreeJS,用这个框架只需要调用 SphereGeometry 的 API 生成一个球,最后把球添加到场景中并进行渲染即可。

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 着色器语言,自己编写着色器代码,繁琐很多。

本博客由 BriFuture 原创,并在个人博客(WordPress构建) BriFuture's Blog 上发布。欢迎访问。
欢迎遵照 CC-BY-NC-SA 协议规定转载,请在正文中标注并保留本人信息。
原文地址:https://www.cnblogs.com/brifuture/p/8331727.html