0

The first triangle must be RED, the second triangle must be GREEN.

But both are GREEN (((

How to set different colors for each sub-triangle in 'triangle-strip' primitive topology ?

My final goal is: Creating new triangles with new colors at mouse click point in 'triangle-strip' mode.

This is my code for 2 triangles for simplicity:

body{ background-color: #000 }
canvas{ display: block; width: 600px; height: 400px; outline: 1px solid #666 }
<canvas width=900 height=600></canvas>
<script type="module">
let C = document.querySelector('canvas').getContext(`webgpu`),

code=`
var<private> fi: i32; // fragment_index ( current triangle )

@vertex
fn vs( @builtin(vertex_index) vi: u32 ) -> @builtin(position) vec4f {
  if(vi<3){ fi = 1;
    var T = array<vec2f, 3>( vec2f(0,0), vec2f(.4,.7), vec2f(.8,0) );
    return vec4f(T[vi],0,1);
  };
  fi = 2;
  return vec4f(.6,-.5,0,1);
}

@fragment
fn fs() -> @location(0) vec4f {
  if(fi == 1){ return vec4f(.7,.2,.2,.5); }; // color for 1st triangle 
  return vec4f(.3,.6,.4,.5);                 // color for 2nd triangle
}`,

 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
      E, R,
 module = device.createShaderModule({ code }),
      P = device.createRenderPipeline({ layout: `auto`, primitive: { topology: `triangle-strip` },
             vertex: { module, entryPoint: `vs`, },
           fragment: { module, entryPoint: `fs`, targets: [{ format }] }
          });

C.configure({ device, format });

function F(){
  A.view = C.getCurrentTexture().createView();
  
  E = device.createCommandEncoder();
  R = E.beginRenderPass(O);
  R.setPipeline(P);
  
  R.draw(4);
  R.end();
  Q.submit([E.finish()]);
  
  requestAnimationFrame(F)
}

F()

</script>
gman
  • 100,619
  • 31
  • 269
  • 393
Teamur
  • 67
  • 1
  • 4

1 Answers1

1

I don't think var<private> fi: i32 is doing what you think it's doing.

vertex shaders and fragment shaders share nothing. You can consider them entirely separate. Your vertex shader is

var<private> fi: i32;

@vertex
fn vs(...) -> ...

and your fragment shader is

var<private> fi: i32;

@fragment
fn fs(...) -> ...

The fi variable is not shared.

To pass data from a vertex shader to a fragment shader you need to use inter-stage variables

So, below I changed fi to an inter-stage variable by putting it a struct, returning that struct from the vertex shader. It's important to note, just like I said above, the fragment shader and vertex shader are not sharing the actual data in the struct. The data is connected by @location(0) not by the struct itself.

Further, inter-stage variables normally get interpolated. You can turn off the interpolation by adding @interpolate(flat) in which case, the value passed to the fragment shader will be the value of the 1st vertex of the triangle. In this case the first vertex of the first triangle is vertex 0. The first vertex of the 2nd triangle is vertex 1

body{ background-color: #000 }
canvas{ display: block; width: 600px; height: 400px; outline: 1px solid #666 }
<canvas width=900 height=600></canvas>
<script type="module">
let C = document.querySelector('canvas').getContext(`webgpu`),

code=`

struct VSOut {
  @builtin(position) pos: vec4f,
  @location(0) @interpolate(flat) fi: i32,
};

@vertex
fn vs( @builtin(vertex_index) vi: u32 ) -> VSOut {

  // inter-stage variables are interpolated. In flat interpolation mode,
  // the values passed to the fragment shader are from the "provoking vertex"
  // which is the value set on the 1st vertex of the triangle
  var vsOut: VSOut;
  vsOut.fi = 1;
  if (vi > 0) {
    vsOut.fi = 2;
  }

  if(vi<3){
    var T = array<vec2f, 3>( vec2f(0,0), vec2f(.4,.7), vec2f(.8,0) );
    vsOut.pos = vec4f(T[vi],0,1);
    return vsOut;
  };
  vsOut.pos = vec4f(.6,-.5,0,1);
  return vsOut;
}

@fragment
fn fs(vsOut: VSOut) -> @location(0) vec4f {
  if(vsOut.fi == 1){ return vec4f(.7,.2,.2,.5); }; // color for 1st triangle 
  return vec4f(.3,.6,.4,.5);                 // color for 2nd triangle
}`,

 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
      E, R,
 module = device.createShaderModule({ code }),
      P = device.createRenderPipeline({ layout: `auto`, primitive: { topology: `triangle-strip` },
             vertex: { module, entryPoint: `vs`, },
           fragment: { module, entryPoint: `fs`, targets: [{ format }] }
          });

C.configure({ device, format });

function F(){
  A.view = C.getCurrentTexture().createView();
  
  E = device.createCommandEncoder();
  R = E.beginRenderPass(O);
  R.setPipeline(P);
  
  R.draw(4);
  R.end();
  Q.submit([E.finish()]);
  
  requestAnimationFrame(F)
}

F()

</script>

with blending

body{ background-color: #000 }
canvas{ display: block; width: 600px; height: 400px; outline: 1px solid #666 }
<canvas width=900 height=600></canvas>
<script type="module">
let C = document.querySelector('canvas').getContext(`webgpu`),

code=`

struct VSOut {
  @builtin(position) pos: vec4f,
  @location(0) @interpolate(flat) fi: i32,
};

@vertex
fn vs( @builtin(vertex_index) vi: u32 ) -> VSOut {

  // inter-stage variables are interpolated. In flat interpolation mode,
  // the values passed to the fragment shader are from the "provoking vertex"
  // which is the value set on the 1st vertex of the triangle
  var vsOut: VSOut;
  vsOut.fi = 1;
  if (vi > 0) {
    vsOut.fi = 2;
  }

  if(vi<3){
    var T = array<vec2f, 3>( vec2f(0,0), vec2f(.4,.7), vec2f(.8,0) );
    vsOut.pos = vec4f(T[vi],0,1);
    return vsOut;
  };
  vsOut.pos = vec4f(.6,-.5,0,1);
  return vsOut;
}

@fragment
fn fs(vsOut: VSOut) -> @location(0) vec4f {
  if(vsOut.fi == 1){ return vec4f(.7,.2,.2,.5); }; // color for 1st triangle 
  return vec4f(.3,.6,.4,.5);                 // color for 2nd triangle
}`,

 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
      E, R,
 module = device.createShaderModule({ code }),
      P = device.createRenderPipeline({ layout: `auto`, primitive: { topology: `triangle-strip` },
             vertex: { module, entryPoint: `vs`, },
           fragment: { module, entryPoint: `fs`, targets: [{ format,           blend: {
            color: {
              srcFactor: 'one',
              dstFactor: 'one-minus-src-alpha',
              operation: 'add',
            },
            alpha: {
              srcFactor: 'one',
              dstFactor: 'one-minus-src-alpha',
              operation: 'add',
            },
          }, }] }
          });

C.configure({ device, format });

function F(){
  A.view = C.getCurrentTexture().createView();
  
  E = device.createCommandEncoder();
  R = E.beginRenderPass(O);
  R.setPipeline(P);
  
  R.draw(4);
  R.end();
  Q.submit([E.finish()]);
  
  requestAnimationFrame(F)
}

F()

</script>
gman
  • 100,619
  • 31
  • 269
  • 393
  • Thank You! Please remove line 'return vec4f(f32(vsOut.fi) / 2, 0, 0, 1);' in your fragment shader – Teamur Jun 02 '23 at 09:55
  • Also why there is opacity fail ? Through the Green triangle we don't see the Red triangle – Teamur Jun 02 '23 at 10:08
  • 1
    There's no opacity because you didn't set the blend state on the color targets in your render pipeline. Added example to answer. Note: you might want to read up on [pre-multiplied alpha](https://developer.nvidia.com/content/alpha-blending-pre-or-not-pre) since the colors returned from your shader are not – gman Jun 02 '23 at 17:21