WebGL笔记(三):绘制立方体

终于要在三维的场景里绘制一个三维的物体了——1个Cube。

我们先来厘清几个事情:

  1. 一个立方体有6个面,每个面4个顶点,所以要绘制24个顶点。
  2. 每个顶点位置由3个数字表示,所以顶点信息是72个数字的数组。
  3. 每个顶点颜色由4个数字表示,所以颜色信息是96个数字的数组。

先看一看长长的顶点数组:

var vertices = [
    // Front face
    -1.0, -1.0,  1.0,
     1.0, -1.0,  1.0,
     1.0,  1.0,  1.0,
    -1.0,  1.0,  1.0,
    
    // Back face
    -1.0, -1.0, -1.0,
    -1.0,  1.0, -1.0,
     1.0,  1.0, -1.0,
     1.0, -1.0, -1.0,
    
    // Top face
    -1.0,  1.0, -1.0,
    -1.0,  1.0,  1.0,
     1.0,  1.0,  1.0,
     1.0,  1.0, -1.0,
    
    // Bottom face
    -1.0, -1.0, -1.0,
     1.0, -1.0, -1.0,
     1.0, -1.0,  1.0,
    -1.0, -1.0,  1.0,
    
    // Right face
     1.0, -1.0, -1.0,
     1.0,  1.0, -1.0,
     1.0,  1.0,  1.0,
     1.0, -1.0,  1.0,
    
    // Left face
    -1.0, -1.0, -1.0,
    -1.0, -1.0,  1.0,
    -1.0,  1.0,  1.0,
    -1.0,  1.0, -1.0
];

 

这没什么可说的。比较有趣的是,还有一个“顶点索引”数组:

 

var cubeVertexIndices = [
    0,  1,  2,      0,  2,  3,    // front
    4,  5,  6,      4,  6,  7,    // back
    8,  9,  10,     8,  10, 11,   // top
    12, 13, 14,     12, 14, 15,   // bottom
    16, 17, 18,     16, 18, 19,   // right
    20, 21, 22,     20, 22, 23    // left
]

 

开头说过了,一共24个顶点,这些数字应该是顶点的索引值。那他们的用处是什么呢?引用MDN原文:

https://developer.mozilla.org/en/WebGL/Creating_3D_objects_using_WebGL

The cubeVertexIndices array defines each face as a pair of triangles, specifying each triangle's vertices as an index into the cube's vertex array. Thus the cube is described as a collection of 12 triangles.

E文不好的童鞋不用担心我来翻译一下,咳:大概其啊,呐,cubeVertexIndices这个数组呢,将每一个面定义为一对三角形,并且标出了每个三角形顶点在vertices数组中的索引键。这样以来呢,这个Cube——就是立方体啦——就被描述为一个12个三角形的集合。接触过3D建模的童鞋,可能对此不会太陌生。

然后是颜色数组。MDN原文为了减少篇幅,只定义了6个颜色给6个面,然后用程序扩展为96个。

var colorGroups = [  
    [1.0,  0.0,  1.0,  1.0],    // white  
    [1.0,  0.0,  0.0,  1.0],    // red  
    [0.0,  1.0,  0.0,  1.0],    // green  
    [0.0,  0.0,  1.0,  1.0],    // blue
    [0.0,  1.0,  1.0,  1.0],
    [1.0,  1.0,  0.0,  1.0]
];
 
var generatedColors = [];
for(var i = 0; i < 6; i++){
    for(var j = 0; j < 4; j++){
        generatedColors = generatedColors.concat(colorGroups[i]);
    }
}

颜色每个值的取值范围是0.0~1.0之间的浮点数,每一组4个值分别是红、绿、蓝和透明度。那段扩展的代码比较简单:用一个嵌套循环,将每个数组复制4次,组成一个新的数组。

虽然颜色和顶点的数据有所变化,但是它们的定义和操作没有变化。

绘制开始前,有两个需要注意的地方:

一、在绘制(draw…)之前,应当对场景做初始设置。

