GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
feat: the traced image is unlit, as the raster pass draws it
A ray that lands on the image takes the image's colour as it is and the
path ends there, where it was a lit surface with the image as its albedo
and a white page traced grey. Its pixels are marked for the denoiser as
the sky's are: a colour that is the image's own has no noise to take out,
and smoothing took its fine print first.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
src/vk/rt.rs | 13 ++++++++-----
src/vk/rt_common.wgsl | 23 ++++++++++++++++++-----
2 files changed, 26 insertions(+), 10 deletions(-)
diff --git a/src/vk/rt.rs b/src/vk/rt.rs
index bdb163c..a6dd2af 100644
--- a/src/vk/rt.rs
+++ b/src/vk/rt.rs
@@ -39,9 +39,11 @@ pub struct RtMaterial {
pub emission: [f32; 3],
}
-/// An image standing in the traced scene: a quad whose surface is the
-/// image's colour, lit as any other surface is, and which lets a ray through
-/// where the image is clear. The tracer adds the quad to the scene itself.
+/// An image standing in the traced scene: a quad that shows the image's
+/// colour as it is — unlit, as the raster pass's `SceneImage` draws it — and
+/// lets a ray through where the image is clear. A path ends on it, so to
+/// the rest of the scene it is a light of its own colour. The tracer adds
+/// the quad to the scene itself.
///
/// The image is one the 2D pass already holds (an id from `upload_rgba`),
/// so a picture shown in the raster viewport costs the tracer nothing more.
@@ -2685,8 +2687,9 @@ mod tests {
};
// The quad spans x in -1..1 of a view about 2.9 wide at z = 0: the
// pane's columns 10 to 54, and rows 21 to 43.
- let (r, g, _) = at(16, 32);
- assert!(r > g + 40, "the image's red half is not red: r={r} g={g}");
+ // Unlit: the texel's own colour, whatever the sky is doing.
+ let (r, g, b) = at(16, 32);
+ assert!(r >= 250 && g <= 5 && b <= 5, "the image's red half is not its red: {r} {g} {b}");
let (r, g, _) = at(48, 32);
assert!(g > r + 40, "the image's clear half hides the wall: r={r} g={g}");
let (r, g, _) = at(32, 6);
diff --git a/src/vk/rt_common.wgsl b/src/vk/rt_common.wgsl
index 2b153da..57f9431 100644
--- a/src/vk/rt_common.wgsl
+++ b/src/vk/rt_common.wgsl
@@ -162,11 +162,14 @@ fn cs_main(@builtin(global_invocation_id) gid: vec3<u32>) {
let tri = tris[hit.tri];
let mat = materials[bitcast<u32>(tri.p0.w)];
let at = ro + rd * hit.t;
- var albedo = mat.albedo.rgb;
+ let albedo = mat.albedo.rgb;
if mat.albedo.w > 0.5 {
- // The image: its colour is the surface's, and where it is
- // clear the ray goes on as if nothing were there — by
- // chance, in proportion, which over the samples is the
+ // The image: a picture carries its own light. What a ray
+ // finds there is the image's colour as it is, neither lit
+ // by the sky nor shadowed by the scene — the colour the
+ // raster pass draws — and the path ends on it. Where the
+ // image is clear the ray goes on as if nothing were there:
+ // by chance, in proportion, which over the samples is the
// image's own alpha.
let rel = at - params.img_origin.xyz;
let u = params.img_u.xyz;
@@ -188,7 +191,17 @@ fn cs_main(@builtin(global_invocation_id) gid: vec3<u32>) {
ro = at + rd * (1e-4 * max(1.0, hit.t));
continue;
}
- albedo = texel.rgb;
+ if primary {
+ // Marked as the sky is, with no depth: the colour is
+ // the image's own and has no noise to take out, so the
+ // denoiser passes it through and mixes it into nothing
+ // — smoothed, the image's fine print is the first
+ // thing to go.
+ features[2u * idx] = vec4<f32>(0.0, 0.0, 0.0, 1e30);
+ features[2u * idx + 1u] = vec4<f32>(texel.rgb, 0.0);
+ }
+ radiance = radiance + throughput * texel.rgb;
+ break;
}
radiance = radiance + throughput * mat.emission.rgb;
var n = normalize(cross(tri.p1.xyz - tri.p0.xyz, tri.p2.xyz - tri.p0.xyz));