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>
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>