1. 3D数学基础

向量

// 3D向量
var direction: vec3f = vec3f(1.0, 0.0, 0.0);
 
// 归一化
let normalized = normalize(direction);
 
// 点积(投影)
let dotProduct = dot(direction, vec3f(0.0, 1.0, 0.0));  // = 0
 
// 叉积(法向量)
let normal = cross(vec3f(1.0, 0.0, 0.0), vec3f(0.0, 1.0, 0.0));  // = (0, 0, 1)

矩阵运算

// 创建4x4矩阵
var matrix: mat4x4f = mat4x4f(
  1.0, 0.0, 0.0, 0.0,  // 第一列
  0.0, 1.0, 0.0, 0.0,  // 第二列
  0.0, 0.0, 1.0, 0.0,  // 第三列
  0.0, 0.0, 0.0, 1.0   // 第四列
);
 
// 矩阵乘法
let result = matrixA * matrixB;
 
// 逆矩阵
let inverseMatrix = inverse(matrix);
 
// 转置矩阵
let transposedMatrix = transpose(matrix);

2. 矩阵变换

变换矩阵类型

变换矩阵形式
平移[1,0,0,tx; 0,1,0,ty; 0,0,1,tz; 0,0,0,1]
缩放[sx,0,0,0; 0,sy,0,0; 0,0,sz,0; 0,0,0,1]
旋转X[1,0,0,0; 0,cos,-sin,0; 0,sin,cos,0; 0,0,0,1]
旋转Y[cos,0,sin,0; 0,1,0,0; -sin,0,cos,0; 0,0,0,1]
旋转Z[cos,-sin,0,0; sin,cos,0,0; 0,0,1,0; 0,0,0,1]

WGSL变换函数

// 平移矩阵
fn translate(d: vec3f) -> mat4x4f {
  return mat4x4f(
    1.0, 0.0, 0.0, 0.0,
    0.0, 1.0, 0.0, 0.0,
    0.0, 0.0, 1.0, 0.0,
    d.x, d.y, d.z, 1.0
  );
}
 
// 缩放矩阵
fn scale(s: vec3f) -> mat4x4f {
  return mat4x4f(
    s.x, 0.0, 0.0, 0.0,
    0.0, s.y, 0.0, 0.0,
    0.0, 0.0, s.z, 0.0,
    0.0, 0.0, 0.0, 1.0
  );
}
 
// 绕Y轴旋转
fn rotateY(angle: f32) -> mat4x4f {
  let c = cos(angle);
  let s = sin(angle);
  return mat4x4f(
    c, 0.0, s, 0.0,
    0.0, 1.0, 0.0, 0.0,
    -s, 0.0, c, 0.0,
    0.0, 0.0, 0.0, 1.0
  );
}

3. 投影变换

正交投影

fn ortho(left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) -> mat4x4f {
  let lr = 1.0 / (right - left);
  let bt = 1.0 / (top - bottom);
  let nf = 1.0 / (near - far);
  return mat4x4f(
    2.0 * lr, 0.0, 0.0, 0.0,
    0.0, 2.0 * bt, 0.0, 0.0,
    0.0, 0.0, 2.0 * nf, 0.0,
    -(right + left) * lr, -(top + bottom) * bt, (far + near) * nf, 1.0
  );
}

透视投影

fn perspective(fovY: f32, aspect: f32, near: f32, far: f32) -> mat4x4f {
  let f = 1.0 / tan(fovY / 2.0);
  let nf = 1.0 / (near - far);
  return mat4x4f(
    f / aspect, 0.0, 0.0, 0.0,
    0.0, f, 0.0, 0.0,
    0.0, 0.0, (far + near) * nf, -1.0,
    0.0, 0.0, 2.0 * far * near * nf, 0.0
  );
}

视图矩阵(LookAt)

fn lookAt(eye: vec3f, center: vec3f, up: vec3f) -> mat4x4f {
  let z = normalize(eye - center);      // Z轴(视线方向)
  let x = normalize(cross(up, z));     // X轴
  let y = cross(z, x);                 // Y轴
  return mat4x4f(
    x.x, y.x, z.x, 0.0,
    x.y, y.y, z.y, 0.0,
    x.z, y.z, z.z, 0.0,
    -dot(x, eye), -dot(y, eye), -dot(z, eye), 1.0
  );
}

4. Uniform缓冲

创建Uniform缓冲

