378 lines
12 KiB
GLSL
378 lines
12 KiB
GLSL
#define MAX_POLYGON_VERTEX_COUNT 8
|
|
#define MIN_POLYGON_VERTEX_COUNT_BEFORE_CLIPPING 3
|
|
#include "peters2021-sampling/polygon_clipping.glsl"
|
|
#include "peters2021-sampling/polygon_sampling.glsl"
|
|
|
|
#include "debug.glsl"
|
|
#include "noise.glsl"
|
|
#include "utils.glsl"
|
|
|
|
#define DO_ALL_IN_CLUSTER 1
|
|
|
|
#ifndef RAY_BOUNCE
|
|
#define PROJECTED
|
|
//#define SOLID
|
|
//#define SIMPLE_SOLID
|
|
#else
|
|
//#define PROJECTED
|
|
//#define SOLID
|
|
//#define SIMPLE_SOLID
|
|
#endif
|
|
|
|
struct SampleContext {
|
|
mat4x3 world_to_shading;
|
|
};
|
|
|
|
SampleContext buildSampleContext(vec3 position, vec3 normal, vec3 view_dir) {
|
|
SampleContext ctx;
|
|
const float normal_dot_outgoing = dot(normal, -view_dir);
|
|
const vec3 x_axis = normalize(fma(vec3(-normal_dot_outgoing), normal, -view_dir));
|
|
const vec3 y_axis = cross(normal, x_axis);
|
|
const mat3 rotation = transpose(mat3(x_axis, y_axis, normal));
|
|
ctx.world_to_shading = mat4x3(rotation[0], rotation[1], rotation[2], -rotation * position);
|
|
return ctx;
|
|
}
|
|
|
|
vec4 getPolygonLightSampleSimple(vec3 P, vec3 view_dir, const PolygonLight poly) {
|
|
const uint vertices_offset = poly.vertices_count_offset & 0xffffu;
|
|
uint vertices_count = poly.vertices_count_offset >> 16;
|
|
|
|
vec3 v[3];
|
|
vertices_count = 3; // FIXME
|
|
|
|
for (uint i = 0; i < vertices_count; ++i) {
|
|
v[i] = lights.m.polygon_vertices[vertices_offset + i].xyz;
|
|
}
|
|
|
|
vec2 rnd = vec2(sqrt(rand01()), rand01());
|
|
rnd.y *= rnd.x;
|
|
rnd.x = 1.f - rnd.x;
|
|
|
|
const vec3 light_dir = baryMix(v[0], v[1], v[2], rnd) - P;
|
|
const vec3 light_dir_n = normalize(light_dir);
|
|
const float contrib = - poly.area * dot(light_dir_n, poly.plane.xyz ) / dot(light_dir, light_dir);
|
|
|
|
#ifdef DEBUG_VALIDATE_EXTRA
|
|
if (IS_INVALID(contrib)) {
|
|
debugPrintfEXT("getPolygonLightSampleSimple: poly.area=%f light_dir=(%f,%f,%f) INVALID contrib=%f",
|
|
poly.area,
|
|
PRIVEC3(light_dir),
|
|
contrib);
|
|
}
|
|
#endif
|
|
|
|
return vec4(light_dir_n, contrib);
|
|
}
|
|
|
|
vec4 getPolygonLightSampleSimpleSolid(vec3 P, vec3 view_dir, const PolygonLight poly) {
|
|
const uint vertices_offset = poly.vertices_count_offset & 0xffffu;
|
|
uint vertices_count = poly.vertices_count_offset >> 16;
|
|
|
|
uint selected = 0;
|
|
float total_contrib = 0.;
|
|
float eps1 = rand01();
|
|
vec3 v[3];
|
|
v[0] = normalize(lights.m.polygon_vertices[vertices_offset + 0].xyz - P);
|
|
v[1] = normalize(lights.m.polygon_vertices[vertices_offset + 1].xyz - P);
|
|
const float householder_sign = (v[0].x > 0.0f) ? -1.0f : 1.0f;
|
|
const vec2 householder_yz = v[0].yz * (1.0f / (abs(v[0].x) + 1.0f));
|
|
for (uint i = 2; i < vertices_count; ++i) {
|
|
v[2] = normalize(lights.m.polygon_vertices[vertices_offset + i].xyz - P);
|
|
|
|
// effectively mindlessly copypasted from polygon_sampling.glsl, Peters 2021
|
|
// https://github.com/MomentsInGraphics/vulkan_renderer/blob/main/src/shaders/polygon_sampling.glsl
|
|
const float dot_0_1 = dot(v[0], v[1]);
|
|
const float dot_0_2 = dot(v[1], v[2]);
|
|
const float dot_1_2 = dot(v[0], v[2]);
|
|
const float dot_householder_0 = fma(-householder_sign, v[1].x, dot_0_1);
|
|
const float dot_householder_2 = fma(-householder_sign, v[2].x, dot_1_2);
|
|
const mat2 bottom_right_minor = mat2(
|
|
fma(vec2(-dot_householder_0), householder_yz, v[1].yz),
|
|
fma(vec2(-dot_householder_2), householder_yz, v[2].yz));
|
|
const float simplex_volume = abs(determinant(bottom_right_minor));
|
|
const float dot_0_2_plus_1_2 = dot_0_2 + dot_1_2;
|
|
const float one_plus_dot_0_1 = 1.0f + dot_0_1;
|
|
const float tangent = simplex_volume / (one_plus_dot_0_1 + dot_0_2_plus_1_2);
|
|
const float contrib = 2.f * (atan(tangent) + (tangent < 0.f ? M_PI : 0.));
|
|
|
|
if (contrib < 1e-6)
|
|
continue;
|
|
|
|
const float tau = total_contrib / (total_contrib + contrib);
|
|
total_contrib += contrib;
|
|
|
|
if (eps1 < tau) {
|
|
eps1 /= tau;
|
|
} else {
|
|
selected = i;
|
|
eps1 = (eps1 - tau) / (1. - tau);
|
|
}
|
|
|
|
// selected = 2;
|
|
// break;
|
|
v[1] = v[2];
|
|
}
|
|
|
|
if (selected == 0)
|
|
return vec4(0.);
|
|
|
|
vec2 rnd = vec2(sqrt(rand01()), rand01());
|
|
rnd.y *= rnd.x;
|
|
rnd.x = 1.f - rnd.x;
|
|
|
|
const vec3 light_dir = baryMix(
|
|
lights.m.polygon_vertices[vertices_offset + 0].xyz,
|
|
lights.m.polygon_vertices[vertices_offset + selected - 1].xyz,
|
|
lights.m.polygon_vertices[vertices_offset + selected].xyz,
|
|
rnd) - P;
|
|
const vec3 light_dir_n = normalize(light_dir);
|
|
return vec4(light_dir_n, total_contrib);
|
|
}
|
|
|
|
vec4 getPolygonLightSampleProjected(vec3 view_dir, SampleContext ctx, const PolygonLight poly) {
|
|
vec3 clipped[MAX_POLYGON_VERTEX_COUNT];
|
|
|
|
const uint vertices_offset = poly.vertices_count_offset & 0xffffu;
|
|
uint vertices_count = poly.vertices_count_offset >> 16;
|
|
|
|
for (uint i = 0; i < vertices_count; ++i) {
|
|
clipped[i] = ctx.world_to_shading * vec4(lights.m.polygon_vertices[vertices_offset + i].xyz, 1.);
|
|
}
|
|
|
|
vertices_count = clip_polygon(vertices_count, clipped);
|
|
if (vertices_count == 0)
|
|
return vec4(0.f);
|
|
|
|
const projected_solid_angle_polygon_t sap = prepare_projected_solid_angle_polygon_sampling(vertices_count, clipped);
|
|
|
|
#ifdef DEBUG_VALIDATE_EXTRA
|
|
if (IS_INVALID(sap.projected_solid_angle)) {
|
|
debugPrintfEXT("getPolygonLightSampleProjected: vertices_count=%d v0=(%f,%f,%f) v1=(%f,%f,%f) v2=(%f,%f,%f) INVALID sap.projected_solid_angle = %f",
|
|
vertices_count, PRIVEC3(clipped[0]), PRIVEC3(clipped[1]), PRIVEC3(clipped[2]),
|
|
sap.projected_solid_angle);
|
|
}
|
|
#endif
|
|
|
|
const float contrib = sap.projected_solid_angle;
|
|
if (contrib <= 0.f)
|
|
return vec4(0.f);
|
|
|
|
vec2 rnd = vec2(rand01(), rand01());
|
|
const vec3 light_dir = (transpose(ctx.world_to_shading) * sample_projected_solid_angle_polygon(sap, rnd)).xyz;
|
|
|
|
return vec4(light_dir, contrib);
|
|
}
|
|
|
|
vec4 getPolygonLightSampleSolid(vec3 P, vec3 view_dir, SampleContext ctx, const PolygonLight poly) {
|
|
vec3 clipped[MAX_POLYGON_VERTEX_COUNT];
|
|
|
|
const uint vertices_offset = poly.vertices_count_offset & 0xffffu;
|
|
uint vertices_count = poly.vertices_count_offset >> 16;
|
|
|
|
for (uint i = 0; i < vertices_count; ++i) {
|
|
clipped[i] = lights.m.polygon_vertices[vertices_offset + i].xyz;
|
|
}
|
|
|
|
#define DONT_CLIP
|
|
#ifndef DONT_CLIP
|
|
vertices_count = clip_polygon(vertices_count, clipped);
|
|
if (vertices_count == 0)
|
|
return vec4(0.f);
|
|
#endif
|
|
|
|
const solid_angle_polygon_t sap = prepare_solid_angle_polygon_sampling(vertices_count, clipped, P);
|
|
const float contrib = sap.solid_angle;
|
|
if (contrib <= 0.f)
|
|
return vec4(0.f);
|
|
|
|
vec2 rnd = vec2(rand01(), rand01());
|
|
const vec3 light_dir = sample_solid_angle_polygon(sap, rnd).xyz;
|
|
|
|
return vec4(light_dir, contrib);
|
|
}
|
|
|
|
void sampleSinglePolygonLight(in vec3 P, in vec3 N, in vec3 view_dir, in SampleContext ctx, in MaterialProperties material, in PolygonLight poly, inout vec3 diffuse, inout vec3 specular) {
|
|
// TODO cull by poly plane
|
|
|
|
#ifdef PROJECTED
|
|
const vec4 light_sample_dir = getPolygonLightSampleProjected(view_dir, ctx, poly);
|
|
#else
|
|
const vec4 light_sample_dir = getPolygonLightSampleSolid(P, view_dir, ctx, poly);
|
|
#endif
|
|
if (light_sample_dir.w <= 0.)
|
|
return;
|
|
|
|
const float dist = - dot(vec4(P, 1.f), poly.plane) / dot(light_sample_dir.xyz, poly.plane.xyz);
|
|
|
|
if (shadowed(P, light_sample_dir.xyz, dist))
|
|
return;
|
|
|
|
vec3 poly_diffuse = vec3(0.), poly_specular = vec3(0.);
|
|
evalSplitBRDF(N, light_sample_dir.xyz, view_dir, material, poly_diffuse, poly_specular);
|
|
const float estimate = light_sample_dir.w;
|
|
const vec3 emissive = poly.emissive * estimate;
|
|
diffuse += emissive * poly_diffuse;
|
|
specular += emissive * poly_specular;
|
|
}
|
|
|
|
#if 0
|
|
// Sample random one
|
|
void sampleEmissiveSurfaces(vec3 P, vec3 N, vec3 view_dir, MaterialProperties material, uint cluster_index, inout vec3 diffuse, inout vec3 specular) {
|
|
const uint num_polygons = uint(light_grid.clusters_[cluster_index].num_polygons);
|
|
|
|
if (num_polygons == 0)
|
|
return;
|
|
|
|
const uint selected = uint(light_grid.clusters_[cluster_index].polygons[rand_range(num_polygons)]);
|
|
|
|
const PolygonLight poly = lights.m.polygons[selected];
|
|
const SampleContext ctx = buildSampleContext(P, N, view_dir);
|
|
sampleSinglePolygonLight(P, N, view_dir, ctx, material, poly, diffuse, specular);
|
|
|
|
const float sampling_factor = float(num_polygons);
|
|
diffuse *= sampling_factor;
|
|
specular *= sampling_factor;
|
|
}
|
|
|
|
#elif 1
|
|
void sampleEmissiveSurfaces(vec3 P, vec3 N, vec3 view_dir, MaterialProperties material, uint cluster_index, inout vec3 diffuse, inout vec3 specular) {
|
|
#if DO_ALL_IN_CLUSTER
|
|
const SampleContext ctx = buildSampleContext(P, N, view_dir);
|
|
|
|
#define USE_CLUSTERS
|
|
#ifdef USE_CLUSTERS
|
|
const uint num_polygons = uint(light_grid.clusters_[cluster_index].num_polygons);
|
|
for (uint i = 0; i < num_polygons; ++i) {
|
|
const uint index = uint(light_grid.clusters_[cluster_index].polygons[i]);
|
|
|
|
//if (index != 5) // < 6 || index >= 8)
|
|
// continue;
|
|
#else
|
|
for (uint index = 0; index < lights.m.num_polygons; ++index) {
|
|
#endif
|
|
|
|
const PolygonLight poly = lights.m.polygons[index];
|
|
|
|
const float plane_dist = dot(poly.plane, vec4(P, 1.f));
|
|
|
|
if (plane_dist < 0.)
|
|
continue;
|
|
|
|
#ifdef PROJECTED
|
|
const vec4 light_sample_dir = getPolygonLightSampleProjected(view_dir, ctx, poly);
|
|
#elif defined(SOLID)
|
|
const vec4 light_sample_dir = getPolygonLightSampleSolid(P, view_dir, ctx, poly);
|
|
#elif defined(SIMPLE_SOLID)
|
|
const vec4 light_sample_dir = getPolygonLightSampleSimpleSolid(P, view_dir, poly);
|
|
#else
|
|
const vec4 light_sample_dir = getPolygonLightSampleSimple(P, view_dir, poly);
|
|
#endif
|
|
|
|
if (light_sample_dir.w <= 0.)
|
|
continue;
|
|
|
|
const float dist = - plane_dist / dot(light_sample_dir.xyz, poly.plane.xyz);
|
|
const vec3 emissive = poly.emissive;
|
|
|
|
if (!shadowed(P, light_sample_dir.xyz, dist)) {
|
|
//const float estimate = total_contrib;
|
|
const float estimate = light_sample_dir.w;
|
|
vec3 poly_diffuse = vec3(0.), poly_specular = vec3(0.);
|
|
evalSplitBRDF(N, light_sample_dir.xyz, view_dir, material, poly_diffuse, poly_specular);
|
|
diffuse += emissive * estimate * poly_diffuse;
|
|
specular += emissive * estimate * poly_specular;
|
|
|
|
#ifdef DEBUG_VALIDATE_EXTRA
|
|
if (IS_INVALIDV(specular) || any(lessThan(specular,vec3(0.)))) {
|
|
debugPrintfEXT("%d INVALID specular=(%f,%f,%f) light=%d emissive=(%f,%f,%f) estimate=%f poly_specular=(%f,%f,%f)",
|
|
__LINE__, PRIVEC3(specular), index, PRIVEC3(emissive), estimate, PRIVEC3(poly_specular));
|
|
specular = vec3(0.);
|
|
}
|
|
if (IS_INVALIDV(diffuse) || any(lessThan(diffuse,vec3(0.)))) {
|
|
debugPrintfEXT("%d INVALID diffuse=(%f,%f,%f) light=%d emissive=(%f,%f,%f) estimate=%f poly_diffuse=(%f,%f,%f)",
|
|
__LINE__, PRIVEC3(diffuse), index, PRIVEC3(emissive), estimate, PRIVEC3(poly_diffuse));
|
|
diffuse = vec3(0.);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#else // DO_ALL_IN_CLUSTERS
|
|
|
|
#ifdef USE_CLUSTERS
|
|
// TODO move this to pickPolygonLight function
|
|
const uint num_polygons = uint(light_grid.clusters_[cluster_index].num_polygons);
|
|
#else
|
|
const uint num_polygons = lights.m.num_polygons;
|
|
#endif
|
|
|
|
uint selected = 0;
|
|
float total_contrib = 0.;
|
|
float eps1 = rand01();
|
|
for (uint i = 0; i < num_polygons; ++i) {
|
|
#ifdef USE_CLUSTERS
|
|
const uint index = uint(light_grid.clusters_[cluster_index].polygons[i]);
|
|
#else
|
|
const uint index = i;
|
|
#endif
|
|
|
|
const PolygonLight poly = lights.m.polygons[index];
|
|
|
|
const vec3 dir = poly.center - P;
|
|
const vec3 light_dir = normalize(dir);
|
|
float contrib_estimate = poly.area * dot(-light_dir, poly.plane.xyz) / (1e-3 + dot(dir, dir));
|
|
|
|
if (contrib_estimate < 1e-6)
|
|
continue;
|
|
|
|
contrib_estimate = 1.f;
|
|
const float tau = total_contrib / (total_contrib + contrib_estimate);
|
|
total_contrib += contrib_estimate;
|
|
|
|
if (eps1 < tau) {
|
|
eps1 /= tau;
|
|
} else {
|
|
selected = index + 1;
|
|
eps1 = (eps1 - tau) / (1. - tau);
|
|
}
|
|
}
|
|
|
|
if (selected == 0) {
|
|
//diffuse = vec3(1., 0., 0.);
|
|
return;
|
|
}
|
|
|
|
#if 0
|
|
const PolygonLight poly = lights.m.polygons[selected - 1];
|
|
const vec3 emissive = poly.emissive;
|
|
vec3 poly_diffuse = vec3(0.), poly_specular = vec3(0.);
|
|
evalSplitBRDF(N, normalize(poly.center-P), view_dir, material, poly_diffuse, poly_specular);
|
|
diffuse += emissive * total_contrib;
|
|
specular += emissive * total_contrib;
|
|
#else
|
|
const SampleContext ctx = buildSampleContext(P, N, view_dir);
|
|
const PolygonLight poly = lights.m.polygons[selected - 1];
|
|
#ifdef PROJECTED
|
|
const vec4 light_sample_dir = getPolygonLightSampleProjected(view_dir, ctx, poly);
|
|
#else
|
|
const vec4 light_sample_dir = getPolygonLightSampleSolid(P, view_dir, ctx, poly);
|
|
#endif
|
|
if (light_sample_dir.w <= 0.)
|
|
return;
|
|
|
|
const float dist = - dot(vec4(P, 1.f), poly.plane) / dot(light_sample_dir.xyz, poly.plane.xyz);
|
|
const vec3 emissive = poly.emissive;
|
|
|
|
//if (true) {//!shadowed(P, light_sample_dir.xyz, dist)) {
|
|
if (!shadowed(P, light_sample_dir.xyz, dist)) {
|
|
//const float estimate = total_contrib;
|
|
const float estimate = light_sample_dir.w;
|
|
vec3 poly_diffuse = vec3(0.), poly_specular = vec3(0.);
|
|
evalSplitBRDF(N, light_sample_dir.xyz, view_dir, material, poly_diffuse, poly_specular);
|
|
diffuse += emissive * estimate;
|
|
specular += emissive * estimate;
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
#endif
|