WebGPU。如何设置每个三角形的颜色,而不是每个顶点,并仅使用顶点缓冲区每个三角形的颜色

问题描述 投票:0回答:2

我正在尝试设置每个三角形的颜色。

但是它不起作用。

仅设置每个顶点的颜色才有效!

这就是我做的:

<!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)"

javascript vertex-buffer webgpu
2个回答
0
投票

你不能,所有数据都是每个顶点的。即使您使用像 SSBO 这样的东西,您仍然需要在每个顶点中提供查找索引(然后当然要付出额外间接的成本)。


0
投票

您无法访问每个三角形和每个顶点的顶点缓冲区数据。 您可以使用存储缓冲区。如果您遇到错误,您一定是设置错误了

您可以在此处

看到使用顶点数据的存储缓冲区

在此处复制该示例并修改为每个三角形的颜色

// 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)
,使其不插入颜色。

此外,您还可以使用实例化,其中某些顶点数据每个实例仅更改一次。这可能不适合您的用例,但以防万一,这里是如何使用顶点缓冲区进行实例,这里是如何使用存储缓冲区进行实例

© www.soinside.com 2019 - 2024. All rights reserved.