WebGL编程指南学习(2),界明城,2022-3-2
2. 只是一点数学罢了
其实困扰我的,不是代码,而是数学——by某计算机科研狗
2.1 WebGL坐标系统
WebGL的X轴水平(正方向向右),Y轴垂直(正方向向上,书中有误),Z轴垂直于屏幕(正方向向外)
但WebGL好像是右手坐标系,又好像不完全是
2.2 移动、旋转和缩放
可以使用右手法则确定旋转方向
逆时针:观察者沿Z轴负方向进行观察,看到的物体是逆时针旋转的,这种情况又可称为正旋转
2.2.1 4×4的变换矩阵
数组存储矩阵元素可以按行主序,也可以按列主序
WebGL和OpenGL一样,矩阵元素是按列主序存储的
因此,旋转矩阵在数学上是这样的:
1.0
,
0.0
,
0.0
,
T
x
0.0
,
1.0
,
0.0
,
T
y
0.0
,
0.0
,
1.0
,
T
z
0.0
,
0
,
0
,
0.0
,
1.0
\begin{matrix} 1.0, 0.0, 0.0, Tx\\ 0.0,1.0, 0.0, Ty\\ 0.0, 0.0, 1.0, Tz\\ 0.0, 0,0, 0.0, 1.0 \end{matrix}
1.0,0.0,0.0,Tx0.0,1.0,0.0,Ty0.0,0.0,1.0,Tz0.0,0,0,0.0,1.0
但是在JavaScript中是这样的:
var xformMatrix = new Float32Array([
1.0, 0.0, 0.0, 0,0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
Tx, Ty, Yz, 1.0,
])
2.2.2 复习:变换矩阵的”通信“
Shader
在着色器中声明一个变换矩阵,使用uniform
attribute vec4 a_Position;
uniform mat4 u_xformMatrix;
void main(){
gl_Position = u_xformMatrix * a_Position;
}
JavaScript
- 获取变量在着色器中的位置
- 传输数据
var u_xformMatrix = gl.getUniformLocation(gl.program, 'u_xformMatrix');
...
gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix);
...
2.2.3 辅助变换矩阵的函数
《WebGL编程指南》的cuon-matrix.js中提供了一些符合数学思维的创建变换矩阵的方法。这些方法在OpenGL中有,但是WebGL给忘了。
例如:
setRotate(ANGLE, x_axis, y_axis, z_axis);
e.g.
xformMatrix.setRotate(90, 0, 0, 1) // 绕着Z轴正旋转90度
但是这些方法返回的是Matrix4对象,不是类型化数组(Float32Array,诸如此类),所以传输数据的时候还需要使用
xformMatrix.elements
e.g.
gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix.elements)
2.3 纹理坐标
WebGL中的纹理坐标系统是二维的,s轴水平向右,t轴垂直向上。坐标值与图像自身尺寸无关
纹理映射是把纹理坐标(s-t坐标系)映射到WebGL坐标系下(-0.5,0.5)
2.4 视点和视线
WebGL系统,默认情况下视点处于原点(0,0,0),视线为Z轴负半轴(指向屏幕内部)
确定观察者状态,需要3个自由度:视点(viewpoint)、观察目标点(Look-at)和上方向(up-direction)
- Viewpoint:(eyeX, eyeY, eyeZ)
- Look-at point: (atX, atY, atZ)
- Up-direction: (upX, upY, upZ)
上方向可以固定观察者。不指定上方向的话,观察者其实是可以沿着视线方向自由旋转的
WebGL利用上述三个矢量创建视图矩阵(view matrix)
Web编程指南提供的库文件里提供了创建视图矩阵的函数
setLookat
Matrix4.setLookAt(eyeX, eyeY, eyeZ, atX, atY, atZ, upX, upY, upZ)
WebGL默认:
- 视点位于坐标系统原点(0, 0, 0)
- 视线在Z轴负方向(0, 0, -1)
- 上方向为Y轴正方向(0, 1, 0)
- 根据自定义的观察者状态,渲染观察者看到的景象与使用默认的观察状态但是对三维对象进行变换再渲染观察者看到的景象,二者是等价的
在某个视点观察旋转的三角形,逻辑上是先旋转三角形,再对旋转后的三角形进行与“移动视点”等效的变换
<
旋
转
后
顶
点
坐
标
>
=
<
旋
转
矩
阵
>
×
<
原
始
顶
点
坐
标
>
<旋转后顶点坐标>=<旋转矩阵>\times<原始顶点坐标>
<旋转后顶点坐标>=<旋转矩阵>×<原始顶点坐标>
< “ 从 视 点 看 上 去 ” 的 旋 转 后 顶 点 坐 标 > = < 视 图 矩 阵 > × < 模 型 矩 阵 > × < 原 始 顶 点 坐 标 > <“从视点看上去”的旋转后顶点坐标>=<视图矩阵>\times<模型矩阵>\times<原始顶点坐标> <“从视点看上去”的旋转后顶点坐标>=<视图矩阵>×<模型矩阵>×<原始顶点坐标>
模型矩阵可以和视图矩阵提前计算,然后传给着色器,减少着色器里的乘法
<
模
型
视
图
矩
阵
>
=
<
视
图
矩
阵
>
×
<
模
型
矩
阵
>
<模型视图矩阵>=<视图矩阵>\times<模型矩阵>
<模型视图矩阵>=<视图矩阵>×<模型矩阵>
考察矩阵乘法的顺序
var modelViewMatrix = viewMatrix.multiply(modelMatrix);
var modelViewMatrix = modelMatrix.multiply(viewMatrix);
结论:multiply
是右乘,意思说a.multiply(b)
是
a
×
b
a \times b
a×b
再思考:对应模型视图变换的顺序,使用lib库函数的顺序确是反的,先执行的操作在右边:
var modelViewMatrix = new Matrix4();
modelViewMatrix.setLookAt(0.20, 0.25, 0.25, 0, 0, 0, 0, 1, 0).setRotate(-90, 0, 0, 1);