上次是画了一个基础图形,在上次的基础上,这次画个正方体。
线框正方体
准备好正方体的点,画线段所以比较杂……
const vertices=[1,1,1,1,-1,1,-1,-1,1,-1,1,1,-1,1,-1,1,1,-1,1,-1,-1,-1,-1,-1,-1,-1,1,1,-1,1,1,-1,-1,1,1,-1,1,1,1,-1,1,1,-1,1,-1,-1,-1,-1];
然后就是有关摄影机,坐标等等相关内容了。相关概念教程可以看 LearnOpenGL 。
看到矩阵乘法,想起了被论文支配的恐惧,四个坐标系之类的是 3D 场景的共识了。
使用 gl-matrix 库来进行矩阵的运算。最终需要物体,相机,投影三个矩阵。
//物体本身转 45 度
let model = glMatrix.mat4.create();
glMatrix.mat4.rotate(
model,
model,
45,
glMatrix.vec3.fromValues(0.0, 1.0, 0.0)
);
//相机向后移四格
let camera = glMatrix.mat4.create();
glMatrix.mat4.translate(
camera,
camera,
glMatrix.vec3.fromValues(0.0, 0.0, -4.0)
);
//投影矩阵
let projection = glMatrix.mat4.create();
let ratio = 8 / 6;
glMatrix.mat4.perspective(projection, (75 * Math.PI) / 180, ratio, 0.1, 100);
修改顶点着色器,添加放以上三个矩阵的变量。点的最终位置也改为矩阵乘法算出。
attribute vec3 pos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;
void main(){
gl_Position=proj * view * model *vec4(pos.x,pos.y,pos.z,1.0);
}
然后就是把数据传到着色器中。由于用的是uniform
型变量,查找变量的函数也不一样。
//注:下面的要写在启用 program 之后
gl.useProgram(shaderProgram);
//矩阵传入着色器
//1.找到变量的位置
let modelPosIndex = gl.getUniformLocation(shaderProgram, "model");
//2.传值
gl.uniformMatrix4fv(modelPosIndex,false,model);
attribute
型是只能在顶点着色器使用的变量类型,主要描述顶点数据。uniform
型在顶点和片段着色器内都可使用,描述颜色、光照等等信息。
查看结果:
实心正方体
上面画线框的顶点集实在是太扭曲了……这回做个真正的三角片。
const vertices=[1,1,1,1,1,-1,-1,1,-1,1,1,1,-1,1,1,-1,1,-1,1,1,-1,1,-1,-1,1,1,1,1,1,1,1,-1,-1,1,-1,1,1,-1,-1,1,-1,1,-1,-1,1,-1,-1,1,-1,-1,-1,1,-1,-1,-1,-1,1,-1,-1,-1,-1,1,1,-1,1,1,-1,1,-1,-1,-1,-1,1,-1,1,1,1,1,-1,-1,1,-1,-1,1,1,1,1,-1,1,1,1,-1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,1,1,-1];
将绘图参数改为gl.drawArrays(gl.TRIANGLES, 0, 36);
,结果是白色一片的轮廓……这是当然的啦!因为实心白色融合在一起了。为了更清楚的看见,尝试给各个面上不同的颜色。
首先给vertices
里加上各个点的颜色:
const vertices=[1,1,1,1,0,0,1,1,-1,1,0,0,-1,1,-1,1,0,0,1,1,1,1,0,0,-1,1,1,1,0,0,-1,1,-1,1,0,0,1,1,-1,1,1,1,1,-1,-1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,-1,-1,1,1,1,1,-1,1,1,1,1,1,-1,-1,0,1,0,1,-1,1,0,1,0,-1,-1,1,0,1,0,-1,-1,1,0,1,0,-1,-1,-1,0,1,0,1,-1,-1,0,1,0,-1,-1,1,0,0,1,-1,-1,-1,0,0,1,-1,1,1,0,0,1,-1,1,1,0,0,1,-1,1,-1,0,0,1,-1,-1,-1,0,0,1,1,-1,1,1,0,1,1,1,1,1,0,1,-1,-1,1,1,0,1,-1,-1,1,1,0,1,1,1,1,1,0,1,-1,1,1,1,0,1,1,-1,-1,1,1,0,1,1,-1,1,1,0,-1,-1,-1,1,1,0,-1,-1,-1,1,1,0,-1,1,-1,1,1,0,1,1,-1,1,1,0];
uniform
变量是全局的、与顶点无关的,所以颜色使用attribute
型。然后利用varying
型变量传给片段着色器。修改着色器,加上输入的颜色:
attribute vec3 pos;
attribute vec3 in_color;
varying lowp vec3 color;
uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;
void main(){
color = in_color;
gl_Position=proj * view * model *vec4(pos,1.0);
}
varying lowp vec3 color;
void main(){
gl_FragColor = vec4(1.0,color.y,1.0,1.0);
}
修改vertices
的值,使六个数字表示一个点,前三个值为位置,后三个值为颜色。修改传入数据的地方。gl.vertexAttribPointer
的最后两个参数:步长
:一组数据的长度;偏移量
:开始到第一个此类数据的长度。
//float 元素长度
let size = Float32Array.BYTES_PER_ELEMENT;
//输入位置
let index = gl.getAttribLocation(shaderProgram, "pos");
gl.vertexAttribPointer(index, 3, gl.FLOAT, false, 6 * size, 0);
//输入颜色
let colorIndex = gl.getAttribLocation(shaderProgram, "colors");
gl.vertexAttribPointer(colorIndex, 3, gl.FLOAT, false, 6 * size, 3 * size);
//启用属性
gl.enableVertexAttribArray(index);
gl.enableVertexAttribArray(colorIndex);
查看结果,发现颜色堆叠了……深度测试,on!
gl.enable(gl.DEPTH_TEST);
gl.clearDepth(1);
最后调一下物体的旋转角度,好看到更多的面:
索引正方体
写 36 个点的正方体的确挺抽风的。利用gl.drawElements
,用索引来画一系列物体,完成一个线框正方体。
经测试用索引画的话无法分开设置颜色(像上图一样)?
//正方体的点
var vertices = [
-1, -1, -1,//
1, -1, -1,//
1, 1, -1,//
-1, 1, -1,//
-1, 1, 1,//
1, 1, 1,//
-1, -1, 1,//
1, -1, 1
];
//组成面的点索引 (12 个三角形)
const indices = [
1,2,5, 1,5,7,
1,2,3, 1,0,3,
0,3,4, 0,6,4,
6,4,7, 7,5,4,
6,7,0, 0,7,1,
4,5,3, 3,5,2
];
//...
//正方体
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
var index = gl.getAttribLocation(shaderProgram, "pos");
gl.vertexAttribPointer(index, 3, gl.FLOAT, false, 0, 0);
const indexesBuffer = gl.createBuffer();
//传入索引的时候使用 gl.ELEMENT_ARRAY_BUFFER 类型
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexesBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
gl.enableVertexAttribArray(index);
//...
//利用`gl.drawElements',用索引来画一系列物体
//类型,绘制的点数,数据类型,offset(离开始点的偏移量)
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
真实笔记,除了我没人看得懂