WebGL系列教程五(使用索引绘制彩色立方体)
目录
- 1 前言
- 2 立方体
- 3 开始绘制
- 3.1 声明顶点和颜色
- 3.2 使用索引绘制
- 3.3 效果
- 3.4 完整代码
- 4 总结
1 前言
上一讲我们讲了如何绘制彩色的三角形,这一讲我们来说如何绘制立方体。为什么几乎所有的WebGL
教程总是从开始绘制三角形开始,因为三角形是最小的面,其他的一切图形都可以用三角形来拼接。好了,废话不多说,我们直接开整。
2 立方体
一个立方体有8个顶点,如下图所示
现在我们要把立方体的重心放到坐标轴原点去,回忆一下WebGL
中的坐标系,X轴向右,Y轴向上,Z轴向外
,这八个顶点的坐标和对应的颜色为:
顶点 | XYZ坐标 | 颜色(RGB) |
---|---|---|
v0 | 1.0,1.0,1.0, | 1.0,1.0,1.0 |
v1 | -1.0,1.0,1.0, | 1.0,0.0,1.0 |
v2 | -1.0,-1.0,1.0, | 1.0,0.0,1.0 |
v3 | 1.0,-1.0,1.0, | 1.0,1.0,0.0 |
v4 | 1.0,-1.0,-1.0, | 1.0,0.0,1.0 |
v5 | -1.0,-1.0,-1.0, | 1.0,1.0,1.0 |
v6 | -1.0,1.0,-1.0, | 0.0,0.0,1.0 |
v7 | 1.0,1.0,-1.0, | 0.0,1.0,1.0 |
3 开始绘制
3.1 声明顶点和颜色
<script id="vertex-shader" type="x-shader/x-vertex">
//声明一个点,vec3表示3维向量
attribute vec3 aPosition;
attribute vec4 a_Color;
varying vec4 v_Color;
void main(){
//点的位置,将vec3补齐为vec4
gl_Position = vec4(aPosition,1.0);
//传递颜色
v_Color = a_Color;
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision highp float;
varying vec4 v_Color;
void main(){
//点的颜色,rgba形式
gl_FragColor = v_Color;
}
</script>
3.2 使用索引绘制
因为现在我们要使用顶点索引,因此就不能使用DrawArrays方法进行绘制了,改为使用DrawElements。
//顶点和颜色
let verticesColors = new Float32Array([
1.0, 1.0, 1.0, 1.0,1.0,1.0,//v0 近平面 右上 颜色
-1.0, 1.0, 1.0, 1.0,0.0,1.0,//v1 近平面 左上 颜色
-1.0,-1.0, 1.0, 1.0,0.0,1.0,//v2 近平面 左下 颜色
1.0,-1.0, 1.0, 1.0,1.0,0.0,//v3 近平面 右下 颜色
1.0,-1.0,-1.0, 1.0,0.0,1.0,//v4 远平面 右下 颜色
-1.0,-1.0,-1.0, 1.0,1.0,1.0,//v5 远平面 左下 颜色
-1.0, 1.0,-1.0, 0.0,0.0,1.0,//v6 远平面 左上 颜色
1.0, 1.0,-1.0, 0.0,1.0,1.0 //v7 远平面 右上 颜色
]);
//顶点索引
let indices = new Uint8Array([
0,1,2, 0,2,3,//近平面
4,5,6, 4,6,7,//远平面
1,2,5, 1,5,6,//左平面
0,3,4, 0,4,7,//右平面
3,4,2, 3,5,2,//下平面
0,7,6, 0,1,6 //上平面
]);
//使webgl视口和canvas画板一样大
gl.viewport(0, 0, canvas.width, canvas.height);
//开启深度检测
gl.enable(gl.DEPTH_TEST);
//顶点
let vertexColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,vertexColorBuffer);
gl.bufferData(gl.ARRAY_BUFFER,verticesColors,gl.STATIC_DRAW);
let FSIZE = verticesColors.BYTES_PER_ELEMENT;
let a_Position = gl.getAttribLocation(program,'a_Position');
gl.vertexAttribPointer(a_Position,3,gl.FLOAT,false,FSIZE*6,0);
gl.enableVertexAttribArray(a_Position);
//颜色
var a_Color = gl.getAttribLocation(program, 'a_Color');
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
gl.enableVertexAttribArray(a_Color);
//绑定索引缓冲
let indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices,gl.STATIC_DRAW);
gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_BYTE, 0);
3.3 效果
为了使效果明显一点,我将立方体绕Y轴
旋转了60度。虽然看起来效果还不错。但仍然能看到一些问题,比如左下角和右上角为什么有一条明显的连线?那是因为每个面都是由两个三角形组成的。每个三角形都有各自的颜色,当然在拼接的地方就能看到明显的颜色分隔了。我们在这里先留个问题,怎么样使每个面只保留一种颜色呢?
3.4 完整代码
let canvas = document.getElementById("canvas");
let gl = canvas.getContext("webgl");
let vertexSource = document.getElementById("vertex-shader").innerText;
let fragmentSource = document.getElementById("fragment-shader").innerText;
let vertexShader = gl.createShader(gl.VERTEX_SHADER);
let fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vertexShader,vertexSource);
gl.shaderSource(fragmentShader,fragmentSource);
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
console.log(gl.getShaderInfoLog(vertexShader));
console.log(gl.getShaderInfoLog(fragmentShader));
let program = gl.createProgram();
gl.attachShader(program,vertexShader);
gl.attachShader(program,fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
console.log(gl.getProgramInfoLog(program));
//开始正常的逻辑,绘制立方体
// Create a cube
// v6----- v7
// /| /|
// v1------v0|
// | | | |
// | |v5---|-|v4
// |/ |/
// v2------v3
//顶点和颜色
let verticesColors = new Float32Array([
1.0, 1.0, 1.0, 1.0,1.0,1.0,//v0 近平面 右上 颜色
-1.0, 1.0, 1.0, 1.0,0.0,1.0,//v1 近平面 左上 颜色
-1.0,-1.0, 1.0, 1.0,0.0,1.0,//v2 近平面 左下 颜色
1.0,-1.0, 1.0, 1.0,1.0,0.0,//v3 近平面 右下 颜色
1.0,-1.0,-1.0, 1.0,0.0,1.0,//v4 远平面 右下 颜色
-1.0,-1.0,-1.0, 1.0,1.0,1.0,//v5 远平面 左下 颜色
-1.0, 1.0,-1.0, 0.0,0.0,1.0,//v6 远平面 左上 颜色
1.0, 1.0,-1.0, 0.0,1.0,1.0 //v7 远平面 右上 颜色
]);
//顶点索引
let indices = new Uint8Array([
0,1,2, 0,2,3,//近平面
4,5,6, 4,6,7,//远平面
1,2,5, 1,5,6,//左平面
0,3,4, 0,4,7,//右平面
3,4,2, 3,5,2,//下平面
0,7,6, 0,1,6 //上平面
]);
//使webgl视口和canvas画板一样大
gl.viewport(0, 0, canvas.width, canvas.height);
//开启深度检测
gl.enable(gl.DEPTH_TEST);
//顶点
let vertexColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,vertexColorBuffer);
gl.bufferData(gl.ARRAY_BUFFER,verticesColors,gl.STATIC_DRAW);
let FSIZE = verticesColors.BYTES_PER_ELEMENT;
let a_Position = gl.getAttribLocation(program,'a_Position');
gl.vertexAttribPointer(a_Position,3,gl.FLOAT,false,FSIZE*6,0);
gl.enableVertexAttribArray(a_Position);
//颜色
var a_Color = gl.getAttribLocation(program, 'a_Color');
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
gl.enableVertexAttribArray(a_Color);
//绑定索引缓冲
let indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices,gl.STATIC_DRAW);
//绘制三角形,36个点,立方体是6个面,每个面2个三角形,索引数组元素类型gl.UNSIGNED_BYTE,从第0个索引开始绘制
gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_BYTE, 0);
4 总结
本节我们通过梳理立方体的顶点坐标和对应的颜色关系,使用索引绘制的方式,绘制除了一个彩色的立方体,关于立方体是怎么旋转的,我们会在后面的博文中进行讲解。目前我们还遗留了一个思考问题,那就是如何使立方体的每个面都保持一个颜色,希望读者认真思考,我们留在下节进行解答。