代码分析说明:
import { makeSample, SampleInit } from '../../components/SampleLayout';
// 导入Shader 代码
// 顶点着色器 着色器语言 使用WGSL语言
import triangleVertWGSL from '../../shaders/triangle.vert.wgsl';
// 片段着色器 使用WGSL语言
import redFragWGSL from '../../shaders/red.frag.wgsl';
// 页面初始化, 传入Canvas HTMLDOM 对象 与 页面状态
const init: SampleInit = async ({ canvas, pageState }) => {
// 申请一个适配器
const adapter = await navigator.gpu.requestAdapter();
// 通过适配器申请一个设备,大部分的API都是挂在设备上的
const device = await adapter.requestDevice();
// 页面状态没有激活时,不执行
if (!pageState.active) return;
// 通过Canvas获取WebGPU的上下文
const context = canvas.getContext('webgpu') as GPUCanvasContext;
// 获取屏幕像素比
const devicePixelRatio = window.devicePixelRatio;
// 设置画布Canvas的大小
canvas.width = canvas.clientWidth * devicePixelRatio;
canvas.height = canvas.clientHeight * devicePixelRatio;
// 获取显卡的最佳格式
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
// 配置GUP 上下文
context.configure({
device,
format: presentationFormat,
alphaMode: 'premultiplied', // 透明模式,前乘
});
// 创建渲染管线
const pipeline = device.createRenderPipeline({
layout: 'auto', // 默认的布局
vertex: {
module: device.createShaderModule({ // 创建Shader对象
code: triangleVertWGSL,
}),
entryPoint: 'main', // 入口函数
},
fragment: {
module: device.createShaderModule({ // 创建Shader对象
code: redFragWGSL,
}),
entryPoint: 'main', // 入口函数
targets: [
{
format: presentationFormat, // 绘制格式
},
],
},
primitive: {
topology: 'triangle-list', // 三角方式绘制
},
});
function frame() {
// Sample is no longer the active page.
if (!pageState.active) return;
// 创建编码器
const commandEncoder = device.createCommandEncoder();
// 绘制的view来自画布上下文
const textureView = context.getCurrentTexture().createView();
// 绘制描述
const renderPassDescriptor: GPURenderPassDescriptor = {
// 颜色的绘制方式
colorAttachments: [
{
view: textureView, // 绘制的视图
clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 }, // 清屏色
loadOp: 'clear', // 此附件的清除值加载到渲染过程中
storeOp: 'store', // 绘制的结果存储
},
],
};
// 开始编码由 descriptor 描述的渲染过程。
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
// 设置渲染管线
passEncoder.setPipeline(pipeline);
// 渲染绘制,输入顶点数
passEncoder.draw(3);
// 编码结束
passEncoder.end();
// 编码完成并提交
device.queue.submit([commandEncoder.finish()]);
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
};
顶点着色器
@vertex
fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4<f32> {
// 顶点数据
var pos = array<vec2<f32>, 3>(
vec2(0.0, 0.5),
vec2(-0.5, -0.5),
vec2(0.5, -0.5)
);
// 根据顶点索引获取顶点
return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
}
片段着色器
@fragment
fn main() -> @location(0) vec4<f32> {
// 渲染为红色 r g b a
return vec4(1.0, 0.0, 0.0, 1.0);
}
总结步骤:
-
navigator.gpu.requestAdapter
申请适配器 -
adapter.requestDevice
申请设备 -
canvas.getContext('webgpu')
获取GPU上下文 -
context.configure
配置上下文,将设备传给上下文 -
device.createRenderPipeline
创建渲染管线 -
device.createCommandEncoder
创建编码器 -
commandEncoder.beginRenderPass
开始编码由 descriptor 描述的渲染过程。 -
passEncoder.setPipeline
设置渲染管线 -
passEncoder.draw
设置绘制顶点数 -
passEncoder.end
编码结束 -
commandEncoder.finish
完成编码 -
device.queue.submit
提交编码器
网友评论