我正在尝试设置每个三角形的颜色。
但是它不起作用。
仅设置每个顶点的颜色才有效!
这就是我做的:
<!DOCTYPE html><title>Color Per Triangle</title><style>
body{ background-color: #000 }
canvas{ display: block; width: 600px; height: 400px; outline: 1px solid #343438 }
</style><canvas width=900 height=600></canvas><script type=module>
const canvas=document.body.firstChild, C=canvas.getContext(`webgpu`),
red=[.9,.3,.3,1], yel=[.7,.7,.3,1],
tri=[
0,0,0,...red, 0, 1,0,...red, 1,0,0,...red, // ⚠️ Wish: avoid color repeating
0,0,0,...yel, 0,-1,0,...yel, -1,0,0,...yel,
];
/* ♻️ WANT THIS:
tri=[
0,0,0, 0, 1,0, 1,0,0, ...red, // set color per triangle
0,0,0, 0,-1,0, -1,0,0, ...yel,
]
*/
VB_$=window.VB_$=new Float32Array(tri); // Vertex Buffer Source
let code=`
struct _V {
@location(0) p: vec3f,
@location(1) c: vec4f,
};
struct V_ {
@builtin(position) p: vec4f,
@location(0) c: vec4f,
};
@vertex fn vs(_v: _V) -> V_ {
var v_: V_;
v_.p = vec4f(_v.p, 1);
v_.c = _v.c;
return v_;
}
@fragment fn fs(v_: V_) -> @location(0) vec4f {
return v_.c;
}
`,
format = `bgra8unorm`,
adapter = await navigator.gpu.requestAdapter(),
device = await adapter.requestDevice(),
Q = device.queue,
A = { loadOp: `clear`, storeOp: `store` }, // Attachments
O = { colorAttachments: [ A ] }, // Render Pass Descriptor
module = device.createShaderModule({ code }),
PO = { layout: `auto`,
vertex: { module, entryPoint: `vs`,
buffers:[
{
arrayStride: 28, // (3+4)*4.; (xyz+rgba)*4.
attributes: [
{ shaderLocation:0, offset:0, format:`float32x3` }, // p: vec3f [xyz]
{ shaderLocation:1, offset:12, format:`float32x4` }, // c: vec4f [rgba]; 3*4.
]
}
]
},
fragment: { module, entryPoint: `fs`, targets: [{ format }] }
},
P = device.createRenderPipeline( PO ),
E,R, VB = device.createBuffer({ size: VB_$.byteLength, usage: 40 }); // VERTEX | COPY_DST
function draw(){
A.view=C.getCurrentTexture().createView();
E=device.createCommandEncoder();
R=E.beginRenderPass(O);
R.setPipeline(P);
R.setVertexBuffer(0,VB);
R.draw(6); // 2*3 = 2 triangles with 3 vertices each
R.end();
Q.submit([ E.finish() ])
}
C.configure({ device, format })
Q.writeBuffer(VB,0,VB_$);
draw()
</script>
此外,它不需要使用硬编码颜色。 颜色和顶点位置必须使用顶点缓冲区传递给着色器!
PS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 我无法使用统一或存储缓冲区, 因为浏览器显示:“不支持的位标志集(描述符范围标志 10002)。
D3D12 序列化根签名失败,出现 E_INVALIDARG (0x80070057) 在 CheckHRESULTImpl (....hird_party\dawn\src\dawn 原生\d3d\D3DError.cpp:96) 在初始化 (...hird_party\dawn\src\dawn ative\d3d12\PipelineLayoutD3D12.cpp:350)"
你不能,所有数据都是每个顶点的。即使您使用像 SSBO 这样的东西,您仍然需要在每个顶点中提供查找索引(然后当然要付出额外间接的成本)。
您无法访问每个三角形和每个顶点的顶点缓冲区数据。 您可以使用存储缓冲区。如果您遇到错误,您一定是设置错误了
您可以在此处
看到使用顶点数据的存储缓冲区在此处复制该示例并修改为每个三角形的颜色
// WebGPU Storage Buffer vertices
// from https://webgpufundamentals.org/webgpu/webgpu-storage-buffer-vertices.html
// A random number between [min and max)
// With 1 argument it will be [0 to min)
// With no arguments it will be [0 to 1)
const rand = (min, max) => {
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) each.
const numVertices = numSubdivisions * 3 * 2;
const vertexData = new Float32Array(numSubdivisions * 2 * 3 * 2);
const colorData = new Uint32Array(numSubdivisions * 2)
let offset = 0;
const addVertex = (x, y) => {
vertexData[offset++] = x;
vertexData[offset++] = y;
};
let colorOffset = 0;
const addColor = (r, g, b, a) => {
colorData[colorOffset++] =
((a | 0) << 24) |
((b | 0) << 16) |
((g | 0) << 8) |
((r | 0) << 0) ;
};
const randColor = () => {
return [
rand(256),
rand(256),
rand(256),
255,
];
};
// 2 vertices per subdivision
//
// 0--1 4
// | / /|
// |/ / |
// 2 3--5
for (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 triangle
addVertex(c1 * radius, s1 * radius);
addVertex(c2 * radius, s2 * radius);
addVertex(c1 * innerRadius, s1 * innerRadius);
addColor(...randColor());
// second triangle
addVertex(c1 * innerRadius, s1 * innerRadius);
addVertex(c2 * radius, s2 * radius);
addVertex(c2 * innerRadius, s2 * innerRadius);
addColor(...randColor());
}
return {
vertexData,
colorData,
numVertices,
};
}
async function main() {
const adapter = await navigator.gpu?.requestAdapter();
const device = await adapter?.requestDevice();
if (!device) {
fail('need a browser that supports WebGPU');
return;
}
// Get a WebGPU context from the canvas and configure it
const canvas = document.querySelector('canvas');
const context = canvas.getContext('webgpu');
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device,
format: presentationFormat,
});
const module = device.createShaderModule({
code: `
struct PerVertexData {
position: vec2f,
};
struct VSOutput {
@builtin(position) position: vec4f,
@location(0) color: vec4f,
};
struct Uniforms {
mat: mat4x4f,
};
@group(0) @binding(0) var<storage, read> perVertData: array<PerVertexData>;
@group(0) @binding(1) var<storage, read> perTriData: array<u32>;
@vertex fn vs(
@builtin(vertex_index) vertexIndex : u32,
) -> VSOutput {
let triangleIndex = vertexIndex / 3;
let vert = perVertData[vertexIndex];
let tri = perTriData[triangleIndex];
var vsOut: VSOutput;
vsOut.position = vec4f(vert.position, 0.0, 1.0);
vsOut.color = unpack4x8unorm(tri);
return vsOut;
}
@fragment fn fs(vsOut: VSOutput) -> @location(0) vec4f {
return vsOut.color;
}
`,
});
const pipeline = device.createRenderPipeline({
label: 'storage buffer vertices',
layout: 'auto',
vertex: {
module,
entryPoint: 'vs',
},
fragment: {
module,
entryPoint: 'fs',
targets: [{ format: presentationFormat }],
},
});
const kNumObjects = 100;
const objectInfos = [];
// setup a storage buffer with vertex data
const { vertexData, colorData, numVertices } = createCircleVertices({
radius: 0.5,
innerRadius: 0.25,
});
const vertexStorageBuffer = device.createBuffer({
label: 'storage buffer per vert data',
size: vertexData.byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(vertexStorageBuffer, 0, vertexData);
const colorStorageBuffer = device.createBuffer({
label: 'storage buffer per color data',
size: colorData.byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(colorStorageBuffer, 0, colorData);
const bindGroup = device.createBindGroup({
label: 'bind group for objects',
layout: pipeline.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: { buffer: vertexStorageBuffer }},
{ binding: 1, resource: { buffer: colorStorageBuffer }},
],
});
const renderPassDescriptor = {
label: 'our basic canvas renderPass',
colorAttachments: [
{
// view: <- to be filled out when we render
clearValue: [0.3, 0.3, 0.3, 1],
loadOp: 'clear',
storeOp: 'store',
},
],
};
function render() {
// Get the current texture from the canvas context and
// set it as the texture to render to.
renderPassDescriptor.colorAttachments[0].view =
context.getCurrentTexture().createView();
const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass(renderPassDescriptor);
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.draw(numVertices);
pass.end();
const commandBuffer = encoder.finish();
device.queue.submit([commandBuffer]);
}
const observer = new ResizeObserver(entries => {
for (const entry of entries) {
const canvas = entry.target;
const width = entry.contentBoxSize[0].inlineSize;
const height = entry.contentBoxSize[0].blockSize;
canvas.width = Math.max(1, Math.min(width, device.limits.maxTextureDimension2D));
canvas.height = Math.max(1, Math.min(height, device.limits.maxTextureDimension2D));
// re-render
render();
}
});
observer.observe(canvas);
}
function fail(msg) {
alert(msg);
}
main();
@import url(https://webgpufundamentals.org/webgpu/resources/webgpu-lesson.css);
html, body {
margin: 0; /* remove the default margin */
height: 100%; /* make the html,body fill the page */
}
canvas {
display: block; /* make the canvas act like a block */
width: 100%; /* make the canvas fill its container */
height: 100%;
}
<canvas></canvas>
请注意,上述解决方案假设目标是每个三角形有 1 条颜色数据。如果目标是每个三角形有 1 种颜色,忽略额外的数据,那么您可以重复数据。您还可以在阶段间颜色变量中使用
@interpolate(flat)
,使其不插入颜色。
此外,您还可以使用实例化,其中某些顶点数据每个实例仅更改一次。这可能不适合您的用例,但以防万一,这里是如何使用顶点缓冲区进行实例,这里是如何使用存储缓冲区进行实例