Quake II RTX doxygen  1.0 dev
light_lists.h
Go to the documentation of this file.
1 /*
2 Copyright (C) 2018 Tobias Zirr
3 Copyright (C) 2019, NVIDIA CORPORATION. All rights reserved.
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 
20 #ifndef _LIGHT_LISTS_
21 #define _LIGHT_LISTS_
22 
23 #define MAX_BRUTEFORCE_SAMPLING 8
24 
25 float
26 projected_tri_area_simple(mat3 positions, vec3 p)
27 {
28  positions[0] = positions[0] - p;
29  positions[1] = positions[1] - p;
30  positions[2] = positions[2] - p;
31 
32  positions[0] = normalize(positions[0]);
33  positions[1] = normalize(positions[1]);
34  positions[2] = normalize(positions[2]);
35 
36  vec3 a = cross(positions[1] - positions[0], positions[2] - positions[0]);
37  return length(a);
38 }
39 
40 float
41 projected_tri_area(mat3 positions, vec3 p, vec3 n, vec3 V, float phong_exp, float phong_scale, float phong_weight)
42 {
43  positions[0] = positions[0] - p;
44  positions[1] = positions[1] - p;
45  positions[2] = positions[2] - p;
46 
47  vec3 g = cross(positions[1] - positions[0], positions[2] - positions[0]);
48  if ( dot(n, positions[0]) <= 0 && dot(n, positions[1]) <= 0 && dot(n, positions[2]) <= 0 )
49  return 0;
50  if ( dot(g, positions[0]) >= 0 && dot(g, positions[1]) >= 0 && dot(g, positions[2]) >= 0 )
51  return 0;
52 
53  vec3 L = normalize(positions * vec3(1.0 / 3.0));
54  float specular = phong(n, L, V, phong_exp) * phong_scale;
55  float brdf = mix(1.0, specular, phong_weight);
56 
57  positions[0] = normalize(positions[0]);
58  positions[1] = normalize(positions[1]);
59  positions[2] = normalize(positions[2]);
60 
61  vec3 a = cross(positions[1] - positions[0], positions[2] - positions[0]);
62  float pa = max(length(a) - 1e-5, 0.);
63  return pa * brdf;
64 }
65 
66 vec3
67 sample_projected_triangle(vec3 p, mat3 positions, vec2 rnd, out vec3 light_normal, out float projected_area)
68 {
69  light_normal = cross(positions[1] - positions[0], positions[2] - positions[0]);
70  light_normal = normalize(light_normal);
71 
72  positions[0] = positions[0] - p;
73  positions[1] = positions[1] - p;
74  positions[2] = positions[2] - p;
75 
76  float o = dot(light_normal, positions[0]);
77 
78  positions[0] = normalize(positions[0]);
79  positions[1] = normalize(positions[1]);
80  positions[2] = normalize(positions[2]);
81 
82  vec3 projected_normal = cross(positions[1] - positions[0], positions[2] - positions[0]);
83 
84  vec3 direction = positions * sample_triangle(rnd);
85  float dl = length(direction);
86 
87  // n (p + d * t - p[i]) == 0
88  // -n (p - pi) / n d == o / n d == t
89  vec3 lo = direction * (o / dot(light_normal, direction));
90 
91  projected_area = length(projected_normal) * 0.5;
92 
93  return p + lo;
94 }
95 
96 uint get_light_stats_addr(uint cluster, uint light, uint side)
97 {
98  uint addr = cluster;
99  addr = addr * global_ubo.num_static_lights + light;
100  addr = addr * 6 + side;
101  addr = addr * 2;
102  return addr;
103 }
104 
105 void
107  uint list_idx,
108  vec3 p,
109  vec3 n,
110  vec3 gn,
111  vec3 V,
112  float phong_exp,
113  float phong_scale,
114  float phong_weight,
115  bool is_gradient,
116  out vec3 position_light,
117  out vec3 light_color,
118  out int light_index,
119  out float projected_area,
120  vec3 rng)
121 {
122  position_light = vec3(0);
123  light_index = -1;
124  light_color = vec3(0);
125  projected_area = 0;
126 
127  if(list_idx == ~0u)
128  return;
129 
130  uint list_start = get_light_list_offsets(list_idx);
131  uint list_end = get_light_list_offsets(list_idx + 1);
132 
133  float partitions = ceil(float(list_end - list_start) / float(MAX_BRUTEFORCE_SAMPLING));
134  rng.x *= partitions;
135  float fpart = min(floor(rng.x), partitions-1);
136  rng.x -= fpart;
137  list_start += int(fpart);
138  int stride = int(partitions);
139 
140  float mass = 0.;
141 
142  float light_masses[MAX_BRUTEFORCE_SAMPLING];
143 
144  #pragma unroll
145  for(uint i = 0, n_idx = list_start; i < MAX_BRUTEFORCE_SAMPLING; i++, n_idx += stride) {
146  if (n_idx >= list_end)
147  break;
148 
149  uint current_idx = get_light_list_lights(n_idx);
150 
151  if(current_idx == ~0u)
152  {
153  light_masses[i] = 0;
154  continue;
155  }
156 
157  LightPolygon light = get_light_polygon(current_idx);
158 
159  float m = projected_tri_area(light.positions, p, n, V, phong_exp, phong_scale, phong_weight);
160 
161  float light_lum = luminance(light.color);
162 
163  // Apply light style scaling.
164  // For gradient pixels, use the style from the previous frame here
165  // in order to keep the CDF consistent and make sure that the same light is picked,
166  // regardless of animations. This makes the image more stable around blinking lights,
167  // especially in shadowed areas.
168  light_lum *= is_gradient ? light.prev_style_scale : light.light_style_scale;
169 
170  if(light_lum < 0 && global_ubo.environment_type == ENVIRONMENT_DYNAMIC)
171  {
172  // Set limits on sky luminance to avoid oversampling the sky in shadowed areas, or undersampling at dusk and dawn.
173  // Note: the log -> linear conversion of the cvars happens on the CPU, in main.c
174  m *= clamp(sun_color_ubo.sky_luminance, global_ubo.pt_min_log_sky_luminance, global_ubo.pt_max_log_sky_luminance);
175  }
176  else
177  m *= abs(light_lum); // abs because sky lights have negative color
178 
179  // Apply CDF adjustment based on light shadowing statistics from one of the previous frames.
180  // See comments in function `get_direct_illumination` in `path_tracer_rgen.h`
181  if(global_ubo.pt_light_stats != 0
182  && m > 0
183  && current_idx < global_ubo.num_static_lights)
184  {
185  uint buffer_idx = global_ubo.current_frame_idx;
186  // Regular pixels get shadowing stats from the previous frame;
187  // Gradient pixels get the stats from two frames ago because they need to match
188  // the light sampling from the previous frame.
189  buffer_idx += is_gradient ? (NUM_LIGHT_STATS_BUFFERS - 2) : (NUM_LIGHT_STATS_BUFFERS - 1);
190  buffer_idx = buffer_idx % NUM_LIGHT_STATS_BUFFERS;
191 
192  uint addr = get_light_stats_addr(list_idx, current_idx, get_primary_direction(n));
193 
194  uint num_hits = light_stats_bufers[buffer_idx].stats[addr];
195  uint num_misses = light_stats_bufers[buffer_idx].stats[addr + 1];
196  uint num_total = num_hits + num_misses;
197 
198  if(num_total > 0)
199  {
200  // Adjust the mass, but set a lower limit on the factor to avoid
201  // extreme changes in the sampling.
202  m *= max(float(num_hits) / float(num_total), 0.1);
203  }
204  }
205 
206  mass += m;
207  light_masses[i] = m;
208  }
209 
210  if (mass <= 0)
211  return;
212 
213  rng.x *= mass;
214  int current_idx = -1;
215  mass *= partitions;
216  float pdf = 0;
217 
218  #pragma unroll
219  for(uint i = 0, n_idx = list_start; i < MAX_BRUTEFORCE_SAMPLING; i++, n_idx += stride) {
220  if (n_idx >= list_end)
221  break;
222  pdf = light_masses[i];
223  current_idx = int(n_idx);
224  rng.x -= pdf;
225 
226  if (rng.x <= 0)
227  break;
228  }
229 
230  if(rng.x > 0)
231  return;
232 
233  pdf /= mass;
234 
235  // assert: current_idx >= 0?
236  if (current_idx >= 0) {
237  current_idx = int(get_light_list_lights(current_idx));
238 
239  LightPolygon light = get_light_polygon(current_idx);
240 
241  vec3 light_normal;
242  position_light = sample_projected_triangle(p, light.positions, rng.yz, light_normal, projected_area);
243 
244  vec3 L = normalize(position_light - p);
245 
246  if(dot(L, gn) <= 0)
247  projected_area = 0;
248 
249  float LdotNL = max(0, -dot(light_normal, L));
250  float spotlight = sqrt(LdotNL);
251 
252  if(light.color.r >= 0)
253  {
254  light_color = light.color * (projected_area * spotlight * light.light_style_scale);
255  projected_area = 0; // disable MIS with indirect specular rays
256  }
257  else
258  light_color = env_map(L, true) * projected_area * global_ubo.pt_env_scale;
259 
260  light_index = current_idx;
261  }
262 
263  light_color /= pdf;
264 }
265 
266 void
268  vec3 p,
269  vec3 n,
270  vec3 gn,
271  float max_solid_angle,
272  out vec3 position_light,
273  out vec3 light_color,
274  vec3 rng)
275 {
276  position_light = vec3(0);
277  light_color = vec3(0);
278 
279  if(global_ubo.num_sphere_lights == 0)
280  return;
281 
282  float random_light = rng.x * global_ubo.num_sphere_lights;
283  uint light_idx = min(global_ubo.num_sphere_lights - 1, uint(random_light));
284 
285  vec4 light_center_radius = global_ubo.sphere_light_data[light_idx * 2];
286  float sphere_radius = light_center_radius.w;
287 
288  light_color = global_ubo.sphere_light_data[light_idx * 2 + 1].rgb;
289 
290  vec3 c = light_center_radius.xyz - p;
291  float dist = length(c);
292  float rdist = 1.0 / dist;
293  vec3 L = c * rdist;
294 
295  float irradiance = 2 * (1 - sqrt(max(0, 1 - square(sphere_radius * rdist))));
296  irradiance = min(irradiance, max_solid_angle);
297  irradiance *= float(global_ubo.num_sphere_lights); // 1 / pdf
298 
299  mat3 onb = construct_ONB_frisvad(L);
300  vec3 diskpt;
301  diskpt.xy = sample_disk(rng.yz);
302  diskpt.z = sqrt(max(0, 1 - diskpt.x * diskpt.x - diskpt.y * diskpt.y));
303 
304  position_light = light_center_radius.xyz + (onb[0] * diskpt.x + onb[2] * diskpt.y - L * diskpt.z) * sphere_radius;
305  light_color *= irradiance;
306 
307  if(dot(position_light - p, gn) <= 0)
308  light_color = vec3(0);
309 }
310 
311 #endif /*_LIGHT_LISTS_*/
312 
313 // vim: shiftwidth=4 noexpandtab tabstop=4 cindent
sample_polygonal_lights
void sample_polygonal_lights(uint list_idx, vec3 p, vec3 n, vec3 gn, vec3 V, float phong_exp, float phong_scale, float phong_weight, bool is_gradient, out vec3 position_light, out vec3 light_color, out int light_index, out float projected_area, vec3 rng)
Definition: light_lists.h:106
env_map
vec3 env_map(vec3 direction, bool remove_sun)
Definition: path_tracer_rgen.h:81
uint
uint32_t uint
Definition: global_ubo.h:233
sample_spherical_lights
void sample_spherical_lights(vec3 p, vec3 n, vec3 gn, float max_solid_angle, out vec3 position_light, out vec3 light_color, vec3 rng)
Definition: light_lists.h:267
projected_tri_area_simple
float projected_tri_area_simple(mat3 positions, vec3 p)
Definition: light_lists.h:26
sample_projected_triangle
vec3 sample_projected_triangle(vec3 p, mat3 positions, vec2 rnd, out vec3 light_normal, out float projected_area)
Definition: light_lists.h:67
projected_tri_area
float projected_tri_area(mat3 positions, vec3 p, vec3 n, vec3 V, float phong_exp, float phong_scale, float phong_weight)
Definition: light_lists.h:41
mix
static float mix(float a, float b, float s)
Definition: bloom.c:83
ENVIRONMENT_DYNAMIC
#define ENVIRONMENT_DYNAMIC
Definition: constants.h:100
m
static struct mdfour * m
Definition: mdfour.c:32
MAX_BRUTEFORCE_SAMPLING
#define MAX_BRUTEFORCE_SAMPLING
Definition: light_lists.h:23
NUM_LIGHT_STATS_BUFFERS
#define NUM_LIGHT_STATS_BUFFERS
Definition: constants.h:44
V
#define V(name)
Definition: g_save.c:48
c
statCounters_t c
Definition: main.c:30
sample_disk
static void sample_disk(float *u, float *v)
Definition: shadow_map.c:448
get_light_stats_addr
uint get_light_stats_addr(uint cluster, uint light, uint side)
Definition: light_lists.h:96
L
#define L(name)
Definition: g_save.c:47
int
CONST PIXELFORMATDESCRIPTOR int
Definition: wgl.c:26