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 纹理、纹理数组、深度纹理
性能优化
- 使用 VAO:减少状态切换
- 实例化渲染:批量绘制相似物体
- UBO:高效传递 uniform
- 变换反馈:GPU 端计算
兼容性考虑
- 检测 WebGL 2.0 支持
- 提供降级方案
- Polyfill 或替代实现
下一步
继续学习性能优化,掌握 WebGL 性能调优技巧。