WebGPU學習(九):學習“fractalCube”示例
大家好,本文學習Chrome->webgpu-samplers->fractalCube示例。
上一篇博文:
WebGPU學習(八):學習“texturedCube”示例
下一篇博文:
WebGPU學習(十):介紹“GPU實現粒子效果”
學習fractalCube.ts
最終渲染結果:

該示例展示了如何用上一幀渲染的結果作為下一幀的紋理。
與“texturedCube”示例相比,該示例的紋理并不是來自圖片,而是來自上一幀渲染的結果
下面,我們打開fractalCube.ts文件,分析相關代碼:
傳輸頂點的color
它與“texturedCube”示例->“傳遞頂點的uv數據”類似,這里不再分析
上一幀渲染的結果作為下一幀的紋理
- 配置swapChain
因為swapChain保存了上一幀渲染的結果,所以將其作為下一幀紋理的source。因此它的usage需要增加GPUTextureUsage.COPY_SRC:
const swapChain = context.configureSwapChain({
device,
format: "bgra8unorm",
usage: GPUTextureUsage.OUTPUT_ATTACHMENT | GPUTextureUsage.COPY_SRC,
});
- 創(chuàng)建空紋理(cubeTexture)和sampler,設置到uniform bind group中
相關代碼如下:
const fragmentShaderGLSL = `#version 450
...
layout(set = 0, binding = 2) uniform texture2D myTexture;
...
void main() {
vec4 texColor = texture(sampler2D(myTexture, mySampler), fragUV * 0.8 + 0.1);
...
outColor = mix(texColor, fragColor, f);
}`;
...
const cubeTexture = device.createTexture({
size: { width: canvas.width, height: canvas.height, depth: 1 },
format: "bgra8unorm",
usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.SAMPLED,
});
const sampler = device.createSampler({
magFilter: "linear",
minFilter: "linear",
});
const uniformBindGroup = device.createBindGroup({
layout: bindGroupLayout,
bindings: [
...
{
binding: 1,
resource: sampler,
}, {
binding: 2,
//傳遞cubeTexture到fragment shader中
resource: cubeTexture.createView(),
}],
});
- 繪制和拷貝
在每一幀中:
繪制帶紋理的立方體;
將渲染結果(swapChainTexture)拷貝到cubeTexture中。
相關代碼如下:
return function frame() {
const swapChainTexture = swapChain.getCurrentTexture();
renderPassDescriptor.colorAttachments[0].attachment = swapChainTexture.createView();
const commandEncoder = device.createCommandEncoder({});
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
...
passEncoder.setBindGroup(0, uniformBindGroup);
...
passEncoder.draw(36, 1, 0, 0);
passEncoder.endPass();
commandEncoder.copyTextureToTexture({
texture: swapChainTexture,
}, {
texture: cubeTexture,
}, {
width: canvas.width,
height: canvas.height,
depth: 1,
});
device.defaultQueue.submit([commandEncoder.finish()]);
...
}
分析shader代碼
本示例的vertex shader與“texturedCube”示例的vertex shader相比,增加了color attribute:
const vertexShaderGLSL = `#version 450
...
layout(location = 1) in vec4 color;
...
layout(location = 0) out vec4 fragColor;
...
void main() {
...
fragColor = color;
...
}`;
fragment shader的代碼如下:
const fragmentShaderGLSL = `#version 450
layout(set = 0, binding = 1) uniform sampler mySampler;
layout(set = 0, binding = 2) uniform texture2D myTexture;
layout(location = 0) in vec4 fragColor;
layout(location = 1) in vec2 fragUV;
layout(location = 0) out vec4 outColor;
void main() {
vec4 texColor = texture(sampler2D(myTexture, mySampler), fragUV * 0.8 + 0.1);
// 1.0 if we're sampling the background
float f = float(length(texColor.rgb - vec3(0.5, 0.5, 0.5)) < 0.01);
outColor = mix(texColor, fragColor, f);
}`;
第10行對fragUV進行了處理,我們會在分析渲染時間線中分析它。
第13行和第15行相當于做了if判斷:
if(紋理顏色 === 背景色){
outColor = fragColor
}
else{
outColor = 紋理顏色
}
這里之所以不用if判斷而使用計算的方式,是為了減少條件判斷,提高gpu的并行性
分析渲染時間線
下面分析下渲染的時間線:
第一幀
因為紋理為空紋理,它的顏色為背景色,所以fragment shader的outColor始終為fragColor,因此立方體的所有片段的顏色均為fragColor。
第一幀的渲染結果如下:

第一幀繪制結束后,渲染結果會被拷貝到cubeTexture中。
第二幀
分析執(zhí)行的fragment shader代碼:
const fragmentShaderGLSL = `#version 450
layout(set = 0, binding = 1) uniform sampler mySampler;
layout(set = 0, binding = 2) uniform texture2D myTexture;
layout(location = 0) in vec4 fragColor;
layout(location = 1) in vec2 fragUV;
layout(location = 0) out vec4 outColor;
void main() {
vec4 texColor = texture(sampler2D(myTexture, mySampler), fragUV * 0.8 + 0.1);
// 1.0 if we're sampling the background
float f = float(length(texColor.rgb - vec3(0.5, 0.5, 0.5)) < 0.01);
outColor = mix(texColor, fragColor, f);
}`;
- 第10行的“fragUV * 0.8 + 0.1”是為了取紋理坐標u、v方向的[0.1-0.9]部分,從而使紋理中立方體所占比例更大。
得到的紋理區(qū)域如下圖的紅色區(qū)域所示:

- 第13行和第15行代碼,將紋理中的背景色替換為了fragColor
第二幀的渲染結果如下:

- 第三幀
依次類推,第三幀的渲染結果如下:

浙公網安備 33010602011771號