我的webgl探索之旅-第二篇(变换)

echosoar 原创发表于 2019/01/03 21:07:44

简单变换

平移

对一个三角形进行移动也就是说对它的x、y、z三个坐标进行数值上面的加减,因此我们可以在顶点的GLSL代码中声明一个变量,然后使这个变量与顶点坐标变量进行相加,那么就可以实现平移了。
在GLSL中方便的矢量运算是它的特性之一,因此两个vec4类型的矢量可以直接相加,而结果也相当于每个分量各自相加。
attribute vec4 a_Position;
uniform vec4 u_MoveDiatance;
void main () {
  gl_Position = a_Position + u_MoveDiatance;
}
在js中通过gl.getUniformLocation 来获取GLSL中的变量 u_MoveDiatance,并对其赋值:
let u_MoveDiatance = gl.getUniformLocation(gl.program, 'u_MoveDiatance');
gl.uniform4f(u_MoveDiatance, X, Y, Z, 0.0);
因为矢量相加是每个分量进行相加,作为顶点位置坐标的第4个分量所代表的是构成齐次坐标的分母,也就是说这个值相加后必须不能改变,否则绘制出来的坐标就不知道跑哪去啦,因此是0.0。点击查看在线示例

旋转

旋转方向:从Z轴正方向朝着负方向看去,物体绕着轴做 逆时针 旋转,这种情况也可以称作为 正旋转。也可以通过右手螺旋法则,右手握拳,大拇指指向旋转轴的正方向,其余指头指向的方向就是正旋转方向。 设点p(x,y,z)绕z轴旋转β角度到p'(x',y',z'),设r未点p到原点的长度:
那么就有如下公式:
x = r * Math.cos(α);     // 式(1)
y = r * Math.sin(α);      // 式(2)
x' = r * Math.cos(α + β);
y' = r * Math.sin(α + β);
// 根据三角函数公式  sin(a + b) = sin(a)cos(b) - cos(a)sin(b) 可得:
x' = r * (Math.cos(α) * Math.cos(β) -  Math.sin(α) * Math.sin(β));
y' = r * (Math.sin(α) * Math.cos(β) +  Math.cos(α) * Math.sin(β));
// 由式1、2可得
x' = x * Math.cos(β) - y * Math.sin(β);
y' = x * Math.sin(β) + y * Math.cos(β);
z' = z;
因此可以在顶点着色器的GLSL代码中添加sinb和cosb变量,来对原始值进行计算。
attribute vec4 a_Position;
uniform float u_Sinb, u_Cosb;
void main() {
    gl.Position.x = a_Position.x * u_Cosb - a_Position.y * u_Sinb;
    gl.Position.y = a_Position.x * u_Sinb + a_Position.y * u_Cosb;
    gl.Position.z = a_Position.z;
    gl.Position.w = 1.0;
}
在js代码中需要获取gl变量并对其赋值:
let angle = Math.PI * 60 / 180;
let sinb = Math.sin(angle);
let cosb = Math.cos(angle);
let u_sinb = gl.getUniformLocation(gl.program, 'u_Sinb');
let u_cosb = gl.getUniformLocation(gl.program, 'u_Cosb');
gl.uniform1f(u_sinb, sinb);
gl.uniform1f(u_cosb, cosb);
点击查看在线示例

变换矩阵

对于简单的变化可以使用上面的三角函数计算公式等来处理,但是对于稍微复杂一些的变换,比如旋转后再平移,公式就会更复杂一些了,而且我们不可能针对于每一种变换都写新的等式实现新的着色器,所以需要借助于变换矩阵来实现。

平移矩阵

对于上面公式的求证如下:
x' = x + tx;
y' = y + ty;
z' = z + tz;
假设:
那么就有:
x' = ax + by + cz + d;
y' = ex + fy + gz + h;
z' = ix + jy + kz + l;
1 = mz + ny + oz + 1;
所以有:
a = 1, b = 0, c = 0, d = tx;
e = 0, f = 1, g = 0, h = ty;
i = 0, j = 0, k = 1, l = tz;
m = 0, n = 0, z = 0;

旋转矩阵

3x3阶的旋转矩阵公式:
对于上面公式的求证如下: 假设: 那么就有:
x' = ax + by + cz;
y' = dx + ey + fz;
z' = gx + hy + iz;
在上面有计算过:
x' = x * Math.cos(β) - y * Math.sin(β);
y' = x * Math.sin(β) + y * Math.cos(β);
z' = z;
所以:
a = cosβ;
b = -sinβ;
c = 0;
d = sinβ;
e = cosβ;
f = 0;
g = 0;
h = 0;
i = 1;
使用相同的原理可得4阶旋转矩阵为:

旋转或平移矩阵的使用

首先需要在GLSL中定义一个接收矩阵的变量:
attribute vec4 a_Position;
uniform mat4 u_Matrix;
void main() {
    gl_Position = u_Matrix * a_Position;
}
在js中创建一个 按列主序 的4阶矩阵:
var radian = Math.PI * Angle / 180.0;
var sinb = Math.sin(radian), cosb = Math.cos(radian);
var matrix = new Float32Array([
cosb, sinb, 0, 0,
-sinb, cosb, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]);

var u_Matrix = gl.getUniformLocation(gl.program, 'u_Matrix');
gl.uniformMatrix4fx(u_Matrix, false, matrix);
在webgl中,矩阵元素是按照按列主序存储在数组中的,按列主序就是从上到下然后再从左到右,按行主序就是先从左到右,再从上到下:
点击查看在线示例