// 黑底, 不透明
gl.clearColor(0.0, 0.0, 0.0, 1.0);  
// 清除所有
gl.clearDepth(1.0);                 
// Enable depth testing / 开启...深度测试?
gl.enable(gl.DEPTH_TEST);           
// Near things obscure far things / 近处物体遮挡远处物体?
gl.depthFunc(gl.LEQUAL);   

因为这次涉及较为复杂的图形,如果不做设置,尤其是不开启DEPTH_TEST的话,往往会出现比较搞笑的情况。

 

二、有些语句是有顺序依赖的。

在第一篇中曾经提到过类似问题,本例中也有一处:

   1:  //定义顶点
   2:  var cubeVerticesBuffer = gl.createBuffer();
   3:  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesBuffer);
   4:  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
   5:  //传送数据到Shader指定变量
   6:  gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
   7:   
   8:  //定义颜色
   9:  var cubeVerticesColorBuffer = gl.createBuffer();
  10:  gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesColorBuffer);
  11:  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(generatedColors), gl.STATIC_DRAW);
  12:  //传送颜色数据到Shader
  13:  gl.vertexAttribPointer(vertexColorAttribute, 4, gl.FLOAT, false, 0, 0);

在某个数据被bindBuffer之后,当前缓存数据即为该数据,在下次更改之前,必须及时传递到Shader中(vertexAttribPointer)。上面第6、13行如果顺序发生变化的话可能会引发顶点绘制错误。

 

下面附上所有代码,简单写了注释,不再赘述。MatrixHelper增加了rotate方法,末尾写了一个小动画,可以看到立方体旋转起来。

从下一篇开始,要尝试封装自己的WebGL库,像颜色、顶点之类的复杂数据也计划设计为便于操作的js类。

 

