{T}

WebGL 高级特性

WebGL 2.0 概述

WebGL 2.0 基于 OpenGL ES 3.0,提供了许多强大的新特性。

浏览器支持检测

javascript
function checkWebGL2() {
  const canvas = document.createElement('canvas');
  const gl = canvas.getContext('webgl2');
  return !!gl;
}

if (checkWebGL2()) {
  console.log('WebGL 2.0 支持');
} else {
  console.log('浏览器不支持 WebGL 2.0,降级到 WebGL 1.0');
  const gl = canvas.getContext('webgl');
}

WebGL 2.0 新特性列表

特性说明
3D 纹理体积纹理支持
2D 纹理数组纹理数组
深度纹理深度缓冲区采样
实例化渲染高效批量渲染
变换反馈捕获顶点着色器输出
多渲染目标同时渲染到多个纹理
整数纹理整数纹理支持
GLSL ES 3.0新的着色器语言
顶点数组对象VAO 支持
采样器对象独立的采样状态

GLSL ES 3.0

版本声明

glsl
#version 300 es
precision highp float;

新的变量限定符

glsl
// 顶点着色器
#version 300 es

in vec3 a_Position;    // 替代 attribute
in vec2 a_TexCoord;

out vec2 v_TexCoord;   // 替代 varying

void main() {
  gl_Position = vec4(a_Position, 1.0);
  v_TexCoord = a_TexCoord;
}

// 片段着色器
#version 300 es
precision highp float;

in vec2 v_TexCoord;    // 接收顶点着色器输出
out vec4 fragColor;    // 替代 gl_FragColor

uniform sampler2D u_Texture;

void main() {
  fragColor = texture(u_Texture, v_TexCoord);
}

新的纹理采样函数

glsl
// WebGL 1.0
vec4 color = texture2D(u_Texture, v_TexCoord);

// WebGL 2.0
vec4 color = texture(u_Texture, v_TexCoord);

顶点数组对象(VAO)

VAO 存储顶点属性状态,减少 API 调用。

创建 VAO

javascript
// 创建 VAO
const vao = gl.createVertexArray();

// 绑定 VAO
gl.bindVertexArray(vao);

// 配置顶点属性(这些状态会保存在 VAO 中)
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a_Position);

// 解绑 VAO
gl.bindVertexArray(null);

使用 VAO

javascript
// 渲染时绑定 VAO
gl.bindVertexArray(vao);

// 直接绘制,无需重新配置属性
gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_SHORT, 0);

// 解绑
gl.bindVertexArray(null);

VAO 优势

  • 减少 CPU-GPU 通信
  • 简化代码结构
  • 提升渲染性能

实例化渲染

实例化渲染允许用一次绘制调用渲染多个相似物体。

基本实例化

javascript
// 设置实例数量
const instanceCount = 100;

// 使用 gl.drawArraysInstanced
gl.drawArraysInstanced(gl.TRIANGLES, 0, 3, instanceCount);

// 或使用 gl.drawElementsInstanced
gl.drawElementsInstanced(gl.TRIANGLES, indexCount, gl.UNSIGNED_SHORT, 0, instanceCount);

实例化属性

为每个实例设置不同的属性:

glsl
#version 300 es

in vec3 a_Position;
in vec3 a_Offset;  // 实例偏移量

void main() {
  gl_Position = vec4(a_Position + a_Offset, 1.0);
}
javascript
// 创建实例数据
const offsets = new Float32Array(100 * 3);
for (let i = 0; i < 100; i++) {
  offsets[i * 3] = (Math.random() - 0.5) * 10;
  offsets[i * 3 + 1] = (Math.random() - 0.5) * 10;
  offsets[i * 3 + 2] = (Math.random() - 0.5) * 10;
}

// 配置实例属性
const offsetBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, offsetBuffer);
gl.bufferData(gl.ARRAY_BUFFER, offsets, gl.STATIC_DRAW);

const a_Offset = gl.getAttribLocation(program, 'a_Offset');
gl.enableVertexAttribArray(a_Offset);
gl.vertexAttribPointer(a_Offset, 3, gl.FLOAT, false, 0, 0);

// 设置实例化步长
gl.vertexAttribDivisor(a_Offset, 1);  // 每个实例更新一次

性能提升

实例化渲染相比多次绘制调用:

  • 减少 API 调用开销
  • 减少 CPU-GPU 数据传输
  • 显著提升渲染效率

变换反馈

变换反馈(Transform Feedback)捕获顶点着色器的输出。

基本用法

glsl
#version 300 es

in vec3 a_Position;
out vec3 v_Position;

void main() {
  v_Position = a_Position * 2.0;
  gl_Position = vec4(a_Position, 1.0);
}
javascript
// 指定要捕获的变量
const varyings = ['v_Position'];
gl.transformFeedbackVaryings(program, varyings, gl.SEPARATE_ATTRIBS);

// 链接程序
gl.linkProgram(program);

// 创建变换反馈缓冲区
const tfBuffer = gl.createBuffer();
gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer);
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 1000 * 3 * 4, gl.DYNAMIC_COPY);

// 创建变换反馈对象
const tf = gl.createTransformFeedback();
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer);

// 开始变换反馈
gl.beginTransformFeedback(gl.TRIANGLES);
gl.drawArrays(gl.TRIANGLES, 0, vertexCount);
gl.endTransformFeedback();

// 读取数据
const result = new Float32Array(1000 * 3);
gl.getBufferSubData(gl.TRANSFORM_FEEDBACK_BUFFER, 0, result);

