当前位置: 首页 > news >正文

网站通知模板视频类网站建设的成果

网站通知模板,视频类网站建设的成果,北京建王环境发展有限公司,手机网站制作案例五、WebGPU Vertex Buffers 顶点缓冲区 在上一篇文章中#xff0c;我们将顶点数据放入存储缓冲区中#xff0c;并使用内置的vertex_index对其进行索引。虽然这种技术越来越受欢迎#xff0c;但向顶点着色器提供顶点数据的传统方式是通过顶点缓冲和属性。 顶点缓冲区就像任…五、WebGPU Vertex Buffers 顶点缓冲区 在上一篇文章中我们将顶点数据放入存储缓冲区中并使用内置的vertex_index对其进行索引。虽然这种技术越来越受欢迎但向顶点着色器提供顶点数据的传统方式是通过顶点缓冲和属性。 顶点缓冲区就像任何其他WebGPU缓冲区一样。它们保存着数据。不同之处在于我们不直接从顶点着色器访问它们。相反我们告诉WebGPU缓冲区中有什么类型的数据以及它在哪里以及它是如何组织的。然后它将数据从缓冲区中取出并提供给我们。 让我们以上一篇文章中的最后一个示例为例将其从使用存储缓冲区更改为使用顶点缓冲区。 首先要做的是改变着色器从顶点缓冲区中获取顶点数据。 struct OurStruct {color: vec4f,offset: vec2f, };struct OtherStruct {scale: vec2f, };struct Vertex {location(0) position: vec2f, };struct VSOutput {builtin(position) position: vec4f,location(0) color: vec4f, };group(0) binding(0) varstorage, read ourStructs: arrayOurStruct; group(0) binding(1) varstorage, read otherStructs: arrayOtherStruct;vertex fn vs(vert: Vertex,builtin(instance_index) instanceIndex: u32 ) - VSOutput {let otherStruct otherStructs[instanceIndex];let ourStruct ourStructs[instanceIndex];var vsOut: VSOutput;vsOut.position vec4f(vert.position * otherStruct.scale ourStruct.offset, 0.0, 1.0);vsOut.color ourStruct.color;return vsOut; }...正如你所看到的这是一个很小的变化。我们声明了一个结构体Vertex来定义顶点的数据。重要的部分是用location(0)声明位置字段然后当我们创建渲染管道时我们必须告诉WebGPU如何获取location(0)的数据。 然后当我们创建渲染管道时我们必须告诉WebGPU如何获取location(0)的数据。 const pipeline device.createRenderPipeline({label: vertex buffer pipeline,layout: auto,vertex: {module,entryPoint: vs,buffers: [{arrayStride: 2 * 4, // 2 floats, 4 bytes eachattributes: [{shaderLocation: 0, offset: 0, format: float32x2}, // position],},],},fragment: {module,entryPoint: fs,targets: [{ format: presentationFormat }],},});对于pipeline descriptor 的 vertex entry我们添加了一个缓冲区数组用于描述如何从一个或多个顶点缓冲区中提取数据。对于第一个也是唯一一个缓冲区我们以字节数为单位设置arrayStride。在这种情况下步长是指从缓冲区中一个顶点的数据到缓冲区中的下一个顶点的字节数。 因为我们的数据是vec2f这是两个float32数字所以我们将arrayStride设置为8。 接下来我们定义一个属性数组。我们只有一个。shaderLocation: 0对应于我们顶点结构中的location(0)。offset: 0表示此属性的数据从顶点缓冲区中的第0字节开始。最后format:float32x2’表示我们希望WebGPU将数据从缓冲区中取出为两个32位浮点数。 我们需要将保存顶点数据的缓冲区的用法从STORAGE更改为vertex并将其从绑定组中删除。 const vertexBuffer device.createBuffer({label: vertex buffer vertices,size: vertexData.byteLength,usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,});device.queue.writeBuffer(vertexBuffer, 0, vertexData);const bindGroup device.createBindGroup({label: bind group for objects,layout: pipeline.getBindGroupLayout(0),entries: [{ binding: 0, resource: { buffer: staticStorageBuffer }},{ binding: 1, resource: { buffer: changingStorageBuffer }},],});然后在绘制时我们需要告诉webgpu使用哪个顶点缓冲区。 pass.setPipeline(pipeline);pass.setVertexBuffer(0, vertexBuffer);这里的0对应于我们上面指定的渲染管道缓冲区数组的第一个元素。 这样我们就从顶点存储缓冲区切换到了顶点缓冲区。 以下为完整代码及运行结果 HTML !--* Description: * Author: tianyw* Date: 2022-11-11 12:50:23* LastEditTime: 2023-09-19 22:06:18* LastEditors: tianyw -- !DOCTYPE html html langenheadmeta charsetUTF-8 /meta nameviewport contentwidthdevice-width, initial-scale1.0 /title001hello-triangle/titlestylehtml,body {margin: 0;width: 100%;height: 100%;background: #000;color: #fff;display: flex;text-align: center;flex-direction: column;justify-content: center;}div,canvas {height: 100%;width: 100%;}/style /headbodydiv id007vertex-srandom-circlecanvas idgpucanvas/canvas/divscript typemodule src./007vertex-srandom-circle.ts/script/body/htmlTS: /** Description:* Author: tianyw* Date: 2023-04-08 20:03:35* LastEditTime: 2023-09-19 22:12:42* LastEditors: tianyw*/ export type SampleInit (params: {canvas: HTMLCanvasElement; }) void | Promisevoid;import shaderWGSL from ./shaders/shader.wgsl?raw;const rand (min: undefined | number undefined,max: undefined | number undefined ) {if (min undefined) {min 0;max 1;} else if (max undefined) {max min;min 0;}return min Math.random() * (max - min); };function createCircleVertices({radius 1,numSubdivisions 24,innerRadius 0,startAngle 0,endAngle Math.PI * 2 } {}) {// 2 triangles per subdivision, 3 verts per tri, 2 values(xy) eachconst numVertices numSubdivisions * 3 * 2;const vertexData new Float32Array(numSubdivisions * 2 * 3 * 2);let offset 0;const addVertex (x: number, y: number) {vertexData[offset] x;vertexData[offset] y;};// 2 vertices per subdivisionfor (let i 0; i numSubdivisions; i) {const angle1 startAngle ((i 0) * (endAngle - startAngle)) / numSubdivisions;const angle2 startAngle ((i 1) * (endAngle - startAngle)) / numSubdivisions;const c1 Math.cos(angle1);const s1 Math.sin(angle1);const c2 Math.cos(angle2);const s2 Math.sin(angle2);// first angleaddVertex(c1 * radius, s1 * radius);addVertex(c2 * radius, s2 * radius);addVertex(c1 * innerRadius, s1 * innerRadius);// second triangleaddVertex(c1 * innerRadius, s1 * innerRadius);addVertex(c2 * radius, s2 * radius);addVertex(c2 * innerRadius, s2 * innerRadius);}return {vertexData,numVertices}; }const init: SampleInit async ({ canvas }) {const adapter await navigator.gpu?.requestAdapter();if (!adapter) return;const device await adapter?.requestDevice();if (!device) {console.error(need a browser that supports WebGPU);return;}const context canvas.getContext(webgpu);if (!context) return;const devicePixelRatio window.devicePixelRatio || 1;canvas.width canvas.clientWidth * devicePixelRatio;canvas.height canvas.clientHeight * devicePixelRatio;const presentationFormat navigator.gpu.getPreferredCanvasFormat();context.configure({device,format: presentationFormat,alphaMode: premultiplied});const shaderModule device.createShaderModule({label: our hardcoded rgb triangle shaders,code: shaderWGSL});const renderPipeline device.createRenderPipeline({label: hardcoded rgb triangle pipeline,layout: auto,vertex: {module: shaderModule,entryPoint: vs,buffers: [{arrayStride: 2 * 4, // 2 floats, 4 bytes eachattributes: [{shaderLocation: 0, offset: 0,format: float32x2} // position]}]},fragment: {module: shaderModule,entryPoint: fs,targets: [{format: presentationFormat}]},primitive: {// topology: line-list// topology: line-strip// topology: point-listtopology: triangle-list// topology: triangle-strip}});const kNumObjects 100;const staticStorageUnitSize 4 * 4 // color is 4 32bit floats (4bytes each)2 * 4 // scale is 2 32bit floats (4bytes each)2 * 4; // paddingconst storageUnitSzie 2 * 4; // scale is 2 32 bit floatsconst staticStorageBufferSize staticStorageUnitSize * kNumObjects;const storageBufferSize storageUnitSzie * kNumObjects;const staticStorageBuffer device.createBuffer({label: static storage for objects,size: staticStorageBufferSize,usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST});const storageBuffer device.createBuffer({label: changing storage for objects,size: storageBufferSize,usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST});const staticStorageValues new Float32Array(staticStorageBufferSize / 4);const storageValues new Float32Array(storageBufferSize / 4);const kColorOffset 0;const kOffsetOffset 4;const kScaleOffset 0;const objectInfos: {scale: number;}[] [];for (let i 0; i kNumObjects; i) {const staticOffset i * (staticStorageUnitSize / 4);staticStorageValues.set([rand(), rand(), rand(), 1],staticOffset kColorOffset);staticStorageValues.set([rand(-0.9, 0.9), rand(-0.9, 0.9)],staticOffset kOffsetOffset);objectInfos.push({scale: rand(0.2, 0.5)});}device.queue.writeBuffer(staticStorageBuffer, 0, staticStorageValues);const { vertexData, numVertices } createCircleVertices({radius: 0.5,innerRadius: 0.25});const vertexBuffer device.createBuffer({label: vertex buffer vertices,size: vertexData.byteLength,usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST});device.queue.writeBuffer(vertexBuffer, 0, vertexData);const bindGroup device.createBindGroup({label: bind group for objects,layout: renderPipeline.getBindGroupLayout(0),entries: [{ binding: 0, resource: { buffer: staticStorageBuffer } },{ binding: 1, resource: { buffer: storageBuffer } }]});function frame() {const aspect canvas.width / canvas.height;const renderCommandEncoder device.createCommandEncoder({label: render vert frag});if (!context) return;const textureView context.getCurrentTexture().createView();const renderPassDescriptor: GPURenderPassDescriptor {label: our basic canvas renderPass,colorAttachments: [{view: textureView,clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },loadOp: clear,storeOp: store}]};const renderPass renderCommandEncoder.beginRenderPass(renderPassDescriptor);renderPass.setPipeline(renderPipeline);renderPass.setVertexBuffer(0,vertexBuffer);objectInfos.forEach(({ scale }, ndx) {const offset ndx * (storageUnitSzie / 4);storageValues.set([scale / aspect, scale], offset kScaleOffset); // set the scale});device.queue.writeBuffer(storageBuffer, 0, storageValues);renderPass.setBindGroup(0, bindGroup);renderPass.draw(numVertices, kNumObjects);renderPass.end();const renderBuffer renderCommandEncoder.finish();device.queue.submit([renderBuffer]);requestAnimationFrame(frame);}requestAnimationFrame(frame); };const canvas document.getElementById(gpucanvas) as HTMLCanvasElement; init({ canvas: canvas }); Shaders: shader: struct OurStruct {color: vec4f,offset: vec2f };struct OtherStruct {scale: vec2f };struct Vertex {location(0) position: vec2f }struct VSOutput {builtin(position) position: vec4f,location(0) color: vec4f };group(0) binding(0) varstorage,read ourStructs: arrayOurStruct; group(0) binding(1) varstorage,read otherStructs: arrayOtherStruct; vertex fn vs(vert: Vertex, builtin(instance_index) instanceIndex: u32) - VSOutput {let otherStruct otherStructs[instanceIndex];let ourStruct ourStructs[instanceIndex];var vsOut: VSOutput;vsOut.position vec4f(vert.position * otherStruct.scale ourStruct.offset, 0.0, 1.0);vsOut.color ourStruct.color;return vsOut; }fragment fn fs(vsOut: VSOutput) - location(0) vec4f {return vsOut.color; }执行draw命令时的状态如下所示: 属性格式字段可以是这些类型之一: Vertex formatData typeComponentsByte sizeExample WGSL typeuint8x2unsigned int22vec2u32, vec2uuint8x4unsigned int44vec4u32, vec4usint8x2signed int22vec2i32, vec2isint8x4signed int44vec4i32, vec4iunorm8x2unsigned normalized22vec2f32, vec2funorm8x4unsigned normalized44vec4f32, vec4fsnorm8x2signed normalized22vec2f32, vec2fsnorm8x4signed normalized44vec4f32, vec4fuint16x2unsigned int24vec2u32, vec2uuint16x4unsigned int48vec4u32, vec4usint16x2signed int24vec2i32, vec2isint16x4signed int48vec4i32, vec4iunorm16x2unsigned normalized24vec2f32, vec2funorm16x4unsigned normalized48vec4f32, vec4fsnorm16x2signed normalized24vec2f32, vec2fsnorm16x4signed normalized48vec4f32, vec4ffloat16x2float24vec2f16, vec2hfloat16x4float48vec4f16, vec4hfloat32float14f32float32x2float28vec2f32, vec2ffloat32x3float312vec3f32, vec3ffloat32x4float416vec4f32, vec4fuint32unsigned int14u32uint32x2unsigned int28vec2u32, vec2uuint32x3unsigned int312vec3u32, vec3uuint32x4unsigned int416vec4u32, vec4usint32signed int14i32sint32x2signed int28vec2i32, vec2isint32x3signed int312vec3i32, vec3isint32x4signed int416vec4i32, vec4i 实例化顶点缓冲区 属性可以按顶点或实例推进。在每个实例中推进它们实际上是我们在索引otherStructs[instanceIndex]和ourStructs[instanceIndex]时所做的相同的事情其中instanceIndex从builtin(instance_index)获得其值。 让我们去掉存储缓冲区使用顶点缓冲区来完成同样的事情。首先让我们改变着色器使用顶点属性而不是存储缓冲区。 struct Vertex {location(0) position: vec2f,location(1) color: vec4f,location(2) offset: vec2f,location(3) scale: vec2f, };struct VSOutput {builtin(position) position: vec4f,location(0) color: vec4f, };group(0) binding(0) varstorage, read ourStructs: arrayOurStruct; group(0) binding(1) varstorage, read otherStructs: arrayOtherStruct;vertex fn vs(vert: Vertex, ) - VSOutput {var vsOut: VSOutput;vsOut.position vec4f(vert.position * vert.scale vert.offset, 0.0, 1.0);vsOut.color vert.color;return vsOut; }fragment fn fs(vsOut: VSOutput) - location(0) vec4f {return vsOut.color; }现在我们需要更新渲染管道告诉它我们希望如何向这些属性提供数据。为了保持最小的变化我们将使用我们为存储缓冲区创建的数据。我们将使用两个缓冲区一个缓冲区将保存每个实例的颜色和偏移量另一个将保存比例。 const pipeline device.createRenderPipeline({label: flat colors,layout: auto,vertex: {module,entryPoint: vs,buffers: [{arrayStride: 2 * 4, // 2 floats, 4 bytes eachattributes: [{shaderLocation: 0, offset: 0, format: float32x2}, // position],},{arrayStride: 6 * 4, // 6 floats, 4 bytes eachstepMode: instance,attributes: [{shaderLocation: 1, offset: 0, format: float32x4}, // color{shaderLocation: 2, offset: 16, format: float32x2}, // offset],},{arrayStride: 2 * 4, // 2 floats, 4 bytes eachstepMode: instance,attributes: [{shaderLocation: 3, offset: 0, format: float32x2}, // scale],},],},fragment: {module,entryPoint: fs,targets: [{ format: presentationFormat }],},});上面我们在流水线描述中向buffers数组中添加了2个条目所以现在有3个缓冲区条目这意味着我们告诉WebGPU我们将在3个缓冲区中提供数据。 对于我们的两个新实体我们将stepMode设置为instance。这意味着该属性在每个实例中只前进到下一个值一次。默认是stepMode: ‘vertex’每个顶点前进一次(每个实例重新开始)。 我们有2个缓冲区。保持比例的那个很简单。就像我们第一个保存位置的缓冲区一样每个顶点有2*32个浮点数。 另一个缓冲区保存颜色和偏移量它们将像这样在数据中交错: 所以上面我们说从一组数据到下一组数据的arrayStride是6 * 4,6个32位浮点数每4字节(总共24字节)。颜色从偏移量0开始但偏移量从16字节开始。 接下来我们可以更改设置缓冲区的代码。 // create 2 storage buffersconst staticUnitSize 4 * 4 // color is 4 32bit floats (4bytes each)2 * 4; // offset is 2 32bit floats (4bytes each)const changingUnitSize 2 * 4; // scale is 2 32bit floats (4bytes each)const staticVertexBufferSize staticUnitSize * kNumObjects;const changingVertexBufferSize changingUnitSize * kNumObjects;const staticVertexBuffer device.createBuffer({label: static vertex for objects,size: staticVertexBufferSize,usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,});const changingVertexBuffer device.createBuffer({label: changing vertex for objects,size: changingVertexBufferSize,usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,});顶点属性与存储缓冲区中的结构没有相同的填充限制所以我们不再需要填充。否则我们所做的就是将用法从STORAGE更改为VERTEX(我们将所有变量从“STORAGE”重命名为“VERTEX”)。 由于我们不再使用存储缓冲区因此不再需要bindGroup。 最后我们不需要设置bindGroup但我们需要设置顶点缓冲区 const encoder device.createCommandEncoder();const pass encoder.beginRenderPass(renderPassDescriptor);pass.setPipeline(pipeline);pass.setVertexBuffer(0, vertexBuffer);pass.setVertexBuffer(1, staticVertexBuffer);pass.setVertexBuffer(2, changingVertexBuffer);...pass.draw(numVertices, kNumObjects);pass.end();在这里setVertexBuffer的第一个参数对应于我们上面创建的管道中的buffers数组的元素。 这和我们之前做的是一样的但是我们用的都是顶点缓冲区没有存储缓冲区。 以下为完整代码及运行结果 HTML !--* Description: * Author: tianyw* Date: 2022-11-11 12:50:23* LastEditTime: 2023-09-19 22:06:18* LastEditors: tianyw -- !DOCTYPE html html langenheadmeta charsetUTF-8 /meta nameviewport contentwidthdevice-width, initial-scale1.0 /title001hello-triangle/titlestylehtml,body {margin: 0;width: 100%;height: 100%;background: #000;color: #fff;display: flex;text-align: center;flex-direction: column;justify-content: center;}div,canvas {height: 100%;width: 100%;}/style /headbodydiv id007vertex-srandom-circlecanvas idgpucanvas/canvas/divscript typemodule src./007vertex-srandom-circle2.ts/script/body/htmlTS /** Description:* Author: tianyw* Date: 2023-04-08 20:03:35* LastEditTime: 2023-10-08 22:47:31* LastEditors: tianyw*/ export type SampleInit (params: {canvas: HTMLCanvasElement; }) void | Promisevoid;import shaderWGSL from ./shaders/shader.wgsl?raw;const rand (min: undefined | number undefined,max: undefined | number undefined ) {if (min undefined) {min 0;max 1;} else if (max undefined) {max min;min 0;}return min Math.random() * (max - min); };function createCircleVertices({radius 1,numSubdivisions 24,innerRadius 0,startAngle 0,endAngle Math.PI * 2 } {}) {// 2 triangles per subdivision, 3 verts per tri, 2 values(xy) eachconst numVertices numSubdivisions * 3 * 2;const vertexData new Float32Array(numSubdivisions * 2 * 3 * 2);let offset 0;const addVertex (x: number, y: number) {vertexData[offset] x;vertexData[offset] y;};// 2 vertices per subdivisionfor (let i 0; i numSubdivisions; i) {const angle1 startAngle ((i 0) * (endAngle - startAngle)) / numSubdivisions;const angle2 startAngle ((i 1) * (endAngle - startAngle)) / numSubdivisions;const c1 Math.cos(angle1);const s1 Math.sin(angle1);const c2 Math.cos(angle2);const s2 Math.sin(angle2);// first angleaddVertex(c1 * radius, s1 * radius);addVertex(c2 * radius, s2 * radius);addVertex(c1 * innerRadius, s1 * innerRadius);// second triangleaddVertex(c1 * innerRadius, s1 * innerRadius);addVertex(c2 * radius, s2 * radius);addVertex(c2 * innerRadius, s2 * innerRadius);}return {vertexData,numVertices}; }const init: SampleInit async ({ canvas }) {const adapter await navigator.gpu?.requestAdapter();if (!adapter) return;const device await adapter?.requestDevice();if (!device) {console.error(need a browser that supports WebGPU);return;}const context canvas.getContext(webgpu);if (!context) return;const devicePixelRatio window.devicePixelRatio || 1;canvas.width canvas.clientWidth * devicePixelRatio;canvas.height canvas.clientHeight * devicePixelRatio;const presentationFormat navigator.gpu.getPreferredCanvasFormat();context.configure({device,format: presentationFormat,alphaMode: premultiplied});const shaderModule device.createShaderModule({label: our hardcoded rgb triangle shaders,code: shaderWGSL});const renderPipeline device.createRenderPipeline({label: flat colors,layout: auto,vertex: {module: shaderModule,entryPoint: vs,buffers: [{arrayStride: 2 * 4, // 2 floats, 4 bytes eachattributes: [{shaderLocation: 0,offset: 0,format: float32x2} // position]},{arrayStride: 6 * 4, // 6 floats, 4 bytes eachstepMode: instance,attributes: [{shaderLocation: 1,offset: 0,format: float32x4 // color},{shaderLocation: 2,offset: 16,format: float32x2 // offset}]},{arrayStride: 2 * 4, // 2 floats, 4 bytes eachstepMode: instance,attributes: [{shaderLocation: 3,offset: 0,format: float32x2} // scale]}]},fragment: {module: shaderModule,entryPoint: fs,targets: [{format: presentationFormat}]},primitive: {// topology: line-list// topology: line-strip// topology: point-listtopology: triangle-list// topology: triangle-strip}});const kNumObjects 100;const staticUnitSize 4 * 4 // color is 4 32bit floats (4bytes each)2 * 4; // offset is 2 32bit floats (4bytes each)const changingUnitSize 2 * 4; // scale is 2 32 bit floatsconst staticVertexBufferSize staticUnitSize * kNumObjects;const changingVertexBufferSize changingUnitSize * kNumObjects;const staticVertexBuffer device.createBuffer({label: static storage for objects,size: staticVertexBufferSize,usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST});const changingVertexBuffer device.createBuffer({label: changing storage for objects,size: changingVertexBufferSize,usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST});const staticVertexValues new Float32Array(staticVertexBufferSize / 4);const changingVertexValues new Float32Array(changingVertexBufferSize / 4);const kColorOffset 0;const kOffsetOffset 4;const kScaleOffset 0;const objectInfos: {scale: number;}[] [];for (let i 0; i kNumObjects; i) {const staticOffset i * (staticUnitSize / 4);staticVertexValues.set([rand(), rand(), rand(), 1],staticOffset kColorOffset);staticVertexValues.set([rand(-0.9, 0.9), rand(-0.9, 0.9)],staticOffset kOffsetOffset);objectInfos.push({scale: rand(0.2, 0.5)});}device.queue.writeBuffer(staticVertexBuffer, 0, staticVertexValues);const { vertexData, numVertices } createCircleVertices({radius: 0.5,innerRadius: 0.25});const vertexBuffer device.createBuffer({label: vertex buffer vertices,size: vertexData.byteLength,usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST});device.queue.writeBuffer(vertexBuffer, 0, vertexData);function frame() {const aspect canvas.width / canvas.height;const renderCommandEncoder device.createCommandEncoder({label: render vert frag});if (!context) return;const textureView context.getCurrentTexture().createView();const renderPassDescriptor: GPURenderPassDescriptor {label: our basic canvas renderPass,colorAttachments: [{view: textureView,clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },loadOp: clear,storeOp: store}]};const renderPass renderCommandEncoder.beginRenderPass(renderPassDescriptor);renderPass.setPipeline(renderPipeline);renderPass.setVertexBuffer(0, vertexBuffer);objectInfos.forEach(({ scale }, ndx) {const offset ndx * (changingUnitSize / 4);changingVertexValues.set([scale / aspect, scale], offset kScaleOffset); // set the scale});device.queue.writeBuffer(changingVertexBuffer, 0, changingVertexValues);renderPass.setVertexBuffer(1, staticVertexBuffer);renderPass.setVertexBuffer(2, changingVertexBuffer);renderPass.draw(numVertices, kNumObjects);renderPass.end();const renderBuffer renderCommandEncoder.finish();device.queue.submit([renderBuffer]);requestAnimationFrame(frame);}requestAnimationFrame(frame); };const canvas document.getElementById(gpucanvas) as HTMLCanvasElement; init({ canvas: canvas }); Shaders: shader.wgsl: struct Vertex {location(0) position: vec2f,location(1) color: vec4f,location(2) offset: vec2f,location(3) scale: vec2f, };struct VSOutput {builtin(position) position: vec4f,location(0) color: vec4f };vertex fn vs(vert: Vertex) - VSOutput {var vsOut: VSOutput;vsOut.position vec4f(vert.position * vert.scale vert.offset, 0.0, 1.0);vsOut.color vert.color;return vsOut; }fragment fn fs(vsOut: VSOutput) - location(0) vec4f {return vsOut.color; }为了好玩让我们添加第二个让我们为每个顶点的颜色添加另一个属性。首先改变着色器。 struct Vertex {location(0) position: vec2f,location(1) color: vec4f,location(2) offset: vec2f,location(3) scale: vec2f,location(4) perVertexColor: vec3f, };struct VSOutput {builtin(position) position: vec4f,location(0) color: vec4f, };vertex fn vs(vert: Vertex, ) - VSOutput {var vsOut: VSOutput;vsOut.position vec4f(vert.position * vert.scale vert.offset, 0.0, 1.0);vsOut.color vert.color * vec4f(vert.perVertexColor, 1);return vsOut; }fragment fn fs(vsOut: VSOutput) - location(0) vec4f {return vsOut.color; }然后我们需要更新管道以描述我们将如何提供数据。我们将像这样将perVertexColor数据与位置交织起来 因此需要更改arrayStride以覆盖我们的新数据我们需要添加新属性。它从两个32位浮点数开始所以它在缓冲区中的偏移量是8字节。 const pipeline device.createRenderPipeline({label: per vertex color,layout: auto,vertex: {module,entryPoint: vs,buffers: [{arrayStride: 5 * 4, // 5 floats, 4 bytes eachattributes: [{shaderLocation: 0, offset: 0, format: float32x2}, // position{shaderLocation: 4, offset: 8, format: float32x3}, // perVertexColor],},{arrayStride: 6 * 4, // 6 floats, 4 bytes eachstepMode: instance,attributes: [{shaderLocation: 1, offset: 0, format: float32x4}, // color{shaderLocation: 2, offset: 16, format: float32x2}, // offset],},{arrayStride: 2 * 4, // 2 floats, 4 bytes eachstepMode: instance,attributes: [{shaderLocation: 3, offset: 0, format: float32x2}, // scale],},],},fragment: {module,entryPoint: fs,targets: [{ format: presentationFormat }],},});我们将更新圆顶点的生成代码为圆外缘的顶点提供深色为内顶点提供浅色。 function createCircleVertices({radius 1,numSubdivisions 24,innerRadius 0,startAngle 0,endAngle Math.PI * 2, } {}) {// 2 triangles per subdivision, 3 verts per tri, 5 values (xyrgb) each.const numVertices numSubdivisions * 3 * 2;const vertexData new Float32Array(numVertices * (2 3) * 3 * 2);let offset 0;const addVertex (x, y, r, g, b) {vertexData[offset] x;vertexData[offset] y;vertexData[offset] r;vertexData[offset] g;vertexData[offset] b;};const innerColor [1, 1, 1];const outerColor [0.1, 0.1, 0.1];// 2 vertices per subdivision//// 0--1 4// | / /|// |/ / |// 2 3--5for (let i 0; i numSubdivisions; i) {const angle1 startAngle (i 0) * (endAngle - startAngle) / numSubdivisions;const angle2 startAngle (i 1) * (endAngle - startAngle) / numSubdivisions;const c1 Math.cos(angle1);const s1 Math.sin(angle1);const c2 Math.cos(angle2);const s2 Math.sin(angle2);// first triangleaddVertex(c1 * radius, s1 * radius, ...outerColor);addVertex(c2 * radius, s2 * radius, ...outerColor);addVertex(c1 * innerRadius, s1 * innerRadius, ...innerColor);addVertex(c1 * innerRadius, s1 * innerRadius, ...innerColor);addVertex(c2 * radius, s2 * radius, ...outerColor);addVertex(c2 * innerRadius, s2 * innerRadius, ...innerColor);}return {vertexData,numVertices,}; }这样我们就得到了阴影圈。 以下为完整代码及运行效果 HTML !--* Description: * Author: tianyw* Date: 2022-11-11 12:50:23* LastEditTime: 2023-10-08 23:52:55* LastEditors: tianyw -- !DOCTYPE html html langenheadmeta charsetUTF-8 /meta nameviewport contentwidthdevice-width, initial-scale1.0 /title001hello-triangle/titlestylehtml,body {margin: 0;width: 100%;height: 100%;background: #000;color: #fff;display: flex;text-align: center;flex-direction: column;justify-content: center;}div,canvas {height: 100%;width: 100%;}/style /headbodydiv id007vertex-srandom-circle3canvas idgpucanvas/canvas/divscript typemodule src./007vertex-srandom-circle3.ts/script/body/htmlTS: /** Description:* Author: tianyw* Date: 2023-04-08 20:03:35* LastEditTime: 2023-10-09 00:00:17* LastEditors: tianyw*/ export type SampleInit (params: {canvas: HTMLCanvasElement; }) void | Promisevoid;import shaderWGSL from ./shaders/shader.wgsl?raw;const rand (min: undefined | number undefined,max: undefined | number undefined ) {if (min undefined) {min 0;max 1;} else if (max undefined) {max min;min 0;}return min Math.random() * (max - min); };function createCircleVertices({radius 1,numSubdivisions 24,innerRadius 0,startAngle 0,endAngle Math.PI * 2 } {}) {// 2 triangles per subdivision, 3 verts per tri, 5 values(xyrgb) eachconst numVertices numSubdivisions * 3 * 2;const vertexData new Float32Array(numSubdivisions * (2 3) * 3 * 2);let offset 0;const addVertex (x: number, y: number, r: number, g: number, b: number) {vertexData[offset] x;vertexData[offset] y;vertexData[offset] r;vertexData[offset] g;vertexData[offset] b;};const innerColor [1, 1, 1];const outerColor [0.1, 0.1, 0.1];// 2 vertices per subdivision//// 0--1 4// | / /|// |/ / |// 2 3--5for (let i 0; i numSubdivisions; i) {const angle1 startAngle ((i 0) * (endAngle - startAngle)) / numSubdivisions;const angle2 startAngle ((i 1) * (endAngle - startAngle)) / numSubdivisions;const c1 Math.cos(angle1);const s1 Math.sin(angle1);const c2 Math.cos(angle2);const s2 Math.sin(angle2);// first angleaddVertex(c1 * radius,s1 * radius,outerColor[0],outerColor[1],outerColor[2]);addVertex(c2 * radius,s2 * radius,outerColor[0],outerColor[1],outerColor[2]);addVertex(c1 * innerRadius,s1 * innerRadius,innerColor[0],innerColor[1],innerColor[2]);// second triangleaddVertex(c1 * innerRadius,s1 * innerRadius,innerColor[0],innerColor[1],innerColor[2]);addVertex(c2 * radius,s2 * radius,outerColor[0],outerColor[1],outerColor[2]);addVertex(c2 * innerRadius,s2 * innerRadius,innerColor[0],innerColor[1],innerColor[2]);}return {vertexData,numVertices}; }const init: SampleInit async ({ canvas }) {const adapter await navigator.gpu?.requestAdapter();if (!adapter) return;const device await adapter?.requestDevice();if (!device) {console.error(need a browser that supports WebGPU);return;}const context canvas.getContext(webgpu);if (!context) return;const devicePixelRatio window.devicePixelRatio || 1;canvas.width canvas.clientWidth * devicePixelRatio;canvas.height canvas.clientHeight * devicePixelRatio;const presentationFormat navigator.gpu.getPreferredCanvasFormat();context.configure({device,format: presentationFormat,alphaMode: premultiplied});const shaderModule device.createShaderModule({label: our hardcoded rgb triangle shaders,code: shaderWGSL});const renderPipeline device.createRenderPipeline({label: per vertex color,layout: auto,vertex: {module: shaderModule,entryPoint: vs,buffers: [{arrayStride: 5 * 4, // 5 floats, 4 bytes eachattributes: [{shaderLocation: 0,offset: 0,format: float32x2}, // position{shaderLocation: 4,offset: 8,format: float32x3} // position]},{arrayStride: 6 * 4, // 6 floats, 4 bytes eachstepMode: instance,attributes: [{shaderLocation: 1,offset: 0,format: float32x4 // color},{shaderLocation: 2,offset: 16,format: float32x2 // offset}]},{arrayStride: 2 * 4, // 2 floats, 4 bytes eachstepMode: instance,attributes: [{shaderLocation: 3,offset: 0,format: float32x2} // scale]}]},fragment: {module: shaderModule,entryPoint: fs,targets: [{format: presentationFormat}]},primitive: {// topology: line-list// topology: line-strip// topology: point-listtopology: triangle-list// topology: triangle-strip}});const kNumObjects 100;const staticUnitSize 4 * 4 // color is 4 32bit floats (4bytes each)2 * 4; // offset is 2 32bit floats (4bytes each)const changingUnitSize 2 * 4; // scale is 2 32 bit floatsconst staticVertexBufferSize staticUnitSize * kNumObjects;const changingVertexBufferSize changingUnitSize * kNumObjects;const staticVertexBuffer device.createBuffer({label: static vertex for objects,size: staticVertexBufferSize,usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST});const changingVertexBuffer device.createBuffer({label: changing vertex for objects,size: changingVertexBufferSize,usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST});const staticVertexValues new Float32Array(staticVertexBufferSize / 4);const changingVertexValues new Float32Array(changingVertexBufferSize / 4);const kColorOffset 0;const kOffsetOffset 4;const kScaleOffset 0;const objectInfos: {scale: number;}[] [];for (let i 0; i kNumObjects; i) {const staticOffset i * (staticUnitSize / 4);staticVertexValues.set([rand(), rand(), rand(), 1],staticOffset kColorOffset);staticVertexValues.set([rand(-0.9, 0.9), rand(-0.9, 0.9)],staticOffset kOffsetOffset);objectInfos.push({scale: rand(0.2, 0.5)});}device.queue.writeBuffer(staticVertexBuffer, 0, staticVertexValues);const { vertexData, numVertices } createCircleVertices({radius: 0.5,innerRadius: 0.25});const vertexBuffer device.createBuffer({label: vertex buffer vertices,size: vertexData.byteLength,usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST});device.queue.writeBuffer(vertexBuffer, 0, vertexData);function frame() {const aspect canvas.width / canvas.height;const renderCommandEncoder device.createCommandEncoder({label: render vert frag});if (!context) return;const textureView context.getCurrentTexture().createView();const renderPassDescriptor: GPURenderPassDescriptor {label: our basic canvas renderPass,colorAttachments: [{view: textureView,clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },loadOp: clear,storeOp: store}]};const renderPass renderCommandEncoder.beginRenderPass(renderPassDescriptor);renderPass.setPipeline(renderPipeline);renderPass.setVertexBuffer(0, vertexBuffer);objectInfos.forEach(({ scale }, ndx) {const offset ndx * (changingUnitSize / 4);changingVertexValues.set([scale / aspect, scale], offset kScaleOffset); // set the scale});device.queue.writeBuffer(changingVertexBuffer, 0, changingVertexValues);renderPass.setVertexBuffer(1, staticVertexBuffer);renderPass.setVertexBuffer(2, changingVertexBuffer);renderPass.draw(numVertices, kNumObjects);renderPass.end();const renderBuffer renderCommandEncoder.finish();device.queue.submit([renderBuffer]);requestAnimationFrame(frame);}requestAnimationFrame(frame); };const canvas document.getElementById(gpucanvas) as HTMLCanvasElement; init({ canvas: canvas }); Shaders: shader.wgsl: struct Vertex {location(0) position: vec2f,location(1) color: vec4f,location(2) offset: vec2f,location(3) scale: vec2f,location(4) perVertexColor: vec3f, };struct VSOutput {builtin(position) position: vec4f,location(0) color: vec4f };vertex fn vs(vert: Vertex) - VSOutput {var vsOut: VSOutput;vsOut.position vec4f(vert.position * vert.scale vert.offset, 0.0, 1.0);vsOut.color vert.color * vec4f(vert.perVertexColor, 1);return vsOut; }fragment fn fs(vsOut: VSOutput) - location(0) vec4f {return vsOut.color; }WGSL中的属性不必与JavaScript中的属性匹配 在上面的WGSL中我们将perVertexColor属性声明为vec3f如下所示 struct Vertex {location(0) position: vec2f,location(1) color: vec4f,location(2) offset: vec2f,location(3) scale: vec2f,location(4) perVertexColor: vec3f, };像这样使用它 vertex fn vs(vert: Vertex, ) - VSOutput {var vsOut: VSOutput;vsOut.position vec4f(vert.position * vert.scale vert.offset, 0.0, 1.0);vsOut.color vert.color * vec4f(vert.perVertexColor, 1);return vsOut; }我们也可以将它声明为vec4f然后像这样使用它 struct Vertex {location(0) position: vec2f,location(1) color: vec4f,location(2) offset: vec2f,location(3) scale: vec2f,location(4) perVertexColor: vec4f, };...vertex fn vs(vert: Vertex, ) - VSOutput {var vsOut: VSOutput;vsOut.position vec4f(vert.position * vert.scale vert.offset, 0.0, 1.0);vsOut.color vert.color * vert.perVertexColor;return vsOut; }不会改变任何其他东西。在JavaScript中我们仍然只提供每个顶点3个浮点数的数据。 {arrayStride: 5 * 4, // 5 floats, 4 bytes eachattributes: [{shaderLocation: 0, offset: 0, format: float32x2}, // position{shaderLocation: 4, offset: 8, format: float32x3}, // perVertexColor],},这是可行的因为在着色器中属性总是有4个值。它们默认为0,0,0,1所以我们不提供的任何值都会得到默认值。 使用规范化的值来节省空间 我们使用32位浮点数表示颜色。每个perVertexColor有3个值每个顶点每种颜色总共有12字节。每种颜色有4个值每种颜色每个实例总共16字节。 我们可以优化一下使用8位的值并告诉WebGPU它们应该从0↔255到0.0↔1.0进行规范化 查看有效的属性格式列表没有3值8bit格式但有unorm8x4所以让我们使用它。 首先让我们更改生成顶点的代码将颜色存储为8bit值并进行归一化 function createCircleVertices({radius 1,numSubdivisions 24,innerRadius 0,startAngle 0,endAngle Math.PI * 2, } {}) {// 2 triangles per subdivision, 3 verts per triconst numVertices numSubdivisions * 3 * 2;// 2 32-bit values for position (xy) and 1 32-bit value for color (rgb_)// The 32-bit color value will be written/read as 4 8-bit valuesconst vertexData new Float32Array(numVertices * (2 1));const colorData new Uint8Array(vertexData.buffer);let offset 0;let colorOffset 8;const addVertex (x, y, r, g, b) {vertexData[offset] x;vertexData[offset] y;offset 1; // skip the colorcolorData[colorOffset] r * 255;colorData[colorOffset] g * 255;colorData[colorOffset] b * 255;colorOffset 9; // skip extra byte and the position};上面我们创建了colorData它是一个Uint8Array视图与vertexData相同的数据 然后使用colorData来插入颜色从0↔1扩展到0↔255 这些数据的内存布局如下所示 然后我们需要修改管道将数据提取为8位无符号值并将它们归一化回0↔1更新偏移量并将步长更新为新的大小。 const pipeline device.createRenderPipeline({label: per vertex color,layout: auto,vertex: {module,entryPoint: vs,buffers: [{arrayStride: 2 * 4 4, // 2 floats, 4 bytes each 4 bytesattributes: [{shaderLocation: 0, offset: 0, format: float32x2}, // position{shaderLocation: 4, offset: 8, format: unorm8x4}, // perVertexColor],},{arrayStride: 4 2 * 4, // 4 bytes 2 floats, 4 bytes eachstepMode: instance,attributes: [{shaderLocation: 1, offset: 0, format: unorm8x4}, // color{shaderLocation: 2, offset: 4, format: float32x2}, // offset],},{arrayStride: 2 * 4, // 2 floats, 4 bytes eachstepMode: instance,attributes: [{shaderLocation: 3, offset: 0, format: float32x2}, // scale],},],},fragment: {module,entryPoint: fs,targets: [{ format: presentationFormat }],},});这样就节省了一点空间。我们以前每个顶点使用20字节现在我们使用12字节节省了40%。我们在每个实例中使用24字节现在我们使用12字节节省了50%。 以下为完整代码及其运行效果 HTML !--* Description: * Author: tianyw* Date: 2022-11-11 12:50:23* LastEditTime: 2023-10-09 21:00:14* LastEditors: tianyw -- !DOCTYPE html html langenheadmeta charsetUTF-8 /meta nameviewport contentwidthdevice-width, initial-scale1.0 /title001hello-triangle/titlestylehtml,body {margin: 0;width: 100%;height: 100%;background: #000;color: #fff;display: flex;text-align: center;flex-direction: column;justify-content: center;}div,canvas {height: 100%;width: 100%;}/style /headbodydiv id007vertex-srandom-circle4canvas idgpucanvas/canvas/divscript typemodule src./007vertex-srandom-circle4.ts/script/body/htmlTS: /** Description:* Author: tianyw* Date: 2023-04-08 20:03:35* LastEditTime: 2023-10-09 21:13:36* LastEditors: tianyw*/ export type SampleInit (params: {canvas: HTMLCanvasElement; }) void | Promisevoid;import shaderWGSL from ./shaders/shader.wgsl?raw;const rand (min: undefined | number undefined,max: undefined | number undefined ) {if (min undefined) {min 0;max 1;} else if (max undefined) {max min;min 0;}return min Math.random() * (max - min); };function createCircleVertices({radius 1,numSubdivisions 24,innerRadius 0,startAngle 0,endAngle Math.PI * 2 } {}) {// 2 triangles per subdivision, 3 verts per triconst numVertices numSubdivisions * 3 * 2;// 2 32-bit values for position(xy) and 1 32-bit value for color(rgb_)// the 32-bit color value will be written/read as 4 8-bit valuesconst vertexData new Float32Array(numVertices * (2 1));const colorData new Uint8Array(vertexData.buffer);let offset 0;let colorOffset 8;const addVertex (x: number, y: number, r: number, g: number, b: number) {vertexData[offset] x;vertexData[offset] y;offset 1; // skip the colorcolorData[colorOffset] r * 255;colorData[colorOffset] g * 255;colorData[colorOffset] b * 255;colorOffset 9; // skip extra byte and the position};const innerColor [1, 1, 1];const outerColor [0.1, 0.1, 0.1];// 2 vertices per subdivision//// 0--1 4// | / /|// |/ / |// 2 3--5for (let i 0; i numSubdivisions; i) {const angle1 startAngle ((i 0) * (endAngle - startAngle)) / numSubdivisions;const angle2 startAngle ((i 1) * (endAngle - startAngle)) / numSubdivisions;const c1 Math.cos(angle1);const s1 Math.sin(angle1);const c2 Math.cos(angle2);const s2 Math.sin(angle2);// first angleaddVertex(c1 * radius,s1 * radius,outerColor[0],outerColor[1],outerColor[2]);addVertex(c2 * radius,s2 * radius,outerColor[0],outerColor[1],outerColor[2]);addVertex(c1 * innerRadius,s1 * innerRadius,innerColor[0],innerColor[1],innerColor[2]);// second triangleaddVertex(c1 * innerRadius,s1 * innerRadius,innerColor[0],innerColor[1],innerColor[2]);addVertex(c2 * radius,s2 * radius,outerColor[0],outerColor[1],outerColor[2]);addVertex(c2 * innerRadius,s2 * innerRadius,innerColor[0],innerColor[1],innerColor[2]);}return {vertexData,numVertices}; }const init: SampleInit async ({ canvas }) {const adapter await navigator.gpu?.requestAdapter();if (!adapter) return;const device await adapter?.requestDevice();if (!device) {console.error(need a browser that supports WebGPU);return;}const context canvas.getContext(webgpu);if (!context) return;const devicePixelRatio window.devicePixelRatio || 1;canvas.width canvas.clientWidth * devicePixelRatio;canvas.height canvas.clientHeight * devicePixelRatio;const presentationFormat navigator.gpu.getPreferredCanvasFormat();context.configure({device,format: presentationFormat,alphaMode: premultiplied});const shaderModule device.createShaderModule({label: our hardcoded rgb triangle shaders,code: shaderWGSL});const renderPipeline device.createRenderPipeline({label: per vertex color,layout: auto,vertex: {module: shaderModule,entryPoint: vs,buffers: [{arrayStride: 2 * 4 4, // 2 floats, 4 bytes each 4 bytesattributes: [{shaderLocation: 0,offset: 0,format: float32x2}, // position{shaderLocation: 4,offset: 8,format: unorm8x4} // position]},{arrayStride: 4 2 * 4, // 4 bytes 2 floats,4 bytes eachstepMode: instance,attributes: [{shaderLocation: 1,offset: 0,format: unorm8x4 // color},{shaderLocation: 2,offset: 4,format: float32x2 // offset}]},{arrayStride: 2 * 4, // 2 floats, 4 bytes eachstepMode: instance,attributes: [{shaderLocation: 3,offset: 0,format: float32x2} // scale]}]},fragment: {module: shaderModule,entryPoint: fs,targets: [{format: presentationFormat}]},primitive: {// topology: line-list// topology: line-strip// topology: point-listtopology: triangle-list// topology: triangle-strip}});const kNumObjects 100;const staticUnitSize 4 // color is 4 bytes2 * 4; // offset is 2 32bit floats (4bytes each)const changingUnitSize 2 * 4; // scale is 2 32 bit floatsconst staticVertexBufferSize staticUnitSize * kNumObjects;const changingVertexBufferSize changingUnitSize * kNumObjects;const staticVertexBuffer device.createBuffer({label: static vertex for objects,size: staticVertexBufferSize,usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST});const changingVertexBuffer device.createBuffer({label: changing vertex for objects,size: changingVertexBufferSize,usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST});const staticVertexValuesU8 new Uint8Array(staticVertexBufferSize);const staticVertexValuesF32 new Float32Array(staticVertexValuesU8.buffer);const changingVertexValues new Float32Array(changingVertexBufferSize / 4);const kColorOffset 0;const kOffsetOffset 1;const kScaleOffset 0;const objectInfos: {scale: number;}[] [];for (let i 0; i kNumObjects; i) {const staticOffsetU8 i * staticUnitSize;const staticOffsetF32 staticOffsetU8 / 4;staticVertexValuesU8.set([rand() * 255, rand() * 255, rand() * 255, 255],staticOffsetU8 kColorOffset);staticVertexValuesF32.set([rand(-0.9, 0.9), rand(-0.9, 0.9)],staticOffsetF32 kOffsetOffset);objectInfos.push({scale: rand(0.2, 0.5)});}device.queue.writeBuffer(staticVertexBuffer, 0, staticVertexValuesF32);const { vertexData, numVertices } createCircleVertices({radius: 0.5,innerRadius: 0.25});const vertexBuffer device.createBuffer({label: vertex buffer vertices,size: vertexData.byteLength,usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST});device.queue.writeBuffer(vertexBuffer, 0, vertexData);function frame() {const aspect canvas.width / canvas.height;const renderCommandEncoder device.createCommandEncoder({label: render vert frag});if (!context) return;const textureView context.getCurrentTexture().createView();const renderPassDescriptor: GPURenderPassDescriptor {label: our basic canvas renderPass,colorAttachments: [{view: textureView,clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },loadOp: clear,storeOp: store}]};const renderPass renderCommandEncoder.beginRenderPass(renderPassDescriptor);renderPass.setPipeline(renderPipeline);renderPass.setVertexBuffer(0, vertexBuffer);objectInfos.forEach(({ scale }, ndx) {const offset ndx * (changingUnitSize / 4);changingVertexValues.set([scale / aspect, scale], offset kScaleOffset); // set the scale});device.queue.writeBuffer(changingVertexBuffer, 0, changingVertexValues);renderPass.setVertexBuffer(1, staticVertexBuffer);renderPass.setVertexBuffer(2, changingVertexBuffer);renderPass.draw(numVertices, kNumObjects);renderPass.end();const renderBuffer renderCommandEncoder.finish();device.queue.submit([renderBuffer]);requestAnimationFrame(frame);}requestAnimationFrame(frame); };const canvas document.getElementById(gpucanvas) as HTMLCanvasElement; init({ canvas: canvas }); Shaders: shader.wgsl: struct Vertex {location(0) position: vec2f,location(1) color: vec4f,location(2) offset: vec2f,location(3) scale: vec2f,location(4) perVertexColor: vec3f, };struct VSOutput {builtin(position) position: vec4f,location(0) color: vec4f };vertex fn vs(vert: Vertex) - VSOutput {var vsOut: VSOutput;vsOut.position vec4f(vert.position * vert.scale vert.offset, 0.0, 1.0);vsOut.color vert.color * vec4f(vert.perVertexColor, 1);return vsOut; }fragment fn fs(vsOut: VSOutput) - location(0) vec4f {return vsOut.color; }索引缓冲区 这里要介绍的最后一件事是索引缓冲区。索引缓冲区描述了处理和使用顶点的顺序。 你可以把绘制看成是按顺序遍历顶点 0, 1, 2, 3, 4, 5, .....通过索引缓冲区我们可以改变这个顺序。 我们为每个圆的细分创建了6个顶点尽管其中2个是相同的。 现在我们只创建4个顶点然后通过告诉WebGPU按此顺序绘制索引使用索引来使用这4个顶点6次 0, 1, 2, 2, 1, 3, ...function createCircleVertices({radius 1,numSubdivisions 24,innerRadius 0,startAngle 0,endAngle Math.PI * 2, } {}) {// 2 vertices at each subdivision, 1 to wrap around the circle.const numVertices (numSubdivisions 1) * 2;// 2 32-bit values for position (xy) and 1 32-bit value for color (rgb)// The 32-bit color value will be written/read as 4 8-bit valuesconst vertexData new Float32Array(numVertices * (2 1));const colorData new Uint8Array(vertexData.buffer);let offset 0;let colorOffset 8;const addVertex (x, y, r, g, b) {vertexData[offset] x;vertexData[offset] y;offset 1; // skip the colorcolorData[colorOffset] r * 255;colorData[colorOffset] g * 255;colorData[colorOffset] b * 255;colorOffset 9; // skip extra byte and the position};const innerColor [1, 1, 1];const outerColor [0.1, 0.1, 0.1];// 2 vertices per subdivision//// 0 2 4 6 8 ...//// 1 3 5 7 9 ...for (let i 0; i numSubdivisions; i) {const angle startAngle (i 0) * (endAngle - startAngle) / numSubdivisions;const c1 Math.cos(angle);const s1 Math.sin(angle);addVertex(c1 * radius, s1 * radius, ...outerColor);addVertex(c1 * innerRadius, s1 * innerRadius, ...innerColor);}const indexData new Uint32Array(numSubdivisions * 6);let ndx 0;// 0---2---4---...// | //| //|// |// |// |//// 1---3-- 5---...for (let i 0; i numSubdivisions; i) {const ndxOffset i * 2;// first triangleindexData[ndx] ndxOffset;indexData[ndx] ndxOffset 1;indexData[ndx] ndxOffset 2;// second triangleindexData[ndx] ndxOffset 2;indexData[ndx] ndxOffset 1;indexData[ndx] ndxOffset 3;}return {positionData,colorData,indexData,numVertices: indexData.length,}; }然后我们需要创建一个索引缓冲区 const { vertexData, indexData, numVertices } createCircleVertices({radius: 0.5,innerRadius: 0.25,});const vertexBuffer device.createBuffer({label: vertex buffer,size: vertexData.byteLength,usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,});device.queue.writeBuffer(vertexBuffer, 0, vertexData);const indexBuffer device.createBuffer({label: index buffer,size: indexData.byteLength,usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,});device.queue.writeBuffer(indexBuffer, 0, indexData);注意我们将用法设置为INDEX。 最后在绘制时我们需要指定索引缓冲区 pass.setPipeline(pipeline);pass.setVertexBuffer(0, vertexBuffer);pass.setVertexBuffer(1, staticVertexBuffer);pass.setVertexBuffer(2, changingVertexBuffer);pass.setIndexBuffer(indexBuffer, uint32);因为我们的缓冲区包含32位无符号整数索引我们需要在这里传递uint32。我们也可以使用16位无符号索引在这种情况下我们需要传入uint16。 我们需要调用drawindex而不是draw pass.drawIndexed(numVertices, kNumObjects);这样我们节省了一些空间(33%)并且在顶点着色器中计算顶点时的潜在处理量类似因为GPU可以重用它已经计算过的顶点。 以下为完整代码及运行结果 HTML !--* Description: * Author: tianyw* Date: 2022-11-11 12:50:23* LastEditTime: 2023-10-09 21:42:18* LastEditors: tianyw -- !DOCTYPE html html langenheadmeta charsetUTF-8 /meta nameviewport contentwidthdevice-width, initial-scale1.0 /title001hello-triangle/titlestylehtml,body {margin: 0;width: 100%;height: 100%;background: #000;color: #fff;display: flex;text-align: center;flex-direction: column;justify-content: center;}div,canvas {height: 100%;width: 100%;}/style /headbodydiv id007vertex-srandom-circle5canvas idgpucanvas/canvas/divscript typemodule src./007vertex-srandom-circle5.ts/script/body/htmlTS /** Description:* Author: tianyw* Date: 2023-04-08 20:03:35* LastEditTime: 2023-10-09 21:53:07* LastEditors: tianyw*/ export type SampleInit (params: {canvas: HTMLCanvasElement; }) void | Promisevoid;import shaderWGSL from ./shaders/shader.wgsl?raw;const rand (min: undefined | number undefined,max: undefined | number undefined ) {if (min undefined) {min 0;max 1;} else if (max undefined) {max min;min 0;}return min Math.random() * (max - min); };function createCircleVertices({radius 1,numSubdivisions 24,innerRadius 0,startAngle 0,endAngle Math.PI * 2 } {}) {// 2 triangles per subdivision, 3 verts per triconst numVertices (numSubdivisions 1) * 2;// 2 32-bit values for position(xy) and 1 32-bit value for color(rgb_)// the 32-bit color value will be written/read as 4 8-bit valuesconst vertexData new Float32Array(numVertices * (2 1));const colorData new Uint8Array(vertexData.buffer);let offset 0;let colorOffset 8;const addVertex (x: number, y: number, r: number, g: number, b: number) {vertexData[offset] x;vertexData[offset] y;offset 1; // skip the colorcolorData[colorOffset] r * 255;colorData[colorOffset] g * 255;colorData[colorOffset] b * 255;colorOffset 9; // skip extra byte and the position};const innerColor [1, 1, 1];const outerColor [0.1, 0.1, 0.1];// 2 vertices per subdivision//// 0 2 4 6 8 ...// 1 3 5 7 9 ...for (let i 0; i numSubdivisions; i) {const angle startAngle ((i 0) * (endAngle - startAngle)) / numSubdivisions;const c1 Math.cos(angle);const s1 Math.sin(angle);addVertex(c1 * radius,s1 * radius,outerColor[0],outerColor[1],outerColor[2]);addVertex(c1 * innerRadius,s1 * innerRadius,innerColor[0],innerColor[1],innerColor[2]);}const indexData new Uint32Array(numSubdivisions * 6);let ndx 0;// 0---2---4---...// | //| //|// |// |// |//// 1---3-- 5---...for (let i 0; i numSubdivisions; i) {const ndxOffset i * 2;// first triangleindexData[ndx] ndxOffset;indexData[ndx] ndxOffset 1;indexData[ndx] ndxOffset 2;// second triangleindexData[ndx] ndxOffset 2;indexData[ndx] ndxOffset 1;indexData[ndx] ndxOffset 3;}return {vertexData,indexData,numVertices: indexData.length}; }const init: SampleInit async ({ canvas }) {const adapter await navigator.gpu?.requestAdapter();if (!adapter) return;const device await adapter?.requestDevice();if (!device) {console.error(need a browser that supports WebGPU);return;}const context canvas.getContext(webgpu);if (!context) return;const devicePixelRatio window.devicePixelRatio || 1;canvas.width canvas.clientWidth * devicePixelRatio;canvas.height canvas.clientHeight * devicePixelRatio;const presentationFormat navigator.gpu.getPreferredCanvasFormat();context.configure({device,format: presentationFormat,alphaMode: premultiplied});const shaderModule device.createShaderModule({label: our hardcoded rgb triangle shaders,code: shaderWGSL});const renderPipeline device.createRenderPipeline({label: per vertex color,layout: auto,vertex: {module: shaderModule,entryPoint: vs,buffers: [{arrayStride: 2 * 4 4, // 2 floats, 4 bytes each 4 bytesattributes: [{shaderLocation: 0,offset: 0,format: float32x2}, // position{shaderLocation: 4,offset: 8,format: unorm8x4} // position]},{arrayStride: 4 2 * 4, // 4 bytes 2 floats,4 bytes eachstepMode: instance,attributes: [{shaderLocation: 1,offset: 0,format: unorm8x4 // color},{shaderLocation: 2,offset: 4,format: float32x2 // offset}]},{arrayStride: 2 * 4, // 2 floats, 4 bytes eachstepMode: instance,attributes: [{shaderLocation: 3,offset: 0,format: float32x2} // scale]}]},fragment: {module: shaderModule,entryPoint: fs,targets: [{format: presentationFormat}]},primitive: {// topology: line-list// topology: line-strip// topology: point-listtopology: triangle-list// topology: triangle-strip}});const kNumObjects 100;const staticUnitSize 4 // color is 4 bytes2 * 4; // offset is 2 32bit floats (4bytes each)const changingUnitSize 2 * 4; // scale is 2 32 bit floatsconst staticVertexBufferSize staticUnitSize * kNumObjects;const changingVertexBufferSize changingUnitSize * kNumObjects;const staticVertexBuffer device.createBuffer({label: static vertex for objects,size: staticVertexBufferSize,usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST});const changingVertexBuffer device.createBuffer({label: changing vertex for objects,size: changingVertexBufferSize,usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST});const staticVertexValuesU8 new Uint8Array(staticVertexBufferSize);const staticVertexValuesF32 new Float32Array(staticVertexValuesU8.buffer);const changingVertexValues new Float32Array(changingVertexBufferSize / 4);const kColorOffset 0;const kOffsetOffset 1;const kScaleOffset 0;const objectInfos: {scale: number;}[] [];for (let i 0; i kNumObjects; i) {const staticOffsetU8 i * staticUnitSize;const staticOffsetF32 staticOffsetU8 / 4;staticVertexValuesU8.set([rand() * 255, rand() * 255, rand() * 255, 255],staticOffsetU8 kColorOffset);staticVertexValuesF32.set([rand(-0.9, 0.9), rand(-0.9, 0.9)],staticOffsetF32 kOffsetOffset);objectInfos.push({scale: rand(0.2, 0.5)});}device.queue.writeBuffer(staticVertexBuffer, 0, staticVertexValuesF32);const { vertexData, indexData, numVertices } createCircleVertices({radius: 0.5,innerRadius: 0.25});const vertexBuffer device.createBuffer({label: vertex buffer vertices,size: vertexData.byteLength,usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST});device.queue.writeBuffer(vertexBuffer, 0, vertexData);const indexBuffer device.createBuffer({label: index buffer,size: indexData.byteLength,usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST});device.queue.writeBuffer(indexBuffer, 0, indexData);function frame() {const aspect canvas.width / canvas.height;const renderCommandEncoder device.createCommandEncoder({label: render vert frag});if (!context) return;const textureView context.getCurrentTexture().createView();const renderPassDescriptor: GPURenderPassDescriptor {label: our basic canvas renderPass,colorAttachments: [{view: textureView,clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },loadOp: clear,storeOp: store}]};const renderPass renderCommandEncoder.beginRenderPass(renderPassDescriptor);renderPass.setPipeline(renderPipeline);renderPass.setVertexBuffer(0, vertexBuffer);objectInfos.forEach(({ scale }, ndx) {const offset ndx * (changingUnitSize / 4);changingVertexValues.set([scale / aspect, scale], offset kScaleOffset); // set the scale});device.queue.writeBuffer(changingVertexBuffer, 0, changingVertexValues);renderPass.setVertexBuffer(1, staticVertexBuffer);renderPass.setVertexBuffer(2, changingVertexBuffer);renderPass.setIndexBuffer(indexBuffer, uint32);renderPass.drawIndexed(numVertices, kNumObjects);renderPass.end();const renderBuffer renderCommandEncoder.finish();device.queue.submit([renderBuffer]);requestAnimationFrame(frame);}requestAnimationFrame(frame); };const canvas document.getElementById(gpucanvas) as HTMLCanvasElement; init({ canvas: canvas }); Shaders: shader.wgsl: struct Vertex {location(0) position: vec2f,location(1) color: vec4f,location(2) offset: vec2f,location(3) scale: vec2f,location(4) perVertexColor: vec3f, };struct VSOutput {builtin(position) position: vec4f,location(0) color: vec4f };vertex fn vs(vert: Vertex) - VSOutput {var vsOut: VSOutput;vsOut.position vec4f(vert.position * vert.scale vert.offset, 0.0, 1.0);vsOut.color vert.color * vec4f(vert.perVertexColor, 1);return vsOut; }fragment fn fs(vsOut: VSOutput) - location(0) vec4f {return vsOut.color; }请注意我们还可以将索引缓冲区与上一篇文章中的存储缓冲区示例一起使用。在这种情况下传入的builtin(vertex_index)的值与索引缓冲区中的索引匹配。 接下来我们将介绍纹理。
http://www.zqtcl.cn/news/378201/

