WebGL 2.0 基于 OpenGL ES 3.0,提供了许多强大的新特性。
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 = document.createElement('canvas').getContext('webgl');
}| 特性 | 说明 |
|---|---|
| 3D 纹理 | 体积纹理支持 |
| 2D 纹理数组 | 纹理数组 |
| 深度纹理 | 深度缓冲区采样 |
| 实例化渲染 | 高效批量渲染 |
| 变换反馈 | 捕获顶点着色器输出 |
| 多渲染目标 | 同时渲染到多个纹理 |
| 整数纹理 | 整数纹理支持 |
| GLSL ES 3.0 | 新的着色器语言 |
| 顶点数组对象 | VAO 支持 |
| 采样器对象 | 独立的采样状态 |
#version 300 es
precision highp float;// 顶点着色器
#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);
}// WebGL 1.0
vec4 color = texture2D(u_Texture, v_TexCoord);
// WebGL 2.0
vec4 color = texture(u_Texture, v_TexCoord);VAO 存储顶点属性状态,减少 API 调用。
// 创建 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
gl.bindVertexArray(vao);
// 直接绘制,无需重新配置属性
gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_SHORT, 0);
// 解绑
gl.bindVertexArray(null);实例化渲染允许用一次绘制调用渲染多个相似物体。
// 设置实例数量
const instanceCount = 100;
// 使用 gl.drawArraysInstanced
gl.drawArraysInstanced(gl.TRIANGLES, 0, 3, instanceCount);
// 或使用 gl.drawElementsInstanced
gl.drawElementsInstanced(gl.TRIANGLES, indexCount, gl.UNSIGNED_SHORT, 0, instanceCount);为每个实例设置不同的属性:
#version 300 es
in vec3 a_Position;
in vec3 a_Offset; // 实例偏移量
void main() {
gl_Position = vec4(a_Position + a_Offset, 1.0);
}// 创建实例数据
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); // 每个实例更新一次实例化渲染相比多次绘制调用:
变换反馈(Transform Feedback)捕获顶点着色器的输出。
#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);
}// 指定要捕获的变量
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);同时渲染到多个纹理。
// 创建帧缓冲
const framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
// 创建多个纹理
// 注意:WebGL 2 中渲染到浮点纹理(RGBA16F)需先启用 EXT_color_buffer_float 扩展
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]);#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 纹理
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);#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 纹理的数组,类似于纹理图集。
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);#version 300 es
uniform sampler2DArray u_TextureArray;
void main() {
// 使用三维纹理坐标,z 分量是层级索引
vec4 color = texture(u_TextureArray, vec3(v_TexCoord, layerIndex));
}支持整数纹理格式。
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);#version 300 es
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);
}分离纹理数据和采样参数。
// 创建采样器
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允许采样深度缓冲区。
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);
// 深度纹理不可线性过滤(深度格式非 filterable),必须使用 NEAREST
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
// 附加到帧缓冲
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTexture, 0);#version 300 es
uniform sampler2D u_DepthTexture;
void main() {
float depth = texture(u_DepthTexture, v_TexCoord).r;
// 使用深度值
}高效传递大量 uniform 数据。
// 创建统一缓冲区
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);#version 300 es
layout(std140) uniform Matrices {
mat4 projection;
mat4 view;
};
void main() {
gl_Position = projection * view * vec4(a_Position, 1.0);
}const blockIndex = gl.getUniformBlockIndex(program, 'Matrices');
gl.uniformBlockBinding(program, blockIndex, 0); // 绑定到绑定点 0继续学习性能优化,掌握 WebGL 性能调优技巧。