应用场景

  • 粒子系统
  • 物理模拟
  • 几何着色器模拟
  • GPU 计算

多渲染目标(MRT)

同时渲染到多个纹理。

创建帧缓冲

javascript
// 创建帧缓冲
const framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);

// 创建多个纹理
const textures = [];
for (let i = 0; i < 2; i++) {
  const texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, texture);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA16F, width, height, 0, gl.RGBA, gl.FLOAT, null);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  
  // 附加到帧缓冲
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, texture, 0);
  
  textures.push(texture);
}

// 指定绘制缓冲区
gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]);

片段着色器输出

glsl
#version 300 es
precision highp float;

out vec4 fragColor0;  // 输出到 COLOR_ATTACHMENT0
out vec4 fragColor1;  // 输出到 COLOR_ATTACHMENT1

void main() {
  fragColor0 = vec4(1.0, 0.0, 0.0, 1.0);  // 正常颜色
  fragColor1 = vec4(0.0, 1.0, 0.0, 1.0);  // 法线或其他信息
}

应用场景

  • 延迟渲染
  • 后处理效果
  • 深度预传递

3D 纹理

体积纹理,常用于体积渲染、地形等。

创建 3D 纹理

javascript
// 创建 3D 纹理
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_3D, texture);

// 分配存储
const width = 64, height = 64, depth = 64;
const data = new Uint8Array(width * height * depth * 4);
gl.texImage3D(gl.TEXTURE_3D, 0, gl.RGBA, width, height, depth, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);

// 设置参数
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE);

在着色器中采样

glsl
#version 300 es
precision highp float;

uniform sampler3D u_VolumeTexture;
in vec3 v_TexCoord;
out vec4 fragColor;

void main() {
  fragColor = texture(u_VolumeTexture, v_TexCoord);
}

2D 纹理数组

多个 2D 纹理的数组,类似于纹理图集。

创建纹理数组

javascript
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D_ARRAY, texture);

const width = 256, height = 256, layers = 10;
gl.texImage3D(gl.TEXTURE_2D_ARRAY, 0, gl.RGBA, width, height, layers, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);

在着色器中使用

glsl
uniform sampler2DArray u_TextureArray;

void main() {
  // 使用三维纹理坐标,z 分量是层级索引
  vec4 color = texture(u_TextureArray, vec3(v_TexCoord, layerIndex));
}

整数纹理

支持整数纹理格式。

创建整数纹理

javascript
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32UI, width, height, 0, gl.RGBA_INTEGER, gl.UNSIGNED_INT, data);

在着色器中读取

glsl
uniform usampler2D u_IntegerTexture;

void main() {
  uvec4 intColor = texelFetch(u_IntegerTexture, ivec2(gl_FragCoord.xy), 0);
  fragColor = vec4(float(intColor.r) / 255.0, 0.0, 0.0, 1.0);
}

采样器对象

分离纹理数据和采样参数。

创建采样器对象

javascript
// 创建采样器
const sampler = gl.createSampler();

// 设置采样参数
gl.samplerParameteri(sampler, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
gl.samplerParameteri(sampler, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.samplerParameteri(sampler, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.samplerParameteri(sampler, gl.TEXTURE_WRAP_T, gl.REPEAT);

// 绑定采样器到纹理单元
gl.bindSampler(0, sampler);  // 绑定到纹理单元 0

优势

  • 多个纹理共享采样参数
  • 动态切换采样方式
  • 更灵活的纹理管理

深度纹理

允许采样深度缓冲区。

创建深度纹理

javascript
const depthTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, depthTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT24, width, height, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

// 附加到帧缓冲
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTexture, 0);

在着色器中采样

glsl
uniform sampler2D u_DepthTexture;

void main() {
  float depth = texture(u_DepthTexture, v_TexCoord).r;
  // 使用深度值
}

统一缓冲区对象(UBO)

高效传递大量 uniform 数据。

创建 UBO

javascript
// 创建统一缓冲区
const ubo = gl.createBuffer();
gl.bindBuffer(gl.UNIFORM_BUFFER, ubo);
gl.bufferData(gl.UNIFORM_BUFFER, 256, gl.DYNAMIC_DRAW);

// 绑定到绑定点
gl.bindBufferBase(gl.UNIFORM_BUFFER, 0, ubo);

在着色器中使用

glsl
layout(std140) uniform Matrices {
  mat4 projection;
  mat4 view;
};

void main() {
  gl_Position = projection * view * vec4(a_Position, 1.0);
}

绑定 UBO 到着色器

javascript
const blockIndex = gl.getUniformBlockIndex(program, 'Matrices');
gl.uniformBlockBinding(program, blockIndex, 0);  // 绑定到绑定点 0

小结

WebGL 2.0 核心特性

  • VAO:顶点数组对象,简化状态管理
  • 实例化渲染:批量渲染优化
  • 变换反馈:GPU 计算
  • MRT:多渲染目标
  • 高级纹理:3D 纹理、纹理数组、深度纹理

性能优化

  1. 使用 VAO:减少状态切换
  2. 实例化渲染:批量绘制相似物体
  3. UBO:高效传递 uniform
  4. 变换反馈:GPU 端计算

兼容性考虑

  • 检测 WebGL 2.0 支持
  • 提供降级方案
  • Polyfill 或替代实现

下一步

继续学习性能优化,掌握 WebGL 性能调优技巧。