@@ -6,6 +6,7 @@ const RAY_COUNT_POWER = 3; // 2^3 = 8 rays per probe at level 0
66const BRANCHING_FACTOR = 2 ; // 2^2 = 4 rays merge per level
77const INTERVAL_RADIUS = 2 ; // Base interval radius
88const MAX_CASCADE_LEVELS = 6 ;
9+ const RESOLUTION_SCALE = 1 ; // Higher = sharper lines but more GPU work
910
1011/**
1112 * WebGPU-based Radiance Cascades for 2D global illumination.
@@ -123,7 +124,13 @@ export class FolkRadianceCascade extends FolkBaseSet {
123124 const vertices : number [ ] = [ ] ;
124125
125126 for ( const line of this . #lines) {
126- const [ x1 , y1 , x2 , y2 , r , g , b , thickness ] = line ;
127+ const [ x1raw , y1raw , x2raw , y2raw , r , g , b , thicknessRaw ] = line ;
128+ // Scale CSS coordinates to internal resolution
129+ const x1 = x1raw * RESOLUTION_SCALE ;
130+ const y1 = y1raw * RESOLUTION_SCALE ;
131+ const x2 = x2raw * RESOLUTION_SCALE ;
132+ const y2 = y2raw * RESOLUTION_SCALE ;
133+ const thickness = thicknessRaw * RESOLUTION_SCALE ;
127134
128135 // Calculate perpendicular direction for line thickness
129136 const dx = x2 - x1 ;
@@ -187,8 +194,8 @@ export class FolkRadianceCascade extends FolkBaseSet {
187194 this . #device = await adapter . requestDevice ( ) ;
188195
189196 this . #canvas = document . createElement ( 'canvas' ) ;
190- this . #canvas. width = this . clientWidth || 800 ;
191- this . #canvas. height = this . clientHeight || 600 ;
197+ this . #canvas. width = ( this . clientWidth || 800 ) * RESOLUTION_SCALE ;
198+ this . #canvas. height = ( this . clientHeight || 600 ) * RESOLUTION_SCALE ;
192199 this . #canvas. style . position = 'absolute' ;
193200 this . #canvas. style . top = '0' ;
194201 this . #canvas. style . left = '0' ;
@@ -372,10 +379,11 @@ export class FolkRadianceCascade extends FolkBaseSet {
372379 const elements = Array . from ( this . sourceElements ) ;
373380
374381 this . sourceRects . forEach ( ( rect , index ) => {
375- const x0 = ( rect . left / this . #canvas. width ) * 2 - 1 ;
376- const y0 = 1 - ( rect . top / this . #canvas. height ) * 2 ;
377- const x1 = ( rect . right / this . #canvas. width ) * 2 - 1 ;
378- const y1 = 1 - ( rect . bottom / this . #canvas. height ) * 2 ;
382+ // Convert CSS coordinates to clip space
383+ const x0 = ( rect . left / ( this . #canvas. width / RESOLUTION_SCALE ) ) * 2 - 1 ;
384+ const y0 = 1 - ( rect . top / ( this . #canvas. height / RESOLUTION_SCALE ) ) * 2 ;
385+ const x1 = ( rect . right / ( this . #canvas. width / RESOLUTION_SCALE ) ) * 2 - 1 ;
386+ const y1 = 1 - ( rect . bottom / ( this . #canvas. height / RESOLUTION_SCALE ) ) * 2 ;
379387
380388 // Get color from data-color attribute, or use index-based hue
381389 const element = elements [ index ] ;
@@ -648,8 +656,8 @@ export class FolkRadianceCascade extends FolkBaseSet {
648656 }
649657
650658 #handleResize = async ( ) => {
651- const newWidth = this . clientWidth || 800 ;
652- const newHeight = this . clientHeight || 600 ;
659+ const newWidth = ( this . clientWidth || 800 ) * RESOLUTION_SCALE ;
660+ const newHeight = ( this . clientHeight || 600 ) * RESOLUTION_SCALE ;
653661
654662 // Skip if dimensions haven't actually changed
655663 if ( this . #canvas. width === newWidth && this . #canvas. height === newHeight ) {
@@ -675,8 +683,9 @@ export class FolkRadianceCascade extends FolkBaseSet {
675683
676684 #handleMouseMove = ( e : MouseEvent ) => {
677685 const rect = this . getBoundingClientRect ( ) ;
678- this . #mousePosition. x = ( e . clientX - rect . left ) * ( this . #canvas. width / rect . width ) ;
679- this . #mousePosition. y = ( e . clientY - rect . top ) * ( this . #canvas. height / rect . height ) ;
686+ // Convert to internal (scaled) coordinates
687+ this . #mousePosition. x = ( e . clientX - rect . left ) * RESOLUTION_SCALE ;
688+ this . #mousePosition. y = ( e . clientY - rect . top ) * RESOLUTION_SCALE ;
680689 } ;
681690
682691 #cleanupResources( ) {
@@ -746,8 +755,8 @@ fn vertex_main(input: VertexInput) -> VertexOutput {
746755
747756@fragment
748757fn fragment_main(in: VertexOutput) -> @location(0) vec4f {
749- // Shapes emit their color (brighter) and are opaque
750- return vec4f(in.color * 2.0 , 1.0);
758+ // Shapes emit their color and are opaque
759+ return vec4f(in.color, 1.0);
751760}
752761` ;
753762
@@ -1004,8 +1013,8 @@ fn fragment_main(in: VertexOutput) -> @location(0) vec4f {
10041013 // world.a = 1 means opaque emitter, world.rgb is emissive color
10051014 if (world.a > 0.5) {
10061015 // Blend emissive color with fluence for a nice glow effect
1007- return vec4f(world.rgb * 0.5 + fluence.rgb, 1.0);
1016+ return vec4f(world.rgb * 0.3 + fluence.rgb * 0.8 , 1.0);
10081017 }
1009- return fluence;
1018+ return vec4f( fluence.rgb * 0.8, 1.0) ;
10101019}
10111020` ;
0 commit comments