const uniformBufferSize = 256;  // 对齐到256字节边界
const uniformBuffer = device.createBuffer({
  size: uniformBufferSize,
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
 
// Uniform数据布局
const uniformData = new Float32Array([
  // 视图投影矩阵 (16 floats)
  ...viewProjectionMatrix,
  // 模型矩阵 (16 floats)
  ...modelMatrix,
  // 其他uniforms...
]);

着色器中使用Uniform

struct Uniforms {
  viewProjection: mat4x4f,
  model: mat4x4f,
  normalMatrix: mat3x3f,
}
 
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
 
@vertex
fn vertexMain(input: VertexInput) -> VertexOutput {
  var output: VertexOutput;
  output.position = uniforms.viewProjection * uniforms.model * vec4f(input.position, 1.0);
  output.normal = uniforms.normalMatrix * input.normal;
  return output;
}

5. 光照模型

方向光

struct DirectionalLight {
  direction: vec3f,
  color: vec3f,
}
 
fn calcDirectionalLight(
  light: DirectionalLight,
  normal: vec3f,
  viewDir: vec3f,
  baseColor: vec3f
) -> vec3f {
  // 漫反射
  let NdotL = max(dot(normal, light.direction), 0.0);
  let diffuse = baseColor * light.color * NdotL;
  
  // 高光反射
  let reflectDir = reflect(-light.direction, normal);
  let VdotR = pow(max(dot(viewDir, reflectDir), 0.0), 32.0);
  let specular = light.color * VdotR * 0.5;
  
  return diffuse + specular;
}

点光源

struct PointLight {
  position: vec3f,
  color: vec3f,
  constant: f32,
  linear: f32,
  quadratic: f32,
}
 
fn calcPointLight(
  light: PointLight,
  fragPos: vec3f,
  normal: vec3f,
  viewDir: vec3f,
  baseColor: vec3f
) -> vec3f {
  let lightDir = normalize(light.position - fragPos);
  
  // 漫反射
  let diff = max(dot(normal, lightDir), 0.0);
  
  // 衰减
  let distance = length(light.position - fragPos);
  let attenuation = 1.0 / (light.constant + light.linear * distance + 
                            light.quadratic * distance * distance);
  
  return baseColor * light.color * diff * attenuation;
}

聚光灯

struct SpotLight {
  position: vec3f,
  direction: vec3f,
  color: vec3f,
  cutoff: f32,      // 余弦值
  outerCutoff: f32,
}
 
fn calcSpotLight(
  light: SpotLight,
  fragPos: vec3f,
  normal: vec3f,
  viewDir: vec3f,
  baseColor: vec3f
) -> vec3f {
  let lightDir = normalize(light.position - fragPos);
  
  // 漫反射
  let diff = max(dot(normal, lightDir), 0.0);
  
  // 聚光角度
  let theta = dot(lightDir, -light.direction);
  let epsilon = light.cutoff - light.outerCutoff;
  let intensity = clamp((theta - light.outerCutoff) / epsilon, 0.0, 1.0);
  
  return baseColor * light.color * diff * intensity;
}

6. 深度测试与混合

配置深度测试

const pipeline = device.createRenderPipeline({
  // ...
  primitive: {
    topology: 'triangle-list',
    cullMode: 'back',  // 'none' | 'front' | 'back'
    frontFace: 'ccw', // 'ccw' | 'cw'
  },
  depthStencil: {
    format: 'depth24plus',
    depthWriteEnabled: true,
    depthCompare: 'less',  // 'never' | 'less' | 'equal' | 'less-equal' | 'greater' | etc.
  },
});

混合模式

const pipeline = device.createRenderPipeline({
  // ...
  fragment: {
    // ...
    targets: [{
      format: format,
      blend: {
        color: {
          srcFactor: 'src-alpha',
          dstFactor: 'one-minus-src-alpha',
          operation: 'add',
        },
        alpha: {
          srcFactor: 'one',
          dstFactor: 'one-minus-src-alpha',
          operation: 'add',
        },
      },
    }],
  },
});

7. 场景管理与对象

简单场景图

class SceneObject {
  constructor(mesh, material) {
    this.mesh = mesh;
    this.material = material;
    this.transform = mat4.create();
    this.children = [];
  }
  
  addChild(child) {
    this.children.push(child);
  }
  
  getWorldMatrix() {
    return this.transform;
  }
}
 
class Camera {
  constructor() {
    this.position = vec3.create(0, 0, 5);
    this.target = vec3.create(0, 0, 0);
    this.up = vec3.create(0, 1, 0);
    this.viewMatrix = mat4.create();
    this.projectionMatrix = mat4.create();
  }
  
  updateViewMatrix() {
    this.viewMatrix = mat4.lookAt(this.position, this.target, this.up);
  }
}

8. 实战:3D立方体渲染

顶点数据

const vertices = new Float32Array([
  // 位置 (x,y,z)          // 法向量 (nx,ny,nz)
  // 前面
  -0.5, -0.5,  0.5,       0.0, 0.0, 1.0,
   0.5, -0.5,  0.5,       0.0, 0.0, 1.0,
   0.5,  0.5,  0.5,       0.0, 0.0, 1.0,
  -0.5,  0.5,  0.5,       0.0, 0.0, 1.0,
  // 后面
   0.5, -0.5, -0.5,       0.0, 0.0, -1.0,
  -0.5, -0.5, -0.5,       0.0, 0.0, -1.0,
  -0.5,  0.5, -0.5,       0.0, 0.0, -1.0,
   0.5,  0.5, -0.5,       0.0, 0.0, -1.0,
  // 其他面...
]);
 
const indices = new Uint16Array([
  // 前面
  0, 1, 2,  2, 3, 0,
  // 后面
  4, 5, 6,  6, 7, 4,
  // 顶面
  3, 2, 7,  7, 6, 3,
  // 底面
  5, 4, 0,  0, 4, 5,
  // 右面
  1, 4, 7,  7, 2, 1,
  // 左面
  5, 0, 3,  3, 6, 5,
]);

完整着色器

struct Uniforms {
  viewProjection: mat4x4f,
  model: mat4x4f,
  normalMatrix: mat3x3f,
  lightDir: vec3f,
  lightColor: vec3f,
}
 
struct VertexInput {
  @location(0) position: vec3f,
  @location(1) normal: vec3f,
}
 
struct VertexOutput {
  @builtin(position) position: vec4f,
  @location(0) worldPos: vec3f,
  @location(1) normal: vec3f,
}
 
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
 
@vertex
fn vertexMain(input: VertexInput) -> VertexOutput {
  var output: VertexOutput;
  let worldPos = uniforms.model * vec4f(input.position, 1.0);
  output.position = uniforms.viewProjection * worldPos;
  output.worldPos = worldPos.xyz;
  output.normal = uniforms.normalMatrix * input.normal;
  return output;
}
 
@fragment
fn fragmentMain(input: VertexOutput) -> @location(0) vec4f {
  // 基础颜色
  let baseColor = vec3f(0.8, 0.2, 0.2);
  
  // 归一化法向量
  let N = normalize(input.normal);
  let L = normalize(uniforms.lightDir);
  let V = normalize(vec3f(0.0, 0.0, 5.0) - input.worldPos);
  
  // 漫反射
  let diff = max(dot(N, L), 0.0);
  let diffuse = baseColor * uniforms.lightColor * diff;
  
  // 环境光
  let ambient = baseColor * 0.1;
  
  return vec4f(diffuse + ambient, 1.0);
}

渲染循环

function render() {
  const commandEncoder = device.createCommandEncoder();
  
  const renderPass = commandEncoder.beginRenderPass({
    colorAttachments: [{
      view: context.getCurrentTexture().createView(),
      loadOp: 'clear',
      storeOp: 'store',
    }],
    depthStencilAttachment: {
      view: depthTexture.createView(),
      depthLoadOp: 'clear',
      depthClearValue: 1.0,
      depthStoreOp: 'store',
    },
  });
  
  // 更新Uniforms
  const t = performance.now() / 1000;
  const modelMatrix = rotateY(t);
  const normalMatrix = mat3.fromMat4(modelMatrix);
  device.queue.writeBuffer(uniformBuffer, 0, new Float32Array([
    ...projectionMatrix,
    ...viewMatrix,
    ...modelMatrix,
    ...normalMatrix,
    Math.cos(t), Math.sin(t), 1.0,  // lightDir
    1.0, 1.0, 1.0,                   // lightColor
  ]));
  
  renderPass.setPipeline(pipeline);
  renderPass.setVertexBuffer(0, vertexBuffer);
  renderPass.setIndexBuffer(indexBuffer, 'uint16');
  renderPass.setBindGroup(0, bindGroup);
  renderPass.drawIndexed(36);
  
  renderPass.end();
  device.queue.submit([commandEncoder.finish()]);
  
  requestAnimationFrame(render);
}

9. 性能优化

减少Draw Call

  • 实例化渲染:使用 drawIndexedInstanced
  • 批量处理:合并相同材质的对象
  • LOD系统:远处对象使用简化模型

纹理优化

  • MIP Map:减少远处纹理闪烁
  • 纹理图集:合并小纹理减少采样
  • 压缩纹理:使用BC/DXT/ASTC格式

着色器优化

  • 避免分支select() 替代 if
  • 减少纹理采样:缓存计算结果
  • 使用uster存储:高频访问数据放入uster

参考资料