WebGL03

 

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>WebGL Step 03</title>
<style type="text/css">
canvas{ background-color:#666; }
</style>
<script type="text/ecmascript" src="https://files.cnblogs.com/muse/sylvester.js"></script>
<script type="text/ecmascript" src="https://files.cnblogs.com/muse/glUtils.js"></script>
 
<script type="text/ecmascript">
function MatrixHelper(){ this.matrix = Matrix.I(4); }
MatrixHelper.prototype = {
    /* makePerspective */
    make : function(fovy, aspect, znear, zfar){
        this.ppm = makePerspective(fovy, aspect, znear, zfar);
    },
    /* multMatrix */
    mult : function(m){
        this.matrix = this.matrix.x(m);
    },
    /* mvTranslate */
    trans : function(v){
        this.mult(Matrix.Translation($V([v[0], v[1], v[2]])).ensure4x4());
    },
    /* setMatrixUniforms */
    set : function(gl, sProg){
        if(!!this.ppm){
              gl.uniformMatrix4fv(gl.getUniformLocation(sProg, "uPMatrix")
                , false, new Float32Array(this.ppm.flatten()));
        }
        if(!!this.matrix){
              gl.uniformMatrix4fv(gl.getUniformLocation(sProg, "uMVMatrix")
                , false, new Float32Array(this.matrix.flatten())); 
        }
    },
    /* mvRotate */
    rotate : function(angle, v){
        var m = Matrix.Rotation(angle * Math.PI / 180.0, $V([v[0], v[1], v[2]])).ensure4x4();
        this.mult(m);
    }
}
</script>
 
</head>
 
<body>
 
<canvas id="glcanvas" width="640" height="480">看来您的浏览器不支持<code>&lt;canvas&gt;</code>标记</canvas>
 
<script type="text/ecmascript">
var testVertexCode = '\
    attribute vec3 aVertexPosition;\
    attribute vec4 aVertexColor;\
    uniform mat4 uMVMatrix;\
    uniform mat4 uPMatrix;\
    varying lowp vec4 vColor;\
    void main(void){\
        gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);\
        vColor = aVertexColor;\
    }',
    testFragmentCode = '\
    varying lowp vec4 vColor;\
    void main(void){\
        gl_FragColor = vColor;\
    }'
    ;
 
var vertices = [
    // Front face
    -1.0, -1.0,  1.0,
     1.0, -1.0,  1.0,
     1.0,  1.0,  1.0,
    -1.0,  1.0,  1.0,
    
    // Back face
    -1.0, -1.0, -1.0,
    -1.0,  1.0, -1.0,
     1.0,  1.0, -1.0,
     1.0, -1.0, -1.0,
    
    // Top face
    -1.0,  1.0, -1.0,
    -1.0,  1.0,  1.0,
     1.0,  1.0,  1.0,
     1.0,  1.0, -1.0,
    
    // Bottom face
    -1.0, -1.0, -1.0,
     1.0, -1.0, -1.0,
     1.0, -1.0,  1.0,
    -1.0, -1.0,  1.0,
    
    // Right face
     1.0, -1.0, -1.0,
     1.0,  1.0, -1.0,
     1.0,  1.0,  1.0,
     1.0, -1.0,  1.0,
    
    // Left face
    -1.0, -1.0, -1.0,
    -1.0, -1.0,  1.0,
    -1.0,  1.0,  1.0,
    -1.0,  1.0, -1.0
];
 
var cubeVertexIndices = [
    0,  1,  2,      0,  2,  3,    // front
    4,  5,  6,      4,  6,  7,    // back
    8,  9,  10,     8,  10, 11,   // top
    12, 13, 14,     12, 14, 15,   // bottom
    16, 17, 18,     16, 18, 19,   // right
    20, 21, 22,     20, 22, 23    // left
]
 
var colorGroups = [  
    [1.0,  0.0,  1.0,  1.0],    // white  
    [1.0,  0.0,  0.0,  1.0],    // red  
    [0.0,  1.0,  0.0,  1.0],    // green  
    [0.0,  0.0,  1.0,  1.0],    // blue
    [0.0,  1.0,  1.0,  1.0],
    [1.0,  1.0,  0.0,  1.0]
];
 
var generatedColors = [];
for(var i = 0; i < 6; i++){
    for(var j = 0; j < 4; j++){
        generatedColors = generatedColors.concat(colorGroups[i]);
    }
}
 
var canvas = document.getElementById('glcanvas');
var gl = canvas.getContext('experimental-webgl');
 
// 黑底, 不透明
gl.clearColor(0.0, 0.0, 0.0, 1.0);  
// 清除所有
gl.clearDepth(1.0);                 
// Enable depth testing / 开启...深度测试?
gl.enable(gl.DEPTH_TEST);           
// Near things obscure far things / 近处物体遮挡远处物体?
gl.depthFunc(gl.LEQUAL);            
 
//定义Vertext Shader
var vertShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertShader, testVertexCode);
gl.compileShader(vertShader);
//定义Fragment Shader
var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragShader, testFragmentCode);
gl.compileShader(fragShader);
 //定义Program
var program = gl.createProgram();
//附加两个Shader到program
gl.attachShader(program, vertShader);
gl.attachShader(program, fragShader);
//引用
gl.linkProgram(program);
gl.useProgram(program);
 
//定位Shader所需变量并启用
var vertexPositionAttribute = gl.getAttribLocation(program, 'aVertexPosition');
gl.enableVertexAttribArray(vertexPositionAttribute);
 
var vertexColorAttribute = gl.getAttribLocation(program, 'aVertexColor');
gl.enableVertexAttribArray(vertexColorAttribute);
 
//定义顶点索引
var cubeVerticesIndexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
 
//定义顶点
var cubeVerticesBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
//传送数据到Shader指定变量
gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
 
//定义颜色
var cubeVerticesColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesColorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(generatedColors), gl.STATIC_DRAW);
//传送颜色数据到Shader
gl.vertexAttribPointer(vertexColorAttribute, 4, gl.FLOAT, false, 0, 0);
 
 
//调整位置
var matrix = new MatrixHelper();
matrix.trans([0.0, 0.0, -6.0]);
matrix.make(40, 640 / 480, 0.1, 100.0);
 
//动画函数
var animate = function(){
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    matrix.rotate(1, [1, 0, 1]);
    matrix.set(gl, program);
    gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
}
 
//转吧
setInterval(animate, 40);
 
</script>
</body>
</html>
posted @ 2011-12-01 01:43  MKing's Kindom  阅读(2654)  评论(1编辑  收藏  举报