返回
解析仿射变换:绘制3D形体的核心数学知识
前端
2023-10-19 18:36:43
在3D图形学中,仿射变换是一种将点从一个坐标系转换到另一个坐标系的数学运算。仿射变换包括平移、缩放和旋转三种基本类型。
平移
平移是将点沿直线移动一定距离的操作。平移矩阵如下所示:
[1 0 0 Tx]
[0 1 0 Ty]
[0 0 1 Tz]
[0 0 0 1]
其中,Tx、Ty和Tz分别是平移距离。
缩放
缩放是将点沿直线放大或缩小一定倍数的操作。缩放矩阵如下所示:
[Sx 0 0 0]
[0 Sy 0 0]
[0 0 Sz 0]
[0 0 0 1]
其中,Sx、Sy和Sz分别是缩放比例。
旋转
旋转是将点绕轴旋转一定角度的操作。旋转矩阵如下所示:
[cosθ -sinθ 0 0]
[sinθ cosθ 0 0]
[0 0 1 0]
[0 0 0 1]
其中,θ是旋转角度。
仿射变换可以通过将平移、缩放和旋转矩阵相乘来组合。例如,以下矩阵将点沿x轴平移10个单位,沿y轴缩放2倍,并绕z轴旋转45度:
[cos45° -sin45° 0 10]
[sin45° cos45° 0 0]
[0 0 1 0]
[0 0 0 1]
仿射变换在3D图形学中有着广泛的应用。例如,我们可以使用仿射变换来对3D物体进行平移、缩放和旋转,从而实现3D物体的绘制。此外,仿射变换还可以用于实现3D场景的变换,例如,我们可以使用仿射变换来改变摄像机的视角。
WebGL是一种用于渲染3D图形的JavaScript API。WebGL使用仿射变换来实现3D物体的绘制。在WebGL中,我们可以使用translate()
、scale()
和rotate()
函数来对3D物体进行平移、缩放和旋转。
以下代码演示了如何使用WebGL绘制一个立方体:
// 创建WebGL上下文
const gl = canvas.getContext('webgl');
// 定义立方体的顶点坐标
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,
];
// 定义立方体的索引
const indices = [
0, 1, 2, 0, 2, 3, // 前
4, 5, 6, 4, 6, 7, // 后
8, 9, 10, 8, 10, 11, // 上
12, 13, 14, 12, 14, 15, // 下
16, 17, 18, 16, 18, 19, // 左
20, 21, 22, 20, 22, 23, // 右
];
// 创建缓冲区对象
const vertexBuffer = gl.createBuffer();
const indexBuffer = gl.createBuffer();
// 绑定缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
// 填充缓冲区对象
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
// 创建着色器程序
const program = gl.createProgram();
// 加载着色器代码
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vertexShader, `
attribute vec3 a_position;
uniform mat4 u_modelViewMatrix;
uniform mat4 u_projectionMatrix;
void main() {
gl_Position = u_projectionMatrix * u_modelViewMatrix * vec4(a_position, 1.0);
}
`);
gl.shaderSource(fragmentShader, `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`);
// 编译着色器
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
// 附加着色器到程序
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// 链接程序
gl.linkProgram(program);
// 使用程序
gl.useProgram(program);
// 获取属性位置
const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
// 启用属性
gl.enableVertexAttribArray(positionAttributeLocation);
// 设置属性数据
gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0);
// 获取uniform位置
const modelViewMatrixUniformLocation = gl.getUniformLocation(program, "u_modelViewMatrix");
const projectionMatrixUniformLocation = gl.getUniformLocation(program, "u_projectionMatrix");
// 设置uniform数据
const modelViewMatrix = mat4.create();
const projectionMatrix = mat4.create();
mat4.translate(modelViewMatrix, modelViewMatrix, [0, 0, -5]);
mat4.perspective(projectionMatrix, 45 * Math.PI / 180, canvas.width / canvas.height, 0.1, 100.0);
gl.uniformMatrix4fv(modelViewMatrixUniformLocation, false, modelViewMatrix);
gl.uniformMatrix4fv(projectionMatrixUniformLocation, false, projectionMatrix);
// 清空颜色缓冲区
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// 绘制立方体
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);