返回

解析仿射变换:绘制3D形体的核心数学知识

前端

在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);