目录
- 坐标系统
- 向量(Vector)
- 矩阵(Matrix)
- 平移(Translation)
- 旋转(Rotation)
- 缩放(Scaling)
- 组合变换
- 顶点着色器中的变换
- 矩阵堆栈
- 向量和矩阵操作库
- 法线(Normals)
- 纹理坐标
- 视口变换
- 深度测试
- 混合
- 深度缓冲
- 裁剪
- 视口变换
- 投影
- 视口变换
- 视锥体剔除
- 剔除
- 顶点缓冲对象
- 索引缓冲对象
- 纹理单元
在WebGL中,3D数学是不可或缺的一部分,因为它涉及到了几何、变换和着色的计算。以下是3D数学基础的一些关键概念以及在WebGL中的应用:
坐标系统:
右手坐标系统:WebGL使用右手坐标系统,其中X轴正方向指向右,Y轴正方向向上,Z轴正方向从屏幕向外(通常是显示器的后方)。
向量(Vector):
向量表示方向和大小,通常以数组形式表示,如[x, y, z]。在WebGL中,向量用于表示位置、速度、颜色等。
矩阵(Matrix):
矩阵用于描述变换,如平移、旋转和缩放。在WebGL中,我们通常使用4x4的矩阵,因为它们可以同时处理位置和变换。
平移(Translation):
平移矩阵可以通过将原点平移到新的位置来创建。例如,平移向量[tx, ty, tz]
的矩阵是:
var translateMatrix = mat4.create();
mat4.translate(translateMatrix, translateMatrix, [tx, ty, tz]);
旋转(Rotation):
旋转可以通过绕某个轴进行。例如,绕X轴旋转angle度的矩阵是:
var rotateXMatrix = mat4.create();
mat4.rotateX(rotateXMatrix, rotateXMatrix, angle);
对于绕Y轴和Z轴的旋转,有mat4.rotateY()和mat4.rotateZ()。
缩放(Scaling):
缩放矩阵可以沿每个轴独立缩放。例如,缩放因子[sx, sy, sz]的矩阵是:
var scaleMatrix = mat4.create();
mat4.scale(scaleMatrix, scaleMatrix, [sx, sy, sz]);
组合变换:
可以通过矩阵乘法组合多个变换。例如,先平移再旋转:
var transformationMatrix = mat4.create();
mat4.multiply(transformationMatrix, translateMatrix, rotateXMatrix);
顶点着色器中的变换:
在顶点着色器中,我们通常将模型空间中的顶点坐标通过模型视图投影矩阵(model-view-projection matrix, MVP)转换到裁剪空间:
attribute vec3 a_position;
uniform mat4 u_modelMatrix;
uniform mat4 u_viewMatrix;
uniform mat4 u_projectionMatrix;
void main() {
gl_Position = u_projectionMatrix * u_viewMatrix * u_modelMatrix * vec4(a_position, 1.0);
}
矩阵堆栈:
在实际应用中,为了方便管理变换,通常会使用矩阵堆栈(例如,mat4.perspective()
,mat4.lookAt()
等),它们可以帮助你更简洁地组织和回滚变换。
向量和矩阵操作库:
在JavaScript中,可以使用gl-matrix这样的库来处理3D数学。例如,上述代码中的mat4.create()
,mat4.translate()
等都是gl-matrix库的方法。
法线(Normals):
法线向量表示表面的切线方向,用于计算光照。在WebGL中,我们需要将法线从模型空间转换到视图空间,以便正确地计算光照。在顶点着色器中,这通常如下所示:
attribute vec3 a_normal;
varying vec3 v_normal;
uniform mat4 u_normalMatrix;
void main() {
v_normal = normalize(mat3(u_normalMatrix) * a_normal);
// ...其他变换...
}
- 注意,法线矩阵(
u_normalMatrix
)通常是模型视图矩阵的逆转置,以保持法线的方向。
纹理坐标(Texture Coordinates):
- 纹理坐标用于将2D纹理映射到3D模型的表面。在顶点着色器中,我们需要将纹理坐标传递给片段着色器:
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
void main() {
// ...其他变换...
v_texCoord = a_texCoord;
}
- 在片段着色器中,我们可以使用纹理坐标来采样纹理:
uniform sampler2D u_texture;
varying vec2 v_texCoord;
void main() {
gl_FragColor = texture2D(u_texture, v_texCoord);
}
视口变换:
视口变换将裁剪空间的坐标转换为屏幕空间。这是通过gl.viewport()
函数完成的,它设置了WebGL渲染的目标区域。
深度测试(Depth Testing):
深度测试用于确定哪些像素应该覆盖其他像素。WebGL默认开启深度测试,你可以通过gl.enable(gl.DEPTH_TEST)
启用或禁用它。
混合(Blending):
混合允许你将新绘制的颜色与现有颜色混合,创建透明效果。这通常在绘制具有alpha通道的纹理时使用:
gl.enable(gl.BLEND);
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
深度缓冲(Depth Buffer):
深度缓冲用于存储每个像素的深度值,用于深度测试。WebGL默认创建了深度缓冲,但你可以检查并配置它:
gl.clearDepth(1.0);
gl.clear(gl.DEPTH_BUFFER_BIT);
裁剪(Clipping):
裁剪用于限制渲染的区域。你可以使用gl.enable(gl.SCISSOR_TEST)
和gl.scissor()
来设置裁剪区域。
视口变换(Viewport Transform):
视口变换将裁剪空间的坐标映射到设备像素。默认情况下,这由WebGL自动处理,但你可以通过gl.viewport()
自定义。
投影(Projection):
投影用于将3D空间中的点转换为2D屏幕空间。在WebGL中,常见的投影类型有透视投影和正交投影。透视投影用于模拟人的远近感知,而正交投影则用于创建等比例缩放的2D视图。
透视投影通常用于3D场景,使用mat4.perspective()
生成:
var fieldOfView = 45 * Math.PI / 180; // 45度
var aspect = canvas.width / canvas.height; // 布局宽高比
var near = 0.1; // 近裁剪平面
var far = 100; // 远裁剪平面
var projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix, fieldOfView, aspect, near, far);
正交投影用于2D场景或等比例缩放的3D场景,使用mat4.ortho()
生成:
var left = -1; // 左边界
var right = 1; // 右边界
var bottom = -1; // 下边界
var top = 1; // 上边界
var near = -1; // 近裁剪平面
var far = 1; // 远裁剪平面
var orthoMatrix = mat4.create();
mat4.ortho(orthoMatrix, left, right, bottom, top, near, far);
视锥体剔除(Frustum Culling):
视锥体剔除是一种优化技术,用于避免渲染超出视锥体的几何体。WebGL的深度测试已经包含了一些基本的视锥体剔除,但你也可以手动检查每个物体是否在视锥体内。
剔除(Culling):
剔除是另一种优化技术,用于避免渲染不可见的面。WebGL支持背面剔除(back-face culling),它默认开启,可以检查每个面的法线是否面向观察者。
顶点缓冲对象(Vertex Buffer Objects, VBOs):
顶点缓冲对象用于存储顶点数据,可以提高性能,减少内存拷贝。使用gl.bindBuffer()和gl.bufferData()创建和绑定VBO:
var vertices = ... // 顶点数据
var vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
索引缓冲对象(Element Array Buffers, EBOs):
索引缓冲对象用于指定顶点的顺序,从而创建多边形。使用gl.bindBuffer()和gl.bufferData()创建和绑定EBO:
var indices = ... // 索引数据
var indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
纹理单元(Texture Units):
WebGL支持多个纹理单元,可以同时应用多个纹理。使用gl.activeTexture()
和gl.uniform1i()
切换和设置纹理单元:
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture1);
gl.uniform1i(shader.uniforms.u_texture1, 0);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, texture2);
gl.uniform1i(shader.uniforms.u_texture2, 1);