vk: rt: rewrite direct-eval brdfs from scratch

Deprecate `brdf.h` usage, start writing our own BRDF functions.

Use glTF 2.0 BRDF mixing model as a simple starting point.
Mix-in base_color into specular early, as it is light-direction
dependent and cannot be easily separated.

Disable bounces for now, as we don't have yet good sampling story, need
to write "backwards" BRDFs that give us ray directions to sample, and
accommodate for that sampling bias in BRDF attenuation itself.

Also introduces some other weird artifacts on `test_material` map, investigation pending.

Related to black metals in #666
This commit is contained in:
Ivan Avdeev 2024-01-02 13:01:58 -05:00
parent 6ffaad02f2
commit 88b24ad3bd
9 changed files with 70 additions and 63 deletions

View File

@ -94,33 +94,13 @@ bool getHit(vec3 origin, vec3 direction, inout RayPayloadPrimary payload) {
}
const int INDIRECT_SCALE = 2;
void computeBounce(ivec2 pix, vec3 direction, out vec3 diffuse, out vec3 specular) {
diffuse = vec3(0.);
specular = vec3(0.);
const vec4 pos_t = imageLoad(position_t, pix);
if (pos_t.w <= 0.)
return;
const vec4 material_data = imageLoad(material_rmxx, pix);
const vec4 base_a = SRGBtoLINEAR(imageLoad(base_color_a, pix));
MaterialProperties material;
material.baseColor = vec3(1.f);
material.emissive = vec3(0.f);
material.metalness = material_data.g;
material.roughness = material_data.r;
vec3 geometry_normal, shading_normal;
readNormals(pix, geometry_normal, shading_normal);
const float ray_normal_fudge = .01;
vec3 throughput = vec3(1.);
// 1. Make a "random" material-based ray for diffuse lighting
vec3 bounce_direction = vec3(0.);
bool pickBounceDirection(MaterialProperties material, float alpha, out vec3 direction, inout vec3 throughput) {
#if 1
// TODO implement
return false;
#else
int brdf_type = DIFFUSE_TYPE;
// FIXME address translucency properly
const float alpha = (base_a.a);
if (1. > alpha && rand01() > alpha) {
brdf_type = SPECULAR_TYPE;
@ -144,12 +124,43 @@ void computeBounce(ivec2 pix, vec3 direction, out vec3 diffuse, out vec3 specula
const vec2 u = vec2(rand01(), rand01());
vec3 brdf_weight = vec3(0.);
if (!evalIndirectCombinedBRDF(u, shading_normal, geometry_normal, -direction, material, brdf_type, bounce_direction, brdf_weight))
return;
return false;
throughput *= brdf_weight;
}
const float throughput_threshold = 1e-3;
if (dot(throughput, throughput) < throughput_threshold)
return false;
return true;
#endif
}
void computeBounce(ivec2 pix, vec3 direction, out vec3 diffuse, out vec3 specular) {
diffuse = vec3(0.);
specular = vec3(0.);
#if 0
const vec4 pos_t = imageLoad(position_t, pix);
if (pos_t.w <= 0.)
return;
const vec4 material_data = imageLoad(material_rmxx, pix);
const vec4 base_a = SRGBtoLINEAR(imageLoad(base_color_a, pix));
vec3 geometry_normal, shading_normal;
readNormals(pix, geometry_normal, shading_normal);
const float ray_normal_fudge = .01;
vec3 throughput = vec3(1.);
vec3 bounce_direction = vec3(0.);
MaterialProperties material;
material.base_color = base_a.rgb;
material.metalness = material_data.g;
material.roughness = material_data.r;
if (!pickBounceDirection(material, base_a.a, bounce_direction, throughput))
return;
// 2. Rake yuri it, get hit
@ -169,8 +180,7 @@ void computeBounce(ivec2 pix, vec3 direction, out vec3 diffuse, out vec3 specula
vec3 ldiffuse = vec3(0.);
vec3 lspecular = vec3(0.);
MaterialProperties hit_material;
hit_material.baseColor = vec3(1.);
hit_material.emissive = vec3(0.f);
hit_material.base_color = payload.base_color_a.rgb;
hit_material.metalness = payload.material_rmxx.g;
hit_material.roughness = payload.material_rmxx.r;
computeLighting(hit_pos, hit_shading_normal, -bounce_direction, hit_material, ldiffuse, lspecular);
@ -189,6 +199,7 @@ void computeBounce(ivec2 pix, vec3 direction, out vec3 diffuse, out vec3 specula
diffuse += lighting;
else
specular += lighting + payload.emissive.rgb;
#endif
}

View File

@ -10,6 +10,9 @@ This work is published from: Germany. */
// - remove c++ compat
// - port to glsl
#ifndef BRDF_H_INCLUDED
#define BRDF_H_INCLUDED
float rsqrt(float x) { return inversesqrt(x); }
float saturate(float x) { return clamp(x, 0.0f, 1.0f); }
vec3 saturate(vec3 x) { return clamp(x, vec3(0.0f), vec3(1.0f)); }
@ -179,7 +182,7 @@ vec3 saturate(vec3 x) { return clamp(x, vec3(0.0f), vec3(1.0f)); }
struct MaterialProperties
{
vec3 baseColor;
vec3 base_color;
float metalness;
vec3 emissive;
@ -249,13 +252,13 @@ float luminance(vec3 rgb)
return dot(rgb, vec3(0.2126f, 0.7152f, 0.0722f));
}
vec3 baseColorToSpecularF0(vec3 baseColor, float metalness) {
return mix(vec3(MIN_DIELECTRICS_F0, MIN_DIELECTRICS_F0, MIN_DIELECTRICS_F0), baseColor, metalness);
vec3 baseColorToSpecularF0(vec3 base_color, float metalness) {
return mix(vec3(MIN_DIELECTRICS_F0, MIN_DIELECTRICS_F0, MIN_DIELECTRICS_F0), base_color, metalness);
}
vec3 baseColorToDiffuseReflectance(vec3 baseColor, float metalness)
vec3 baseColorToDiffuseReflectance(vec3 base_color, float metalness)
{
return baseColor * (1.0f - metalness);
return base_color * (1.0f - metalness);
}
float none(const BrdfData data) {
@ -867,8 +870,8 @@ BrdfData prepareBRDFData(vec3 N, vec3 L, vec3 V, MaterialProperties material) {
data.VdotH = saturate(dot(V, data.H));
// Unpack material properties
data.specularF0 = baseColorToSpecularF0(material.baseColor, material.metalness);
data.diffuseReflectance = baseColorToDiffuseReflectance(material.baseColor, material.metalness);
data.specularF0 = baseColorToSpecularF0(material.base_color, material.metalness);
data.diffuseReflectance = baseColorToDiffuseReflectance(material.base_color, material.metalness);
// Unpack 'perceptively linear' -> 'linear' -> 'squared' roughness
data.roughness = material.roughness;
@ -957,8 +960,8 @@ float getBrdfProbability(MaterialProperties material, vec3 V, vec3 shadingNormal
// Evaluate Fresnel term using the shading normal
// Note: we use the shading normal instead of the microfacet normal (half-vector) for Fresnel term here. That's suboptimal for rough surfaces at grazing angles, but half-vector is yet unknown at this point
float specularF0 = luminance(baseColorToSpecularF0(material.baseColor, material.metalness));
float diffuseReflectance = luminance(baseColorToDiffuseReflectance(material.baseColor, material.metalness));
float specularF0 = luminance(baseColorToSpecularF0(material.base_color, material.metalness));
float diffuseReflectance = luminance(baseColorToDiffuseReflectance(material.base_color, material.metalness));
float Fresnel = saturate(luminance(evalFresnel(vec3(specularF0), shadowedF90(vec3(specularF0)), max(0.0f, dot(V, shadingNormal)))));
// Approximate relative contribution of BRDFs using the Fresnel term
@ -971,3 +974,4 @@ float getBrdfProbability(MaterialProperties material, vec3 V, vec3 shadingNormal
// Clamp probability to avoid undersampling of less prominent BRDF
return clamp(p, 0.1f, 0.9f);
}
#endif // ifndef BRDF_H_INCLUDED

View File

@ -302,8 +302,16 @@ void main() {
if (ubo.ubo.debug_display_only != DEBUG_DISPLAY_LIGHTING) {
const vec3 base_color = SRGBtoLINEAR(imageLoad(base_color_a, pix).rgb);
colour = diffuse * base_color;
colour += specular * mix(vec3(1.), base_color, imageLoad(material_rmxx, pix).g);
const float metalness = imageLoad(material_rmxx, pix).g;
// Late base_color compositing with diffuse lighting
// see brdf.glsl
const vec3 diffuse_color = mix(base_color, vec3(0.), metalness);
colour = diffuse * diffuse_color;
// Specular color is already computed-in as it is both view and light-source-direction dependent
colour += specular;
} else {
colour = diffuse + specular;
}

View File

@ -3,7 +3,7 @@
const float color_culling_threshold = 0;//600./color_factor;
const float shadow_offset_fudge = .1;
#include "brdf.h"
#include "brdf.glsl"
#include "light_common.glsl"
#if LIGHT_POLYGON

View File

@ -149,23 +149,4 @@ bool shadowedSky(vec3 pos, vec3 dir) {
#endif
}
// This is an entry point for evaluation of all other BRDFs based on selected configuration (for direct light)
void evalSplitBRDF(vec3 N, vec3 L, vec3 V, MaterialProperties material, out vec3 diffuse, out vec3 specular) {
// Prepare data needed for BRDF evaluation - unpack material properties and evaluate commonly used terms (e.g. Fresnel, NdotL, ...)
const BrdfData data = prepareBRDFData(N, L, V, material);
// Ignore V and L rays "below" the hemisphere
//if (data.Vbackfacing || data.Lbackfacing) return vec3(0.0f, 0.0f, 0.0f);
// Eval specular and diffuse BRDFs
specular = evalSpecular(data);
diffuse = evalDiffuse(data);
// Combine specular and diffuse layers
#if COMBINE_BRDFS_WITH_FRESNEL
// Specular is already multiplied by F, just attenuate diffuse
diffuse *= vec3(1.) - data.F;
#endif
}
#endif //ifndef LIGHT_COMMON_GLSL_INCLUDED

View File

@ -2,6 +2,7 @@
#include "noise.glsl"
#include "ray_kusochki.glsl"
#include "color_spaces.glsl"
#include "light.glsl"
@ -35,11 +36,12 @@ void main() {
const vec4 material_data = imageLoad(material_rmxx, pix);
MaterialProperties material;
material.baseColor = vec3(1.);
material.emissive = vec3(0.f);
material.base_color = SRGBtoLINEAR(imageLoad(base_color_a, pix).rgb);
material.metalness = material_data.g;
material.roughness = material_data.r;
//g_mat_gltf2 = pix.y > ubo.ubo.res.y / 2.;
const vec4 pos_t = imageLoad(position_t, pix);
vec3 diffuse = vec3(0.), specular = vec3(0.);

View File

@ -22,6 +22,7 @@ layout (set = 0, binding = 8, align = 1) readonly buffer UBOLightClusters {
layout(set = 0, binding = 10, rgba32f) uniform readonly image2D position_t;
layout(set = 0, binding = 11, rgba16f) uniform readonly image2D normals_gs;
layout(set = 0, binding = 12, rgba8) uniform readonly image2D material_rmxx;
layout(set = 0, binding = 13, rgba8) uniform readonly image2D base_color_a;
layout(set = 0, binding = 20, rgba16f) uniform writeonly image2D out_light_point_diffuse;
layout(set = 0, binding = 21, rgba16f) uniform writeonly image2D out_light_point_specular;

View File

@ -22,6 +22,7 @@ layout (set = 0, binding = 8, align = 1) readonly buffer UBOLightClusters {
layout(set = 0, binding = 10, rgba32f) uniform readonly image2D position_t;
layout(set = 0, binding = 11, rgba16f) uniform readonly image2D normals_gs;
layout(set = 0, binding = 12, rgba8) uniform readonly image2D material_rmxx;
layout(set = 0, binding = 13, rgba8) uniform readonly image2D base_color_a;
layout(set = 0, binding = 20, rgba16f) uniform writeonly image2D out_light_poly_diffuse;
layout(set = 0, binding = 21, rgba16f) uniform writeonly image2D out_light_poly_specular;

View File

@ -125,7 +125,6 @@ void main() {
imageStore(out_position_t, pix, payload.hit_t);
imageStore(out_base_color_a, pix, LINEARtoSRGB(payload.base_color_a));
imageStore(out_normals_gs, pix, payload.normals_gs);
//imageStore(out_material_rmxx, pix, vec4(payload.material_rmxx.rg, 0, uintToFloat01(xxhash32(debug_geometry_index))));
imageStore(out_material_rmxx, pix, payload.material_rmxx);
imageStore(out_emissive, pix, payload.emissive);
imageStore(out_geometry_prev_position, pix, payload.prev_pos_t);