返回

斜面创造:赋予 3D 世界真实维度的艺术

前端

在 3D 场景中,面不只有水平面这一个,空间是由无数个面组成的,因此我们有可能会在任意一个面上放置物体,那么,空间中的面该如何确定呢?我们知道,空间中的面可以由一个点和一条法线组成。

斜面生成的基础

斜面的生成过程涉及到一些基本的数学知识,包括:

  • 点积: 点积是两个向量的乘积,计算公式为:A · B = |A| · |B| · cos(θ), 其中A和B是两个向量,|A|和|B|分别是它们的长度,θ是它们之间的夹角。
  • 法线: 法线是垂直于平面的向量。在三维空间中,法线可以由三个分量来表示:x、y 和 z。
  • 平面方程: 平面方程是一个数学方程,它了三维空间中的一个平面。平面方程可以由多种形式表示,其中最常见的是点法式方程:Ax + By + Cz + D = 0, 其中A、B、C和D是常数。

在 WebGL 中生成斜面

在 WebGL 中,我们可以使用以下步骤来生成斜面:

  1. 创建顶点数据: 首先,我们需要创建一个包含斜面顶点数据的数组。每个顶点数据包含一个位置和一个法线。
  2. 创建索引数据: 接下来,我们需要创建一个包含斜面索引数据的数组。索引数据告诉 WebGL 如何将顶点数据组合成三角形。
  3. 创建着色器程序: 着色器程序是 WebGL 用于渲染场景的程序。着色器程序由两个着色器组成:顶点着色器和片段着色器。顶点着色器负责计算每个顶点的位置和法线。片段着色器负责计算每个像素的颜色。
  4. 绑定数据: 将顶点数据、索引数据和着色器程序绑定到 WebGL 上下文。
  5. 绘制场景: 最后,我们可以使用 WebGL 的 drawElements 函数来绘制场景。

示例代码

以下是在 WebGL 中生成斜面的示例代码:

// 创建顶点数据
const vertices = [
  // 前面的顶点
  -1.0, -1.0,  1.0,
   1.0, -1.0,  1.0,
   1.0,  1.0,  1.0,
  -1.0,  1.0,  1.0,

  // 右侧的顶点
  1.0, -1.0,  1.0,
  1.0, -1.0, -1.0,
  1.0,  1.0, -1.0,
  1.0,  1.0,  1.0,

  // 后面的顶点
  1.0, -1.0, -1.0,
  -1.0, -1.0, -1.0,
  -1.0,  1.0, -1.0,
  1.0,  1.0, -1.0,

  // 左侧的顶点
  -1.0, -1.0, -1.0,
  -1.0, -1.0,  1.0,
  -1.0,  1.0,  1.0,
  -1.0,  1.0, -1.0,

  // 上面的顶点
  -1.0,  1.0,  1.0,
  1.0,  1.0,  1.0,
  1.0,  1.0, -1.0,
  -1.0,  1.0, -1.0,

  // 下面的顶点
  -1.0, -1.0, -1.0,
  1.0, -1.0, -1.0,
  1.0, -1.0,  1.0,
  -1.0, -1.0,  1.0
];

// 创建法线数据
const normals = [
  // 前面的法线
  0.0, 0.0,  1.0,
  0.0, 0.0,  1.0,
  0.0, 0.0,  1.0,
  0.0, 0.0,  1.0,

  // 右侧的法线
  1.0, 0.0, 0.0,
  1.0, 0.0, 0.0,
  1.0, 0.0, 0.0,
  1.0, 0.0, 0.0,

  // 后面的法线
  0.0, 0.0, -1.0,
  0.0, 0.0, -1.0,
  0.0, 0.0, -1.0,
  0.0, 0.0, -1.0,

  // 左侧的法线
  -1.0, 0.0, 0.0,
  -1.0, 0.0, 0.0,
  -1.0, 0.0, 0.0,
  -1.0, 0.0, 0.0,

  // 上面的法线
  0.0, 1.0, 0.0,
  0.0, 1.0, 0.0,
  0.0, 1.0, 0.0,
  0.0, 1.0, 0.0,

  // 下面的法线
  0.0, -1.0, 0.0,
  0.0, -1.0, 0.0,
  0.0, -1.0, 0.0,
  0.0, -1.0, 0.0
];

// 创建索引数据
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   // 下面
];

// 创建 WebGL 上下文
const canvas = document.getElementById('canvas');
const gl = canvas.getContext('webgl');

// 创建顶点缓冲区
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

// 创建法线缓冲区
const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);

// 创建索引缓冲区
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);

// 创建着色器程序
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, `
  attribute vec3 a_position;
  attribute vec3 a_normal;

  uniform mat4 u_modelViewMatrix;
  uniform mat4 u_projectionMatrix;

  varying vec3 v_position;
  varying vec3 v_normal;

  void main() {
    v_position = a_position;
    v_normal = a_normal;

    gl_Position = u_projectionMatrix * u_modelViewMatrix * vec4(a_position, 1.0);
  }
`);
gl