本篇文章的内容假定你懂的矩阵的基本知识,例如线性变换,其次坐标,平移矩阵等知识。 有了二维变换的知识,我们就可以为三维变化做好准备。好戏开场了,我们先来画一个,字母E如图:
顶点坐标如下:
markdown
var positions = [
0, 0,
300, 0,
300, 30,
0, 0,
0, 30,
300,30,
0,0,
0,400,
30,400,
0,0,
30,0,
30,400,
0,400,
300,400,
0,370,
0,370,
300,370,
300,400,
0,185,
200,185,
200,215,
0,215,
0,185,
200,215
];
drawPoints(gl.TRIANGLES,positions,2,24) //画出字母E,
接下来有的同学就会想:那不简单了,我们给出变换矩阵,然后修改每个顶点的坐标,重新画一次不就行了!,我只能告诉你,同学醒醒,我们要充分利用gpu的性能,要节省出cpu,让gpu处理矩阵比cpu快的多。所以我们要修改shader,我们通过修改顶点的shader重新绘制,并且GLSL语言本身就支持向量计算。接下来我们实现一个平移动画,我们手把手实现:
平移变换
-
修改shader:顶点着色器。添加转换变量。
ini// an attribute will receive data from a buffer attribute vec2 a_position; uniform vec2 u_resolution; // attribute vec4 a_position; // all shaders have a main function varying vec4 v_color; uniform vec2 u_translation; void main() { // gl_Position is a special variable a vertex shader // is responsible for setting // 从像素坐标转换到 0.0 到 1.0 vec2 position = a_position + u_translation; vec2 zeroToOne = position - u_resolution / 2.0; // 再把 0->1 转换 0->2 vec2 zeroToTwo = zeroToOne / u_resolution * 2.0; // 把 0->2 转换到 -1->+1 (裁剪空间) vec2 clipSpace = zeroToTwo * vec2(1.0,-1.0); gl_Position = vec4(clipSpace, 0, 1); gl_PointSize = 10.0; v_color = vec4(clipSpace, 0.5, 1); }
-
修改u_translation变量并绘制:
scssvar translationLocation = gl.getUniformLocation(program, "u_translation"); gl.uniform2f(resolutionUniformLocation, translation, 0); // Set clear color to black, fully opaque gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear the color buffer with specified clear color gl.clear(gl.COLOR_BUFFER_BIT); gl.drawArrays(gl.TRIANGLES, 0, 24);
-
添加动画控制:
inifunction animateTranslate(duration,distance) { let start = Date.now(); function step() { console.log('run step;') let timePassed = Date.now() - start; let progress = timePassed / duration; if (progress <= 1) { drawE(distance*progress); window.requestAnimationFrame(step); } else { return; } } window.requestAnimationFrame(step); } animateTranslate(3000,150);
变换矩阵
我们常见的几个矩阵是:
javascript
var m3 = {
//平移矩阵
translation: function(tx, ty) {
return [
1, 0, 0,
0, 1, 0,
tx, ty, 1,
];
},
//旋转矩阵
rotation: function(angleInRadians) {
var c = Math.cos(angleInRadians);
var s = Math.sin(angleInRadians);
return [
c,-s, 0,
s, c, 0,
0, 0, 1,
];
},
//缩放矩阵
scaling: function(sx, sy) {
return [
sx, 0, 0,
0, sy, 0,
0, 0, 1,
];
},
};
如果不太清楚为什么是这样的同学,可以补习一下矩阵变换的相关知识。
由于GLSL语言本身是支持矩阵计算的。所以我们可以继续修改shader。如下:
ini
// an attribute will receive data from a buffer
attribute vec2 a_position;
uniform vec2 u_resolution;
uniform mat3 matrix_translate;
uniform mat3 matrix_rotate;
uniform mat3 matrix_scale;
// attribute vec4 a_position;
// all shaders have a main function
varying vec4 v_color;
//uniform vec2 u_translation;
void main() {
// gl_Position is a special variable a vertex shader
// is responsible for setting
// 从像素坐标转换到 0.0 到 1.0
// vec2 position = a_position + u_translation;
vec2 position = (matrix_translate * matrix_rotate * matrix_scale * vec3(a_position, 1)).xy;
vec2 zeroToOne = position - u_resolution / 2.0;
// 再把 0->1 转换 0->2
vec2 zeroToTwo = zeroToOne / u_resolution * 2.0;
// 把 0->2 转换到 -1->+1 (裁剪空间)
vec2 clipSpace = zeroToTwo * vec2(1.0,-1.0);
gl_Position = vec4(clipSpace, 0, 1);
gl_PointSize = 10.0;
v_color = vec4(clipSpace, 0.5, 1);
}
然后就可以对全局变量赋值,进行旋转平移和缩放了。
ini
var translateUniform = gl.getUniformLocation(program, "matrix_translate");
gl.uniformMatrix3fv(translateUniform, false, m3.translation(100,200));
var rotateUniformn = gl.getUniformLocation(program, "matrix_rotate");
gl.uniformMatrix3fv(rotateUniformn, false, m3.rotation(1));
var scaleUniform = gl.getUniformLocation(program, "matrix_scale");
gl.uniformMatrix3fv(scaleUniform, false, m3.scaling(0.5,0.5));
gl.drawArrays(gl.TRIANGLES, 0, 24);
效果如下:
关于矩阵
有一点需要注意glsl里面的矩阵和向量和数学里面的矩阵向量结构是不相同的。 在数学里面,矩阵的列是向量,但是在编码里面,列是向量不太符合我们的编码习惯,在glsl里面行是向量,其实就是数学矩阵里面的转置矩阵,参考平移矩阵,这是由于历史原因,向量的数字在内存里面是连续的。
总结
我们已经介绍了基本作图和矩阵变换,接下来就让我们一起进入3d的世界吧,我们已经做好了充分的准备。我们来一起了解透视基本原理,下节更加精彩。