├── .gitignore ├── README.md ├── drawio ├── ortho.drawio ├── webgpu工作流.drawio ├── 渲染管线.drawio └── 着色器传参.drawio ├── proj1_gameOfLife ├── .gitignore ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── helper │ │ ├── bindGroup.ts │ │ ├── gpuBuffer.ts │ │ └── init.ts │ ├── main.ts │ ├── shader │ │ ├── compute.wgsl │ │ ├── frag.wgsl │ │ └── vert.wgsl │ └── style.css └── tsconfig.json ├── proj2_firework ├── .gitignore ├── README.md ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── helper │ │ ├── bindGroup.ts │ │ ├── gpuBuffer.ts │ │ └── init.ts │ ├── main.ts │ ├── shader │ │ ├── compute.wgsl │ │ ├── frag.wgsl │ │ └── vert.wgsl │ └── style.css └── tsconfig.json ├── proj2_firework_2 ├── .gitignore ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── helper │ │ ├── bindGroup.ts │ │ ├── gpuBuffer.ts │ │ └── init.ts │ ├── main.ts │ ├── shader │ │ ├── compute.wgsl │ │ ├── frag.wgsl │ │ └── vert.wgsl │ └── style.css └── tsconfig.json ├── proj3_imageBlur ├── .gitignore ├── README.md ├── index.html ├── package.json ├── pnpm-lock.yaml ├── public │ └── zs.png ├── src │ ├── helper │ │ ├── bindGroup.ts │ │ ├── gpuBuffer.ts │ │ └── init.ts │ ├── main.ts │ ├── shader │ │ ├── compute.wgsl │ │ ├── frag.wgsl │ │ └── vert.wgsl │ └── style.css └── tsconfig.json ├── proj3_imageBlur_Gaussian ├── .gitignore ├── README.md ├── index.html ├── package.json ├── pnpm-lock.yaml ├── public │ └── zs.png ├── src │ ├── helper │ │ ├── bindGroup.ts │ │ ├── gpuBuffer.ts │ │ └── init.ts │ ├── main.ts │ ├── shader │ │ ├── frag.wgsl │ │ └── vert.wgsl │ └── style.css └── tsconfig.json ├── proj4_galaxy ├── .gitignore ├── README.md ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── helper │ │ ├── bindGroup.ts │ │ ├── color.ts │ │ ├── gpuBuffer.ts │ │ ├── init.ts │ │ └── math.ts │ ├── main.ts │ ├── shader │ │ ├── compute.wgsl │ │ ├── frag.wgsl │ │ └── vert.wgsl │ └── style.css └── tsconfig.json ├── vol0_triangle ├── .gitignore ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── main.ts │ ├── shader │ │ ├── frag.wgsl │ │ └── vert.wgsl │ └── style.css └── tsconfig.json ├── vol10_renderBundles ├── .gitignore ├── README.md ├── index.html ├── package.json ├── pnpm-lock.yaml ├── public │ ├── moon.jpg │ └── saturn.jpg ├── src │ ├── helper │ │ ├── bindGroup.ts │ │ ├── color.ts │ │ ├── gpuBuffer.ts │ │ ├── init.ts │ │ ├── math.ts │ │ ├── sphere.ts │ │ └── texture.ts │ ├── main.ts │ ├── shader │ │ ├── frag.wgsl │ │ └── vert.wgsl │ └── style.css └── tsconfig.json ├── vol11_glb ├── .gitignore ├── README.md ├── index.html ├── package.json ├── pnpm-lock.yaml ├── public │ ├── Buggy.glb │ └── DamagedHelmet.glb ├── src │ ├── components │ │ ├── Camera.ts │ │ └── InputManager.ts │ ├── glb │ │ ├── glb_accessor.ts │ │ ├── glb_main.ts │ │ ├── glb_material.ts │ │ ├── glb_mesh.ts │ │ ├── glb_model.ts │ │ ├── glb_node.ts │ │ ├── glb_primitive.ts │ │ ├── glb_sampler.ts │ │ ├── glb_shader_cache.ts │ │ ├── glb_texture.ts │ │ ├── glb_tool.ts │ │ └── glb_viewbuffer.ts │ ├── main.ts │ ├── style.css │ └── types │ │ ├── glb.d.ts │ │ └── index.d.ts ├── tsconfig.json └── vite.config.js ├── vol11_glb_simple ├── .gitignore ├── README.md ├── index.html ├── package.json ├── pnpm-lock.yaml ├── public │ ├── Buggy.glb │ └── DamagedHelmet.glb ├── src │ ├── components │ │ ├── Camera.ts │ │ └── InputManager.ts │ ├── glb │ │ ├── glb_accessor.ts │ │ ├── glb_main.ts │ │ ├── glb_mesh.ts │ │ ├── glb_model.ts │ │ ├── glb_node.ts │ │ ├── glb_primitive.ts │ │ ├── glb_tool.ts │ │ └── glb_viewbuffer.ts │ ├── main.ts │ ├── shader │ │ └── glb.wgsl │ ├── style.css │ └── types │ │ ├── glb.d.ts │ │ └── index.d.ts ├── tsconfig.json └── vite.config.js ├── vol1_oneCube ├── .gitignore ├── README.md ├── index.html ├── package.json ├── pnpm-lock.yaml ├── public │ └── ortho.svg ├── src │ ├── main.ts │ ├── shader │ │ ├── cubeFrag.wgsl │ │ └── cubeVert.wgsl │ └── style.css └── tsconfig.json ├── vol1_oneCube_MSAA ├── .gitignore ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── main.ts │ ├── shader │ │ ├── cubeFrag.wgsl │ │ └── cubeVert.wgsl │ └── style.css └── tsconfig.json ├── vol1_oneCube_canvas_textureSampling ├── .gitignore ├── README.md ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── helper │ │ ├── init.ts │ │ ├── math.ts │ │ └── vertexData.ts │ ├── main.ts │ ├── shader │ │ ├── cubeFrag.wgsl │ │ └── cubeVert.wgsl │ └── style.css └── tsconfig.json ├── vol1_oneCube_image_textureSampling ├── .gitignore ├── README.md ├── index.html ├── package.json ├── pnpm-lock.yaml ├── public │ └── zs.png ├── src │ ├── main.ts │ ├── shader │ │ ├── cubeFrag.wgsl │ │ └── cubeVert.wgsl │ └── style.css └── tsconfig.json ├── vol1_oneCube_indexBuffer ├── .gitignore ├── README.md ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── helper │ │ ├── init.ts │ │ ├── math.ts │ │ └── vertexData.ts │ ├── main.ts │ ├── shader │ │ ├── cubeFrag.wgsl │ │ └── cubeVert.wgsl │ └── style.css └── tsconfig.json ├── vol1_oneCube_video_textureSampling ├── .gitignore ├── README.md ├── index.html ├── package.json ├── pnpm-lock.yaml ├── public │ └── 1.mp4 ├── src │ ├── helper │ │ ├── init.ts │ │ ├── math.ts │ │ └── vertexData.ts │ ├── main.ts │ ├── shader │ │ ├── cubeFrag.wgsl │ │ └── cubeVert.wgsl │ └── style.css └── tsconfig.json ├── vol2_twoCubes ├── .gitignore ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── helper │ │ ├── gpuBuffer.ts │ │ ├── init.ts │ │ ├── math.ts │ │ └── vertexData.ts │ ├── main.ts │ ├── shader │ │ ├── cubeFrag.wgsl │ │ └── cubeVert.wgsl │ └── style.css └── tsconfig.json ├── vol2_twoCubes_MSAA ├── .gitignore ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── helper │ │ ├── gpuBuffer.ts │ │ ├── init.ts │ │ ├── math.ts │ │ └── vertexData.ts │ ├── main.ts │ ├── shader │ │ ├── cubeFrag.wgsl │ │ └── cubeVert.wgsl │ └── style.css └── tsconfig.json ├── vol3_sphere_wireFrame ├── .gitignore ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── helper │ │ ├── gpuBuffer.ts │ │ ├── init.ts │ │ ├── math.ts │ │ └── vertexData.ts │ ├── main.ts │ ├── shader │ │ ├── sphereFrag.wgsl │ │ └── sphereVert.wgsl │ └── style.css └── tsconfig.json ├── vol4_oneCube_light_mv+p ├── .gitignore ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── helper │ │ ├── gpuBuffer.ts │ │ ├── init.ts │ │ ├── math.ts │ │ └── vertexData.ts │ ├── main.ts │ ├── shader │ │ ├── cubeFrag.wgsl │ │ └── cubeVert.wgsl │ └── style.css └── tsconfig.json ├── vol4_oneCube_light_vp+m ├── .gitignore ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── helper │ │ ├── gpuBuffer.ts │ │ ├── init.ts │ │ ├── math.ts │ │ └── vertexData.ts │ ├── main.ts │ ├── shader │ │ ├── cubeFrag.wgsl │ │ └── cubeVert.wgsl │ └── style.css └── tsconfig.json ├── vol5_objects_light ├── .gitignore ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── helper │ │ ├── box.ts │ │ ├── gpuBuffer.ts │ │ ├── init.ts │ │ ├── math.ts │ │ └── sphere.ts │ ├── main.ts │ ├── shader │ │ ├── frag.wgsl │ │ └── vert.wgsl │ └── style.css └── tsconfig.json ├── vol5_objects_light_layout ├── .gitignore ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── helper │ │ ├── box.ts │ │ ├── gpuBuffer.ts │ │ ├── init.ts │ │ ├── math.ts │ │ └── sphere.ts │ ├── main.ts │ ├── shader │ │ ├── frag.wgsl │ │ └── vert.wgsl │ └── style.css └── tsconfig.json ├── vol6_shadowMapping ├── .gitignore ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── helper │ │ ├── bindGroup.ts │ │ ├── box.ts │ │ ├── gpuBuffer.ts │ │ ├── init.ts │ │ └── math.ts │ ├── main.ts │ ├── shader │ │ ├── frag.wgsl │ │ └── vert.wgsl │ └── style.css └── tsconfig.json ├── vol6_shadowMapping_two_lights ├── .gitignore ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── helper │ │ ├── bindGroup.ts │ │ ├── box.ts │ │ ├── gpuBuffer.ts │ │ ├── init.ts │ │ └── math.ts │ ├── main.ts │ ├── shader │ │ ├── frag.wgsl │ │ └── vert.wgsl │ └── style.css └── tsconfig.json ├── vol7_computeShader ├── .gitignore ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── helper │ │ ├── bindGroup.ts │ │ ├── box.ts │ │ ├── gpuBuffer.ts │ │ ├── init.ts │ │ └── math.ts │ ├── main.ts │ ├── shader │ │ ├── compute.wgsl │ │ ├── frag.wgsl │ │ └── vert.wgsl │ └── style.css └── tsconfig.json ├── vol8_worker ├── .gitignore ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── helper │ │ ├── bindGroup.ts │ │ ├── cube.ts │ │ ├── gpuBuffer.ts │ │ └── math.ts │ ├── main.ts │ ├── shader │ │ ├── frag.wgsl │ │ └── vert.wgsl │ ├── style.css │ └── worker.ts └── tsconfig.json ├── vol8_worker_axes ├── .gitignore ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── components │ │ ├── Axes.ts │ │ ├── Camera.ts │ │ ├── Cube.ts │ │ └── shader │ │ │ ├── axes.frag.wgsl │ │ │ ├── axes.vert.wgsl │ │ │ ├── frag.wgsl │ │ │ └── vert.wgsl │ ├── helper │ │ ├── bindGroup.ts │ │ └── gpuBuffer.ts │ ├── main.ts │ ├── style.css │ └── worker.ts └── tsconfig.json ├── vol9_camera_control ├── .gitignore ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src │ ├── components │ │ ├── Axes.ts │ │ ├── Camera.ts │ │ ├── Cube.ts │ │ ├── GPUManager.ts │ │ ├── InputManager.ts │ │ ├── RenderableObject.ts │ │ ├── Scene.ts │ │ └── shader │ │ │ ├── axes.frag.wgsl │ │ │ ├── axes.vert.wgsl │ │ │ ├── cube.frag.wgsl │ │ │ └── cube.vert.wgsl │ ├── helper │ │ ├── bindGroup.ts │ │ ├── gpuBuffer.ts │ │ └── init.ts │ ├── main.ts │ ├── math │ │ └── quaternion.ts │ └── style.css └── tsconfig.json └── vol9_camera_control_simple ├── .gitignore ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src ├── components │ ├── Axes.ts │ ├── Camera.ts │ ├── CameraController.ts │ ├── Cube.ts │ ├── GPUManager.ts │ ├── RenderableObject.ts │ ├── Scene.ts │ └── shader │ │ ├── axes.frag.wgsl │ │ ├── axes.vert.wgsl │ │ ├── cube.frag.wgsl │ │ └── cube.vert.wgsl ├── helper │ ├── bindGroup.ts │ ├── gpuBuffer.ts │ └── init.ts ├── main.ts ├── math │ └── quaternion.ts └── style.css └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | .history 27 | 28 | test 29 | 30 | eng* -------------------------------------------------------------------------------- /proj1_gameOfLife/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /proj1_gameOfLife/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /proj1_gameOfLife/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@types/dat.gui": "^0.7.10", 13 | "@webgpu/types": "^0.1.34", 14 | "typescript": "^5.0.4", 15 | "vite": "^4.3.2" 16 | }, 17 | "dependencies": { 18 | "dat.gui": "^0.7.9", 19 | "wgpu-matrix": "^2.5.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /proj1_gameOfLife/src/helper/gpuBuffer.ts: -------------------------------------------------------------------------------- 1 | // 创建顶点缓冲区 VBO 2 | // 获取一块状态为映射了的显存,以及一个对应的 arrayBuffer 对象来写数据 3 | /** 4 | * 应用程序可以请求映射一个 GPUBuffer,这样它们就可以通过代表 GPUBuffer 分配的部分的 arraybuffer 访问它的内容。 5 | * 映射一个 GPUBuffer 是通过 mapAsync() 异步请求的,这样用户代理可以确保 GPU 在应用程序访问它的内容之前完成了对 GPUBuffer 的使用。 6 | * 映射的 GPUBuffer 不能被 GPU 使用,必须使用 unmap() 解除映射,然后才能将使用它的工作提交到 Queue 时间轴。 7 | * 一旦映射了 GPUBuffer,应用程序就可以通过 getMappedRange 同步请求访问其内容的范围 8 | */ 9 | export const CreateGPUBuffer = ( 10 | device: GPUDevice, 11 | data: Float32Array, 12 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.VERTEX | 13 | GPUBufferUsage.COPY_DST 14 | ) => { 15 | const buffer = device.createBuffer({ 16 | size: data.byteLength, 17 | usage: usageFlag, 18 | mappedAtCreation: true, 19 | }); 20 | new Float32Array(buffer.getMappedRange()).set(data); 21 | buffer.unmap(); 22 | return buffer; 23 | }; 24 | 25 | export const CreateGPUBufferUint = ( 26 | device: GPUDevice, 27 | data: Uint32Array, 28 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 29 | GPUBufferUsage.COPY_DST 30 | ) => { 31 | const buffer = device.createBuffer({ 32 | size: data.byteLength, 33 | usage: usageFlag, 34 | mappedAtCreation: true, 35 | }); 36 | new Uint32Array(buffer.getMappedRange()).set(data); 37 | buffer.unmap(); 38 | return buffer; 39 | }; 40 | 41 | export const CreateGPUBufferUint16 = ( 42 | device: GPUDevice, 43 | data: Uint16Array, 44 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 45 | GPUBufferUsage.COPY_DST 46 | ) => { 47 | const buffer = device.createBuffer({ 48 | size: data.byteLength, 49 | usage: usageFlag, 50 | mappedAtCreation: true, 51 | }); 52 | new Uint16Array(buffer.getMappedRange()).set(data); 53 | buffer.unmap(); 54 | return buffer; 55 | }; 56 | -------------------------------------------------------------------------------- /proj1_gameOfLife/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.querySelector("canvas") as HTMLCanvasElement; 10 | canvas.width = window.innerWidth; 11 | canvas.height = window.innerHeight; 12 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 13 | // 请求WebGPU适配器与GPU设备 14 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 15 | const device = await adapter.requestDevice(); 16 | const format = navigator.gpu.getPreferredCanvasFormat(); 17 | // 配置上下文 18 | context.configure({ 19 | device: device, 20 | // 上下文格式 21 | format: format, 22 | // 不透明度 23 | alphaMode: "opaque", 24 | }); 25 | return { device, canvas, format, context }; 26 | }; 27 | -------------------------------------------------------------------------------- /proj1_gameOfLife/src/shader/frag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment 2 | fn main(@location(0) cell: f32) -> @location(0) vec4 { 3 | return vec4(cell, cell, cell, 1.); 4 | } 5 | -------------------------------------------------------------------------------- /proj1_gameOfLife/src/shader/vert.wgsl: -------------------------------------------------------------------------------- 1 | struct Out { 2 | @builtin(position) pos: vec4, 3 | @location(0) cell: f32, // 表示每个顶点的细胞属性 4 | } 5 | 6 | @binding(0) @group(0) var size: vec2; 7 | 8 | @vertex 9 | fn main( 10 | @builtin(instance_index) i: u32, 11 | @location(0) cell: u32, 12 | @location(1) pos: vec2 13 | ) -> Out { 14 | let w = size.x; 15 | let h = size.y; 16 | // 这两行代码计算了顶点的标准化设备坐标(Normalized Device Coordinates,NDC),用于将顶点的位置从模拟世界坐标映射到屏幕上的坐标 17 | // 考虑 x 坐标。模拟世界的 x 坐标可能在 [0, width] 的范围内,其中 width 是模拟世界的宽度。但 NDC 的范围是 [-1, 1]。所以,我们需要将模拟世界的 x 坐标映射到 NDC 范围。 18 | // i % w + pos.x 计算出顶点在模拟世界中的 x 坐标 19 | // f32(i % w + pos.x) / f32(w) 将上述计算结果除以宽度 w,以将 x 坐标映射到 [0, 1] 范围内的浮点数。 20 | // - 0.5:将范围限制到 [-0.5, 0.5] 21 | // * 2. * f32(w) / f32(max(w, h)):将范围扩展到 [-w/h, w/h] 范围内,确保横纵比保持一致。 22 | let x = (f32(i % w + pos.x) / f32(w) - 0.5) * 2. * f32(w) / f32(max(w, h)); 23 | // (i - (i % w)) / w + pos.y:这是为了计算每一行的 y 坐标。 24 | // i - (i % w) 将 i 对齐到当前行的起始索引,然后除以 w 得到行索引,再加上顶点属性中的 y 坐标偏移量 pos.y。 25 | let y = (f32((i - (i % w)) / w + pos.y) / f32(h) - 0.5) * 2. * f32(h) / f32(max(w, h)); 26 | return Out(vec4(x, y, 0., 1.), f32(cell)); 27 | } 28 | -------------------------------------------------------------------------------- /proj1_gameOfLife/src/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, 7 | body { 8 | margin: 0; 9 | width: 100%; 10 | height: 100%; 11 | background: #000; 12 | color: #fff; 13 | display: flex; 14 | text-align: center; 15 | flex-direction: column; 16 | justify-content: center; 17 | align-items: center; 18 | font-family: Avenir, Helvetica, Arial, sans-serif; 19 | -webkit-font-smoothing: antialiased; 20 | } 21 | p { 22 | font-size: 14px; 23 | margin: 0; 24 | } 25 | 26 | canvas { 27 | width: 60vw; 28 | height: 60vw; 29 | } 30 | -------------------------------------------------------------------------------- /proj1_gameOfLife/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"], 19 | "typeRoots": ["./node_modules/@webgpu/types", "./node_modules/@types"] 20 | }, 21 | "include": ["src"] 22 | } 23 | -------------------------------------------------------------------------------- /proj2_firework/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /proj2_firework/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /proj2_firework/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@types/dat.gui": "^0.7.10", 13 | "@webgpu/types": "^0.1.34", 14 | "typescript": "^5.0.4", 15 | "vite": "^4.3.2" 16 | }, 17 | "dependencies": { 18 | "dat.gui": "^0.7.9", 19 | "wgpu-matrix": "^2.5.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /proj2_firework/src/helper/gpuBuffer.ts: -------------------------------------------------------------------------------- 1 | // 创建顶点缓冲区 VBO 2 | // 获取一块状态为映射了的显存,以及一个对应的 arrayBuffer 对象来写数据 3 | /** 4 | * 应用程序可以请求映射一个 GPUBuffer,这样它们就可以通过代表 GPUBuffer 分配的部分的 arraybuffer 访问它的内容。 5 | * 映射一个 GPUBuffer 是通过 mapAsync() 异步请求的,这样用户代理可以确保 GPU 在应用程序访问它的内容之前完成了对 GPUBuffer 的使用。 6 | * 映射的 GPUBuffer 不能被 GPU 使用,必须使用 unmap() 解除映射,然后才能将使用它的工作提交到 Queue 时间轴。 7 | * 一旦映射了 GPUBuffer,应用程序就可以通过 getMappedRange 同步请求访问其内容的范围 8 | */ 9 | export const CreateGPUBuffer = ( 10 | device: GPUDevice, 11 | data: Float32Array, 12 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.VERTEX | 13 | GPUBufferUsage.COPY_DST 14 | ) => { 15 | const buffer = device.createBuffer({ 16 | size: data.byteLength, 17 | usage: usageFlag, 18 | mappedAtCreation: true, 19 | }); 20 | new Float32Array(buffer.getMappedRange()).set(data); 21 | buffer.unmap(); 22 | return buffer; 23 | }; 24 | 25 | export const CreateGPUBufferUint = ( 26 | device: GPUDevice, 27 | data: Uint32Array, 28 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 29 | GPUBufferUsage.COPY_DST 30 | ) => { 31 | const buffer = device.createBuffer({ 32 | size: data.byteLength, 33 | usage: usageFlag, 34 | mappedAtCreation: true, 35 | }); 36 | new Uint32Array(buffer.getMappedRange()).set(data); 37 | buffer.unmap(); 38 | return buffer; 39 | }; 40 | 41 | export const CreateGPUBufferUint16 = ( 42 | device: GPUDevice, 43 | data: Uint16Array, 44 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 45 | GPUBufferUsage.COPY_DST 46 | ) => { 47 | const buffer = device.createBuffer({ 48 | size: data.byteLength, 49 | usage: usageFlag, 50 | mappedAtCreation: true, 51 | }); 52 | new Uint16Array(buffer.getMappedRange()).set(data); 53 | buffer.unmap(); 54 | return buffer; 55 | }; 56 | -------------------------------------------------------------------------------- /proj2_firework/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.querySelector("canvas") as HTMLCanvasElement; 10 | canvas.width = window.innerWidth; 11 | canvas.height = window.innerHeight; 12 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 13 | // 请求WebGPU适配器与GPU设备 14 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 15 | const device = await adapter.requestDevice(); 16 | const format = navigator.gpu.getPreferredCanvasFormat(); 17 | // 配置上下文 18 | context.configure({ 19 | device: device, 20 | // 上下文格式 21 | format: format, 22 | // 不透明度 23 | alphaMode: "opaque", 24 | }); 25 | return { device, canvas, format, context }; 26 | }; 27 | -------------------------------------------------------------------------------- /proj2_firework/src/shader/compute.wgsl: -------------------------------------------------------------------------------- 1 | struct Particle { 2 | position : vec2, 3 | velocity : vec2, 4 | lifetime : f32, 5 | r: f32, 6 | g: f32, 7 | b: f32, 8 | }; 9 | 10 | struct ParticleBuffer { 11 | particles : array 12 | }; 13 | 14 | @group(0) @binding(0) var particleBuffer: ParticleBuffer; 15 | 16 | 17 | const size = u32(64); 18 | @compute @workgroup_size(size) 19 | fn main( 20 | @builtin(global_invocation_id) GlobalInvocationID : vec3 21 | ) { 22 | // 由于这是一个1维的线程分配(只有x轴),所以只需要使用GlobalInvocationID.x 23 | var index = GlobalInvocationID.x; 24 | var particle = particleBuffer.particles[index]; 25 | particle.position += particle.velocity; 26 | particle.velocity *= 0.5; 27 | particle.lifetime = max(0.0, particle.lifetime - 0.005); 28 | if (particle.lifetime <= 0.02) { 29 | particle.position = vec2(0.0, 0.0); // 重置到初始位置 30 | particle.velocity = vec2(0.0, 0.0); 31 | } 32 | particleBuffer.particles[index] = particle; 33 | } -------------------------------------------------------------------------------- /proj2_firework/src/shader/frag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment 2 | fn main( 3 | @location(0) Color : vec4, 4 | ) -> @location(0) vec4 { 5 | return Color; 6 | } -------------------------------------------------------------------------------- /proj2_firework/src/shader/vert.wgsl: -------------------------------------------------------------------------------- 1 | struct Particle { 2 | position : vec2, 3 | velocity : vec2, 4 | lifetime : f32, 5 | r: f32, 6 | g: f32, 7 | b: f32, 8 | }; 9 | 10 | struct ParticleBuffer { 11 | particles : array 12 | }; 13 | 14 | @group(0) @binding(0) var particleBuffer: ParticleBuffer; 15 | 16 | struct VertexOutput { 17 | @builtin(position) Position : vec4, 18 | @location(0) Color : vec4, 19 | }; 20 | 21 | @vertex 22 | fn main( 23 | @builtin(vertex_index) index : u32, 24 | ) -> VertexOutput { 25 | var output : VertexOutput; 26 | var particle = particleBuffer.particles[index]; 27 | output.Position = vec4(particle.position, 0.0, 1.0); 28 | output.Color = vec4(particle.r, particle.g, particle.b, particle.lifetime); 29 | return output; 30 | } -------------------------------------------------------------------------------- /proj2_firework/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /proj2_firework/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"], 19 | "typeRoots": ["./node_modules/@webgpu/types", "./node_modules/@types"] 20 | }, 21 | "include": ["src"] 22 | } 23 | -------------------------------------------------------------------------------- /proj2_firework_2/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /proj2_firework_2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /proj2_firework_2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@types/dat.gui": "^0.7.10", 13 | "@types/stats.js": "^0.17.0", 14 | "@webgpu/types": "^0.1.34", 15 | "typescript": "^5.0.4", 16 | "vite": "^4.3.2" 17 | }, 18 | "dependencies": { 19 | "dat.gui": "^0.7.9", 20 | "stats.js": "^0.17.0", 21 | "wgpu-matrix": "^2.5.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /proj2_firework_2/src/helper/gpuBuffer.ts: -------------------------------------------------------------------------------- 1 | // 创建顶点缓冲区 VBO 2 | // 获取一块状态为映射了的显存,以及一个对应的 arrayBuffer 对象来写数据 3 | /** 4 | * 应用程序可以请求映射一个 GPUBuffer,这样它们就可以通过代表 GPUBuffer 分配的部分的 arraybuffer 访问它的内容。 5 | * 映射一个 GPUBuffer 是通过 mapAsync() 异步请求的,这样用户代理可以确保 GPU 在应用程序访问它的内容之前完成了对 GPUBuffer 的使用。 6 | * 映射的 GPUBuffer 不能被 GPU 使用,必须使用 unmap() 解除映射,然后才能将使用它的工作提交到 Queue 时间轴。 7 | * 一旦映射了 GPUBuffer,应用程序就可以通过 getMappedRange 同步请求访问其内容的范围 8 | */ 9 | export const CreateGPUBuffer = ( 10 | device: GPUDevice, 11 | data: Float32Array, 12 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.VERTEX | 13 | GPUBufferUsage.COPY_DST 14 | ) => { 15 | const buffer = device.createBuffer({ 16 | size: data.byteLength, 17 | usage: usageFlag, 18 | mappedAtCreation: true, 19 | }); 20 | new Float32Array(buffer.getMappedRange()).set(data); 21 | buffer.unmap(); 22 | return buffer; 23 | }; 24 | 25 | export const CreateGPUBufferUint = ( 26 | device: GPUDevice, 27 | data: Uint32Array, 28 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 29 | GPUBufferUsage.COPY_DST 30 | ) => { 31 | const buffer = device.createBuffer({ 32 | size: data.byteLength, 33 | usage: usageFlag, 34 | mappedAtCreation: true, 35 | }); 36 | new Uint32Array(buffer.getMappedRange()).set(data); 37 | buffer.unmap(); 38 | return buffer; 39 | }; 40 | 41 | export const CreateGPUBufferUint16 = ( 42 | device: GPUDevice, 43 | data: Uint16Array, 44 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 45 | GPUBufferUsage.COPY_DST 46 | ) => { 47 | const buffer = device.createBuffer({ 48 | size: data.byteLength, 49 | usage: usageFlag, 50 | mappedAtCreation: true, 51 | }); 52 | new Uint16Array(buffer.getMappedRange()).set(data); 53 | buffer.unmap(); 54 | return buffer; 55 | }; 56 | -------------------------------------------------------------------------------- /proj2_firework_2/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.querySelector("canvas") as HTMLCanvasElement; 10 | canvas.width = window.innerWidth; 11 | canvas.height = window.innerHeight; 12 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 13 | // 请求WebGPU适配器与GPU设备 14 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 15 | const device = await adapter.requestDevice(); 16 | const format = navigator.gpu.getPreferredCanvasFormat(); 17 | // 配置上下文 18 | context.configure({ 19 | device: device, 20 | // 上下文格式 21 | format: format, 22 | // 不透明度 23 | alphaMode: "opaque", 24 | }); 25 | return { device, canvas, format, context }; 26 | }; 27 | -------------------------------------------------------------------------------- /proj2_firework_2/src/shader/frag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment 2 | fn main( 3 | @builtin(position) Position : vec4, 4 | @location(0) Color : vec4, 5 | ) -> @location(0) vec4 { 6 | return Color; 7 | } -------------------------------------------------------------------------------- /proj2_firework_2/src/shader/vert.wgsl: -------------------------------------------------------------------------------- 1 | struct Particle { 2 | position : vec2, 3 | velocity : vec2, 4 | lifetime : f32, 5 | r: f32, 6 | g: f32, 7 | b: f32, 8 | }; 9 | 10 | struct ParticleBuffer { 11 | particles : array 12 | }; 13 | 14 | @group(0) @binding(0) var particleBuffer: ParticleBuffer; 15 | 16 | struct VertexOutput { 17 | @builtin(position) Position : vec4, 18 | @location(0) Color : vec4, 19 | }; 20 | 21 | @vertex 22 | fn main( 23 | @builtin(vertex_index) index : u32, 24 | ) -> VertexOutput { 25 | var output : VertexOutput; 26 | var particle = particleBuffer.particles[index]; 27 | output.Position = vec4(particle.position, 0.0, 1.0); 28 | output.Color = vec4(particle.r, particle.g, particle.b, particle.lifetime); 29 | return output; 30 | } -------------------------------------------------------------------------------- /proj2_firework_2/src/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, 7 | body { 8 | margin: 0; 9 | width: 100%; 10 | height: 100%; 11 | background: #000; 12 | color: #fff; 13 | display: flex; 14 | text-align: center; 15 | flex-direction: column; 16 | justify-content: center; 17 | align-items: center; 18 | font-family: Avenir, Helvetica, Arial, sans-serif; 19 | -webkit-font-smoothing: antialiased; 20 | } 21 | p { 22 | font-size: 14px; 23 | margin: 0; 24 | } 25 | 26 | canvas { 27 | width: 60vw; 28 | height: 60vw; 29 | } 30 | -------------------------------------------------------------------------------- /proj2_firework_2/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"], 19 | "typeRoots": ["./node_modules/@webgpu/types", "./node_modules/@types"] 20 | }, 21 | "include": ["src"] 22 | } 23 | -------------------------------------------------------------------------------- /proj3_imageBlur/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /proj3_imageBlur/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /proj3_imageBlur/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@types/dat.gui": "^0.7.10", 13 | "@webgpu/types": "^0.1.34", 14 | "typescript": "^5.0.4", 15 | "vite": "^4.3.2" 16 | }, 17 | "dependencies": { 18 | "dat.gui": "^0.7.9", 19 | "wgpu-matrix": "^2.5.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /proj3_imageBlur/public/zs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyirs/webgpu_study/effbf26b3cb71d1c05cb6f7ed77aafcaad35d3e9/proj3_imageBlur/public/zs.png -------------------------------------------------------------------------------- /proj3_imageBlur/src/helper/gpuBuffer.ts: -------------------------------------------------------------------------------- 1 | // 创建顶点缓冲区 VBO 2 | // 获取一块状态为映射了的显存,以及一个对应的 arrayBuffer 对象来写数据 3 | /** 4 | * 应用程序可以请求映射一个 GPUBuffer,这样它们就可以通过代表 GPUBuffer 分配的部分的 arraybuffer 访问它的内容。 5 | * 映射一个 GPUBuffer 是通过 mapAsync() 异步请求的,这样用户代理可以确保 GPU 在应用程序访问它的内容之前完成了对 GPUBuffer 的使用。 6 | * 映射的 GPUBuffer 不能被 GPU 使用,必须使用 unmap() 解除映射,然后才能将使用它的工作提交到 Queue 时间轴。 7 | * 一旦映射了 GPUBuffer,应用程序就可以通过 getMappedRange 同步请求访问其内容的范围 8 | */ 9 | export const CreateGPUBuffer = ( 10 | device: GPUDevice, 11 | data: Float32Array, 12 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.VERTEX | 13 | GPUBufferUsage.COPY_DST 14 | ) => { 15 | const buffer = device.createBuffer({ 16 | size: data.byteLength, 17 | usage: usageFlag, 18 | mappedAtCreation: true, 19 | }); 20 | new Float32Array(buffer.getMappedRange()).set(data); 21 | buffer.unmap(); 22 | return buffer; 23 | }; 24 | 25 | export const CreateGPUBufferUint = ( 26 | device: GPUDevice, 27 | data: Uint32Array, 28 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 29 | GPUBufferUsage.COPY_DST 30 | ) => { 31 | const buffer = device.createBuffer({ 32 | size: data.byteLength, 33 | usage: usageFlag, 34 | mappedAtCreation: true, 35 | }); 36 | new Uint32Array(buffer.getMappedRange()).set(data); 37 | buffer.unmap(); 38 | return buffer; 39 | }; 40 | 41 | export const CreateGPUBufferUint16 = ( 42 | device: GPUDevice, 43 | data: Uint16Array, 44 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 45 | GPUBufferUsage.COPY_DST 46 | ) => { 47 | const buffer = device.createBuffer({ 48 | size: data.byteLength, 49 | usage: usageFlag, 50 | mappedAtCreation: true, 51 | }); 52 | new Uint16Array(buffer.getMappedRange()).set(data); 53 | buffer.unmap(); 54 | return buffer; 55 | }; 56 | -------------------------------------------------------------------------------- /proj3_imageBlur/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.querySelector("canvas") as HTMLCanvasElement; 10 | canvas.width = window.innerWidth; 11 | canvas.height = window.innerHeight; 12 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 13 | // 请求WebGPU适配器与GPU设备 14 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 15 | const device = await adapter.requestDevice(); 16 | const format = navigator.gpu.getPreferredCanvasFormat(); 17 | // 配置上下文 18 | context.configure({ 19 | device: device, 20 | // 上下文格式 21 | format: format, 22 | // 不透明度 23 | alphaMode: "opaque", 24 | }); 25 | return { device, canvas, format, context }; 26 | }; 27 | -------------------------------------------------------------------------------- /proj3_imageBlur/src/shader/frag.wgsl: -------------------------------------------------------------------------------- 1 | @group(0) @binding(0) var mySampler : sampler; 2 | @group(0) @binding(1) var myTexture : texture_2d; 3 | 4 | @fragment 5 | fn main(@location(0) fragUV : vec2) -> @location(0) vec4 { 6 | return textureSample(myTexture, mySampler, fragUV); 7 | } 8 | -------------------------------------------------------------------------------- /proj3_imageBlur/src/shader/vert.wgsl: -------------------------------------------------------------------------------- 1 | struct VertexOutput { 2 | @builtin(position) Position : vec4, 3 | @location(0) fragUV : vec2, 4 | } 5 | 6 | @vertex 7 | fn main(@builtin(vertex_index) VertexIndex : u32) -> VertexOutput { 8 | const pos = array( 9 | vec2( 1.0, 1.0), 10 | vec2( 1.0, -1.0), 11 | vec2(-1.0, -1.0), 12 | vec2( 1.0, 1.0), 13 | vec2(-1.0, -1.0), 14 | vec2(-1.0, 1.0), 15 | ); 16 | 17 | const uv = array( 18 | vec2(1.0, 0.0), 19 | vec2(1.0, 1.0), 20 | vec2(0.0, 1.0), 21 | vec2(1.0, 0.0), 22 | vec2(0.0, 1.0), 23 | vec2(0.0, 0.0), 24 | ); 25 | 26 | var output : VertexOutput; 27 | output.Position = vec4(pos[VertexIndex], 0.0, 1.0); 28 | output.fragUV = uv[VertexIndex]; 29 | return output; 30 | } -------------------------------------------------------------------------------- /proj3_imageBlur/src/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, 7 | body { 8 | margin: 0; 9 | width: 100%; 10 | height: 100%; 11 | background: #000; 12 | color: #fff; 13 | display: flex; 14 | text-align: center; 15 | flex-direction: column; 16 | justify-content: center; 17 | align-items: center; 18 | font-family: Avenir, Helvetica, Arial, sans-serif; 19 | -webkit-font-smoothing: antialiased; 20 | } 21 | p { 22 | font-size: 14px; 23 | margin: 0; 24 | } 25 | 26 | canvas { 27 | width: 60vw; 28 | height: 60vw; 29 | } 30 | -------------------------------------------------------------------------------- /proj3_imageBlur/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"], 19 | "typeRoots": ["./node_modules/@webgpu/types", "./node_modules/@types"] 20 | }, 21 | "include": ["src"] 22 | } 23 | -------------------------------------------------------------------------------- /proj3_imageBlur_Gaussian/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /proj3_imageBlur_Gaussian/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /proj3_imageBlur_Gaussian/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@types/dat.gui": "^0.7.10", 13 | "@webgpu/types": "^0.1.34", 14 | "typescript": "^5.0.4", 15 | "vite": "^4.3.2" 16 | }, 17 | "dependencies": { 18 | "dat.gui": "^0.7.9", 19 | "wgpu-matrix": "^2.5.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /proj3_imageBlur_Gaussian/public/zs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyirs/webgpu_study/effbf26b3cb71d1c05cb6f7ed77aafcaad35d3e9/proj3_imageBlur_Gaussian/public/zs.png -------------------------------------------------------------------------------- /proj3_imageBlur_Gaussian/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.querySelector("canvas") as HTMLCanvasElement; 10 | canvas.width = 256; 11 | canvas.height = 256; 12 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 13 | // 请求WebGPU适配器与GPU设备 14 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 15 | const device = await adapter.requestDevice(); 16 | const format = navigator.gpu.getPreferredCanvasFormat(); 17 | // 配置上下文 18 | context.configure({ 19 | device: device, 20 | // 上下文格式 21 | format: format, 22 | // 不透明度 23 | alphaMode: "opaque", 24 | }); 25 | return { device, canvas, format, context }; 26 | }; 27 | -------------------------------------------------------------------------------- /proj3_imageBlur_Gaussian/src/shader/frag.wgsl: -------------------------------------------------------------------------------- 1 | // 首先将纹理的颜色从 RGB 转换到 YUV 空间,然后对亮度(Y 分量)应用高斯模糊,最后将模糊后的亮度值用于输出颜色的 RGB 分量。 2 | // 这种方法特别适用于灰度图像或者只关注亮度变化的场景。 3 | 4 | @group(0) @binding(0) var rgb2yuv : mat3x3; // 注意缓冲区 5 | @group(0) @binding(1) var gaussian : array,3>; // 注意缓冲区 6 | @group(0) @binding(2) var kernel_offsets : array,9>; 7 | @group(0) @binding(3) var mySampler : sampler; 8 | @group(0) @binding(4) var myTexture : texture_2d; 9 | 10 | struct VertexOutput { 11 | @builtin(position) Position: vec4, 12 | @location(0) fragUV: vec2, 13 | } 14 | 15 | @fragment 16 | fn main(input: VertexOutput) -> @location(0) vec4 { 17 | var val = 0.0; 18 | for (var i = 0u; i < 3; i++) { 19 | // 将采样得到的 RGB 颜色转换为 YUV 颜色空间,并提取出 Y 分量(亮度) 20 | let a = vec3f( 21 | (rgb2yuv * textureSample(myTexture, mySampler, input.fragUV + kernel_offsets[i * 3].xy).xyz).x, 22 | (rgb2yuv * textureSample(myTexture, mySampler, input.fragUV + kernel_offsets[i * 3 + 1].xy).xyz).x, 23 | (rgb2yuv * textureSample(myTexture, mySampler, input.fragUV + kernel_offsets[i * 3 + 2].xy).xyz).x 24 | ); 25 | val += dot(a, gaussian[i]); 26 | } 27 | return vec4(val, val, val, 1.0); 28 | } 29 | -------------------------------------------------------------------------------- /proj3_imageBlur_Gaussian/src/shader/vert.wgsl: -------------------------------------------------------------------------------- 1 | struct VertexOutput { 2 | @builtin(position) Position: vec4, 3 | @location(0) fragUV: vec2, 4 | } 5 | 6 | @vertex 7 | fn main(@location(0) pos: vec2) -> VertexOutput { 8 | var output: VertexOutput; 9 | output.Position = vec4(pos, 0.0, 1.0); 10 | output.fragUV = pos * 0.5 + 0.5; 11 | return output; 12 | } -------------------------------------------------------------------------------- /proj3_imageBlur_Gaussian/src/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, 7 | body { 8 | margin: 0; 9 | width: 100%; 10 | height: 100%; 11 | background: #000; 12 | color: #fff; 13 | display: flex; 14 | text-align: center; 15 | flex-direction: column; 16 | justify-content: center; 17 | align-items: center; 18 | font-family: Avenir, Helvetica, Arial, sans-serif; 19 | -webkit-font-smoothing: antialiased; 20 | } 21 | p { 22 | font-size: 14px; 23 | margin: 0; 24 | } 25 | -------------------------------------------------------------------------------- /proj3_imageBlur_Gaussian/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"], 19 | "typeRoots": ["./node_modules/@webgpu/types", "./node_modules/@types"] 20 | }, 21 | "include": ["src"] 22 | } 23 | -------------------------------------------------------------------------------- /proj4_galaxy/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /proj4_galaxy/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /proj4_galaxy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@types/dat.gui": "^0.7.10", 13 | "@webgpu/types": "^0.1.34", 14 | "typescript": "^5.0.4", 15 | "vite": "^4.3.2" 16 | }, 17 | "dependencies": { 18 | "dat.gui": "^0.7.9", 19 | "wgpu-matrix": "^2.5.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /proj4_galaxy/src/helper/color.ts: -------------------------------------------------------------------------------- 1 | export class Color { 2 | r: number; 3 | g: number; 4 | b: number; 5 | constructor(hex: string); 6 | constructor(r: number, g: number, b: number); 7 | constructor(r: string | number, g?: number, b?: number) { 8 | if (typeof r === "string") { 9 | const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(r); 10 | if (result) { 11 | this.r = parseInt(result[1], 16) / 255.0; 12 | this.g = parseInt(result[2], 16) / 255.0; 13 | this.b = parseInt(result[3], 16) / 255.0; 14 | } else { 15 | throw new Error("Invalid color format. Use hex format: #RRGGBB"); 16 | } 17 | } else { 18 | if (g === undefined || b === undefined) { 19 | throw new Error("Invalid arguments."); 20 | } 21 | 22 | this.r = r; 23 | this.g = g; 24 | this.b = b; 25 | } 26 | } 27 | 28 | clone() { 29 | return new Color(this.r, this.g, this.b); 30 | } 31 | 32 | lerp(targetColor: Color, alpha: number) { 33 | this.r += (targetColor.r - this.r) * alpha; 34 | this.g += (targetColor.g - this.g) * alpha; 35 | this.b += (targetColor.b - this.b) * alpha; 36 | return this; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /proj4_galaxy/src/helper/gpuBuffer.ts: -------------------------------------------------------------------------------- 1 | // 创建顶点缓冲区 VBO 2 | // 获取一块状态为映射了的显存,以及一个对应的 arrayBuffer 对象来写数据 3 | /** 4 | * 应用程序可以请求映射一个 GPUBuffer,这样它们就可以通过代表 GPUBuffer 分配的部分的 arraybuffer 访问它的内容。 5 | * 映射一个 GPUBuffer 是通过 mapAsync() 异步请求的,这样用户代理可以确保 GPU 在应用程序访问它的内容之前完成了对 GPUBuffer 的使用。 6 | * 映射的 GPUBuffer 不能被 GPU 使用,必须使用 unmap() 解除映射,然后才能将使用它的工作提交到 Queue 时间轴。 7 | * 一旦映射了 GPUBuffer,应用程序就可以通过 getMappedRange 同步请求访问其内容的范围 8 | */ 9 | export const CreateGPUBuffer = ( 10 | device: GPUDevice, 11 | data: Float32Array, 12 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.VERTEX | 13 | GPUBufferUsage.COPY_DST 14 | ) => { 15 | const buffer = device.createBuffer({ 16 | size: data.byteLength, 17 | usage: usageFlag, 18 | mappedAtCreation: true, 19 | }); 20 | new Float32Array(buffer.getMappedRange()).set(data); 21 | buffer.unmap(); 22 | return buffer; 23 | }; 24 | 25 | export const CreateGPUBufferUint = ( 26 | device: GPUDevice, 27 | data: Uint32Array, 28 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 29 | GPUBufferUsage.COPY_DST 30 | ) => { 31 | const buffer = device.createBuffer({ 32 | size: data.byteLength, 33 | usage: usageFlag, 34 | mappedAtCreation: true, 35 | }); 36 | new Uint32Array(buffer.getMappedRange()).set(data); 37 | buffer.unmap(); 38 | return buffer; 39 | }; 40 | 41 | export const CreateGPUBufferUint16 = ( 42 | device: GPUDevice, 43 | data: Uint16Array, 44 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 45 | GPUBufferUsage.COPY_DST 46 | ) => { 47 | const buffer = device.createBuffer({ 48 | size: data.byteLength, 49 | usage: usageFlag, 50 | mappedAtCreation: true, 51 | }); 52 | new Uint16Array(buffer.getMappedRange()).set(data); 53 | buffer.unmap(); 54 | return buffer; 55 | }; 56 | -------------------------------------------------------------------------------- /proj4_galaxy/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.querySelector("canvas") as HTMLCanvasElement; 10 | canvas.width = window.innerWidth; 11 | canvas.height = window.innerHeight; 12 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 13 | // 请求WebGPU适配器与GPU设备 14 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 15 | const device = await adapter.requestDevice(); 16 | const format = navigator.gpu.getPreferredCanvasFormat(); 17 | // 配置上下文 18 | context.configure({ 19 | device: device, 20 | // 上下文格式 21 | format: format, 22 | // 不透明度 23 | alphaMode: "opaque", 24 | }); 25 | return { device, canvas, format, context }; 26 | }; 27 | -------------------------------------------------------------------------------- /proj4_galaxy/src/shader/compute.wgsl: -------------------------------------------------------------------------------- 1 | struct Particle { 2 | position : vec3, 3 | r: f32, 4 | g: f32, 5 | b: f32, 6 | pad : vec2 7 | }; 8 | 9 | struct ParticleBuffer { 10 | particles : array 11 | }; 12 | 13 | @group(0) @binding(0) var particleBuffer: ParticleBuffer; 14 | 15 | 16 | const size = u32(64); 17 | @compute @workgroup_size(size) 18 | fn main( 19 | @builtin(global_invocation_id) GlobalInvocationID : vec3 20 | ) { 21 | // 由于这是一个1维的线程分配(只有x轴),所以只需要使用GlobalInvocationID.x 22 | var index = GlobalInvocationID.x; 23 | var particle = particleBuffer.particles[index]; 24 | particleBuffer.particles[index] = particle; 25 | } -------------------------------------------------------------------------------- /proj4_galaxy/src/shader/frag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment 2 | fn main( 3 | @location(0) Color : vec4, 4 | ) -> @location(0) vec4 { 5 | return Color; 6 | } -------------------------------------------------------------------------------- /proj4_galaxy/src/shader/vert.wgsl: -------------------------------------------------------------------------------- 1 | struct Particle { 2 | position : vec3, 3 | r: f32, 4 | g: f32, 5 | b: f32, 6 | pad : vec2 7 | }; 8 | 9 | struct ParticleBuffer { 10 | particles : array 11 | }; 12 | 13 | struct Uniforms { 14 | modelViewProjectionMatrix : mat4x4, 15 | } 16 | 17 | @group(0) @binding(0) var particleBuffer: ParticleBuffer; 18 | @group(1) @binding(0) var uniforms : Uniforms; 19 | 20 | 21 | struct VertexOutput { 22 | @builtin(position) Position : vec4, 23 | @location(0) Color : vec4, 24 | }; 25 | 26 | @vertex 27 | fn main( 28 | @builtin(vertex_index) index : u32, 29 | ) -> VertexOutput { 30 | var output : VertexOutput; 31 | var particle = particleBuffer.particles[index]; 32 | output.Position = uniforms.modelViewProjectionMatrix * vec4(particle.position, 1.0); 33 | output.Color = vec4(particle.r, particle.g, particle.b, 1.0); 34 | return output; 35 | } -------------------------------------------------------------------------------- /proj4_galaxy/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /proj4_galaxy/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"], 19 | "typeRoots": ["./node_modules/@webgpu/types", "./node_modules/@types"] 20 | }, 21 | "include": ["src"] 22 | } 23 | -------------------------------------------------------------------------------- /vol0_triangle/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol0_triangle/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /vol0_triangle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@webgpu/types": "^0.1.32", 13 | "typescript": "^5.0.4", 14 | "vite": "^4.3.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /vol0_triangle/src/shader/frag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment 2 | fn main() -> @location(0) vec4 { 3 | return vec4(1.0, 0.0, 0.0, 1.0); 4 | } -------------------------------------------------------------------------------- /vol0_triangle/src/shader/vert.wgsl: -------------------------------------------------------------------------------- 1 | @vertex 2 | fn main( 3 | @builtin(vertex_index) vertexIndex: u32, 4 | ) -> @builtin(position) vec4 { 5 | // 设置三角形顶点坐标 6 | var pos = array, 3>( 7 | vec2(0.0, 0.5), 8 | vec2(-0.5, -0.5), 9 | vec2(0.5, -0.5), 10 | ); 11 | // 返回顶点坐标 12 | return vec4(pos[vertexIndex], 0.0, 1.0); 13 | } -------------------------------------------------------------------------------- /vol0_triangle/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol0_triangle/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"] 19 | }, 20 | "include": ["src"], 21 | } 22 | -------------------------------------------------------------------------------- /vol10_renderBundles/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol10_renderBundles/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 💡 **命令束** 💡 4 | 5 |

