Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions sample/primitivePicking/computePickPrimitive.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
struct Frame {
viewProjectionMatrix : mat4x4f,
invViewProjectionMatrix : mat4x4f,
pickCoord : vec2f,
pickedPrimitive : u32,
}
@group(0) @binding(0) var<storage, read_write> frame : Frame;
@group(0) @binding(1) var primitiveTex : texture_2d<u32>;

@compute @workgroup_size(1)
fn main() {
// Load the primitive index from the picking texture and store it in the
// pickedPrimitive value (exposed to the rendering shaders as a uniform).
let texel = vec2u(frame.pickCoord);
frame.pickedPrimitive = textureLoad(primitiveTex, texel, 0).x;
}
38 changes: 38 additions & 0 deletions sample/primitivePicking/fragmentForwardRendering.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
enable primitive_index;

struct Frame {
viewProjectionMatrix : mat4x4f,
invViewProjectionMatrix : mat4x4f,
pickCoord : vec2f,
pickedPrimitive : u32,
}
@group(0) @binding(1) var<uniform> frame : Frame;

struct PassOutput {
@location(0) color : vec4f,
@location(1) primitive : u32,
}

@fragment
fn main(
@location(0) fragNormal : vec3f,
@builtin(primitive_index) primIndex : u32
) -> PassOutput {
// Very simple N-dot-L lighting model
let lightDirection = normalize(vec3f(4, 10, 6));
let light = dot(normalize(fragNormal), lightDirection) * 0.5 + 0.5;
let surfaceColor = vec4f(0.8, 0.8, 0.8, 1.0);

var output : PassOutput;

// Highlight the primitive if it's the selected one, otherwise shade normally.
if (primIndex+1 == frame.pickedPrimitive) {
output.color = vec4f(1.0, 1.0, 0.0, 1.0);
} else {
output.color = vec4f(surfaceColor.xyz * light, surfaceColor.a);
}

// Adding one to each primitive index so that 0 can mean "nothing picked"
output.primitive = primIndex+1;
return output;
}
22 changes: 22 additions & 0 deletions sample/primitivePicking/fragmentPrimitivesDebugView.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@group(0) @binding(0) var primitiveTex: texture_2d<u32>;

@fragment
fn main(
@builtin(position) coord : vec4f
) -> @location(0) vec4f {
// Load the primitive index for this pixel from the picking texture.
let primitiveIndex = textureLoad(primitiveTex, vec2i(floor(coord.xy)), 0).x;
var result : vec4f;

// Generate a color for the primitive index. If we only increment the color
// channels by 1 for each primitive index we can show a very large range of
// unique values but it can make the individual primitives hard to distinguish.
// This code steps through 8 distinct values per-channel, which may end up
// repeating some colors for larger meshes but makes the unique primitive
// index values easier to see.
result.r = f32(primitiveIndex % 8) / 8;
result.g = f32((primitiveIndex / 8) % 8) / 8;
result.b = f32((primitiveIndex / 64) % 8) / 8;
result.a = 1.0;
return result;
}
40 changes: 40 additions & 0 deletions sample/primitivePicking/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>webgpu-samples: primitivePicking</title>
<!-- WebGPUCompatibilityMode origin token for https://webgpu.github.io expiring April 21, 2026 -->
<meta
http-equiv="origin-trial"
content="Aktu7041jFm00ls336/bRinubASRzg1tPs4wxXOZkF1uP0LaIURinGC7ti0Vf352Q9OKFL1siRfpptLjNIKpKQcAAABheyJvcmlnaW4iOiJodHRwczovL3dlYmdwdS5naXRodWIuaW86NDQzIiwiZmVhdHVyZSI6IldlYkdQVUNvbXBhdGliaWxpdHlNb2RlIiwiZXhwaXJ5IjoxNzc2NzI5NjAwfQ=="
/>
<!-- WebGPUCompatibilityMode origin token for http://localhost:8080 expiring April 21, 2026 -->
<meta
http-equiv="origin-trial"
content="AqW27Ayelg5vbcAaYcweU+sLjZq5r6idHCWU4MJgnkP1YBgmOMqazdGuakSnGylTkyA/bRHkCJZFdfYjFlylOgAAAABaeyJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjgwODAiLCJmZWF0dXJlIjoiV2ViR1BVQ29tcGF0aWJpbGl0eU1vZGUiLCJleHBpcnkiOjE3NzY3Mjk2MDB9"
/>
<style>
:root {
color-scheme: light dark;
}
html, body {
margin: 0; /* remove default margin */
height: 100%; /* make body fill the browser window */
display: flex;
place-content: center center;
}
canvas {
width: 600px;
height: 600px;
max-width: 100%;
display: block;
}
</style>
<script defer src="main.js" type="module"></script>
<script defer type="module" src="../../js/iframe-helper.js"></script>
</head>
<body>
<canvas></canvas>
</body>
</html>
Loading