从 WebGL 到 WebGPU
原文标题:WebGPU from WebGL
原作者:webgpufundamentals Contributors
原始链接:https://webgpufundamentals.org/webgpu/lessons/webgpu-from-webgl.html
本文面向已经熟悉 WebGL、希望开始使用 WebGPU 的开发者。
如果你从 WebGL 转向 WebGPU,会发现许多概念是相通的。两者都允许你在 GPU 上运行小型函数。WebGL 有顶点着色器(vertex shader)和片元着色器(fragment shader);WebGPU 除此之外还有计算着色器(compute shader)。WebGL 使用 GLSL 作为着色语言,而 WebGPU 使用 WGSL。虽然语言不同,但核心概念大体一致。
两种 API 都有属性(attribute)——从缓冲区(buffer)中取出数据并喂给顶点着色器的每次迭代;都有统一变量(uniform)——被着色器函数的所有迭代共享的值;都有可变变量(varying)——从顶点着色器传递到片元着色器,并在光栅化时进行插值;都有纹理(texture)和采样器(sampler)——提供 2D/3D 数据并采样(将多个像素过滤为一个值);都能渲染到纹理;并且都有大量关于像素混合(blending)、深度缓冲(depth buffer)、模板缓冲(stencil buffer)的设置。
最大的区别在于:WebGL 是有状态 API,而 WebGPU 不是。在 WebGL 中,存在大量全局状态:当前绑定的纹理、缓冲区、程序,以及混合、深度、模板设置等。通过调用 gl.bindBuffer、gl.enable、gl.blendFunc 等 API 来设置,并且它们会全局保持,直到被改变。
相反,WebGPU 几乎没有全局状态。取而代之的是**渲染管线(render pipeline)和渲染通道(render pass)**的概念,它们一起包含了 WebGL 中原本全局的大部分状态。纹理、属性、缓冲区以及各种其他设置都包含在其中。未显式设置的选项使用默认值。管线不可修改——创建后即为不可变(immutable)对象,若需要不同设置则必须创建新的管线。渲染通道也有状态,但仅限于该通道内部。
第二大区别是 WebGPU 更底层。WebGL 中许多事物是通过名字连接的。例如,在 GLSL 中声明一个 uniform,然后查询它的位置:
loc = gl.getUniformLocation(program, 'nameOfUniform');
另一个例子是 varying:在顶点着色器中使用 varying vec2 v_texcoord 或 out vec2 v_texcoord,在片元着色器中声明对应的 varying 并命名为 v_texcoord。好处是拼写错误会报错。
而 WebGPU 完全通过索引或字节偏移来连接。你不会像 WebGL 那样创建单独的 uniform,而是声明 uniform 块(uniform block,即声明 uniform 的结构体)。然后由你负责手动组织传递给着色器的数据以匹配该结构。注意:WebGL2 也有类似概念(Uniform Blocks),但 WebGL2 仍然支持按名字访问 uniform;且即便需要字节偏移,WebGL2 也能查询偏移量和块位置。WebGPU 则一切靠字节偏移或索引(常称为“位置 location”),且没有查询 API,完全靠手动保持同步。
用 JavaScript 类比:
function likeWebGL(inputs) {
const {position, texcoords, normal, color} = inputs;
...
}
function likeWebGPU(inputs) {
const [position, texcoords, normal, color] = inputs;
...
}
likeWebGL 按名字连接,参数顺序无关,也能跳过缺失参数;likeWebGPU 则按数组索引传递,必须知道每个参数的索引位置。在 WebGPU 中,保持 WGSL 内部与 JavaScript/WASM 外部的位置同步完全由你负责。
其他显著差异
-
画布(Canvas)
WebGL 为你管理画布,创建上下文时选择抗锯齿、preserveDrawingBuffer、模板、深度、alpha,之后只需设置canvas.width和canvas.height。
WebGPU 需要自己完成大部分工作:深度缓冲需自己创建(可带模板缓冲),抗锯齿需自己创建多重采样纹理并解析到画布纹理。但因此一个 WebGPU 设备可以渲染到多个画布。 -
WebGPU 不生成 mipmap
WebGL 中可以调用gl.generateMipmap自动生成所有 mip 层级;WebGPU 没有此函数,需要自行生成(参考本文)。 -
WebGPU 强制要求采样器(sampler)
WebGL1 中没有采样器概念(由内部处理),WebGL2 可选,WebGPU 必需。 -
缓冲区和纹理不可调整大小
WebGL 可在运行时重新分配缓冲区或纹理;WebGPU 中纹理和缓冲区的大小、用途、格式不可变,只能修改内容。需要更新尺寸时必须销毁旧资源并创建新资源。
着色器对比
下面是一个绘制带纹理、受光照三角形的着色器,GLSL 与 WGSL 对照:
// GLSL
const vSrc = `
uniform mat4 u_worldViewProjection;
uniform mat4 u_worldInverseTranspose;
attribute vec4 a_position;
attribute vec3 a_normal;
attribute vec2 a_texcoord;
varying vec2 v_texCoord;
varying vec3 v_normal;
void main() {
gl_Position = u_worldViewProjection * a_position;
v_texCoord = a_texcoord;
v_normal = (u_worldInverseTranspose * vec4(a_normal, 0)).xyz;
}
`;
const fSrc = `
precision highp float;
varying vec2 v_texCoord;
varying vec3 v_normal;
uniform sampler2D u_diffuse;
uniform vec3 u_lightDirection;
void main() {
vec4 diffuseColor = texture2D(u_diffuse, v_texCoord);
vec3 a_normal = normalize(v_normal);
float l = dot(a_normal, u_lightDirection) * 0.5 + 0.5;
gl_FragColor = vec4(diffuseColor.rgb * l, diffuseColor.a);
}
`;
// WGSL
const shaderSrc = `
struct VSUniforms {
worldViewProjection: mat4x4f,
worldInverseTranspose: mat4x4f,
};
@group(0) binding(0) `var<uniform>` vsUniforms: VSUniforms;
struct MyVSInput {
@location(0) position: vec4f,
@location(1) normal: vec3f,
@location(2) texcoord: vec2f,
};
struct MyVSOutput {
@builtin(position) position: vec4f,
@location(0) normal: vec3f,
@location(1) texcoord: vec2f,
};
@vertex
fn myVSMain(v: MyVSInput) -> MyVSOutput {
var vsOut: MyVSOutput;
vsOut.position = vsUniforms.worldViewProjection * v.position;
vsOut.normal = (vsUniforms.worldInverseTranspose * vec4f(v.normal, 0.0)).xyz;
vsOut.texcoord = v.texcoord;
return vsOut;
}
struct FSUniforms {
lightDirection: vec3f,
};
@group(0) binding(1) `var<uniform>` fsUniforms: FSUniforms;
@group(0) binding(2) var diffuseSampler: sampler;
@group(0) binding(3) var diffuseTexture: `texture_2d<f32>`;
@fragment
fn myFSMain(v: MyVSOutput) -> @location(0) vec4f {
var diffuseColor = textureSample(diffuseTexture, diffuseSampler, v.texcoord);
var a_normal = normalize(v.normal);
var l = dot(a_normal, fsUniforms.lightDirection) * 0.5 + 0.5;
return vec4f(diffuseColor.rgb * l, diffuseColor.a);
}
`;
两者核心相似,vec4 变成 vec4f,mat4 变成 mat4x4f,其他如 int→i32,uint→u32,ivec2→vec2i,uvec3→vec3u。GLSL 风格类似 C/C++,WGSL 类似 Rust:类型在 GLSL 中位于左侧,在 WGSL 中位于右侧。WGSL 支持类型推断,而 GLSL 必须显式声明类型。
最大的区别是 @??? 注解,它们显式声明数据来源。uniform 的 @group(?) @binding(?) 必须保证不冲突。属性通过 @location 指定,必须手动给出。varying 在 WGSL 中通过结构体定义,并指定每个字段的位置。片元着色器的输入结构无需与顶点输出结构同名,只要位置匹配即可。gl_Position 在 WGSL 中对应 @builtin(position),片元输出用 @location(0) 标识。
获取 API
// WebGL
function main() {
const gl = document.querySelector('canvas').getContext('webgl');
if (!gl) {
fail('need webgl');
return;
}
}
main();
// WebGPU
async function main() {
const adapter = await navigator.gpu?.requestAdapter();
const device = await adapter?.requestDevice();
if (!device) {
fail('need a browser that supports WebGPU');
return;
}
}
main();
adapter 代表 GPU 本身,device 代表该 GPU 上的 API 实例。最大区别是 WebGPU 获取 API 是异步的。
创建缓冲区
// WebGL
function createBuffer(gl, data, type = gl.ARRAY_BUFFER) {
const buf = gl.createBuffer();
gl.bindBuffer(type, buf);
gl.bufferData(type, data, gl.STATIC_DRAW);
return buf;
}
const positionBuffer = createBuffer(gl, positions);
const normalBuffer = createBuffer(gl, normals);
const texcoordBuffer = createBuffer(gl, texcoords);
const indicesBuffer = createBuffer(gl, indices, gl.ELEMENT_ARRAY_BUFFER);
// WebGPU
function createBuffer(device, data, usage) {
const buffer = device.createBuffer({
size: data.byteLength,
usage,
mappedAtCreation: true,
});
const dst = new data.constructor(buffer.getMappedRange());
dst.set(data);
buffer.unmap();
return buffer;
}
const positionBuffer = createBuffer(device, positions, GPUBufferUsage.VERTEX);
const normalBuffer = createBuffer(device, normals, GPUBufferUsage.VERTEX);
const texcoordBuffer = createBuffer(device, texcoords, GPUBufferUsage.VERTEX);
const indicesBuffer = createBuffer(device, indices, GPUBufferUsage.INDEX);
两者差异不大,主要是调用函数不同。
创建纹理与采样器
// WebGL
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([...]));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
// WebGPU
const tex = device.createTexture({
size: [2, 2],
format: 'rgba8unorm',
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
});
device.queue.writeTexture(
{ texture: tex },
new Uint8Array([...]),
{ bytesPerRow: 8, rowsPerImage: 2 },
{ width: 2, height: 2 },
);
const sampler = device.createSampler({
magFilter: 'nearest',
minFilter: 'nearest',
});
WebGPU 需要设置纹理用途标志,并且必须显式创建采样器。
编译着色器
// WebGL
const vs = createShader(gl, gl.VERTEX_SHADER, vSrc);
const fs = createShader(gl, gl.FRAGMENT_SHADER, fSrc);
// WebGPU
const shaderModule = device.createShaderModule({ code: shaderSrc });
WebGL 需要手动检查编译状态并获取错误日志;WebGPU 大多数实现会将错误打印到 JavaScript 控制台,更加方便。
链接程序 / 设置管线
WebGL 中着色器链接、顶点属性设置、图元类型、深度缓冲使用等分散在不同调用中;WebGPU 将这些合并到渲染管线的创建过程。
// WebGL 简化
const program = createProgram(gl, vs, fs);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(positionLoc);
// ... 其他属性
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
// WebGPU
const pipeline = device.createRenderPipeline({
layout: 'auto',
vertex: {
module: shaderModule,
buffers: [
{ arrayStride: 12, attributes: [{ shaderLocation: 0, offset: 0, format: 'float32x3' }] },
{ arrayStride: 12, attributes: [{ shaderLocation: 1, offset: 0, format: 'float32x3' }] },
{ arrayStride: 8, attributes: [{ shaderLocation: 2, offset: 0, format: 'float32x2' }] },
],
},
fragment: {
module: shaderModule,
targets: [{ format: presentationFormat }],
},
primitive: { topology: 'triangle-list', cullMode: 'back' },
depthStencil: { depthWriteEnabled: true, depthCompare: 'less', format: 'depth24plus' },
...(canvasInfo.sampleCount > 1 && { multisample: { count: canvasInfo.sampleCount } }),
});
注意 createRenderPipeline 是一个较慢的调用,内部可能会调整着色器。顶点缓冲区布局在此声明,实际缓冲区在绘制时绑定。若要绘制不同图元(三角形或线)或不同顶点布局,需要创建多个管线。
准备 uniform
// WebGL
const u_lightDirectionLoc = gl.getUniformLocation(program, 'u_lightDirection');
const u_diffuseLoc = gl.getUniformLocation(program, 'u_diffuse');
const u_worldInverseTransposeLoc = gl.getUniformLocation(program, 'u_worldInverseTranspose');
const u_worldViewProjectionLoc = gl.getUniformLocation(program, 'u_worldViewProjection');
// WebGPU
const vUniformBufferSize = 2 * 16 * 4; // 2 mat4s * 16 floats * 4 bytes
const fUniformBufferSize = 3 * 4; // 1 vec3 * 3 floats * 4 bytes
const vsUniformBuffer = device.createBuffer({
size: vUniformBufferSize,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
const fsUniformBuffer = device.createBuffer({
size: fUniformBufferSize,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
const vsUniformValues = new Float32Array(2 * 16);
const worldViewProjection = vsUniformValues.subarray(0, 16);
const worldInverseTranspose = vsUniformValues.subarray(16, 32);
const fsUniformValues = new Float32Array(3);
const lightDirection = fsUniformValues.subarray(0, 3);
WebGPU 没有查询 uniform 位置的 API,必须手动计算字节偏移和大小。
准备绘制:绑定组与渲染通道描述符
// WebGPU
const bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: vsUniformBuffer },
{ binding: 1, resource: fsUniformBuffer },
{ binding: 2, resource: sampler },
{ binding: 3, resource: tex },
],
});
绑定组中的 binding 必须与着色器中 @binding 匹配。
渲染通道描述符:
// WebGPU
const renderPassDescriptor = {
colorAttachments: [{
clearValue: [0.5, 0.5, 0.5, 1],
loadOp: 'clear',
storeOp: 'store',
}],
depthStencilAttachment: {
depthClearValue: 1,
depthLoadOp: 'clear',
depthStoreOp: 'store',
},
};
WebGL 中渲染到画布时这些设置由 API 自动处理,WebGPU 中即使渲染到画布也需要自行描述。
设置 uniform 数据
// WebGL
gl.uniform3fv(u_lightDirectionLoc, v3.normalize([1, 8, -10]));
gl.uniform1i(u_diffuseLoc, 0);
gl.uniformMatrix4fv(u_worldInverseTransposeLoc, false, m4.transpose(m4.inverse(world)));
gl.uniformMatrix4fv(u_worldViewProjectionLoc, false, m4.multiply(viewProjection, world));
// WebGPU
m4.transpose(m4.inverse(world), worldInverseTranspose);
m4.multiply(viewProjection, world, worldViewProjection);
v3.normalize([1, 8, -10], lightDirection);
device.queue.writeBuffer(vsUniformBuffer, 0, vsUniformValues);
device.queue.writeBuffer(fsUniformBuffer, 0, fsUniformValues);
调整绘制缓冲区大小
WebGL 只需设置 canvas.width 和 canvas.height 即可;WebGPU 需要手动管理纹理和深度缓冲的销毁与重建。
// WebGL
function resizeCanvasToDisplaySize(canvas) {
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = width !== canvas.width || height !== canvas.height;
if (needResize) {
canvas.width = width;
canvas.height = height;
}
return needResize;
}
// WebGPU 简化
function resizeToDisplaySize(device, canvasInfo) {
const { canvas, context, renderTarget, presentationFormat, depthTexture, sampleCount } = canvasInfo;
const width = Math.max(1, Math.min(device.limits.maxTextureDimension2D, canvas.clientWidth));
const height = Math.max(1, Math.min(device.limits.maxTextureDimension2D, canvas.clientHeight));
const needResize = !canvasInfo.renderTarget || width !== canvas.width || height !== canvas.height;
if (needResize) {
if (renderTarget) renderTarget.destroy();
if (depthTexture) depthTexture.destroy();
canvas.width = width;
canvas.height = height;
if (sampleCount > 1) {
const newRenderTarget = device.createTexture({
size: [canvas.width, canvas.height],
format: presentationFormat,
sampleCount,
usage: GPUTextureUsage.RENDER_ATTACHMENT,
});
canvasInfo.renderTarget = newRenderTarget;
canvasInfo.renderTargetView = newRenderTarget.createView();
}
const newDepthTexture = device.createTexture({
size: [canvas.width, canvas.height],
format: 'depth24plus',
sampleCount,
usage: GPUTextureUsage.RENDER_ATTACHMENT,
});
canvasInfo.depthTexture = newDepthTexture;
canvasInfo.depthTextureView = newDepthTexture.createView();
}
return needResize;
}
WebGPU 还需要检查硬件限制(如最大纹理尺寸),并自行处理内存不足的情况(可通过 pushErrorScope('out-of-memory') 异步捕获)。
绘制
// WebGL 简化
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// 绑定纹理、缓冲区、设置 uniform 等
gl.drawElements(gl.TRIANGLES, 6 * 6, gl.UNSIGNED_SHORT, 0);
// WebGPU
if (canvasInfo.sampleCount === 1) {
const colorTexture = context.getCurrentTexture();
renderPassDescriptor.colorAttachments[0].view = colorTexture.createView();
} else {
renderPassDescriptor.colorAttachments[0].view = canvasInfo.renderTargetView;
renderPassDescriptor.colorAttachments[0].resolveTarget = context.getCurrentTexture().createView();
}
renderPassDescriptor.depthStencilAttachment.view = canvasInfo.depthTextureView;
const commandEncoder = device.createCommandEncoder();
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.setPipeline(pipeline);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.setVertexBuffer(0, positionBuffer);
passEncoder.setVertexBuffer(1, normalBuffer);
passEncoder.setVertexBuffer(2, texcoordBuffer);
passEncoder.setIndexBuffer(indicesBuffer, 'uint16');
passEncoder.drawIndexed(indices.length);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
WebGPU 中需要更新渲染通道描述符的纹理视图,创建命令编码器(command encoder),开始渲染通道,设置管线、绑定组、顶点缓冲区、索引缓冲区,最后提交命令队列。注意命令队列(device.queue):所有上传和提交都通过队列完成,不能在同一个命令编码器内部的两次绘制之间修改缓冲区。
绘制多个物体
在 WebGPU 中,绘制多个物体通常需要为每个物体创建独立的 uniform 缓冲区(以及对应的绑定组),因为 uniform 缓冲区无法在命令编码器中间更新。
// WebGPU 多物体初始化简化
const numObjects = 100;
const objectInfos = [];
for (let i = 0; i < numObjects; ++i) {
const vsUniformBuffer = device.createBuffer({
size: Math.max(16, vUniformBufferSize),
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
const vsUniformValues = new Float32Array(2 * 16);
const worldViewProjection = vsUniformValues.subarray(0, 16);
const worldInverseTranspose = vsUniformValues.subarray(16, 32);
const bindGroup = device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: vsUniformBuffer },
{ binding: 1, resource: fsUniformBuffer },
{ binding: 2, resource: sampler },
{ binding: 3, resource: tex },
],
});
// 计算位置并保存引用
objectInfos.push({ vsUniformBuffer, vsUniformValues, worldViewProjection, worldInverseTranspose, bindGroup, translation: [x, y, 0] });
}
渲染循环中,为每个物体更新其 uniform 缓冲区并编码绘制命令:
// WebGPU 渲染循环简化
passEncoder.setPipeline(pipeline);
passEncoder.setVertexBuffer(0, positionBuffer);
passEncoder.setVertexBuffer(1, normalBuffer);
passEncoder.setVertexBuffer(2, texcoordBuffer);
passEncoder.setIndexBuffer(indicesBuffer, 'uint16');
const projection = mat4.perspective(...);
const view = mat4.lookAt(...);
const viewProjection = mat4.multiply(projection, view);
vec3.normalize([1, 8, -10], lightDirection);
device.queue.writeBuffer(fsUniformBuffer, 0, fsUniformValues);
objectInfos.forEach(({ vsUniformBuffer, vsUniformValues, worldViewProjection, worldInverseTranspose, bindGroup, translation }, ndx) => {
passEncoder.setBindGroup(0, bindGroup);
const world = mat4.translation(translation);
mat4.rotateX(world, time * 0.9 + ndx, world);
mat4.rotateY(world, time + ndx, world);
mat4.transpose(mat4.inverse(world), worldInverseTranspose);
mat4.multiply(viewProjection, world, worldViewProjection);
device.queue.writeBuffer(vsUniformBuffer, 0, vsUniformValues);
passEncoder.drawIndexed(indices.length);
});
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
核心要点:与 WebGL 不同,WebGPU 中每个物体特定的 uniform 都需要独立的 uniform 缓冲区,并因此可能需要独立的绑定组。
其他细节差异
- Z 裁剪空间:WebGL 为 -1 到 +1;WebGPU 为 0 到 1。
- 帧缓冲/视口坐标系 Y 轴向下:与 WebGL 相反,尽管裁剪空间中 Y 轴向上(相同)。
- WGSL 内建变量:
@builtin(position)对应gl_Position;@builtin(vertex_index)对应gl_VertexID;@builtin(instance_index)对应gl_InstanceID;@builtin(position)也用于片元着色器中的gl_FragCoord,但方向与 WebGL 相反(左上为原点)。 - 点与线的宽度:WebGPU 只支持 1 像素宽的点与线,事实上大多数 WebGL 实现也不支持更宽的线,但 WebGL 通常支持更大的点(有 64 像素限制)。WebGPU 强制这一限制有利于编写可移植的解决方案。
- WebGPU 优化方式不同于 WebGL:直接将 WebGL 应用转换为 WebGPU 可能运行更慢。要获得 WebGPU 的性能优势,需要重新组织数据并优化绘制方式(参见优化文章)。
三个核心优化要点
基于本文内容,从 WebGL 迁移到 WebGPU 时,以下三个优化方向尤为关键:
-
利用管线不可变性,预创建并复用管线
WebGPU 渲染管线(pipeline)创建成本高,且不可修改。不同顶点布局、图元类型、混合/深度设置都需要独立管线。应在初始化阶段预先创建所有需要的管线变体,避免在渲染循环中动态创建。 -
显式管理资源生命周期与内存布局,避免运行时重新分配
缓冲区、纹理的大小和格式不可变,mipmap 需自行生成,uniform 数据必须按字节偏移手动布局。应预先分配足够大小的缓冲区,使用池化或提前销毁重建的方式管理资源,同时保持 WGSL 与 JavaScript 之间的位置/偏移同步,减少运行时开销。 -
重新组织绘制数据,减少绑定组与命令提交开销
WebGPU 中每个物体特有的 uniform 需要独立的缓冲区并对应独立的绑定组。应尽量合并共享数据(如光照方向、投影矩阵),并考虑使用一个大块 uniform buffer 配合动态偏移,或使用存储缓冲区(storage buffer)批量处理,以降低绑定组切换和命令队列提交的次数。
GLSL/WebGL 专业术语检查
翻译过程中涉及的核心术语均按行业标准处理:
| 英文术语 | 中文翻译 | 备注 |
|---|---|---|
| vertex shader | 顶点着色器 | |
| fragment shader | 片元着色器 | 亦译“片段着色器” |
| compute shader | 计算着色器 | |
| uniform | 统一变量 / 统一值 | 常见音译“uniform” |
| attribute | 顶点属性 | |
| varying | 可变变量 / 插值变量 | |
| texture | 纹理 | |
| sampler | 采样器 | |
| buffer | 缓冲区 | |
| render pipeline | 渲染管线 | |
| render pass | 渲染通道 | |
| bind group | 绑定组 | |
| depth buffer | 深度缓冲 | |
| stencil buffer | 模板缓冲 | |
| blending | 混合 | |
| mipmap | 多级渐远纹理 / mipmap | 保留英文更常见 |
| framebuffer | 帧缓冲 | |
| viewport | 视口 | |
| draw call | 绘制调用 | |
| command encoder | 命令编码器 | |
| uniform block | 统一变量块 | |
| location | 位置(索引) | |
| byte offset | 字节偏移 | |
| topology | 图元拓扑 | |
| cull mode | 剔除模式 | |
| multisample | 多重采样 | |
| resolve | 解析 |
所有代码中的英文注释与标识符保持原样,以符合技术文章惯例。