6 | passEncoder.executeBundles 是 WebGPU API 中的一个方法,它允许你执行预先录制的命令束(bundles)。 7 | 8 | 命令束是一组预先录制的渲染命令,这些命令可以在多个渲染通道中重复使用。 9 | 10 | - 减少 CPU 与 GPU 之间的通信:当你录制一个命令束,你实际上是在 CPU 上预先组织和优化一组命令,然后在需要时发送给 GPU。这减少了每帧都重新组织和发送命令的开销。 11 | - 命令重用:如果在多个渲染通道或多个帧中有相同的命令序列,你可以简单地重复使用同一个命令束,而不是每次都重新录制这些命令。 12 | - 并行录制:在某些实现中,命令束可以在多个线程上并行录制,从而利用多核 CPU 的优势。 13 | - 状态设置优化:由于命令束是预先录制的,某些实现可能会对状态设置进行优化,例如合并连续的状态设置命令或消除不必要的状态更改。 14 | 15 |
16 | 17 | device.createRenderBundleEncoder 是 WebGPU API 的一部分,它允许开发者创建一个 GPURenderBundleEncoder 对象。 18 | 19 | 这个对象可以预先录制渲染命令,然后在渲染过程中快速重复执行这些命令,而不需要每帧重新编码它们。这种方法可以提高渲染性能,特别是当有大量重复的渲染命令时。 20 | 21 | ``` 22 | const renderBundleEncoder = device.createRenderBundleEncoder(descriptor); 23 | ``` 24 | 25 | - descriptor: 一个对象,描述了渲染束编码器的配置。它包括以下属性: 26 | 27 | - colorFormats: 一个数组,列出了渲染目标的颜色格式。 28 | - depthStencilFormat (可选): 深度/模板附件的格式。 29 | - sampleCount (可选): 多重采样的数量,默认为 1。 30 | 31 | 1.创建渲染束编码器: 32 | 33 | ``` 34 | const renderBundleEncoder = device.createRenderBundleEncoder({ 35 | colorFormats: ['rgba8unorm'], 36 | depthStencilFormat: 'depth24plus', 37 | }); 38 | ``` 39 | 40 | 2.编码渲染命令: 41 | 42 | 使用渲染束编码器的方法(如 setPipeline, setVertexBuffer 等)来编码渲染命令。 43 | 44 | ``` 45 | renderBundleEncoder.setPipeline(pipeline); 46 | renderBundleEncoder.setVertexBuffer(0, vertexBuffer); 47 | // ... 其他渲染命令 48 | ``` 49 | 50 | 3.完成渲染束: 51 | 52 | ``` 53 | const renderBundle = renderBundleEncoder.finish(); 54 | ``` 55 | 56 | 4.在渲染过程中使用渲染束: 57 | 58 | 在渲染过程中,使用 executeBundles 方法执行预先录制的渲染束。 59 | 60 | ``` 61 | renderPassEncoder.executeBundles([renderBundle]); 62 | ``` 63 | -------------------------------------------------------------------------------- /vol10_renderBundles/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /vol10_renderBundles/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@types/dat.gui": "^0.7.10", 13 | "@webgpu/types": "^0.1.34", 14 | "typescript": "^5.0.4", 15 | "vite": "^4.3.2" 16 | }, 17 | "dependencies": { 18 | "dat.gui": "^0.7.9", 19 | "wgpu-matrix": "^2.5.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /vol10_renderBundles/public/moon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyirs/webgpu_study/effbf26b3cb71d1c05cb6f7ed77aafcaad35d3e9/vol10_renderBundles/public/moon.jpg -------------------------------------------------------------------------------- /vol10_renderBundles/public/saturn.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyirs/webgpu_study/effbf26b3cb71d1c05cb6f7ed77aafcaad35d3e9/vol10_renderBundles/public/saturn.jpg -------------------------------------------------------------------------------- /vol10_renderBundles/src/helper/color.ts: -------------------------------------------------------------------------------- 1 | export class Color { 2 | r: number; 3 | g: number; 4 | b: number; 5 | constructor(hex: string); 6 | constructor(r: number, g: number, b: number); 7 | constructor(r: string | number, g?: number, b?: number) { 8 | if (typeof r === "string") { 9 | const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(r); 10 | if (result) { 11 | this.r = parseInt(result[1], 16) / 255.0; 12 | this.g = parseInt(result[2], 16) / 255.0; 13 | this.b = parseInt(result[3], 16) / 255.0; 14 | } else { 15 | throw new Error("Invalid color format. Use hex format: #RRGGBB"); 16 | } 17 | } else { 18 | if (g === undefined || b === undefined) { 19 | throw new Error("Invalid arguments."); 20 | } 21 | 22 | this.r = r; 23 | this.g = g; 24 | this.b = b; 25 | } 26 | } 27 | 28 | clone() { 29 | return new Color(this.r, this.g, this.b); 30 | } 31 | 32 | lerp(targetColor: Color, alpha: number) { 33 | this.r += (targetColor.r - this.r) * alpha; 34 | this.g += (targetColor.g - this.g) * alpha; 35 | this.b += (targetColor.b - this.b) * alpha; 36 | return this; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /vol10_renderBundles/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.querySelector("canvas") as HTMLCanvasElement; 10 | canvas.width = window.innerWidth; 11 | canvas.height = window.innerHeight; 12 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 13 | // 请求WebGPU适配器与GPU设备 14 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 15 | const device = await adapter.requestDevice(); 16 | const format = navigator.gpu.getPreferredCanvasFormat(); 17 | // 配置上下文 18 | context.configure({ 19 | device: device, 20 | // 上下文格式 21 | format: format, 22 | // 不透明度 23 | alphaMode: "opaque", 24 | }); 25 | return { device, canvas, format, context }; 26 | }; 27 | -------------------------------------------------------------------------------- /vol10_renderBundles/src/helper/texture.ts: -------------------------------------------------------------------------------- 1 | export const CreateTextureFromImage = async ( 2 | device: GPUDevice, 3 | imageSrc: string 4 | ) => { 5 | let texture: GPUTexture; 6 | // const response = await fetch(new URL(imageSrc, import.meta.url).toString()); 7 | // const imageBitmap = await createImageBitmap(await response.blob()); 8 | const image = new Image(); 9 | image.src = imageSrc; 10 | await image.decode(); 11 | const imageBitmap = await createImageBitmap(image); 12 | texture = device.createTexture({ 13 | size: [imageBitmap.width, imageBitmap.height, 1], 14 | format: "rgba8unorm", 15 | usage: 16 | GPUTextureUsage.TEXTURE_BINDING | 17 | GPUTextureUsage.COPY_DST | 18 | GPUTextureUsage.RENDER_ATTACHMENT, 19 | }); 20 | device.queue.copyExternalImageToTexture( 21 | { source: imageBitmap }, 22 | { texture: texture }, 23 | [imageBitmap.width, imageBitmap.height] 24 | ); 25 | return texture; 26 | }; 27 | -------------------------------------------------------------------------------- /vol10_renderBundles/src/shader/frag.wgsl: -------------------------------------------------------------------------------- 1 | @group(1) @binding(1) var meshSampler: sampler; 2 | @group(1) @binding(2) var meshTexture: texture_2d; 3 | 4 | struct VertexOutput { 5 | @builtin(position) position: vec4f, 6 | @location(0) normal: vec3f, 7 | @location(1) uv: vec2f, 8 | } 9 | 10 | // Static directional lighting 11 | const lightDir = vec3f(1, 1, 1); 12 | const dirColor = vec3(1); 13 | const ambientColor = vec3f(0.05); 14 | 15 | @fragment 16 | fn main(input: VertexOutput) -> @location(0) vec4f { 17 | let textureColor = textureSample(meshTexture, meshSampler, input.uv); 18 | // Very simplified lighting algorithm. 19 | let lightColor = saturate(ambientColor + max(dot(input.normal, lightDir), 0.0) * dirColor); 20 | return vec4f(textureColor.rgb * lightColor, textureColor.a); 21 | } -------------------------------------------------------------------------------- /vol10_renderBundles/src/shader/vert.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | viewProjectionMatrix: mat4x4f 3 | } 4 | @group(0) @binding(0) var uniforms : Uniforms; 5 | @group(1) @binding(0) var modelMatrix : mat4x4f; 6 | 7 | struct VertexInput { 8 | @location(0) position: vec4f, 9 | @location(1) normal: vec3f, 10 | @location(2) uv: vec2f 11 | } 12 | 13 | struct VertexOutput { 14 | @builtin(position) position: vec4f, 15 | @location(0) normal: vec3f, 16 | @location(1) uv: vec2f, 17 | } 18 | 19 | @vertex 20 | fn main(input: VertexInput) -> VertexOutput { 21 | var output: VertexOutput; 22 | output.position = uniforms.viewProjectionMatrix * modelMatrix * input.position; 23 | output.normal = normalize((modelMatrix * vec4(input.normal, 0)).xyz); 24 | output.uv = input.uv; 25 | return output; 26 | } -------------------------------------------------------------------------------- /vol10_renderBundles/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol10_renderBundles/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"], 19 | "typeRoots": ["./node_modules/@webgpu/types", "./node_modules/@types"] 20 | }, 21 | "include": ["src"] 22 | } 23 | -------------------------------------------------------------------------------- /vol11_glb/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol11_glb/README.md: -------------------------------------------------------------------------------- 1 | [参考代码](https://github.com/Twinklebear/webgpu-gltf) 2 | 3 | ## materials 4 | 5 | 在没有任何扩展的情况下,glTf 仅支持一种材质,即 pbrMetallicRoughness 材质模型,这是一种物理渲染模型,通过表面金属度和粗糙度进行描述,还可以定义表面基本颜色和反射率,以及法线贴图,遮挡贴图,以及其他一些属性控制几何形状和混合状态。完整的属性: 6 | 7 | ``` 8 | "materials": [{ 9 | "pbrMetallicRoughness": { 10 | "baseColorTexture": { "index": 1 }, 11 | "baseColorFactor": [ 1.0, 0.75, 0.35, 1.0 ], 12 | "metallicRoughnessTexture": { "index": 5 }, 13 | "metallicFactor": 1.0, 14 | "roughnessFactor": 0.0 15 | }, 16 | "normalTexture": { "index": 2 }, 17 | "occlusionTexture": { 18 | "index": 4, 19 | "strength": 0.9 20 | }, 21 | "emissiveTexture": { "index": 3 }, 22 | "emissiveFactor": [0.4, 0.8, 0.6], 23 | "alphaMode": "OPAQUE", 24 | "doubleSided": true, 25 | }] 26 | ``` 27 | -------------------------------------------------------------------------------- /vol11_glb/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /vol11_glb/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@webgpu/types": "^0.1.32", 13 | "typescript": "^5.0.4", 14 | "vite": "^4.3.2" 15 | }, 16 | "dependencies": { 17 | "wgpu-matrix": "^2.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vol11_glb/public/Buggy.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyirs/webgpu_study/effbf26b3cb71d1c05cb6f7ed77aafcaad35d3e9/vol11_glb/public/Buggy.glb -------------------------------------------------------------------------------- /vol11_glb/public/DamagedHelmet.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyirs/webgpu_study/effbf26b3cb71d1c05cb6f7ed77aafcaad35d3e9/vol11_glb/public/DamagedHelmet.glb -------------------------------------------------------------------------------- /vol11_glb/src/glb/glb_accessor.ts: -------------------------------------------------------------------------------- 1 | import { gltfTypeNumComponents, gltfTypeSize } from "./glb_tool"; 2 | import { GLTFBufferView } from "./glb_viewbuffer"; 3 | 4 | // 访问GLB文件中的顶点、索引等数据。 5 | export class GLTFAccessor { 6 | count: number; 7 | componentType: GLTFComponentType; 8 | gltfType: string; 9 | numComponents: number; 10 | numScalars: number; 11 | view: GLTFBufferView; 12 | byteOffset: number; 13 | constructor(view: GLTFBufferView, accessor: IAccessor) { 14 | this.count = accessor.count; 15 | this.componentType = accessor.componentType; 16 | this.gltfType = accessor.type; 17 | this.numComponents = gltfTypeNumComponents(accessor["type"])!; 18 | this.numScalars = this.count * this.numComponents; 19 | this.view = view; 20 | this.byteOffset = 0; 21 | if (accessor.byteOffset !== undefined) { 22 | this.byteOffset = accessor.byteOffset; 23 | } 24 | } 25 | 26 | get byteStride() { 27 | let elementSize = gltfTypeSize(this.componentType, this.gltfType); 28 | return Math.max(elementSize, this.view.byteStride); 29 | } 30 | 31 | get byteLength() { 32 | return this.count * this.byteStride; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /vol11_glb/src/glb/glb_mesh.ts: -------------------------------------------------------------------------------- 1 | import { GLTFPrimitive } from "./glb_primitive"; 2 | 3 | export class GLTFMesh { 4 | name: string; 5 | primitives: GLTFPrimitive[]; 6 | constructor(name: string, primitives: GLTFPrimitive[]) { 7 | this.name = name; 8 | this.primitives = primitives; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /vol11_glb/src/glb/glb_model.ts: -------------------------------------------------------------------------------- 1 | import { GLTFNode } from "./glb_node"; 2 | import { GLBShaderCache } from "./glb_shader_cache"; 3 | 4 | export class GLBModel { 5 | nodes: GLTFNode[]; 6 | constructor(nodes: GLTFNode[]) { 7 | this.nodes = nodes; 8 | } 9 | 10 | buildRenderBundles( 11 | device: GPUDevice, 12 | shaderCache: GLBShaderCache, 13 | viewParamsLayout: GPUBindGroupLayout, 14 | viewParamsBindGroup: GPUBindGroup | null, 15 | swapChainFormat: GPUTextureFormat, 16 | depthFormat: GPUTextureFormat 17 | ) { 18 | let renderBundles = []; 19 | for (let i = 0; i < this.nodes.length; ++i) { 20 | let n = this.nodes[i]; 21 | let bundle = n.buildRenderBundle( 22 | device, 23 | shaderCache, 24 | viewParamsLayout, 25 | viewParamsBindGroup, 26 | swapChainFormat, 27 | depthFormat 28 | ); 29 | renderBundles.push(bundle); 30 | } 31 | return renderBundles; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /vol11_glb/src/glb/glb_sampler.ts: -------------------------------------------------------------------------------- 1 | import { GLTFTextureFilter } from "./glb_tool"; 2 | 3 | export class GLTFSampler { 4 | sampler: GPUSampler; 5 | constructor(sampler: IGLTFSampler, device: GPUDevice) { 6 | let magFilter: GPUFilterMode = 7 | sampler.magFilter === undefined || 8 | sampler.magFilter == GLTFTextureFilter.LINEAR 9 | ? "linear" 10 | : "nearest"; 11 | let minFilter: GPUFilterMode = 12 | sampler.minFilter === undefined || 13 | sampler.minFilter == GLTFTextureFilter.LINEAR 14 | ? "linear" 15 | : "nearest"; 16 | let wrapS: GPUAddressMode = "repeat"; 17 | if (sampler.wrapS !== undefined) { 18 | if (sampler.wrapS == GLTFTextureFilter.REPEAT) { 19 | wrapS = "repeat"; 20 | } else if (sampler.wrapS == GLTFTextureFilter.CLAMP_TO_EDGE) { 21 | wrapS = "clamp-to-edge"; 22 | } else { 23 | wrapS = "mirror-repeat"; 24 | } 25 | } 26 | let wrapT: GPUAddressMode = "repeat"; 27 | if (sampler.wrapT !== undefined) { 28 | if (sampler.wrapT == GLTFTextureFilter.REPEAT) { 29 | wrapT = "repeat"; 30 | } else if (sampler.wrapT == GLTFTextureFilter.CLAMP_TO_EDGE) { 31 | wrapT = "clamp-to-edge"; 32 | } else { 33 | wrapT = "mirror-repeat"; 34 | } 35 | } 36 | this.sampler = device.createSampler({ 37 | magFilter: magFilter, 38 | minFilter: minFilter, 39 | addressModeU: wrapS, 40 | addressModeV: wrapT, 41 | mipmapFilter: "linear", 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /vol11_glb/src/glb/glb_texture.ts: -------------------------------------------------------------------------------- 1 | import { GLTFSampler } from "./glb_sampler"; 2 | 3 | // GLTFTexture和GLTFSampler类 处理GLB文件中的纹理和采样器,包括纹理的创建和配置 4 | export class GLTFTexture { 5 | gltfsampler: GLTFSampler; 6 | sampler: GPUSampler; 7 | image: GPUTexture; 8 | imageView: GPUTextureView; 9 | constructor(sampler: GLTFSampler, image: GPUTexture) { 10 | this.gltfsampler = sampler; 11 | this.sampler = sampler.sampler; 12 | this.image = image; 13 | this.imageView = image.createView(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /vol11_glb/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol11_glb/src/types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.glb" { 2 | const src: string; 3 | export default src; 4 | } 5 | -------------------------------------------------------------------------------- /vol11_glb/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"] 19 | }, 20 | "include": ["src"] 21 | } 22 | -------------------------------------------------------------------------------- /vol11_glb/vite.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | assetsInclude: ["**/*.glb"], 3 | }; 4 | -------------------------------------------------------------------------------- /vol11_glb_simple/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol11_glb_simple/README.md: -------------------------------------------------------------------------------- 1 | [参考代码](https://github.com/Twinklebear/webgpu-gltf) 2 | 3 | ## materials 4 | 5 | 在没有任何扩展的情况下,glTf 仅支持一种材质,即 pbrMetallicRoughness 材质模型,这是一种物理渲染模型,通过表面金属度和粗糙度进行描述,还可以定义表面基本颜色和反射率,以及法线贴图,遮挡贴图,以及其他一些属性控制几何形状和混合状态。完整的属性: 6 | 7 | ``` 8 | "materials": [{ 9 | "pbrMetallicRoughness": { 10 | "baseColorTexture": { "index": 1 }, 11 | "baseColorFactor": [ 1.0, 0.75, 0.35, 1.0 ], 12 | "metallicRoughnessTexture": { "index": 5 }, 13 | "metallicFactor": 1.0, 14 | "roughnessFactor": 0.0 15 | }, 16 | "normalTexture": { "index": 2 }, 17 | "occlusionTexture": { 18 | "index": 4, 19 | "strength": 0.9 20 | }, 21 | "emissiveTexture": { "index": 3 }, 22 | "emissiveFactor": [0.4, 0.8, 0.6], 23 | "alphaMode": "OPAQUE", 24 | "doubleSided": true, 25 | }] 26 | ``` 27 | -------------------------------------------------------------------------------- /vol11_glb_simple/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /vol11_glb_simple/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@webgpu/types": "^0.1.32", 13 | "typescript": "^5.0.4", 14 | "vite": "^4.3.2" 15 | }, 16 | "dependencies": { 17 | "wgpu-matrix": "^2.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vol11_glb_simple/public/Buggy.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyirs/webgpu_study/effbf26b3cb71d1c05cb6f7ed77aafcaad35d3e9/vol11_glb_simple/public/Buggy.glb -------------------------------------------------------------------------------- /vol11_glb_simple/public/DamagedHelmet.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyirs/webgpu_study/effbf26b3cb71d1c05cb6f7ed77aafcaad35d3e9/vol11_glb_simple/public/DamagedHelmet.glb -------------------------------------------------------------------------------- /vol11_glb_simple/src/glb/glb_accessor.ts: -------------------------------------------------------------------------------- 1 | import { gltfTypeNumComponents, gltfTypeSize } from "./glb_tool"; 2 | import { GLTFBufferView } from "./glb_viewbuffer"; 3 | 4 | // 访问GLB文件中的顶点、索引等数据。 5 | export class GLTFAccessor { 6 | count: number; 7 | componentType: GLTFComponentType; 8 | gltfType: string; 9 | numComponents: number; 10 | numScalars: number; 11 | view: GLTFBufferView; 12 | byteOffset: number; 13 | constructor(view: GLTFBufferView, accessor: IAccessor) { 14 | this.count = accessor.count; 15 | this.componentType = accessor.componentType; 16 | this.gltfType = accessor.type; 17 | this.numComponents = gltfTypeNumComponents(accessor["type"])!; 18 | this.numScalars = this.count * this.numComponents; 19 | this.view = view; 20 | this.byteOffset = 0; 21 | if (accessor.byteOffset !== undefined) { 22 | this.byteOffset = accessor.byteOffset; 23 | } 24 | } 25 | 26 | get byteStride() { 27 | let elementSize = gltfTypeSize(this.componentType, this.gltfType); 28 | return Math.max(elementSize, this.view.byteStride); 29 | } 30 | 31 | get byteLength() { 32 | return this.count * this.byteStride; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /vol11_glb_simple/src/glb/glb_mesh.ts: -------------------------------------------------------------------------------- 1 | import { GLTFPrimitive } from "./glb_primitive"; 2 | 3 | export class GLTFMesh { 4 | name: string; 5 | primitives: GLTFPrimitive[]; 6 | constructor(name: string, primitives: GLTFPrimitive[]) { 7 | this.name = name; 8 | this.primitives = primitives; 9 | } 10 | 11 | render(renderPassEncoder: GPURenderPassEncoder) { 12 | for (let prim of this.primitives) { 13 | prim.render(renderPassEncoder); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /vol11_glb_simple/src/glb/glb_model.ts: -------------------------------------------------------------------------------- 1 | import { GLTFNode } from "./glb_node"; 2 | 3 | export class GLBModel { 4 | nodes: GLTFNode[]; 5 | constructor(nodes: GLTFNode[]) { 6 | this.nodes = nodes; 7 | } 8 | 9 | buildRenderPipeline( 10 | device: GPUDevice, 11 | shaderModule: GPUShaderModule, 12 | uniformsBindGroupLayout: GPUBindGroupLayout, 13 | swapChainFormat: GPUTextureFormat, 14 | depthFormat: GPUTextureFormat 15 | ) { 16 | for (let i = 0; i < this.nodes.length; ++i) { 17 | let n = this.nodes[i]; 18 | n.buildRenderPipeline( 19 | device, 20 | shaderModule, 21 | uniformsBindGroupLayout, 22 | swapChainFormat, 23 | depthFormat 24 | ); 25 | } 26 | } 27 | 28 | render(renderPassEncoder: GPURenderPassEncoder, uniformsBindGroup: GPUBindGroup) { 29 | renderPassEncoder.setBindGroup(0, uniformsBindGroup); 30 | for (let n of this.nodes) { 31 | n.render(renderPassEncoder); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /vol11_glb_simple/src/shader/glb.wgsl: -------------------------------------------------------------------------------- 1 | struct VertexInput { 2 | @location(0) position: vec3, 3 | }; 4 | 5 | struct VertexOutput { 6 | @builtin(position) position: vec4, 7 | @location(0) world_pos: vec3, 8 | }; 9 | 10 | struct ViewParams { 11 | view_proj: mat4x4, 12 | }; 13 | 14 | struct NodeParams { 15 | transform: mat4x4, 16 | }; 17 | 18 | @group(0) @binding(0) 19 | var view_params: ViewParams; 20 | 21 | @group(1) @binding(0) 22 | var node_params: NodeParams; 23 | 24 | @vertex 25 | fn vertex_main(vert: VertexInput) -> VertexOutput { 26 | var out: VertexOutput; 27 | out.position = view_params.view_proj * node_params.transform * vec4(vert.position, 1.0); 28 | out.world_pos = vert.position.xyz; 29 | return out; 30 | } 31 | 32 | @fragment 33 | fn fragment_main(in: VertexOutput) -> @location(0) vec4 { 34 | let dx = dpdx(in.world_pos); 35 | let dy = dpdy(in.world_pos); 36 | let n = normalize(cross(dx, dy)); 37 | return vec4((n + 1.0) * 0.5, 1.0); 38 | } 39 | -------------------------------------------------------------------------------- /vol11_glb_simple/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol11_glb_simple/src/types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.glb" { 2 | const src: string; 3 | export default src; 4 | } 5 | -------------------------------------------------------------------------------- /vol11_glb_simple/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"] 19 | }, 20 | "include": ["src"] 21 | } 22 | -------------------------------------------------------------------------------- /vol11_glb_simple/vite.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | assetsInclude: ["**/*.glb"], 3 | }; 4 | -------------------------------------------------------------------------------- /vol1_oneCube/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol1_oneCube/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /vol1_oneCube/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@webgpu/types": "^0.1.32", 13 | "typescript": "^5.0.4", 14 | "vite": "^4.3.2" 15 | }, 16 | "dependencies": { 17 | "wgpu-matrix": "^2.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vol1_oneCube/src/shader/cubeFrag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment 2 | fn main( 3 | @location(0) fragUV: vec2, 4 | @location(1) fragPosition: vec4, 5 | ) -> @location(0) vec4 { 6 | return fragPosition; 7 | } -------------------------------------------------------------------------------- /vol1_oneCube/src/shader/cubeVert.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | modelViewProjectionMatrix: mat4x4, 3 | } 4 | @binding(0) @group(0) var uniforms: Uniforms; 5 | 6 | struct VertexOutput { 7 | @builtin(position) position: vec4, 8 | @location(0) fragUV: vec2, 9 | @location(1) fragPosition: vec4, 10 | } 11 | 12 | @vertex 13 | fn main( 14 | @location(0) position:vec4, 15 | @location(1) uv:vec2, 16 | ) -> VertexOutput { 17 | var output:VertexOutput; 18 | output.position = uniforms.modelViewProjectionMatrix * position; 19 | output.fragUV = uv; 20 | output.fragPosition = (position + vec4(1.0,1.0,1.0,1.0))* 0.5; 21 | return output; 22 | } 23 | 24 | 25 | // output.position: 26 | // 这是顶点着色器的输出,它决定了当前顶点在屏幕上的位置。 27 | // uniforms.modelViewProjectionMatrix * position 这个操作是将模型坐标转换为屏幕坐标。这里使用的 modelViewProjectionMatrix 是模型矩阵、视图矩阵和投影矩阵的组合。它将模型坐标转换为归一化设备坐标 (NDC)。 28 | // NDC 范围是 [-1, 1]。这意味着,经过这个变换后,任何在视野之外的顶点将有一个超出这个范围的 x、y 或 z 值。 29 | // 在顶点着色器执行后,图形管线将执行裁剪,删除那些超出 NDC 范围的部分,并将剩余的部分转换为屏幕坐标。 30 | 31 | // fragPosition: 32 | // 这是顶点着色器为每个顶点输出的一个值,之后将由光栅化器插值并传递给片元着色器。 33 | // fragPosition 是模型空间中的坐标值,它被稍微调整了以使其范围在 [0, 1] 而不是 [-1, 1]。这样做可能是为了满足某种特定需求,例如将其用作纹理坐标或其他计算。 34 | // 与 output.position 不同,fragPosition 不影响顶点在屏幕上的位置。它只是一个额外的数据,可以在后续的计算中使用。 -------------------------------------------------------------------------------- /vol1_oneCube/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol1_oneCube/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"] 19 | }, 20 | "include": ["src"], 21 | } 22 | -------------------------------------------------------------------------------- /vol1_oneCube_MSAA/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol1_oneCube_MSAA/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /vol1_oneCube_MSAA/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@webgpu/types": "^0.1.32", 13 | "typescript": "^5.0.4", 14 | "vite": "^4.3.2" 15 | }, 16 | "dependencies": { 17 | "wgpu-matrix": "^2.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vol1_oneCube_MSAA/src/shader/cubeFrag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment 2 | fn main( 3 | @location(0) fragUV: vec2, 4 | @location(1) fragPosition: vec4, 5 | ) -> @location(0) vec4 { 6 | return fragPosition; 7 | } -------------------------------------------------------------------------------- /vol1_oneCube_MSAA/src/shader/cubeVert.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | modelViewProjectionMatrix: mat4x4, 3 | } 4 | @binding(0) @group(0) var uniforms: Uniforms; 5 | 6 | struct VertexOutput { 7 | @builtin(position) position: vec4, 8 | @location(0) fragUV: vec2, 9 | @location(1) fragPosition: vec4, 10 | } 11 | 12 | @vertex 13 | 14 | fn main( 15 | @location(0) position:vec4, 16 | @location(1) uv:vec2, 17 | ) -> VertexOutput { 18 | var output:VertexOutput; 19 | output.position = uniforms.modelViewProjectionMatrix * position; 20 | output.fragUV = uv; 21 | output.fragPosition = (position + vec4(1.0,1.0,1.0,1.0))* 0.5; 22 | return output; 23 | } -------------------------------------------------------------------------------- /vol1_oneCube_MSAA/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol1_oneCube_MSAA/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"] 19 | }, 20 | "include": ["src"], 21 | } 22 | -------------------------------------------------------------------------------- /vol1_oneCube_canvas_textureSampling/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol1_oneCube_canvas_textureSampling/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /vol1_oneCube_canvas_textureSampling/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@webgpu/types": "^0.1.32", 13 | "typescript": "^5.0.4", 14 | "vite": "^4.3.2" 15 | }, 16 | "dependencies": { 17 | "wgpu-matrix": "^2.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vol1_oneCube_canvas_textureSampling/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.querySelector("canvas#webgpu") as HTMLCanvasElement; 10 | canvas.width = window.innerWidth; 11 | canvas.height = window.innerHeight; 12 | const drawCanvas = document.querySelector( 13 | "canvas#canvas" 14 | ) as HTMLCanvasElement; 15 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 16 | // 请求WebGPU适配器与GPU设备 17 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 18 | const device = await adapter.requestDevice(); 19 | const format = navigator.gpu.getPreferredCanvasFormat(); 20 | // 配置上下文 21 | context.configure({ 22 | device: device, 23 | // 上下文格式 24 | format: format, 25 | // 不透明度 26 | alphaMode: "opaque", 27 | }); 28 | return { device, canvas, drawCanvas, format, context }; 29 | }; 30 | -------------------------------------------------------------------------------- /vol1_oneCube_canvas_textureSampling/src/helper/vertexData.ts: -------------------------------------------------------------------------------- 1 | export const CubeData = () => { 2 | const vertexData = new Float32Array([ 3 | // float3 position, float2 uv 4 | // face1 5 | +1, -1, +1, 1, 1, 6 | -1, -1, +1, 0, 1, 7 | -1, -1, -1, 0, 0, 8 | +1, -1, -1, 1, 0, 9 | +1, -1, +1, 1, 1, 10 | -1, -1, -1, 0, 0, 11 | // face2 12 | +1, +1, +1, 1, 1, 13 | +1, -1, +1, 0, 1, 14 | +1, -1, -1, 0, 0, 15 | +1, +1, -1, 1, 0, 16 | +1, +1, +1, 1, 1, 17 | +1, -1, -1, 0, 0, 18 | // face3 19 | -1, +1, +1, 1, 1, 20 | +1, +1, +1, 0, 1, 21 | +1, +1, -1, 0, 0, 22 | -1, +1, -1, 1, 0, 23 | -1, +1, +1, 1, 1, 24 | +1, +1, -1, 0, 0, 25 | // face4 26 | -1, -1, +1, 1, 1, 27 | -1, +1, +1, 0, 1, 28 | -1, +1, -1, 0, 0, 29 | -1, -1, -1, 1, 0, 30 | -1, -1, +1, 1, 1, 31 | -1, +1, -1, 0, 0, 32 | // face5 33 | +1, +1, +1, 1, 1, 34 | -1, +1, +1, 0, 1, 35 | -1, -1, +1, 0, 0, 36 | -1, -1, +1, 0, 0, 37 | +1, -1, +1, 1, 0, 38 | +1, +1, +1, 1, 1, 39 | // face6 40 | +1, -1, -1, 1, 1, 41 | -1, -1, -1, 0, 1, 42 | -1, +1, -1, 0, 0, 43 | +1, +1, -1, 1, 0, 44 | +1, -1, -1, 1, 1, 45 | -1, +1, -1, 0, 0 46 | ]) 47 | 48 | return { 49 | vertexData, 50 | }; 51 | }; -------------------------------------------------------------------------------- /vol1_oneCube_canvas_textureSampling/src/shader/cubeFrag.wgsl: -------------------------------------------------------------------------------- 1 | @group(1) @binding(0) var Sampler: sampler; 2 | @group(1) @binding(1) var Texture: texture_2d; 3 | 4 | @fragment 5 | fn main(@location(0) fragUV: vec2, 6 | @location(1) fragPosition: vec4) -> @location(0) vec4 { 7 | return textureSample(Texture, Sampler, fragUV) * fragPosition; 8 | } -------------------------------------------------------------------------------- /vol1_oneCube_canvas_textureSampling/src/shader/cubeVert.wgsl: -------------------------------------------------------------------------------- 1 | @binding(0) @group(0) var mvpMatrix : mat4x4; 2 | 3 | struct VertexOutput { 4 | @builtin(position) Position : vec4, 5 | @location(0) fragUV : vec2, 6 | @location(1) fragPosition: vec4 7 | }; 8 | 9 | @vertex 10 | fn main( 11 | @location(0) position : vec4, 12 | @location(1) uv : vec2 13 | ) -> VertexOutput { 14 | var output : VertexOutput; 15 | output.Position = mvpMatrix * position; 16 | output.fragUV = uv; 17 | output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0)); 18 | return output; 19 | } -------------------------------------------------------------------------------- /vol1_oneCube_canvas_textureSampling/src/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, 7 | body { 8 | margin: 0; 9 | width: 100%; 10 | height: 100%; 11 | background: #000; 12 | color: #fff; 13 | display: flex; 14 | text-align: center; 15 | flex-direction: column; 16 | justify-content: center; 17 | align-items: center; 18 | font-family: Avenir, Helvetica, Arial, sans-serif; 19 | -webkit-font-smoothing: antialiased; 20 | } 21 | 22 | canvas#webgpu { 23 | width: 80vw; 24 | height: 80vw; 25 | } 26 | canvas#canvas { 27 | position: fixed; 28 | left: 0; 29 | bottom: 0; 30 | } 31 | -------------------------------------------------------------------------------- /vol1_oneCube_canvas_textureSampling/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"] 19 | }, 20 | "include": ["src"] 21 | } 22 | -------------------------------------------------------------------------------- /vol1_oneCube_image_textureSampling/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol1_oneCube_image_textureSampling/README.md: -------------------------------------------------------------------------------- 1 | ### GPUTextureUsage 标志 2 | 3 | GPUTextureUsage 标志决定了 GPUTexture 在创建后如何使用: 4 | | 标志 | 描述 | 示例 | 5 | | ------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | 6 | | COPY_SRC | 纹理可以用作复制操作的来源。 | 作为 copyTextureToTexture() 或 copyTextureToBuffer() 调用的 source 参数。 | 7 | | COPY_DST | 纹理可用作复制或写入操作的目标。 |作为 copyTextureToTexture() 或 copyBufferToTexture() 调用的“目标”参数,或作为 writeTexture() 调用的目标。 | 8 | | TEXTURE_BINDING | 纹理可以绑定用作着色器中的采样纹理 | 作为绑定组 GPUTextureBindingLayout 的条目。 | 9 | | STORAGE_BINDING | 纹理可以绑定用作着色器中的存储纹理 | 作为 GPUStorageTextureBindingLayout 的绑定组条目。 | 10 | | RENDER_ATTACHMENT | 纹理可以用作渲染过程中的颜色或深度/模板附件。 | 作为 GPURenderPassColorAttachment.view 或 GPURenderPassDepthStencilAttachment.view。 | 11 | -------------------------------------------------------------------------------- /vol1_oneCube_image_textureSampling/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /vol1_oneCube_image_textureSampling/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@webgpu/types": "^0.1.32", 13 | "typescript": "^5.0.4", 14 | "vite": "^4.3.2" 15 | }, 16 | "dependencies": { 17 | "wgpu-matrix": "^2.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vol1_oneCube_image_textureSampling/public/zs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyirs/webgpu_study/effbf26b3cb71d1c05cb6f7ed77aafcaad35d3e9/vol1_oneCube_image_textureSampling/public/zs.png -------------------------------------------------------------------------------- /vol1_oneCube_image_textureSampling/src/shader/cubeFrag.wgsl: -------------------------------------------------------------------------------- 1 | @group(0) @binding(1) var mySampler: sampler; 2 | @group(0) @binding(2) var myTexture: texture_2d; 3 | 4 | @fragment 5 | fn main( 6 | @location(0) fragUV: vec2, 7 | @location(1) fragPosition: vec4, 8 | ) -> @location(0) vec4 { 9 | return fragPosition + textureSample(myTexture, mySampler, fragUV); 10 | } -------------------------------------------------------------------------------- /vol1_oneCube_image_textureSampling/src/shader/cubeVert.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | modelViewProjectionMatrix: mat4x4, 3 | } 4 | @binding(0) @group(0) var uniforms: Uniforms; 5 | 6 | struct VertexOutput { 7 | @builtin(position) position: vec4, 8 | @location(0) fragUV: vec2, 9 | @location(1) fragPosition: vec4, 10 | } 11 | 12 | @vertex 13 | 14 | fn main( 15 | @location(0) position:vec4, 16 | @location(1) uv:vec2, 17 | ) -> VertexOutput { 18 | var output:VertexOutput; 19 | output.position = uniforms.modelViewProjectionMatrix * position; 20 | output.fragUV = uv; 21 | output.fragPosition = (position + vec4(1.0,1.0,1.0,1.0))* 0.5; 22 | return output; 23 | } -------------------------------------------------------------------------------- /vol1_oneCube_image_textureSampling/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol1_oneCube_image_textureSampling/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"] 19 | }, 20 | "include": ["src"] 21 | } 22 | -------------------------------------------------------------------------------- /vol1_oneCube_indexBuffer/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol1_oneCube_indexBuffer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /vol1_oneCube_indexBuffer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@webgpu/types": "^0.1.32", 13 | "typescript": "^5.0.4", 14 | "vite": "^4.3.2" 15 | }, 16 | "dependencies": { 17 | "wgpu-matrix": "^2.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vol1_oneCube_indexBuffer/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.querySelector("canvas#webgpu") as HTMLCanvasElement; 10 | canvas.width = window.innerWidth; 11 | canvas.height = window.innerHeight; 12 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 13 | // 请求WebGPU适配器与GPU设备 14 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 15 | const device = await adapter.requestDevice(); 16 | const format = navigator.gpu.getPreferredCanvasFormat(); 17 | // 配置上下文 18 | context.configure({ 19 | device: device, 20 | // 上下文格式 21 | format: format, 22 | // 不透明度 23 | alphaMode: "opaque", 24 | }); 25 | return { device, canvas, format, context }; 26 | }; 27 | -------------------------------------------------------------------------------- /vol1_oneCube_indexBuffer/src/helper/vertexData.ts: -------------------------------------------------------------------------------- 1 | export const CubeData = () => { 2 | const vertexData = new Float32Array([ 3 | // float3 position, float2 uv 4 | +1, -1, +1, 1, 1, // 0 5 | -1, -1, +1, 0, 1, // 1 6 | -1, -1, -1, 0, 0, // 2 7 | +1, -1, -1, 1, 0, // 3 8 | +1, +1, +1, 1, 1, // 4 9 | +1, -1, +1, 0, 1, // 5 10 | +1, -1, -1, 0, 0, // 6 11 | +1, +1, -1, 1, 0, // 7 12 | -1, +1, +1, 1, 1, // 8 13 | +1, +1, +1, 0, 1, // 9 14 | +1, +1, -1, 0, 0, // 10 15 | -1, +1, -1, 1, 0, // 11 16 | -1, -1, +1, 1, 1, // 12 17 | -1, +1, +1, 0, 1, // 13 18 | -1, +1, -1, 0, 0, // 14 19 | -1, -1, -1, 1, 0, // 15 20 | -1, -1, +1, 0, 0, // 16 21 | +1, -1, +1, 1, 0, // 17 22 | +1, -1, -1, 1, 1, // 18 23 | -1, -1, -1, 0, 1, // 19 24 | ]); 25 | 26 | const indexData = new Uint32Array([ 27 | 0, 1, 2, 3, 0, 2, // face1 28 | 4, 5, 6, 7, 4, 6, // face2 29 | 8, 9, 10, 11, 8, 10, // face3 30 | 12, 13, 14, 15, 12, 14, // face4 31 | 4, 13, 16, 16, 17, 4, // face5 32 | 18, 19, 14, 7, 18, 14, // face6 33 | ]); 34 | 35 | return { 36 | vertexData, 37 | indexData 38 | }; 39 | }; 40 | -------------------------------------------------------------------------------- /vol1_oneCube_indexBuffer/src/shader/cubeFrag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment 2 | fn main(@location(0) fragUV: vec2, 3 | @location(1) fragPosition: vec4) -> @location(0) vec4 { 4 | return fragPosition; 5 | } -------------------------------------------------------------------------------- /vol1_oneCube_indexBuffer/src/shader/cubeVert.wgsl: -------------------------------------------------------------------------------- 1 | @binding(0) @group(0) var mvpMatrix : mat4x4; 2 | 3 | struct VertexOutput { 4 | @builtin(position) Position : vec4, 5 | @location(0) fragUV : vec2, 6 | @location(1) fragPosition: vec4 7 | }; 8 | 9 | @vertex 10 | fn main( 11 | @location(0) position : vec4, 12 | @location(1) uv : vec2 13 | ) -> VertexOutput { 14 | var output : VertexOutput; 15 | output.Position = mvpMatrix * position; 16 | output.fragUV = uv; 17 | output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0)); 18 | return output; 19 | } -------------------------------------------------------------------------------- /vol1_oneCube_indexBuffer/src/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, 7 | body { 8 | margin: 0; 9 | width: 100%; 10 | height: 100%; 11 | background: #000; 12 | color: #fff; 13 | display: flex; 14 | text-align: center; 15 | flex-direction: column; 16 | justify-content: center; 17 | align-items: center; 18 | font-family: Avenir, Helvetica, Arial, sans-serif; 19 | -webkit-font-smoothing: antialiased; 20 | } 21 | 22 | canvas#webgpu { 23 | width: 60vw; 24 | height: 60vw; 25 | } -------------------------------------------------------------------------------- /vol1_oneCube_indexBuffer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"] 19 | }, 20 | "include": ["src"] 21 | } 22 | -------------------------------------------------------------------------------- /vol1_oneCube_video_textureSampling/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol1_oneCube_video_textureSampling/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /vol1_oneCube_video_textureSampling/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@webgpu/types": "^0.1.32", 13 | "typescript": "^5.0.4", 14 | "vite": "^4.3.2" 15 | }, 16 | "dependencies": { 17 | "wgpu-matrix": "^2.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vol1_oneCube_video_textureSampling/public/1.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyirs/webgpu_study/effbf26b3cb71d1c05cb6f7ed77aafcaad35d3e9/vol1_oneCube_video_textureSampling/public/1.mp4 -------------------------------------------------------------------------------- /vol1_oneCube_video_textureSampling/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.querySelector("canvas#webgpu") as HTMLCanvasElement; 10 | canvas.width = window.innerWidth; 11 | canvas.height = window.innerHeight; 12 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 13 | // 请求WebGPU适配器与GPU设备 14 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 15 | const device = await adapter.requestDevice(); 16 | const format = navigator.gpu.getPreferredCanvasFormat(); 17 | // 配置上下文 18 | context.configure({ 19 | device: device, 20 | // 上下文格式 21 | format: format, 22 | // 不透明度 23 | alphaMode: "opaque", 24 | }); 25 | return { device, canvas, format, context }; 26 | }; 27 | -------------------------------------------------------------------------------- /vol1_oneCube_video_textureSampling/src/helper/vertexData.ts: -------------------------------------------------------------------------------- 1 | export const CubeData = () => { 2 | const vertexData = new Float32Array([ 3 | // float3 position, float2 uv 4 | // face1 5 | +1, -1, +1, 1, 1, 6 | -1, -1, +1, 0, 1, 7 | -1, -1, -1, 0, 0, 8 | +1, -1, -1, 1, 0, 9 | +1, -1, +1, 1, 1, 10 | -1, -1, -1, 0, 0, 11 | // face2 12 | +1, +1, +1, 1, 1, 13 | +1, -1, +1, 0, 1, 14 | +1, -1, -1, 0, 0, 15 | +1, +1, -1, 1, 0, 16 | +1, +1, +1, 1, 1, 17 | +1, -1, -1, 0, 0, 18 | // face3 19 | -1, +1, +1, 1, 1, 20 | +1, +1, +1, 0, 1, 21 | +1, +1, -1, 0, 0, 22 | -1, +1, -1, 1, 0, 23 | -1, +1, +1, 1, 1, 24 | +1, +1, -1, 0, 0, 25 | // face4 26 | -1, -1, +1, 1, 1, 27 | -1, +1, +1, 0, 1, 28 | -1, +1, -1, 0, 0, 29 | -1, -1, -1, 1, 0, 30 | -1, -1, +1, 1, 1, 31 | -1, +1, -1, 0, 0, 32 | // face5 33 | +1, +1, +1, 1, 1, 34 | -1, +1, +1, 0, 1, 35 | -1, -1, +1, 0, 0, 36 | -1, -1, +1, 0, 0, 37 | +1, -1, +1, 1, 0, 38 | +1, +1, +1, 1, 1, 39 | // face6 40 | +1, -1, -1, 1, 1, 41 | -1, -1, -1, 0, 1, 42 | -1, +1, -1, 0, 0, 43 | +1, +1, -1, 1, 0, 44 | +1, -1, -1, 1, 1, 45 | -1, +1, -1, 0, 0 46 | ]) 47 | 48 | return { 49 | vertexData, 50 | }; 51 | }; -------------------------------------------------------------------------------- /vol1_oneCube_video_textureSampling/src/shader/cubeFrag.wgsl: -------------------------------------------------------------------------------- 1 | @group(1) @binding(0) var Sampler: sampler; 2 | @group(1) @binding(1) var Texture: texture_external; 3 | 4 | @fragment 5 | fn main(@location(0) fragUV: vec2, 6 | @location(1) fragPosition: vec4) -> @location(0) vec4 { 7 | return textureSampleBaseClampToEdge(Texture, Sampler, fragUV) + fragPosition; 8 | } -------------------------------------------------------------------------------- /vol1_oneCube_video_textureSampling/src/shader/cubeVert.wgsl: -------------------------------------------------------------------------------- 1 | @binding(0) @group(0) var mvpMatrix : mat4x4; 2 | 3 | struct VertexOutput { 4 | @builtin(position) Position : vec4, 5 | @location(0) fragUV : vec2, 6 | @location(1) fragPosition: vec4 7 | }; 8 | 9 | @vertex 10 | fn main( 11 | @location(0) position : vec4, 12 | @location(1) uv : vec2 13 | ) -> VertexOutput { 14 | var output : VertexOutput; 15 | output.Position = mvpMatrix * position; 16 | output.fragUV = uv; 17 | output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0)); 18 | return output; 19 | } -------------------------------------------------------------------------------- /vol1_oneCube_video_textureSampling/src/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, 7 | body { 8 | margin: 0; 9 | width: 100%; 10 | height: 100%; 11 | background: #000; 12 | color: #fff; 13 | display: flex; 14 | text-align: center; 15 | flex-direction: column; 16 | justify-content: center; 17 | align-items: center; 18 | font-family: Avenir, Helvetica, Arial, sans-serif; 19 | -webkit-font-smoothing: antialiased; 20 | } 21 | 22 | canvas#webgpu { 23 | width: 60vw; 24 | height: 60vw; 25 | } -------------------------------------------------------------------------------- /vol1_oneCube_video_textureSampling/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"] 19 | }, 20 | "include": ["src"] 21 | } 22 | -------------------------------------------------------------------------------- /vol2_twoCubes/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol2_twoCubes/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /vol2_twoCubes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@webgpu/types": "^0.1.32", 13 | "typescript": "^5.0.4", 14 | "vite": "^4.3.2" 15 | }, 16 | "dependencies": { 17 | "wgpu-matrix": "^2.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vol2_twoCubes/src/helper/gpuBuffer.ts: -------------------------------------------------------------------------------- 1 | // 创建顶点缓冲区 VBO 2 | // 获取一块状态为映射了的显存,以及一个对应的 arrayBuffer 对象来写数据 3 | /** 4 | * 应用程序可以请求映射一个 GPUBuffer,这样它们就可以通过代表 GPUBuffer 分配的部分的 arraybuffer 访问它的内容。 5 | * 映射一个 GPUBuffer 是通过 mapAsync() 异步请求的,这样用户代理可以确保 GPU 在应用程序访问它的内容之前完成了对 GPUBuffer 的使用。 6 | * 映射的 GPUBuffer 不能被 GPU 使用,必须使用 unmap() 解除映射,然后才能将使用它的工作提交到 Queue 时间轴。 7 | * 一旦映射了 GPUBuffer,应用程序就可以通过 getMappedRange 同步请求访问其内容的范围 8 | */ 9 | export const CreateGPUBuffer = ( 10 | device: GPUDevice, 11 | data: Float32Array, 12 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.VERTEX | 13 | GPUBufferUsage.COPY_DST 14 | ) => { 15 | const buffer = device.createBuffer({ 16 | size: data.byteLength, 17 | usage: usageFlag, 18 | mappedAtCreation: true, 19 | }); 20 | new Float32Array(buffer.getMappedRange()).set(data); 21 | buffer.unmap(); 22 | return buffer; 23 | }; 24 | 25 | export const CreateGPUBufferUint = ( 26 | device: GPUDevice, 27 | data: Uint32Array, 28 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 29 | GPUBufferUsage.COPY_DST 30 | ) => { 31 | const buffer = device.createBuffer({ 32 | size: data.byteLength, 33 | usage: usageFlag, 34 | mappedAtCreation: true, 35 | }); 36 | new Uint32Array(buffer.getMappedRange()).set(data); 37 | buffer.unmap(); 38 | return buffer; 39 | }; 40 | -------------------------------------------------------------------------------- /vol2_twoCubes/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.createElement("canvas"); 10 | document.body.appendChild(canvas); 11 | canvas.width = window.innerWidth; 12 | canvas.height = window.innerHeight; 13 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 14 | // 请求WebGPU适配器与GPU设备 15 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 16 | const device = await adapter.requestDevice(); 17 | const format = navigator.gpu.getPreferredCanvasFormat(); 18 | // 配置上下文 19 | context.configure({ 20 | device: device, 21 | // 上下文格式 22 | format: format, 23 | // 不透明度 24 | alphaMode: "opaque", 25 | }); 26 | return { device, canvas, format, context }; 27 | }; 28 | -------------------------------------------------------------------------------- /vol2_twoCubes/src/helper/vertexData.ts: -------------------------------------------------------------------------------- 1 | export const CubeData = () => { 2 | const vertexData = new Float32Array([ 3 | // position, color 4 | -1, -1, 1, 0, 0, 1, // vertex a, index 0 5 | 1, -1, 1, 1, 0, 1, // vertex b, index 1 6 | 1, 1, 1, 1, 1, 1, // vertex c, index 2 7 | -1, 1, 1, 0, 1, 1, // vertex d, index 3 8 | -1, -1, -1, 0, 0, 0, // vertex e, index 4 9 | 1, -1, -1, 1, 0, 0, // vertex f, index 5 10 | 1, 1, -1, 1, 1, 0, // vertex g, index 6 11 | -1, 1, -1, 0, 1, 0, // vertex h, index 7 12 | ]); 13 | 14 | const indexData = new Uint32Array([ 15 | // front 16 | 0, 1, 2, 2, 3, 0, 17 | // right 18 | 1, 5, 6, 6, 2, 1, 19 | // back 20 | 4, 7, 6, 6, 5, 4, 21 | // left 22 | 0, 3, 7, 7, 4, 0, 23 | // top 24 | 3, 2, 6, 6, 7, 3, 25 | // bottom 26 | 0, 4, 5, 5, 1, 0 27 | ]); 28 | 29 | return { 30 | vertexData, 31 | indexData 32 | }; 33 | }; -------------------------------------------------------------------------------- /vol2_twoCubes/src/shader/cubeFrag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment 2 | fn main( 3 | @location(0) vColor: vec4 4 | ) -> @location(0) vec4 { 5 | return vColor; 6 | } -------------------------------------------------------------------------------- /vol2_twoCubes/src/shader/cubeVert.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | modelViewProjectionMatrix: mat4x4, 3 | } 4 | @binding(0) @group(0) var uniforms: Uniforms; 5 | 6 | struct VertexOutput { 7 | @builtin(position) position : vec4, 8 | @location(0) vColor : vec4, 9 | } 10 | 11 | @vertex 12 | 13 | fn main( 14 | @location(0) position: vec4, 15 | @location(1) color: vec4 16 | ) -> VertexOutput { 17 | var output:VertexOutput; 18 | output.position = uniforms.modelViewProjectionMatrix * position; 19 | output.vColor = color; 20 | return output; 21 | } -------------------------------------------------------------------------------- /vol2_twoCubes/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol2_twoCubes/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"] 19 | }, 20 | "include": ["src"], 21 | } 22 | -------------------------------------------------------------------------------- /vol2_twoCubes_MSAA/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol2_twoCubes_MSAA/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /vol2_twoCubes_MSAA/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@webgpu/types": "^0.1.32", 13 | "typescript": "^5.0.4", 14 | "vite": "^4.3.2" 15 | }, 16 | "dependencies": { 17 | "wgpu-matrix": "^2.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vol2_twoCubes_MSAA/src/helper/gpuBuffer.ts: -------------------------------------------------------------------------------- 1 | // 创建顶点缓冲区 VBO 2 | // 获取一块状态为映射了的显存,以及一个对应的 arrayBuffer 对象来写数据 3 | /** 4 | * 应用程序可以请求映射一个 GPUBuffer,这样它们就可以通过代表 GPUBuffer 分配的部分的 arraybuffer 访问它的内容。 5 | * 映射一个 GPUBuffer 是通过 mapAsync() 异步请求的,这样用户代理可以确保 GPU 在应用程序访问它的内容之前完成了对 GPUBuffer 的使用。 6 | * 映射的 GPUBuffer 不能被 GPU 使用,必须使用 unmap() 解除映射,然后才能将使用它的工作提交到 Queue 时间轴。 7 | * 一旦映射了 GPUBuffer,应用程序就可以通过 getMappedRange 同步请求访问其内容的范围 8 | */ 9 | export const CreateGPUBuffer = ( 10 | device: GPUDevice, 11 | data: Float32Array, 12 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.VERTEX | 13 | GPUBufferUsage.COPY_DST 14 | ) => { 15 | const buffer = device.createBuffer({ 16 | size: data.byteLength, 17 | usage: usageFlag, 18 | mappedAtCreation: true, 19 | }); 20 | new Float32Array(buffer.getMappedRange()).set(data); 21 | buffer.unmap(); 22 | return buffer; 23 | }; 24 | 25 | export const CreateGPUBufferUint = ( 26 | device: GPUDevice, 27 | data: Uint32Array, 28 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 29 | GPUBufferUsage.COPY_DST 30 | ) => { 31 | const buffer = device.createBuffer({ 32 | size: data.byteLength, 33 | usage: usageFlag, 34 | mappedAtCreation: true, 35 | }); 36 | new Uint32Array(buffer.getMappedRange()).set(data); 37 | buffer.unmap(); 38 | return buffer; 39 | }; 40 | -------------------------------------------------------------------------------- /vol2_twoCubes_MSAA/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.createElement("canvas"); 10 | document.body.appendChild(canvas); 11 | canvas.width = window.innerWidth; 12 | canvas.height = window.innerHeight; 13 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 14 | // 请求WebGPU适配器与GPU设备 15 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 16 | const device = await adapter.requestDevice(); 17 | const format = navigator.gpu.getPreferredCanvasFormat(); 18 | // 配置上下文 19 | context.configure({ 20 | device: device, 21 | // 上下文格式 22 | format: format, 23 | // 不透明度 24 | alphaMode: "opaque", 25 | }); 26 | return { device, canvas, format, context }; 27 | }; 28 | -------------------------------------------------------------------------------- /vol2_twoCubes_MSAA/src/helper/vertexData.ts: -------------------------------------------------------------------------------- 1 | export const CubeData = () => { 2 | const vertexData = new Float32Array([ 3 | // position, color 4 | -1, -1, 1, 0, 0, 1, // vertex a, index 0 5 | 1, -1, 1, 1, 0, 1, // vertex b, index 1 6 | 1, 1, 1, 1, 1, 1, // vertex c, index 2 7 | -1, 1, 1, 0, 1, 1, // vertex d, index 3 8 | -1, -1, -1, 0, 0, 0, // vertex e, index 4 9 | 1, -1, -1, 1, 0, 0, // vertex f, index 5 10 | 1, 1, -1, 1, 1, 0, // vertex g, index 6 11 | -1, 1, -1, 0, 1, 0, // vertex h, index 7 12 | ]); 13 | 14 | const indexData = new Uint32Array([ 15 | // front 16 | 0, 1, 2, 2, 3, 0, 17 | // right 18 | 1, 5, 6, 6, 2, 1, 19 | // back 20 | 4, 7, 6, 6, 5, 4, 21 | // left 22 | 0, 3, 7, 7, 4, 0, 23 | // top 24 | 3, 2, 6, 6, 7, 3, 25 | // bottom 26 | 0, 4, 5, 5, 1, 0 27 | ]); 28 | 29 | return { 30 | vertexData, 31 | indexData 32 | }; 33 | }; -------------------------------------------------------------------------------- /vol2_twoCubes_MSAA/src/shader/cubeFrag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment 2 | fn main( 3 | @location(0) vColor: vec4 4 | ) -> @location(0) vec4 { 5 | return vColor; 6 | } -------------------------------------------------------------------------------- /vol2_twoCubes_MSAA/src/shader/cubeVert.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | modelViewProjectionMatrix: mat4x4, 3 | } 4 | @binding(0) @group(0) var uniforms: Uniforms; 5 | 6 | struct VertexOutput { 7 | @builtin(position) position : vec4, 8 | @location(0) vColor : vec4, 9 | } 10 | 11 | @vertex 12 | fn main( 13 | @location(0) position: vec4, 14 | @location(1) color: vec4 15 | ) -> VertexOutput { 16 | var output:VertexOutput; 17 | output.position = uniforms.modelViewProjectionMatrix * position; 18 | output.vColor = color; 19 | return output; 20 | } -------------------------------------------------------------------------------- /vol2_twoCubes_MSAA/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol2_twoCubes_MSAA/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"] 19 | }, 20 | "include": ["src"], 21 | } 22 | -------------------------------------------------------------------------------- /vol3_sphere_wireFrame/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol3_sphere_wireFrame/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /vol3_sphere_wireFrame/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@webgpu/types": "^0.1.32", 13 | "typescript": "^5.0.4", 14 | "vite": "^4.3.2" 15 | }, 16 | "dependencies": { 17 | "wgpu-matrix": "^2.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vol3_sphere_wireFrame/src/helper/gpuBuffer.ts: -------------------------------------------------------------------------------- 1 | // 创建顶点缓冲区 VBO 2 | // 获取一块状态为映射了的显存,以及一个对应的 arrayBuffer 对象来写数据 3 | /** 4 | * 应用程序可以请求映射一个 GPUBuffer,这样它们就可以通过代表 GPUBuffer 分配的部分的 arraybuffer 访问它的内容。 5 | * 映射一个 GPUBuffer 是通过 mapAsync() 异步请求的,这样用户代理可以确保 GPU 在应用程序访问它的内容之前完成了对 GPUBuffer 的使用。 6 | * 映射的 GPUBuffer 不能被 GPU 使用,必须使用 unmap() 解除映射,然后才能将使用它的工作提交到 Queue 时间轴。 7 | * 一旦映射了 GPUBuffer,应用程序就可以通过 getMappedRange 同步请求访问其内容的范围 8 | */ 9 | export const CreateGPUBuffer = ( 10 | device: GPUDevice, 11 | data: Float32Array, 12 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.VERTEX | 13 | GPUBufferUsage.COPY_DST 14 | ) => { 15 | const buffer = device.createBuffer({ 16 | size: data.byteLength, 17 | usage: usageFlag, 18 | mappedAtCreation: true, 19 | }); 20 | new Float32Array(buffer.getMappedRange()).set(data); 21 | buffer.unmap(); 22 | return buffer; 23 | }; 24 | 25 | export const CreateGPUBufferUint = ( 26 | device: GPUDevice, 27 | data: Uint32Array, 28 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 29 | GPUBufferUsage.COPY_DST 30 | ) => { 31 | const buffer = device.createBuffer({ 32 | size: data.byteLength, 33 | usage: usageFlag, 34 | mappedAtCreation: true, 35 | }); 36 | new Uint32Array(buffer.getMappedRange()).set(data); 37 | buffer.unmap(); 38 | return buffer; 39 | }; 40 | -------------------------------------------------------------------------------- /vol3_sphere_wireFrame/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.createElement("canvas"); 10 | document.body.appendChild(canvas); 11 | canvas.width = window.innerWidth; 12 | canvas.height = window.innerHeight; 13 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 14 | // 请求WebGPU适配器与GPU设备 15 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 16 | const device = await adapter.requestDevice(); 17 | const format = navigator.gpu.getPreferredCanvasFormat(); 18 | // 配置上下文 19 | context.configure({ 20 | device: device, 21 | // 上下文格式 22 | format: format, 23 | // 不透明度 24 | alphaMode: "opaque", 25 | }); 26 | return { device, canvas, format, context }; 27 | }; 28 | -------------------------------------------------------------------------------- /vol3_sphere_wireFrame/src/helper/vertexData.ts: -------------------------------------------------------------------------------- 1 | import { vec3 } from "wgpu-matrix"; 2 | 3 | const SpherePosition = ( 4 | radius: number, 5 | theta: number, 6 | phi: number, 7 | center = [0, 0, 0] 8 | ) => { 9 | const snt = Math.sin((theta * Math.PI) / 180); 10 | const cnt = Math.cos((theta * Math.PI) / 180); 11 | const snp = Math.sin((phi * Math.PI) / 180); 12 | const cnp = Math.cos((phi * Math.PI) / 180); 13 | return vec3.fromValues( 14 | radius * snt * cnp + center[0], 15 | radius * cnt + center[1], 16 | -radius * snt * snp + center[2] 17 | ); 18 | }; 19 | 20 | export const SphereWireframeData = ( 21 | radius: number, 22 | u: number, // 经度分段数 23 | v: number, // 纬度分段数 24 | center: any = [0, 0, 0] 25 | ) => { 26 | if (u < 2 || v < 2) return; 27 | let pts = []; 28 | let pt; 29 | for (let i = 0; i < u; i++) { 30 | let pt1 = []; 31 | for (let j = 0; j < v; j++) { 32 | pt = SpherePosition( 33 | radius, 34 | (i * 180) / (u - 1), 35 | (j * 360) / (v - 1), 36 | center 37 | ); 38 | pt1.push(pt); 39 | } 40 | pts.push(pt1); 41 | } 42 | 43 | let p = [] as any; 44 | let p0, p1, p2, p3; 45 | for (let i = 0; i < u - 1; i++) { 46 | for (let j = 0; j < v - 1; j++) { 47 | p0 = pts[i][j]; 48 | p1 = pts[i + 1][j]; 49 | //p2 = pts[i+1][j+1]; 50 | p3 = pts[i][j + 1]; 51 | p.push([ 52 | p0[0], 53 | p0[1], 54 | p0[2], 55 | p1[0], 56 | p1[1], 57 | p1[2], 58 | p0[0], 59 | p0[1], 60 | p0[2], 61 | p3[0], 62 | p3[1], 63 | p3[2], 64 | ]); 65 | } 66 | } 67 | return new Float32Array(p.flat()); 68 | }; 69 | -------------------------------------------------------------------------------- /vol3_sphere_wireFrame/src/shader/sphereFrag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment 2 | fn main() -> @location(0) vec4 { 3 | return vec4(1.0, 1.0, 0.0, 1.0); 4 | } -------------------------------------------------------------------------------- /vol3_sphere_wireFrame/src/shader/sphereVert.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | modelViewProjectionMatrix: mat4x4, 3 | } 4 | @binding(0) @group(0) var uniforms: Uniforms; 5 | 6 | @vertex 7 | 8 | fn main( 9 | @location(0) position: vec4, 10 | ) -> @builtin(position) vec4 { 11 | return uniforms.modelViewProjectionMatrix * position; 12 | } -------------------------------------------------------------------------------- /vol3_sphere_wireFrame/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol3_sphere_wireFrame/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"] 19 | }, 20 | "include": ["src"], 21 | } 22 | -------------------------------------------------------------------------------- /vol4_oneCube_light_mv+p/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol4_oneCube_light_mv+p/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /vol4_oneCube_light_mv+p/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@webgpu/types": "^0.1.32", 13 | "typescript": "^5.0.4", 14 | "vite": "^4.3.2" 15 | }, 16 | "dependencies": { 17 | "wgpu-matrix": "^2.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vol4_oneCube_light_mv+p/src/helper/gpuBuffer.ts: -------------------------------------------------------------------------------- 1 | // 创建顶点缓冲区 VBO 2 | // 获取一块状态为映射了的显存,以及一个对应的 arrayBuffer 对象来写数据 3 | /** 4 | * 应用程序可以请求映射一个 GPUBuffer,这样它们就可以通过代表 GPUBuffer 分配的部分的 arraybuffer 访问它的内容。 5 | * 映射一个 GPUBuffer 是通过 mapAsync() 异步请求的,这样用户代理可以确保 GPU 在应用程序访问它的内容之前完成了对 GPUBuffer 的使用。 6 | * 映射的 GPUBuffer 不能被 GPU 使用,必须使用 unmap() 解除映射,然后才能将使用它的工作提交到 Queue 时间轴。 7 | * 一旦映射了 GPUBuffer,应用程序就可以通过 getMappedRange 同步请求访问其内容的范围 8 | */ 9 | export const CreateGPUBuffer = ( 10 | device: GPUDevice, 11 | data: Float32Array, 12 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.VERTEX | 13 | GPUBufferUsage.COPY_DST 14 | ) => { 15 | const buffer = device.createBuffer({ 16 | size: data.byteLength, 17 | usage: usageFlag, 18 | mappedAtCreation: true, 19 | }); 20 | new Float32Array(buffer.getMappedRange()).set(data); 21 | buffer.unmap(); 22 | return buffer; 23 | }; 24 | 25 | export const CreateGPUBufferUint = ( 26 | device: GPUDevice, 27 | data: Uint32Array, 28 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 29 | GPUBufferUsage.COPY_DST 30 | ) => { 31 | const buffer = device.createBuffer({ 32 | size: data.byteLength, 33 | usage: usageFlag, 34 | mappedAtCreation: true, 35 | }); 36 | new Uint32Array(buffer.getMappedRange()).set(data); 37 | buffer.unmap(); 38 | return buffer; 39 | }; 40 | -------------------------------------------------------------------------------- /vol4_oneCube_light_mv+p/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.createElement("canvas"); 10 | document.body.appendChild(canvas); 11 | canvas.width = window.innerWidth; 12 | canvas.height = window.innerHeight; 13 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 14 | // 请求WebGPU适配器与GPU设备 15 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 16 | const device = await adapter.requestDevice(); 17 | const format = navigator.gpu.getPreferredCanvasFormat(); 18 | // 配置上下文 19 | context.configure({ 20 | device: device, 21 | // 上下文格式 22 | format: format, 23 | // 不透明度 24 | alphaMode: "opaque", 25 | }); 26 | return { device, canvas, format, context }; 27 | }; 28 | -------------------------------------------------------------------------------- /vol4_oneCube_light_mv+p/src/shader/cubeFrag.wgsl: -------------------------------------------------------------------------------- 1 | struct FragUniforms { 2 | light_position : vec4, 3 | eye_position : vec4, 4 | }; 5 | @binding(1) @group(0) var frag_uniforms : FragUniforms; 6 | 7 | struct LightUniforms { 8 | color : vec4, 9 | specular_color : vec4, 10 | params: vec4, // ambient_intensity, diffuse_intensity, specular_intensity, specular_shininess 11 | }; 12 | @binding(2) @group(0) var light_uniforms : LightUniforms; 13 | 14 | @fragment 15 | fn main(@location(0) v_position: vec4, @location(1) v_normal: vec4) -> @location(0) vec4 { 16 | let N:vec3 = normalize(v_normal.xyz); 17 | let L:vec3 = normalize(frag_uniforms.light_position.xyz - v_position.xyz); // Both in view space 18 | let V:vec3 = -v_position.xyz; // Eye position is at origin in view space 19 | let H:vec3 = normalize(L + V); 20 | let diffuse:f32 = light_uniforms.params[1] * max(dot(N, L), 0.0); 21 | let specular: f32 = light_uniforms.params[2] * pow(max(dot(N, H),0.0), light_uniforms.params[3]); 22 | let ambient:f32 = light_uniforms.params[0]; 23 | let final_color = light_uniforms.color*(ambient + diffuse) + light_uniforms.specular_color * specular; 24 | return vec4(final_color.rgb, 1.0); 25 | } -------------------------------------------------------------------------------- /vol4_oneCube_light_mv+p/src/shader/cubeVert.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | model_view_mat : mat4x4, 3 | project_mat : mat4x4, 4 | normal_mat : mat4x4, 5 | }; 6 | @binding(0) @group(0) var uniforms : Uniforms; 7 | 8 | struct Output { 9 | @builtin(position) position : vec4, 10 | @location(0) v_position : vec4, 11 | @location(1) v_normal : vec4, 12 | }; 13 | 14 | @vertex 15 | fn main(@location(0) pos: vec4, @location(1) normal: vec4) -> Output { 16 | var output: Output; 17 | let m_position:vec4 = uniforms.model_view_mat * pos; 18 | output.v_position = m_position; 19 | output.v_normal = uniforms.normal_mat * normal; 20 | output.position = uniforms.project_mat * m_position; 21 | return output; 22 | } 23 | 24 | // mv + p 实现 25 | // 模型矩阵(Model Matrix):这个矩阵负责把模型从模型空间(Model Space)变换到世界空间(World Space)。模型空间是模型的原始坐标系统,原点通常在模型的中心。世界空间是一个更大的坐标系统,用于表示场景中所有对象的位置。 26 | // 视图矩阵(View Matrix):这个矩阵负责把模型从世界空间变换到视图空间(View Space,也叫相机空间)。视图空间的原点是相机的位置,向前看的方向是Z轴的负方向。 27 | // 投影矩阵(Projection Matrix):这个矩阵负责把模型从视图空间变换到裁剪空间(Clip Space)。在这个空间中,所有可见的对象都会被映射到一个单位立方体(从-1到1的范围)。 28 | 29 | // 在vp+m方式中,你先在世界空间进行物体的变换(模型变换),然后将变换后的坐标投影到摄像机空间(视图投影变换)。在这个过程中,光源位置和视点位置都是在世界空间中定义的,因此它们无需进行任何转换。 30 | // 而在mv+p方式中,你先将物体从世界空间变换到视图空间(视图变换),然后在视图空间中进行投影变换(投影变换)。这就意味着你的着色器是在视图空间中进行计算的,因此需要将光源位置和视点位置也转换到视图空间中,以便于进行正确的光照计算。 -------------------------------------------------------------------------------- /vol4_oneCube_light_mv+p/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol4_oneCube_light_mv+p/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"] 19 | }, 20 | "include": ["src"], 21 | } 22 | -------------------------------------------------------------------------------- /vol4_oneCube_light_vp+m/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol4_oneCube_light_vp+m/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /vol4_oneCube_light_vp+m/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@webgpu/types": "^0.1.32", 13 | "typescript": "^5.0.4", 14 | "vite": "^4.3.2" 15 | }, 16 | "dependencies": { 17 | "wgpu-matrix": "^2.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vol4_oneCube_light_vp+m/src/helper/gpuBuffer.ts: -------------------------------------------------------------------------------- 1 | // 创建顶点缓冲区 VBO 2 | // 获取一块状态为映射了的显存,以及一个对应的 arrayBuffer 对象来写数据 3 | /** 4 | * 应用程序可以请求映射一个 GPUBuffer,这样它们就可以通过代表 GPUBuffer 分配的部分的 arraybuffer 访问它的内容。 5 | * 映射一个 GPUBuffer 是通过 mapAsync() 异步请求的,这样用户代理可以确保 GPU 在应用程序访问它的内容之前完成了对 GPUBuffer 的使用。 6 | * 映射的 GPUBuffer 不能被 GPU 使用,必须使用 unmap() 解除映射,然后才能将使用它的工作提交到 Queue 时间轴。 7 | * 一旦映射了 GPUBuffer,应用程序就可以通过 getMappedRange 同步请求访问其内容的范围 8 | */ 9 | export const CreateGPUBuffer = ( 10 | device: GPUDevice, 11 | data: Float32Array, 12 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.VERTEX | 13 | GPUBufferUsage.COPY_DST 14 | ) => { 15 | const buffer = device.createBuffer({ 16 | size: data.byteLength, 17 | usage: usageFlag, 18 | mappedAtCreation: true, 19 | }); 20 | new Float32Array(buffer.getMappedRange()).set(data); 21 | buffer.unmap(); 22 | return buffer; 23 | }; 24 | 25 | export const CreateGPUBufferUint = ( 26 | device: GPUDevice, 27 | data: Uint32Array, 28 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 29 | GPUBufferUsage.COPY_DST 30 | ) => { 31 | const buffer = device.createBuffer({ 32 | size: data.byteLength, 33 | usage: usageFlag, 34 | mappedAtCreation: true, 35 | }); 36 | new Uint32Array(buffer.getMappedRange()).set(data); 37 | buffer.unmap(); 38 | return buffer; 39 | }; 40 | -------------------------------------------------------------------------------- /vol4_oneCube_light_vp+m/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.createElement("canvas"); 10 | document.body.appendChild(canvas); 11 | canvas.width = window.innerWidth; 12 | canvas.height = window.innerHeight; 13 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 14 | // 请求WebGPU适配器与GPU设备 15 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 16 | const device = await adapter.requestDevice(); 17 | const format = navigator.gpu.getPreferredCanvasFormat(); 18 | // 配置上下文 19 | context.configure({ 20 | device: device, 21 | // 上下文格式 22 | format: format, 23 | // 不透明度 24 | alphaMode: "opaque", 25 | }); 26 | return { device, canvas, format, context }; 27 | }; 28 | -------------------------------------------------------------------------------- /vol4_oneCube_light_vp+m/src/shader/cubeFrag.wgsl: -------------------------------------------------------------------------------- 1 | struct FragUniforms { 2 | light_position : vec4, 3 | eye_position : vec4, 4 | }; 5 | @binding(1) @group(0) var frag_uniforms : FragUniforms; 6 | 7 | struct LightUniforms { 8 | color : vec4, 9 | specular_color : vec4, 10 | params: vec4, // ambient_intensity, diffuse_intensity, specular_intensity, specular_shininess 11 | }; 12 | @binding(2) @group(0) var light_uniforms : LightUniforms; 13 | 14 | @fragment 15 | fn main(@location(0) v_position: vec4, @location(1) v_normal: vec4) -> @location(0) vec4 { 16 | let N:vec3 = normalize(v_normal.xyz); // 法线 17 | let L:vec3 = normalize(frag_uniforms.light_position.xyz - v_position.xyz); // 光线方向 18 | let V:vec3 = normalize(frag_uniforms.eye_position.xyz - v_position.xyz); // 视线方向 19 | let H:vec3 = normalize(L + V); // 半向量 20 | let diffuse:f32 = light_uniforms.params[1] * max(dot(N, L), 0.0); // 漫反射光分量(取决于N和L的点积) 21 | let specular: f32 = light_uniforms.params[2] * pow(max(dot(N, H),0.0), light_uniforms.params[3]); // 高光分量 (取决于N和H的点积以及高光光泽度) 22 | let ambient:f32 = light_uniforms.params[0]; 23 | // 该颜色是环境光、漫反射光和高光的组合,每种光的颜色由光源的颜色(对于环境光和漫反射光)和光源的高光颜色(对于高光)决定。每种光的强度由对应的光照强度参数决定。 24 | let final_color = light_uniforms.color*(ambient + diffuse) + light_uniforms.specular_color * specular; 25 | return vec4(final_color.rgb, 1.0); 26 | } -------------------------------------------------------------------------------- /vol4_oneCube_light_vp+m/src/shader/cubeVert.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | view_project_mat : mat4x4, 3 | model_mat : mat4x4, 4 | normal_mat : mat4x4, 5 | }; 6 | @binding(0) @group(0) var uniforms : Uniforms; 7 | 8 | struct Output { 9 | @builtin(position) position : vec4, 10 | @location(0) v_position : vec4, 11 | @location(1) v_normal : vec4, 12 | }; 13 | 14 | @vertex 15 | fn main(@location(0) pos: vec4, @location(1) normal: vec4) -> Output { 16 | var output: Output; 17 | let m_position:vec4 = uniforms.model_mat * pos; 18 | output.v_position = m_position; 19 | // 矩阵对向量进行变换 20 | // normal_mat是法线矩阵,它用来将法线从模型空间变换到世界空间。 21 | // 将顶点的法线(normal)从模型空间变换到世界空间。通过用法线矩阵乘以模型空间中的法线向量实现 22 | // 光照计算通常在世界空间中进行。在模型空间中,法线向量可能不正确地表示顶点相对于世界的方向 23 | output.v_normal = uniforms.normal_mat * normal; 24 | // view_project_mat是视图投影矩阵,它将顶点从世界空间变换到裁剪空间。裁剪空间中的顶点会经过裁剪操作,以删除位于视锥体以外的顶点, 25 | // 然后通过透视除法变换到归一化设备坐标(NDC)空间,并最终被光栅化为屏幕空间中的像素。 26 | // m_position是已经通过模型矩阵变换到世界空间的顶点位置,现在我们通过应用视图投影矩阵将其变换到裁剪空间。 27 | output.position = uniforms.view_project_mat * m_position; 28 | return output; 29 | } -------------------------------------------------------------------------------- /vol4_oneCube_light_vp+m/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol4_oneCube_light_vp+m/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"] 19 | }, 20 | "include": ["src"], 21 | } 22 | -------------------------------------------------------------------------------- /vol5_objects_light/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol5_objects_light/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@webgpu/types": "^0.1.32", 13 | "typescript": "^5.0.4", 14 | "vite": "^4.3.2" 15 | }, 16 | "dependencies": { 17 | "wgpu-matrix": "^2.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vol5_objects_light/src/helper/box.ts: -------------------------------------------------------------------------------- 1 | const vertex = new Float32Array([ 2 | // float3 position, float3 normal, float2 uv 3 | 0.5,0.5,0.5, 1,0,0, 0,1, 4 | 0.5,0.5,-0.5, 1,0,0, 1,1, 5 | 0.5,-0.5,0.5, 1,0,0, 0,0, 6 | 0.5,-0.5,-0.5, 1,0,0, 1,0, 7 | -0.5,0.5,-0.5, -1,0,0, 0,1, 8 | -0.5,0.5,0.5, -1,0,0, 1,1, 9 | -0.5,-0.5,-0.5, -1,0,0, 0,0, 10 | -0.5,-0.5,0.5, -1,0,0, 1,0, 11 | -0.5,0.5,-0.5, 0,1,0, 0,1, 12 | 0.5,0.5,-0.5, 0,1,0, 1,1, 13 | -0.5,0.5,0.5, 0,1,0, 0,0, 14 | 0.5,0.5,0.5, 0,1,0, 1,0, 15 | -0.5,-0.5,0.5, 0,-1,0, 0,1, 16 | 0.5,-0.5,0.5, 0,-1,0, 1,1, 17 | -0.5,-0.5,-0.5, 0,-1,0, 0,0, 18 | 0.5,-0.5,-0.5, 0,-1,0, 1,0, 19 | -0.5,0.5,0.5, 0,0,1, 0,1, 20 | 0.5,0.5,0.5, 0,0,1, 1,1, 21 | -0.5,-0.5,0.5, 0,0,1, 0,0, 22 | 0.5,-0.5,0.5, 0,0,1, 1,0, 23 | 0.5,0.5,-0.5, 0,0,-1, 0,1, 24 | -0.5,0.5,-0.5, 0,0,-1, 1,1, 25 | 0.5,-0.5,-0.5, 0,0,-1, 0,0, 26 | -0.5,-0.5,-0.5, 0,0,-1, 1,0 27 | ]) 28 | 29 | const index = new Uint16Array([ 30 | 0,2,1, 31 | 2,3,1, 32 | 4,6,5, 33 | 6,7,5, 34 | 8,10,9, 35 | 10,11,9, 36 | 12,14,13, 37 | 14,15,13, 38 | 16,18,17, 39 | 18,19,17, 40 | 20,22,21, 41 | 22,23,21 42 | ]) 43 | const vertexCount = 24 44 | const indexCount = 36 45 | 46 | export {vertex, index, vertexCount, indexCount} -------------------------------------------------------------------------------- /vol5_objects_light/src/helper/gpuBuffer.ts: -------------------------------------------------------------------------------- 1 | // 创建顶点缓冲区 VBO 2 | // 获取一块状态为映射了的显存,以及一个对应的 arrayBuffer 对象来写数据 3 | /** 4 | * 应用程序可以请求映射一个 GPUBuffer,这样它们就可以通过代表 GPUBuffer 分配的部分的 arraybuffer 访问它的内容。 5 | * 映射一个 GPUBuffer 是通过 mapAsync() 异步请求的,这样用户代理可以确保 GPU 在应用程序访问它的内容之前完成了对 GPUBuffer 的使用。 6 | * 映射的 GPUBuffer 不能被 GPU 使用,必须使用 unmap() 解除映射,然后才能将使用它的工作提交到 Queue 时间轴。 7 | * 一旦映射了 GPUBuffer,应用程序就可以通过 getMappedRange 同步请求访问其内容的范围 8 | */ 9 | export const CreateGPUBuffer = ( 10 | device: GPUDevice, 11 | data: Float32Array, 12 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.VERTEX | 13 | GPUBufferUsage.COPY_DST 14 | ) => { 15 | const buffer = device.createBuffer({ 16 | size: data.byteLength, 17 | usage: usageFlag, 18 | mappedAtCreation: true, 19 | }); 20 | new Float32Array(buffer.getMappedRange()).set(data); 21 | buffer.unmap(); 22 | return buffer; 23 | }; 24 | 25 | export const CreateGPUBufferUint = ( 26 | device: GPUDevice, 27 | data: Uint32Array, 28 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 29 | GPUBufferUsage.COPY_DST 30 | ) => { 31 | const buffer = device.createBuffer({ 32 | size: data.byteLength, 33 | usage: usageFlag, 34 | mappedAtCreation: true, 35 | }); 36 | new Uint32Array(buffer.getMappedRange()).set(data); 37 | buffer.unmap(); 38 | return buffer; 39 | }; 40 | 41 | export const CreateGPUBufferUint16 = ( 42 | device: GPUDevice, 43 | data: Uint16Array, 44 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 45 | GPUBufferUsage.COPY_DST 46 | ) => { 47 | const buffer = device.createBuffer({ 48 | size: data.byteLength, 49 | usage: usageFlag, 50 | mappedAtCreation: true, 51 | }); 52 | new Uint16Array(buffer.getMappedRange()).set(data); 53 | buffer.unmap(); 54 | return buffer; 55 | }; 56 | -------------------------------------------------------------------------------- /vol5_objects_light/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.querySelector("canvas") as HTMLCanvasElement; 10 | canvas.width = window.innerWidth; 11 | canvas.height = window.innerHeight; 12 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 13 | // 请求WebGPU适配器与GPU设备 14 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 15 | const device = await adapter.requestDevice(); 16 | const format = navigator.gpu.getPreferredCanvasFormat(); 17 | // 配置上下文 18 | context.configure({ 19 | device: device, 20 | // 上下文格式 21 | format: format, 22 | // 不透明度 23 | alphaMode: "opaque", 24 | }); 25 | return { device, canvas, format, context }; 26 | }; 27 | -------------------------------------------------------------------------------- /vol5_objects_light/src/shader/vert.wgsl: -------------------------------------------------------------------------------- 1 | @group(0) @binding(0) var model : array>; 2 | @group(0) @binding(1) var viewProjection : mat4x4; 3 | @group(0) @binding(2) var colors : array>; 4 | 5 | struct VertexOutput { 6 | @builtin(position) Position : vec4, 7 | @location(0) fragPosition : vec3, 8 | @location(1) fragNormal : vec3, 9 | @location(2) fragUV: vec2, 10 | @location(3) fragColor: vec4 11 | }; 12 | 13 | @vertex 14 | fn main( 15 | @builtin(instance_index) index : u32, 16 | @location(0) position : vec3, 17 | @location(1) normal : vec3, 18 | @location(2) uv : vec2, 19 | ) -> VertexOutput { 20 | let modelMatrix = model[index]; 21 | let mvp = viewProjection * modelMatrix; 22 | let pos = vec4(position, 1.0); 23 | 24 | var output : VertexOutput; 25 | output.Position = mvp * pos; 26 | output.fragPosition = (modelMatrix * pos).xyz; 27 | // 如果你考虑到非均匀缩放,那么在变换法线时,你应该使用模型视图矩阵的逆矩阵的转置,而不是模型视图矩阵本身 28 | // 但在WGSL中,并没有提供直接计算矩阵逆的函数,因此注释建议在JavaScript或计算着色器(Compute Shader)中进行此操作。 29 | // 这里为了简化 直接使用模型视图矩阵 30 | output.fragNormal = (modelMatrix * vec4(normal, 0.0)).xyz; 31 | output.fragUV = uv; 32 | output.fragColor = colors[index]; 33 | return output; 34 | } -------------------------------------------------------------------------------- /vol5_objects_light/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol5_objects_light/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"] 19 | }, 20 | "include": ["src"], 21 | } 22 | -------------------------------------------------------------------------------- /vol5_objects_light_layout/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol5_objects_light_layout/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@webgpu/types": "^0.1.34", 13 | "typescript": "^5.0.4", 14 | "vite": "^4.3.2" 15 | }, 16 | "dependencies": { 17 | "wgpu-matrix": "^2.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vol5_objects_light_layout/src/helper/box.ts: -------------------------------------------------------------------------------- 1 | const vertex = new Float32Array([ 2 | // float3 position, float3 normal, float2 uv 3 | 0.5,0.5,0.5, 1,0,0, 0,1, 4 | 0.5,0.5,-0.5, 1,0,0, 1,1, 5 | 0.5,-0.5,0.5, 1,0,0, 0,0, 6 | 0.5,-0.5,-0.5, 1,0,0, 1,0, 7 | -0.5,0.5,-0.5, -1,0,0, 0,1, 8 | -0.5,0.5,0.5, -1,0,0, 1,1, 9 | -0.5,-0.5,-0.5, -1,0,0, 0,0, 10 | -0.5,-0.5,0.5, -1,0,0, 1,0, 11 | -0.5,0.5,-0.5, 0,1,0, 0,1, 12 | 0.5,0.5,-0.5, 0,1,0, 1,1, 13 | -0.5,0.5,0.5, 0,1,0, 0,0, 14 | 0.5,0.5,0.5, 0,1,0, 1,0, 15 | -0.5,-0.5,0.5, 0,-1,0, 0,1, 16 | 0.5,-0.5,0.5, 0,-1,0, 1,1, 17 | -0.5,-0.5,-0.5, 0,-1,0, 0,0, 18 | 0.5,-0.5,-0.5, 0,-1,0, 1,0, 19 | -0.5,0.5,0.5, 0,0,1, 0,1, 20 | 0.5,0.5,0.5, 0,0,1, 1,1, 21 | -0.5,-0.5,0.5, 0,0,1, 0,0, 22 | 0.5,-0.5,0.5, 0,0,1, 1,0, 23 | 0.5,0.5,-0.5, 0,0,-1, 0,1, 24 | -0.5,0.5,-0.5, 0,0,-1, 1,1, 25 | 0.5,-0.5,-0.5, 0,0,-1, 0,0, 26 | -0.5,-0.5,-0.5, 0,0,-1, 1,0 27 | ]) 28 | 29 | const index = new Uint16Array([ 30 | 0,2,1, 31 | 2,3,1, 32 | 4,6,5, 33 | 6,7,5, 34 | 8,10,9, 35 | 10,11,9, 36 | 12,14,13, 37 | 14,15,13, 38 | 16,18,17, 39 | 18,19,17, 40 | 20,22,21, 41 | 22,23,21 42 | ]) 43 | const vertexCount = 24 44 | const indexCount = 36 45 | 46 | export {vertex, index, vertexCount, indexCount} -------------------------------------------------------------------------------- /vol5_objects_light_layout/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.querySelector("canvas") as HTMLCanvasElement; 10 | canvas.width = window.innerWidth; 11 | canvas.height = window.innerHeight; 12 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 13 | // 请求WebGPU适配器与GPU设备 14 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 15 | const device = await adapter.requestDevice(); 16 | const format = navigator.gpu.getPreferredCanvasFormat(); 17 | // 配置上下文 18 | context.configure({ 19 | device: device, 20 | // 上下文格式 21 | format: format, 22 | // 不透明度 23 | alphaMode: "opaque", 24 | }); 25 | return { device, canvas, format, context }; 26 | }; 27 | -------------------------------------------------------------------------------- /vol5_objects_light_layout/src/shader/vert.wgsl: -------------------------------------------------------------------------------- 1 | @group(0) @binding(0) var model : array>; 2 | @group(0) @binding(1) var viewProjection : mat4x4; 3 | @group(0) @binding(2) var colors : array>; 4 | 5 | struct VertexOutput { 6 | @builtin(position) Position : vec4, 7 | @location(0) fragPosition : vec3, 8 | @location(1) fragNormal : vec3, 9 | @location(2) fragUV: vec2, 10 | @location(3) fragColor: vec4 11 | }; 12 | 13 | @vertex 14 | fn main( 15 | @builtin(instance_index) index : u32, 16 | @location(0) position : vec3, 17 | @location(1) normal : vec3, 18 | @location(2) uv : vec2, 19 | ) -> VertexOutput { 20 | let modelMatrix = model[index]; 21 | let mvp = viewProjection * modelMatrix; 22 | let pos = vec4(position, 1.0); 23 | 24 | var output : VertexOutput; 25 | output.Position = mvp * pos; 26 | output.fragPosition = (modelMatrix * pos).xyz; 27 | // 如果你考虑到非均匀缩放,那么在变换法线时,你应该使用模型视图矩阵的逆矩阵的转置,而不是模型视图矩阵本身 28 | // 但在WGSL中,并没有提供直接计算矩阵逆的函数,因此注释建议在JavaScript或计算着色器(Compute Shader)中进行此操作。 29 | // 这里为了简化 直接使用模型视图矩阵 30 | output.fragNormal = (modelMatrix * vec4(normal, 0.0)).xyz; 31 | output.fragUV = uv; 32 | output.fragColor = colors[index]; 33 | return output; 34 | } -------------------------------------------------------------------------------- /vol5_objects_light_layout/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol5_objects_light_layout/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"], 19 | "typeRoots": ["./node_modules/@webgpu/types", "./node_modules/@types"] 20 | }, 21 | "include": ["src"] 22 | } 23 | -------------------------------------------------------------------------------- /vol6_shadowMapping/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol6_shadowMapping/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /vol6_shadowMapping/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@webgpu/types": "^0.1.34", 13 | "typescript": "^5.0.4", 14 | "vite": "^4.3.2" 15 | }, 16 | "dependencies": { 17 | "wgpu-matrix": "^2.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vol6_shadowMapping/src/helper/box.ts: -------------------------------------------------------------------------------- 1 | const vertex = new Float32Array([ 2 | // float3 position, float3 normal, float2 uv 3 | 0.5,0.5,0.5, 1,0,0, 0,1, 4 | 0.5,0.5,-0.5, 1,0,0, 1,1, 5 | 0.5,-0.5,0.5, 1,0,0, 0,0, 6 | 0.5,-0.5,-0.5, 1,0,0, 1,0, 7 | -0.5,0.5,-0.5, -1,0,0, 0,1, 8 | -0.5,0.5,0.5, -1,0,0, 1,1, 9 | -0.5,-0.5,-0.5, -1,0,0, 0,0, 10 | -0.5,-0.5,0.5, -1,0,0, 1,0, 11 | -0.5,0.5,-0.5, 0,1,0, 0,1, 12 | 0.5,0.5,-0.5, 0,1,0, 1,1, 13 | -0.5,0.5,0.5, 0,1,0, 0,0, 14 | 0.5,0.5,0.5, 0,1,0, 1,0, 15 | -0.5,-0.5,0.5, 0,-1,0, 0,1, 16 | 0.5,-0.5,0.5, 0,-1,0, 1,1, 17 | -0.5,-0.5,-0.5, 0,-1,0, 0,0, 18 | 0.5,-0.5,-0.5, 0,-1,0, 1,0, 19 | -0.5,0.5,0.5, 0,0,1, 0,1, 20 | 0.5,0.5,0.5, 0,0,1, 1,1, 21 | -0.5,-0.5,0.5, 0,0,1, 0,0, 22 | 0.5,-0.5,0.5, 0,0,1, 1,0, 23 | 0.5,0.5,-0.5, 0,0,-1, 0,1, 24 | -0.5,0.5,-0.5, 0,0,-1, 1,1, 25 | 0.5,-0.5,-0.5, 0,0,-1, 0,0, 26 | -0.5,-0.5,-0.5, 0,0,-1, 1,0 27 | ]) 28 | 29 | const index = new Uint16Array([ 30 | 0,2,1, 31 | 2,3,1, 32 | 4,6,5, 33 | 6,7,5, 34 | 8,10,9, 35 | 10,11,9, 36 | 12,14,13, 37 | 14,15,13, 38 | 16,18,17, 39 | 18,19,17, 40 | 20,22,21, 41 | 22,23,21 42 | ]) 43 | const vertexCount = 24 44 | const indexCount = 36 45 | 46 | export {vertex, index, vertexCount, indexCount} -------------------------------------------------------------------------------- /vol6_shadowMapping/src/helper/gpuBuffer.ts: -------------------------------------------------------------------------------- 1 | // 创建顶点缓冲区 VBO 2 | // 获取一块状态为映射了的显存,以及一个对应的 arrayBuffer 对象来写数据 3 | /** 4 | * 应用程序可以请求映射一个 GPUBuffer,这样它们就可以通过代表 GPUBuffer 分配的部分的 arraybuffer 访问它的内容。 5 | * 映射一个 GPUBuffer 是通过 mapAsync() 异步请求的,这样用户代理可以确保 GPU 在应用程序访问它的内容之前完成了对 GPUBuffer 的使用。 6 | * 映射的 GPUBuffer 不能被 GPU 使用,必须使用 unmap() 解除映射,然后才能将使用它的工作提交到 Queue 时间轴。 7 | * 一旦映射了 GPUBuffer,应用程序就可以通过 getMappedRange 同步请求访问其内容的范围 8 | */ 9 | export const CreateGPUBuffer = ( 10 | device: GPUDevice, 11 | data: Float32Array, 12 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.VERTEX | 13 | GPUBufferUsage.COPY_DST 14 | ) => { 15 | const buffer = device.createBuffer({ 16 | size: data.byteLength, 17 | usage: usageFlag, 18 | mappedAtCreation: true, 19 | }); 20 | new Float32Array(buffer.getMappedRange()).set(data); 21 | buffer.unmap(); 22 | return buffer; 23 | }; 24 | 25 | export const CreateGPUBufferUint = ( 26 | device: GPUDevice, 27 | data: Uint32Array, 28 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 29 | GPUBufferUsage.COPY_DST 30 | ) => { 31 | const buffer = device.createBuffer({ 32 | size: data.byteLength, 33 | usage: usageFlag, 34 | mappedAtCreation: true, 35 | }); 36 | new Uint32Array(buffer.getMappedRange()).set(data); 37 | buffer.unmap(); 38 | return buffer; 39 | }; 40 | 41 | export const CreateGPUBufferUint16 = ( 42 | device: GPUDevice, 43 | data: Uint16Array, 44 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 45 | GPUBufferUsage.COPY_DST 46 | ) => { 47 | const buffer = device.createBuffer({ 48 | size: data.byteLength, 49 | usage: usageFlag, 50 | mappedAtCreation: true, 51 | }); 52 | new Uint16Array(buffer.getMappedRange()).set(data); 53 | buffer.unmap(); 54 | return buffer; 55 | }; 56 | -------------------------------------------------------------------------------- /vol6_shadowMapping/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.querySelector("canvas") as HTMLCanvasElement; 10 | canvas.width = window.innerWidth; 11 | canvas.height = window.innerHeight; 12 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 13 | // 请求WebGPU适配器与GPU设备 14 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 15 | const device = await adapter.requestDevice(); 16 | const format = navigator.gpu.getPreferredCanvasFormat(); 17 | // 配置上下文 18 | context.configure({ 19 | device: device, 20 | // 上下文格式 21 | format: format, 22 | // 不透明度 23 | alphaMode: "opaque", 24 | }); 25 | return { device, canvas, format, context }; 26 | }; 27 | -------------------------------------------------------------------------------- /vol6_shadowMapping/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol6_shadowMapping/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"], 19 | "typeRoots": ["./node_modules/@webgpu/types", "./node_modules/@types"] 20 | }, 21 | "include": ["src"] 22 | } 23 | -------------------------------------------------------------------------------- /vol6_shadowMapping_two_lights/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol6_shadowMapping_two_lights/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /vol6_shadowMapping_two_lights/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@webgpu/types": "^0.1.34", 13 | "typescript": "^5.0.4", 14 | "vite": "^4.3.2" 15 | }, 16 | "dependencies": { 17 | "wgpu-matrix": "^2.5.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vol6_shadowMapping_two_lights/src/helper/box.ts: -------------------------------------------------------------------------------- 1 | const vertex = new Float32Array([ 2 | // float3 position, float3 normal, float2 uv 3 | 0.5,0.5,0.5, 1,0,0, 0,1, 4 | 0.5,0.5,-0.5, 1,0,0, 1,1, 5 | 0.5,-0.5,0.5, 1,0,0, 0,0, 6 | 0.5,-0.5,-0.5, 1,0,0, 1,0, 7 | -0.5,0.5,-0.5, -1,0,0, 0,1, 8 | -0.5,0.5,0.5, -1,0,0, 1,1, 9 | -0.5,-0.5,-0.5, -1,0,0, 0,0, 10 | -0.5,-0.5,0.5, -1,0,0, 1,0, 11 | -0.5,0.5,-0.5, 0,1,0, 0,1, 12 | 0.5,0.5,-0.5, 0,1,0, 1,1, 13 | -0.5,0.5,0.5, 0,1,0, 0,0, 14 | 0.5,0.5,0.5, 0,1,0, 1,0, 15 | -0.5,-0.5,0.5, 0,-1,0, 0,1, 16 | 0.5,-0.5,0.5, 0,-1,0, 1,1, 17 | -0.5,-0.5,-0.5, 0,-1,0, 0,0, 18 | 0.5,-0.5,-0.5, 0,-1,0, 1,0, 19 | -0.5,0.5,0.5, 0,0,1, 0,1, 20 | 0.5,0.5,0.5, 0,0,1, 1,1, 21 | -0.5,-0.5,0.5, 0,0,1, 0,0, 22 | 0.5,-0.5,0.5, 0,0,1, 1,0, 23 | 0.5,0.5,-0.5, 0,0,-1, 0,1, 24 | -0.5,0.5,-0.5, 0,0,-1, 1,1, 25 | 0.5,-0.5,-0.5, 0,0,-1, 0,0, 26 | -0.5,-0.5,-0.5, 0,0,-1, 1,0 27 | ]) 28 | 29 | const index = new Uint16Array([ 30 | 0,2,1, 31 | 2,3,1, 32 | 4,6,5, 33 | 6,7,5, 34 | 8,10,9, 35 | 10,11,9, 36 | 12,14,13, 37 | 14,15,13, 38 | 16,18,17, 39 | 18,19,17, 40 | 20,22,21, 41 | 22,23,21 42 | ]) 43 | const vertexCount = 24 44 | const indexCount = 36 45 | 46 | export {vertex, index, vertexCount, indexCount} -------------------------------------------------------------------------------- /vol6_shadowMapping_two_lights/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.querySelector("canvas") as HTMLCanvasElement; 10 | canvas.width = window.innerWidth; 11 | canvas.height = window.innerHeight; 12 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 13 | // 请求WebGPU适配器与GPU设备 14 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 15 | const device = await adapter.requestDevice(); 16 | const format = navigator.gpu.getPreferredCanvasFormat(); 17 | // 配置上下文 18 | context.configure({ 19 | device: device, 20 | // 上下文格式 21 | format: format, 22 | // 不透明度 23 | alphaMode: "opaque", 24 | }); 25 | return { device, canvas, format, context }; 26 | }; 27 | -------------------------------------------------------------------------------- /vol6_shadowMapping_two_lights/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol6_shadowMapping_two_lights/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"], 19 | "typeRoots": ["./node_modules/@webgpu/types", "./node_modules/@types"] 20 | }, 21 | "include": ["src"] 22 | } 23 | -------------------------------------------------------------------------------- /vol7_computeShader/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol7_computeShader/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /vol7_computeShader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@types/dat.gui": "^0.7.10", 13 | "@webgpu/types": "^0.1.34", 14 | "typescript": "^5.0.4", 15 | "vite": "^4.3.2" 16 | }, 17 | "dependencies": { 18 | "dat.gui": "^0.7.9", 19 | "wgpu-matrix": "^2.5.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /vol7_computeShader/src/helper/box.ts: -------------------------------------------------------------------------------- 1 | const vertex = new Float32Array([ 2 | // float3 position, float3 normal, float2 uv 3 | 0.5,0.5,0.5, 1,0,0, 0,1, 4 | 0.5,0.5,-0.5, 1,0,0, 1,1, 5 | 0.5,-0.5,0.5, 1,0,0, 0,0, 6 | 0.5,-0.5,-0.5, 1,0,0, 1,0, 7 | -0.5,0.5,-0.5, -1,0,0, 0,1, 8 | -0.5,0.5,0.5, -1,0,0, 1,1, 9 | -0.5,-0.5,-0.5, -1,0,0, 0,0, 10 | -0.5,-0.5,0.5, -1,0,0, 1,0, 11 | -0.5,0.5,-0.5, 0,1,0, 0,1, 12 | 0.5,0.5,-0.5, 0,1,0, 1,1, 13 | -0.5,0.5,0.5, 0,1,0, 0,0, 14 | 0.5,0.5,0.5, 0,1,0, 1,0, 15 | -0.5,-0.5,0.5, 0,-1,0, 0,1, 16 | 0.5,-0.5,0.5, 0,-1,0, 1,1, 17 | -0.5,-0.5,-0.5, 0,-1,0, 0,0, 18 | 0.5,-0.5,-0.5, 0,-1,0, 1,0, 19 | -0.5,0.5,0.5, 0,0,1, 0,1, 20 | 0.5,0.5,0.5, 0,0,1, 1,1, 21 | -0.5,-0.5,0.5, 0,0,1, 0,0, 22 | 0.5,-0.5,0.5, 0,0,1, 1,0, 23 | 0.5,0.5,-0.5, 0,0,-1, 0,1, 24 | -0.5,0.5,-0.5, 0,0,-1, 1,1, 25 | 0.5,-0.5,-0.5, 0,0,-1, 0,0, 26 | -0.5,-0.5,-0.5, 0,0,-1, 1,0 27 | ]) 28 | 29 | const index = new Uint16Array([ 30 | 0,2,1, 31 | 2,3,1, 32 | 4,6,5, 33 | 6,7,5, 34 | 8,10,9, 35 | 10,11,9, 36 | 12,14,13, 37 | 14,15,13, 38 | 16,18,17, 39 | 18,19,17, 40 | 20,22,21, 41 | 22,23,21 42 | ]) 43 | const vertexCount = 24 44 | const indexCount = 36 45 | 46 | export {vertex, index, vertexCount, indexCount} -------------------------------------------------------------------------------- /vol7_computeShader/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.querySelector("canvas") as HTMLCanvasElement; 10 | canvas.width = window.innerWidth; 11 | canvas.height = window.innerHeight; 12 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 13 | // 请求WebGPU适配器与GPU设备 14 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 15 | const device = await adapter.requestDevice(); 16 | const format = navigator.gpu.getPreferredCanvasFormat(); 17 | // 配置上下文 18 | context.configure({ 19 | device: device, 20 | // 上下文格式 21 | format: format, 22 | // 不透明度 23 | alphaMode: "opaque", 24 | }); 25 | return { device, canvas, format, context }; 26 | }; 27 | -------------------------------------------------------------------------------- /vol7_computeShader/src/shader/frag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment 2 | fn main( 3 | @location(0) fragUV: vec2, 4 | @location(1) fragPosition: vec4 5 | ) -> @location(0) vec4 { 6 | return fragPosition; 7 | } -------------------------------------------------------------------------------- /vol7_computeShader/src/shader/vert.wgsl: -------------------------------------------------------------------------------- 1 | @binding(0) @group(0) var mvpMatrix : array>; 2 | 3 | struct VertexOutput { 4 | @builtin(position) Position : vec4, 5 | @location(0) fragUV : vec2, 6 | @location(1) fragPosition: vec4 7 | 8 | }; 9 | 10 | @vertex 11 | fn main( 12 | @builtin(instance_index) index : u32, 13 | @location(0) position : vec4, 14 | @location(1) uv : vec2 15 | ) -> VertexOutput { 16 | var output : VertexOutput; 17 | output.Position = mvpMatrix[index] * position; 18 | output.fragUV = uv; 19 | output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0)); 20 | return output; 21 | } -------------------------------------------------------------------------------- /vol7_computeShader/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol7_computeShader/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"], 19 | "typeRoots": ["./node_modules/@webgpu/types", "./node_modules/@types"] 20 | }, 21 | "include": ["src", "../test/src/helper/init.ts"] 22 | } 23 | -------------------------------------------------------------------------------- /vol8_worker/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol8_worker/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /vol8_worker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@types/dat.gui": "^0.7.10", 13 | "@webgpu/types": "^0.1.34", 14 | "typescript": "^5.0.4", 15 | "vite": "^4.3.2" 16 | }, 17 | "dependencies": { 18 | "dat.gui": "^0.7.9", 19 | "wgpu-matrix": "^2.5.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /vol8_worker/src/helper/gpuBuffer.ts: -------------------------------------------------------------------------------- 1 | // 创建顶点缓冲区 VBO 2 | // 获取一块状态为映射了的显存,以及一个对应的 arrayBuffer 对象来写数据 3 | /** 4 | * 应用程序可以请求映射一个 GPUBuffer,这样它们就可以通过代表 GPUBuffer 分配的部分的 arraybuffer 访问它的内容。 5 | * 映射一个 GPUBuffer 是通过 mapAsync() 异步请求的,这样用户代理可以确保 GPU 在应用程序访问它的内容之前完成了对 GPUBuffer 的使用。 6 | * 映射的 GPUBuffer 不能被 GPU 使用,必须使用 unmap() 解除映射,然后才能将使用它的工作提交到 Queue 时间轴。 7 | * 一旦映射了 GPUBuffer,应用程序就可以通过 getMappedRange 同步请求访问其内容的范围 8 | */ 9 | export const CreateGPUBuffer = ( 10 | device: GPUDevice, 11 | data: Float32Array, 12 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.VERTEX | 13 | GPUBufferUsage.COPY_DST 14 | ) => { 15 | const buffer = device.createBuffer({ 16 | size: data.byteLength, 17 | usage: usageFlag, 18 | mappedAtCreation: true, 19 | }); 20 | new Float32Array(buffer.getMappedRange()).set(data); 21 | buffer.unmap(); 22 | return buffer; 23 | }; 24 | 25 | export const CreateGPUBufferUint = ( 26 | device: GPUDevice, 27 | data: Uint32Array, 28 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 29 | GPUBufferUsage.COPY_DST 30 | ) => { 31 | const buffer = device.createBuffer({ 32 | size: data.byteLength, 33 | usage: usageFlag, 34 | mappedAtCreation: true, 35 | }); 36 | new Uint32Array(buffer.getMappedRange()).set(data); 37 | buffer.unmap(); 38 | return buffer; 39 | }; 40 | 41 | export const CreateGPUBufferUint16 = ( 42 | device: GPUDevice, 43 | data: Uint16Array, 44 | usageFlag: GPUBufferUsageFlags = GPUBufferUsage.INDEX | 45 | GPUBufferUsage.COPY_DST 46 | ) => { 47 | const buffer = device.createBuffer({ 48 | size: data.byteLength, 49 | usage: usageFlag, 50 | mappedAtCreation: true, 51 | }); 52 | new Uint16Array(buffer.getMappedRange()).set(data); 53 | buffer.unmap(); 54 | return buffer; 55 | }; 56 | -------------------------------------------------------------------------------- /vol8_worker/src/main.ts: -------------------------------------------------------------------------------- 1 | import "./style.css"; 2 | 3 | // 创建一个 Worker,并通过配置项 { type: "module" } 来设置其类型为模块。 4 | const worker = new Worker(new URL("./worker.ts", import.meta.url), { 5 | type: "module", 6 | }); 7 | 8 | // 添加一个消息监听器,处理从 Worker 接收的消息。 9 | worker.addEventListener("message", (ev) => { 10 | // 消息的格式可以是任意的,但是约定一致的消息类型可以在应用程序变得更加复杂时更容易区分消息内容。 11 | // 在这里,我们设定一种约定,即所有与 Worker 之间的消息都会包含一个 `type` 字段,我们可以使用它来确定消息的内容。 12 | switch (ev.data.type) { 13 | case "log": { 14 | // Worker 没有内置的机制来将日志输出到控制台,因此可以创建一个方法来输出控制台消息。 15 | console.log(ev.data.message); 16 | break; 17 | } 18 | default: { 19 | console.error(`Unknown Message Type: ${ev.data.type}`); 20 | } 21 | } 22 | }); 23 | 24 | // 为了使 Worker 在页面上显示内容,必须使用 OffscreenCanvas。 25 | // OffscreenCanvas 是在 Web Workers 中使用的一种特殊类型的画布,它可以在后台线程中进行渲染,而不会阻塞主线程。 26 | // 在这里,我们可以通过调用 transferControlToOffscreen() 来从普通的 canvas 创建一个 OffscreenCanvas。 27 | // 任何绘制在此 OffscreenCanvas 上的内容都会自动显示在页面上的源 canvas 上。 28 | const canvas = document.querySelector("canvas") as HTMLCanvasElement; 29 | // @ts-ignore 30 | const offscreenCanvas = canvas.transferControlToOffscreen(); 31 | const devicePixelRatio = window.devicePixelRatio || 1; 32 | offscreenCanvas.width = canvas.clientWidth * devicePixelRatio; 33 | offscreenCanvas.height = canvas.clientHeight * devicePixelRatio; 34 | 35 | // 向 Worker 发送一条消息,告知它使用 OffscreenCanvas 初始化 WebGPU。 36 | // 第二个参数中的数组表示 OffscreenCanvas 将被传输给 Worker,这意味着主线程将失去对它的访问权限,它将完全归 Worker 所有。 37 | worker.postMessage({ type: "init", offscreenCanvas }, [offscreenCanvas]); 38 | -------------------------------------------------------------------------------- /vol8_worker/src/shader/frag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment 2 | fn main( 3 | @location(0) fragUV: vec2, 4 | @location(1) fragPosition: vec4 5 | ) -> @location(0) vec4 { 6 | return fragPosition; 7 | } 8 | -------------------------------------------------------------------------------- /vol8_worker/src/shader/vert.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | modelViewProjectionMatrix : mat4x4, 3 | } 4 | @binding(0) @group(0) var uniforms : Uniforms; 5 | 6 | struct VertexOutput { 7 | @builtin(position) Position : vec4, 8 | @location(0) fragUV : vec2, 9 | @location(1) fragPosition: vec4, 10 | } 11 | 12 | @vertex 13 | fn main( 14 | @location(0) position : vec4, 15 | @location(1) uv : vec2 16 | ) -> VertexOutput { 17 | var output : VertexOutput; 18 | output.Position = uniforms.modelViewProjectionMatrix * position; 19 | output.fragUV = uv; 20 | output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0)); 21 | return output; 22 | } 23 | -------------------------------------------------------------------------------- /vol8_worker/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol8_worker/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"], 19 | "typeRoots": ["./node_modules/@webgpu/types", "./node_modules/@types"] 20 | }, 21 | "include": ["src"] 22 | } 23 | -------------------------------------------------------------------------------- /vol8_worker_axes/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol8_worker_axes/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /vol8_worker_axes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@types/dat.gui": "^0.7.10", 13 | "@webgpu/types": "^0.1.34", 14 | "typescript": "^5.0.4", 15 | "vite": "^4.3.2" 16 | }, 17 | "dependencies": { 18 | "dat.gui": "^0.7.9", 19 | "wgpu-matrix": "^2.5.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /vol8_worker_axes/src/components/shader/axes.frag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment 2 | fn main( 3 | @location(0) fragPosition: vec4 4 | ) -> @location(0) vec4 { 5 | return fragPosition; 6 | } 7 | -------------------------------------------------------------------------------- /vol8_worker_axes/src/components/shader/axes.vert.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | modelViewProjectionMatrix : mat4x4, 3 | } 4 | @binding(0) @group(0) var uniforms : Uniforms; 5 | 6 | struct VertexOutput { 7 | @builtin(position) Position : vec4, 8 | @location(0) fragPosition: vec4, 9 | } 10 | 11 | @vertex 12 | fn main( 13 | @location(0) position : vec4, 14 | ) -> VertexOutput { 15 | var output : VertexOutput; 16 | output.Position = uniforms.modelViewProjectionMatrix * position; 17 | output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0)); 18 | return output; 19 | } 20 | -------------------------------------------------------------------------------- /vol8_worker_axes/src/components/shader/frag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment 2 | fn main( 3 | @location(0) fragUV: vec2, 4 | @location(1) fragPosition: vec4 5 | ) -> @location(0) vec4 { 6 | return fragPosition; 7 | } 8 | -------------------------------------------------------------------------------- /vol8_worker_axes/src/components/shader/vert.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | modelViewProjectionMatrix : mat4x4, 3 | } 4 | @binding(0) @group(0) var uniforms : Uniforms; 5 | 6 | struct VertexOutput { 7 | @builtin(position) Position : vec4, 8 | @location(0) fragUV : vec2, 9 | @location(1) fragPosition: vec4, 10 | } 11 | 12 | @vertex 13 | fn main( 14 | @location(0) position : vec4, 15 | @location(1) uv : vec2 16 | ) -> VertexOutput { 17 | var output : VertexOutput; 18 | output.Position = uniforms.modelViewProjectionMatrix * position; 19 | output.fragUV = uv; 20 | output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0)); 21 | return output; 22 | } 23 | -------------------------------------------------------------------------------- /vol8_worker_axes/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol8_worker_axes/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"], 19 | "typeRoots": ["./node_modules/@webgpu/types", "./node_modules/@types"] 20 | }, 21 | "include": ["src"] 22 | } 23 | -------------------------------------------------------------------------------- /vol9_camera_control/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol9_camera_control/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /vol9_camera_control/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@types/dat.gui": "^0.7.10", 13 | "@webgpu/types": "^0.1.34", 14 | "typescript": "^5.0.4", 15 | "vite": "^4.3.2" 16 | }, 17 | "dependencies": { 18 | "dat.gui": "^0.7.9", 19 | "wgpu-matrix": "^2.5.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /vol9_camera_control/src/components/GPUManager.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export class GPUManager { 4 | private static _instance: GPUManager; 5 | public device: GPUDevice | null = null; 6 | public format: GPUTextureFormat | null = null; 7 | public adapter: GPUAdapter | null = null; 8 | public context: GPUCanvasContext | null = null; 9 | public canvas: HTMLCanvasElement | null = null; 10 | 11 | private constructor() {} 12 | 13 | public static getInstance(): GPUManager { 14 | if (!this._instance) { 15 | this._instance = new GPUManager(); 16 | } 17 | return this._instance; 18 | } 19 | 20 | public async init() { 21 | if (navigator.gpu === undefined) { 22 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 23 | throw new Error("当前浏览器不支持WebGPU"); 24 | } 25 | // 创建canvas 26 | const canvas = document.querySelector("canvas") as HTMLCanvasElement; 27 | canvas.width = window.innerWidth; 28 | canvas.height = window.innerHeight; 29 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 30 | // 请求WebGPU适配器与GPU设备 31 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 32 | const device = await adapter.requestDevice(); 33 | const format = navigator.gpu.getPreferredCanvasFormat(); 34 | // 配置上下文 35 | context.configure({ 36 | device: device, 37 | // 上下文格式 38 | format: format, 39 | // 不透明度 40 | alphaMode: "opaque", 41 | }); 42 | this.format = format; 43 | this.device = device; 44 | this.adapter = adapter; 45 | this.context = context; 46 | this.canvas = canvas; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /vol9_camera_control/src/components/shader/axes.frag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment 2 | fn main( 3 | @location(0) fragPosition: vec4 4 | ) -> @location(0) vec4 { 5 | return fragPosition; 6 | } 7 | -------------------------------------------------------------------------------- /vol9_camera_control/src/components/shader/axes.vert.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | modelViewProjectionMatrix : mat4x4, 3 | } 4 | @binding(0) @group(0) var uniforms : Uniforms; 5 | 6 | struct VertexOutput { 7 | @builtin(position) Position : vec4, 8 | @location(0) fragPosition: vec4, 9 | } 10 | 11 | @vertex 12 | fn main( 13 | @location(0) position : vec4, 14 | ) -> VertexOutput { 15 | var output : VertexOutput; 16 | output.Position = uniforms.modelViewProjectionMatrix * position; 17 | output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0)); 18 | return output; 19 | } 20 | -------------------------------------------------------------------------------- /vol9_camera_control/src/components/shader/cube.frag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment 2 | fn main( 3 | @location(0) fragUV: vec2, 4 | @location(1) fragPosition: vec4 5 | ) -> @location(0) vec4 { 6 | return fragPosition; 7 | } 8 | -------------------------------------------------------------------------------- /vol9_camera_control/src/components/shader/cube.vert.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | modelViewProjectionMatrix : mat4x4, 3 | } 4 | @binding(0) @group(0) var uniforms : Uniforms; 5 | 6 | struct VertexOutput { 7 | @builtin(position) Position : vec4, 8 | @location(0) fragUV : vec2, 9 | @location(1) fragPosition: vec4, 10 | } 11 | 12 | @vertex 13 | fn main( 14 | @location(0) position : vec4, 15 | @location(1) uv : vec2 16 | ) -> VertexOutput { 17 | var output : VertexOutput; 18 | output.Position = uniforms.modelViewProjectionMatrix * position; 19 | output.fragUV = uv; 20 | output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0)); 21 | return output; 22 | } 23 | -------------------------------------------------------------------------------- /vol9_camera_control/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.querySelector("canvas") as HTMLCanvasElement; 10 | canvas.width = window.innerWidth; 11 | canvas.height = window.innerHeight; 12 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 13 | // 请求WebGPU适配器与GPU设备 14 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 15 | const device = await adapter.requestDevice(); 16 | const format = navigator.gpu.getPreferredCanvasFormat(); 17 | // 配置上下文 18 | context.configure({ 19 | device: device, 20 | // 上下文格式 21 | format: format, 22 | // 不透明度 23 | alphaMode: "opaque", 24 | }); 25 | return { device, canvas, format, context }; 26 | }; 27 | -------------------------------------------------------------------------------- /vol9_camera_control/src/main.ts: -------------------------------------------------------------------------------- 1 | import "./style.css"; 2 | import Cube from "./components/Cube"; 3 | import Axes from "./components/Axes"; 4 | import { Camera } from "./components/Camera"; 5 | import { GPUManager } from "./components/GPUManager"; 6 | import { Scene } from "./components/Scene"; 7 | import { vec3 } from "wgpu-matrix"; 8 | 9 | const gpuManager = GPUManager.getInstance(); 10 | await gpuManager.init(); 11 | const canvas = gpuManager.canvas as HTMLCanvasElement; 12 | 13 | const camera = new Camera(vec3.create(3, 2, 5)); 14 | camera.aspect = canvas.width / canvas.height; 15 | 16 | const cube = new Cube(); 17 | const axes = new Axes(5); 18 | 19 | const scene = new Scene(); 20 | scene.addObject(cube); 21 | scene.addObject(axes); 22 | 23 | // cube.setRotation({ x: 1, y: 1, z: 0 }); 24 | // axes.setRotation({ x: 1, y: 1, z: 0 }); 25 | 26 | let lastFrameMS = Date.now(); 27 | // 渲染 28 | const render = () => { 29 | const now = Date.now(); 30 | const deltaTime = (now - lastFrameMS) / 1000; 31 | lastFrameMS = now; 32 | 33 | scene.render(camera, deltaTime); 34 | requestAnimationFrame(render); 35 | }; 36 | requestAnimationFrame(render); 37 | -------------------------------------------------------------------------------- /vol9_camera_control/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol9_camera_control/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"], 19 | "typeRoots": ["./node_modules/@webgpu/types", "./node_modules/@types"] 20 | }, 21 | "include": ["src"] 22 | } 23 | -------------------------------------------------------------------------------- /vol9_camera_control_simple/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /vol9_camera_control_simple/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite App 7 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /vol9_camera_control_simple/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "helloworld", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "@types/dat.gui": "^0.7.10", 13 | "@webgpu/types": "^0.1.34", 14 | "typescript": "^5.0.4", 15 | "vite": "^4.3.2" 16 | }, 17 | "dependencies": { 18 | "dat.gui": "^0.7.9", 19 | "wgpu-matrix": "^2.5.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /vol9_camera_control_simple/src/components/CameraController.ts: -------------------------------------------------------------------------------- 1 | import { Camera } from "./Camera"; // 假设你的相机类在这个路径下 2 | 3 | export class CameraController { 4 | private camera: Camera; 5 | private dragging: boolean = false; 6 | private lastX: number = 0; 7 | private lastY: number = 0; 8 | 9 | constructor(camera: Camera, canvas: HTMLCanvasElement) { 10 | this.camera = camera; 11 | 12 | canvas.addEventListener("mousedown", this.handleMouseDown.bind(this)); 13 | canvas.addEventListener("mousemove", this.handleMouseMove.bind(this)); 14 | canvas.addEventListener("mouseup", this.handleMouseUp.bind(this)); 15 | canvas.addEventListener("wheel", this.handleMouseWheel.bind(this)); 16 | } 17 | 18 | private handleMouseDown(e: MouseEvent) { 19 | this.dragging = true; 20 | this.lastX = e.clientX; 21 | this.lastY = e.clientY; 22 | } 23 | 24 | private handleMouseMove(e: MouseEvent) { 25 | if (!this.dragging) return; 26 | 27 | const deltaX = e.clientX - this.lastX; 28 | const deltaY = e.clientY - this.lastY; 29 | 30 | this.lastX = e.clientX; 31 | this.lastY = e.clientY; 32 | 33 | this.camera.rotateAroundCenter(deltaX, deltaY); 34 | } 35 | 36 | private handleMouseUp() { 37 | this.dragging = false; 38 | } 39 | 40 | private handleMouseWheel(e: WheelEvent) { 41 | this.camera.mouseZoom(e.deltaY); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /vol9_camera_control_simple/src/components/GPUManager.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export class GPUManager { 4 | private static _instance: GPUManager; 5 | public device: GPUDevice | null = null; 6 | public format: GPUTextureFormat | null = null; 7 | public adapter: GPUAdapter | null = null; 8 | public context: GPUCanvasContext | null = null; 9 | public canvas: HTMLCanvasElement | null = null; 10 | 11 | private constructor() {} 12 | 13 | public static getInstance(): GPUManager { 14 | if (!this._instance) { 15 | this._instance = new GPUManager(); 16 | } 17 | return this._instance; 18 | } 19 | 20 | public async init() { 21 | if (navigator.gpu === undefined) { 22 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 23 | throw new Error("当前浏览器不支持WebGPU"); 24 | } 25 | // 创建canvas 26 | const canvas = document.querySelector("canvas") as HTMLCanvasElement; 27 | canvas.width = window.innerWidth; 28 | canvas.height = window.innerHeight; 29 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 30 | // 请求WebGPU适配器与GPU设备 31 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 32 | const device = await adapter.requestDevice(); 33 | const format = navigator.gpu.getPreferredCanvasFormat(); 34 | // 配置上下文 35 | context.configure({ 36 | device: device, 37 | // 上下文格式 38 | format: format, 39 | // 不透明度 40 | alphaMode: "opaque", 41 | }); 42 | this.format = format; 43 | this.device = device; 44 | this.adapter = adapter; 45 | this.context = context; 46 | this.canvas = canvas; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /vol9_camera_control_simple/src/components/shader/axes.frag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment 2 | fn main( 3 | @location(0) fragPosition: vec4 4 | ) -> @location(0) vec4 { 5 | return fragPosition; 6 | } 7 | -------------------------------------------------------------------------------- /vol9_camera_control_simple/src/components/shader/axes.vert.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | modelViewProjectionMatrix : mat4x4, 3 | } 4 | @binding(0) @group(0) var uniforms : Uniforms; 5 | 6 | struct VertexOutput { 7 | @builtin(position) Position : vec4, 8 | @location(0) fragPosition: vec4, 9 | } 10 | 11 | @vertex 12 | fn main( 13 | @location(0) position : vec4, 14 | ) -> VertexOutput { 15 | var output : VertexOutput; 16 | output.Position = uniforms.modelViewProjectionMatrix * position; 17 | output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0)); 18 | return output; 19 | } 20 | -------------------------------------------------------------------------------- /vol9_camera_control_simple/src/components/shader/cube.frag.wgsl: -------------------------------------------------------------------------------- 1 | @fragment 2 | fn main( 3 | @location(0) fragUV: vec2, 4 | @location(1) fragPosition: vec4 5 | ) -> @location(0) vec4 { 6 | return fragPosition; 7 | } 8 | -------------------------------------------------------------------------------- /vol9_camera_control_simple/src/components/shader/cube.vert.wgsl: -------------------------------------------------------------------------------- 1 | struct Uniforms { 2 | modelViewProjectionMatrix : mat4x4, 3 | } 4 | @binding(0) @group(0) var uniforms : Uniforms; 5 | 6 | struct VertexOutput { 7 | @builtin(position) Position : vec4, 8 | @location(0) fragUV : vec2, 9 | @location(1) fragPosition: vec4, 10 | } 11 | 12 | @vertex 13 | fn main( 14 | @location(0) position : vec4, 15 | @location(1) uv : vec2 16 | ) -> VertexOutput { 17 | var output : VertexOutput; 18 | output.Position = uniforms.modelViewProjectionMatrix * position; 19 | output.fragUV = uv; 20 | output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0)); 21 | return output; 22 | } 23 | -------------------------------------------------------------------------------- /vol9_camera_control_simple/src/helper/init.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export const InitGPU = async () => { 4 | if (navigator.gpu === undefined) { 5 | alert("当前浏览器不支持WebGPU,确保chrome版本在113及以上。"); 6 | throw new Error("当前浏览器不支持WebGPU"); 7 | } 8 | // 创建canvas 9 | const canvas = document.querySelector("canvas") as HTMLCanvasElement; 10 | canvas.width = window.innerWidth; 11 | canvas.height = window.innerHeight; 12 | const context = canvas.getContext("webgpu") as GPUCanvasContext; 13 | // 请求WebGPU适配器与GPU设备 14 | const adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; 15 | const device = await adapter.requestDevice(); 16 | const format = navigator.gpu.getPreferredCanvasFormat(); 17 | // 配置上下文 18 | context.configure({ 19 | device: device, 20 | // 上下文格式 21 | format: format, 22 | // 不透明度 23 | alphaMode: "opaque", 24 | }); 25 | return { device, canvas, format, context }; 26 | }; 27 | -------------------------------------------------------------------------------- /vol9_camera_control_simple/src/main.ts: -------------------------------------------------------------------------------- 1 | import "./style.css"; 2 | import Cube from "./components/Cube"; 3 | import Axes from "./components/Axes"; 4 | import { Camera } from "./components/Camera"; 5 | import { CameraController } from "./components/CameraController"; 6 | import { GPUManager } from "./components/GPUManager"; 7 | import { Scene } from "./components/Scene"; 8 | 9 | const gpuManager = GPUManager.getInstance(); 10 | await gpuManager.init(); 11 | const canvas = gpuManager.canvas as HTMLCanvasElement; 12 | 13 | const aspect = canvas.width / canvas.height; 14 | 15 | const camera = new Camera(); 16 | camera.perspective(aspect); 17 | camera.lookAt({ x: 0, y: 0, z: 10 }, { x: 0, y: 0, z: 0 }); 18 | 19 | new CameraController(camera, canvas); 20 | 21 | const cube = new Cube(); 22 | const axes = new Axes(5); 23 | 24 | const scene = new Scene(); 25 | scene.addObject(cube); 26 | scene.addObject(axes); 27 | 28 | // cube.setRotation({ x: 1, y: 1, z: 0 }); 29 | // axes.setRotation({ x: 1, y: 1, z: 0 }); 30 | 31 | // 渲染 32 | const render = () => { 33 | scene.render(camera); 34 | requestAnimationFrame(render); 35 | }; 36 | requestAnimationFrame(render); 37 | -------------------------------------------------------------------------------- /vol9_camera_control_simple/src/style.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | canvas{ 7 | width: 100vw; 8 | height: 100vh; 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | } -------------------------------------------------------------------------------- /vol9_camera_control_simple/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx", 18 | "types": ["vite/client", "@webgpu/types"], 19 | "typeRoots": ["./node_modules/@webgpu/types", "./node_modules/@types"] 20 | }, 21 | "include": ["src"] 22 | } 23 | --------------------------------------------------------------------------------