11struct Params {
22 camera_pos : vec3f ,
33 _pad1 : u32 ,
4+ light_dir : vec3f ,
5+ _pad2 : u32 ,
46 width : u32 ,
57 height : u32 ,
68 iTime : f32 ,
7- _pad2 : u32 ,
9+ sphere_count : u32 ,
810};
911
1012struct Material {
1113 diffuse_color : vec3f ,
12- _pad : f32 ,
14+ smoothness : f32 ,
1315 emission_color : vec3f ,
1416 emission_strength : f32 ,
1517};
1618
1719struct Sphere {
18- center : vec3f ,
20+ position : vec3f ,
1921 radius : f32 ,
2022 material : Material ,
2123};
@@ -27,6 +29,7 @@ struct Sphere {
2729const PI : f32 = 3 .141592 ;
2830
2931const MAX_BOUNCES : u32 = 5u ;
32+ const RAYS_PER_PIXEL : u32 = 5u ;
3033
3134struct Ray {
3235 origin : vec3f ,
@@ -71,46 +74,74 @@ fn random_direction(state: ptr<function, u32>) -> vec3f {
7174 let z = random_normal_distribution (state );
7275 return normalize (vec3f (x , y , z ));
7376}
77+ const GROUND_COLOR : vec3f = vec3f (0 .35 , 0 .3 , 0 .35 );
78+ const SKY_COLOR_HORIZON : vec3f = vec3f (1 .0 , 1 .0 , 1 .0 );
79+ const SKY_COLOR_ZENITH : vec3f = vec3f (0 .8 , 0 .8 , 0 .8 );
7480
75- fn random_hemisphere_dir (normal : vec3f , state : ptr <function , u32 >) -> vec3f {
76- let dir = random_direction (state );
77- if dot (dir , normal ) < 0 .0 {
78- return - dir ;
79- } else {
80- return dir ;
81- }
81+ const SUN_INTENSITY : f32 = 10 .0 ;
82+ const SUN_FOCUS : f32 = 500 .0 ;
83+ const SUN_COLOR : vec3f = vec3f (1 .0 , 0 .9 , 0 .6 );
84+
85+ fn get_environment_light (ray : Ray , light_dir : vec3f ) -> vec3f {
86+ let sky_gradient = mix (SKY_COLOR_HORIZON ,
87+ SKY_COLOR_ZENITH ,
88+ pow (smoothstep (0 .0 , 0 .4 , ray . direction . y ), 0 .35 ));
89+
90+ let sun = pow (max (dot (ray . direction , light_dir ), 0 .0 ), SUN_FOCUS ) * SUN_INTENSITY ;
91+
92+ let ground_to_sky = smoothstep (- 0 .01 , 0 .0 , ray . direction . y );
93+ let sun_mask = ground_to_sky >= 1 .0 ;
94+
95+ return mix (GROUND_COLOR , sky_gradient , ground_to_sky ) + sun * SUN_COLOR * f32 (u32 (sun_mask ));
96+ }
97+
98+ fn smoothstep (edge0 : f32 , edge1 : f32 , x : f32 ) -> f32 {
99+ let t = clamp ((x - edge0 ) / (edge1 - edge0 ), 0 .0 , 1 .0 );
100+ return t * t * (3 .0 - t * 2 .0 );
82101}
83102
84103fn trace (ray : ptr <function , Ray >, state : ptr <function , u32 >) -> vec3f {
104+ var total_light = vec3f (0 .0 );
105+ for (var i = 0u ; i < RAYS_PER_PIXEL ; i = i + 1u ) {
106+ var r = *ray ;
107+ total_light = total_light + trace_single (& r , state );
108+ }
109+
110+ return total_light / f32 (RAYS_PER_PIXEL );
111+ }
112+
113+ fn trace_single (ray : ptr <function , Ray >, state : ptr <function , u32 >) -> vec3f {
85114 var light = vec3f (0 .0 , 0 .0 , 0 .0 );
86115 var color = vec3f (1 .0 , 1 .0 , 1 .0 );
87116
88117 for (var bounce : u32 = 0u ; bounce < MAX_BOUNCES ; bounce = bounce + 1u ) {
89118 let hit = calculate_collision (*ray );
90119 if hit . hit {
91120 (*ray ). origin = hit . position + hit . normal * 0 .001 ;
92- (*ray ). direction = random_hemisphere_dir (hit . normal , state );
121+ let diffuse = normalize (hit . normal + random_direction (state ));
122+ let specular = reflect ((*ray ). direction , hit . normal );
123+
124+ (*ray ). direction = mix (diffuse , specular , hit . material . smoothness );
93125
94126 let emitted = hit . material . emission_color * hit . material . emission_strength ;
95- light = light + color * emitted ;
96127 color = color * hit . material . diffuse_color ;
128+ light = light + color * emitted ;
97129 } else {
130+ light = light + get_environment_light (*ray , params . light_dir ) * color ;
98131 break ;
99132 }
100133 }
101134
102135 return light ;
103136}
104137
138+ fn reflect (I : vec3f , N : vec3f ) -> vec3f {
139+ return I - 2 .0 * dot (N , I ) * N ;
140+ }
141+
105142fn calculate_collision (ray : Ray ) -> RayHit {
106- var closest_hit = RayHit (
107- 0 .0 ,
108- vec3f (0 .0 ),
109- vec3f (0 .0 ),
110- Material (vec3f (0 .0 ), 0 .0 , vec3f (0 .0 ), 0 .0 ),
111- false
112- );
113- for (var i : u32 = 0u ; i < params . _pad2 ; i = i + 1u ) {
143+ var closest_hit : RayHit ;
144+ for (var i : u32 = 0u ; i < params . sphere_count ; i = i + 1u ) {
114145 let hit = sphere_intersect (ray , spheres [i ]);
115146 if hit . hit && (! closest_hit . hit || hit . distance < closest_hit . distance ) {
116147 closest_hit = hit ;
@@ -120,14 +151,8 @@ fn calculate_collision(ray: Ray) -> RayHit {
120151}
121152
122153fn sphere_intersect (ray : Ray , sphere : Sphere ) -> RayHit {
123- var hit = RayHit (
124- 0 .0 ,
125- vec3f (0 .0 ),
126- vec3f (0 .0 ),
127- Material (vec3f (0 .0 ), 0 .0 , vec3f (0 .0 ), 0 .0 ),
128- false
129- );
130- let oc = ray . origin - sphere . center ;
154+ var hit : RayHit ;
155+ let oc = ray . origin - sphere . position ;
131156 let a = dot (ray . direction , ray . direction );
132157 let b = 2 .0 * dot (oc , ray . direction );
133158 let c = dot (oc , oc ) - sphere . radius * sphere . radius ;
@@ -140,7 +165,7 @@ fn sphere_intersect(ray: Ray, sphere: Sphere) -> RayHit {
140165 hit . distance = t ;
141166 hit . material = sphere . material ;
142167 hit . position = ray . origin + t * ray . direction ;
143- hit . normal = normalize (hit . position - sphere . center );
168+ hit . normal = normalize (hit . position - sphere . position );
144169 hit . hit = true ;
145170 return hit ;
146171 } else {
0 commit comments