WebGPU學習(九):學習“fractalCube”示例
大家好,本文學習Chrome->webgpu-samplers->fractalCube示例。
上一篇博文:
WebGPU學習(八):學習“texturedCube”示例
學習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,
});
- 建立空紋理和sampler,設定到uniform bind group中
程式碼如下:
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, resource: cubeTexture.createView(), }], });
- 繪製和拷貝
在每一幀中:
繪製帶紋理的立方體;
將渲染結果拷貝到紋理中。
相關程式碼如下:
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”示例相比,增加了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。
第一幀的渲染結果如下:
第一幀繪製結束後,渲染結果會被拷貝到紋理中。
第二幀
分析執行的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]部分,從而使紋理中立方體所佔比例更大。
得到的紋理區域如下圖的紅色區域所示:
- 第13行和第15行程式碼,將紋理中的背景色替換為了fragColor,使紋理中立方體區域的顏色保持不變
第二幀的渲染結果如下:
- 第三幀
依次類推,第三幀的渲染結果如下:
參考資料
WebGPU規範
webgpu-samplers Github Repo
WebGP