相关文章:

  • app开发和网站开发公司网站怎么做百度竞价
  • 医疗机构网站备案网站建设面试常见问题
  • 建设网银登录网站国内欣赏电商设计的网站
  • 自适应网站优点缺点网站上的在线答题是怎么做的
  • 查询单位信息的网站免费学编程的网站有哪些
  • 建设企业网站进去无法显示wordpress 在线课程
  • 博客型网站建设广州网站建设如何做
  • 网站导航栏全屏怎么做万年网站建设
  • flash源码网站百度关键词价格
  • 个人网站如何发布怎么做记步数的程序到网站
  • 石家庄网站制作公司排名前十可视化网站开发工具有哪些
  • 网站个人博客怎么做杭州网站改版公司电话
  • 烟台北京网站建设公司中国建筑信息资讯网
  • 硬盘做网站空间高端网站设计杭州
  • 南昌网站建设方案网站建设需求分析班级
  • 汉阳做网站关键词站长工具
  • 做海报图片的网站营销软件
  • 能先做网站再绑定域名吗石家庄公司建设网站
  • 设计网站的收费图是怎么做的公司网站简介怎么做
  • 医院网站案例结合七牛云做视频网站
  • wordpress数据库缓存插件aso优化吧
  • 网站二维码代码国贸汽车网站建设
  • 医疗网站建设多少钱信息查询类网站是怎么做的
  • 网站开发辅助工具搜索引擎推广实训
  • 如何用手机制作网站比价网站
  • 商城类网站备案四川全网推网络推广
  • 好设计购物网站wordpress 公网访问不了
  • 局域网网站建设需要什么条件wordpress文章列表高度
  • 长春怎样建网站?学服装设计培训机构
  • 怎么用织梦制作响应式布局网站阳江网红