diff --git a/viewer/brdf.hxx b/viewer/brdf.hxx new file mode 100644 index 0000000..9436f61 --- /dev/null +++ b/viewer/brdf.hxx @@ -0,0 +1,153 @@ +// Adapted from https://github.com/KhronosGroup/glTF-Sample-Viewer/blob/master/src/shaders/brdf.glsl + +#pragma once +#include + +// +// Fresnel +// +// http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html +// https://github.com/wdas/brdf/tree/master/src/brdfs +// https://google.github.io/filament/Filament.md.html +inline vec3 F_None(vec3 f0, vec3 f90, float VdotH) { + return f0; +} + +// The following equation models the Fresnel reflectance term of the spec equation (aka F()) +// Implementation of fresnel from [4], Equation 15 +inline vec3 F_Schlick(vec3 f0, vec3 f90, float VdotH) { + return f0 + (f90 - f0) * pow(clamp(1 - VdotH, 0.0f, 1.0f), 5); +} + +inline vec3 F_CookTorrance(vec3 f0, vec3 f90, float VdotH) { + vec3 f0_sqrt = sqrt(f0); + vec3 ior = (1 + f0_sqrt) / (1 - f0_sqrt); + vec3 c = vec3(VdotH); + vec3 g = sqrt(ior * ior + c*c - 1); + return 0.5f * pow(g-c, vec3(2)) / + pow(g+c, vec3(2)) * (1 + pow(c*(g+c) - 1, 2) / pow(c*(g-c) + 1, 2)); +} + +// Smith Joint GGX +// Note: Vis = G / (4 * NdotL * NdotV) +// see Eric Heitz. 2014. Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs. Journal of Computer Graphics Techniques, 3 +// see Real-Time Rendering. Page 331 to 336. +// see https://google.github.io/filament/Filament.md.html#materialsystem/specularbrdf/geometricshadowing(specularg) +inline float V_GGX(float NdotL, float NdotV, float alphaRoughness) { + float r2 = alphaRoughness * alphaRoughness; + + float GGXV = NdotL * sqrt(NdotV * NdotV * (1 - r2) + r2); + float GGXL = NdotV * sqrt(NdotL * NdotL * (1 - r2) + r2); + + float GGX = GGXV + GGXL; + return (GGX > 0) ? 0.5f / GGX : 0; +} + +// Anisotropic GGX visibility function, with height correlation. +// T: Tanget, B: Bi-tanget +inline float V_GGX_anisotropic(float NdotL, float NdotV, float BdotV, + float TdotV, float TdotL, float BdotL, float anisotropy, float at, float ab) { + float GGXV = NdotL * length(vec3(at * TdotV, ab * BdotV, NdotV)); + float GGXL = NdotV * length(vec3(at * TdotL, ab * BdotL, NdotL)); + float v = 0.5f / (GGXV + GGXL); + return clamp(v, 0.f, 1.f); +} + +// https://github.com/google/filament/blob/master/shaders/src/brdf.fs#L136 +// https://github.com/google/filament/blob/master/libs/ibl/src/CubemapIBL.cpp#L179 +// Note: Google call it V_Ashikhmin and V_Neubelt +inline float V_Ashikhmin(float NdotL, float NdotV) { + return clamp(1 / (4 * (NdotL + NdotV - NdotL * NdotV)), 0.f, 1.f); +} + +// https://github.com/google/filament/blob/master/shaders/src/brdf.fs#L131 +inline float V_Kelemen(float LdotH) { + // Kelemen 2001, "A Microfacet Based Coupled Specular-Matte BRDF Model with Importance Sampling" + return 0.25f / (LdotH * LdotH); +} + +// The following equation(s) model the distribution of microfacet normals across the area being drawn (aka D()) +// Implementation from "Average Irregularity Representation of a Roughened Surface for Ray Reflection" by T. S. Trowbridge, and K. P. Reitz +// Follows the distribution function recommended in the SIGGRAPH 2013 course notes from EPIC Games [1], Equation 3. +inline float D_GGX(float NdotH, float alphaRoughness) { + float alphaRoughnessSq = alphaRoughness * alphaRoughness; + float f = (NdotH * NdotH) * (alphaRoughnessSq - 1) + 1; + return alphaRoughnessSq / (M_PIf32 * f * f); +} + +// Anisotropic GGX NDF with a single anisotropy parameter controlling the normal orientation. +// See https://google.github.io/filament/Filament.html#materialsystem/anisotropicmodel +// T: Tanget, B: Bi-tanget +inline float D_GGX_anisotropic(float NdotH, float TdotH, float BdotH, + float anisotropy, float at, float ab) { + + float a2 = at * ab; + vec3 f = vec3(ab * TdotH, at * BdotH, a2 * NdotH); + float w2 = a2 / dot(f, f); + return a2 * w2 * w2 / M_PIf32; +} + +inline float D_Ashikhmin(float NdotH, float alphaRoughness) { + // Ashikhmin 2007, "Distribution-based BRDFs" + float a2 = alphaRoughness * alphaRoughness; + float cos2h = NdotH * NdotH; + float sin2h = 1.0 - cos2h; + float sin4h = sin2h * sin2h; + float cot2 = -cos2h / (a2 * sin2h); + return 1 / (M_PIf32 * (4 * a2 + 1) * sin4h) * (4 * exp(cot2) + sin4h); +} + +//Sheen implementation------------------------------------------------------------------------------------- +// See https://github.com/sebavan/glTF/tree/KHR_materials_sheen/extensions/2.0/Khronos/KHR_materials_sheen + +// Estevez and Kulla http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf +inline float D_Charlie(float sheenRoughness, float NdotH) { + sheenRoughness = max(sheenRoughness, 0.000001f); //clamp (0,1] + float alphaG = sheenRoughness * sheenRoughness; + float invR = 1 / alphaG; + float cos2h = NdotH * NdotH; + float sin2h = 1 - cos2h; + return (2 + invR) * pow(sin2h, invR * 0.5f) / (2 * M_PIf32); +} + +//https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#acknowledgments AppendixB +inline vec3 BRDF_lambertian(vec3 f0, vec3 f90, vec3 diffuseColor, float VdotH) { + // see https://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/ + return (1 - F_Schlick(f0, f90, VdotH)) * (diffuseColor / M_PIf32); +} + +// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#acknowledgments AppendixB +inline vec3 BRDF_specularGGX(vec3 f0, vec3 f90, float alphaRoughness, + float VdotH, float NdotL, float NdotV, float NdotH) { + + vec3 F = F_Schlick(f0, f90, VdotH); + float Vis = V_GGX(NdotL, NdotV, alphaRoughness); + float D = D_GGX(NdotH, alphaRoughness); + return F * Vis * D; +} + +inline vec3 BRDF_specularAnisotropicGGX(vec3 f0, vec3 f90, + float alphaRoughness, float VdotH, float NdotL, float NdotV, float NdotH, + float BdotV, float TdotV, float TdotL, float BdotL, float TdotH, float BdotH, + float anisotropy) { + + // Roughness along tangent and bitangent. + // Christopher Kulla and Alejandro Conty. 2017. Revisiting Physically Based Shading at Imageworks + float at = max(alphaRoughness * (1 + anisotropy), 0.00001f); + float ab = max(alphaRoughness * (1 - anisotropy), 0.00001f); + + vec3 F = F_Schlick(f0, f90, VdotH); + float V = V_GGX_anisotropic(NdotL, NdotV, BdotV, TdotV, TdotL, BdotL, + anisotropy, at, ab); + float D = D_GGX_anisotropic(NdotH, TdotH, BdotH, anisotropy, at, ab); + + return F * V * D; +} + +// f_sheen +inline vec3 BRDF_specularSheen(vec3 sheenColor, float sheenIntensity, + float sheenRoughness, float NdotL, float NdotV, float NdotH) { + float sheenDistribution = D_Charlie(sheenRoughness, NdotH); + float sheenVisibility = V_Ashikhmin(NdotL, NdotV); + return sheenColor * sheenIntensity * sheenDistribution * sheenVisibility; +} diff --git a/viewer/tonemapping.hxx b/viewer/tonemapping.hxx new file mode 100644 index 0000000..ac715e8 --- /dev/null +++ b/viewer/tonemapping.hxx @@ -0,0 +1,77 @@ +#pragma once + +// linear to sRGB approximation +// see http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html +inline vec3 linearTosRGB(vec3 color) { + const float GAMMA = 2.2; + const float INV_GAMMA = 1.0 / GAMMA; + + return pow(color, vec3(INV_GAMMA)); +} + +// sRGB to linear approximation +// see http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html +inline vec3 sRGBToLinear(vec3 srgbIn) { + const float GAMMA = 2.2; + return vec3(pow(srgbIn.xyz, vec3(GAMMA))); +} + +inline vec4 sRGBToLinear(vec4 srgbIn) { + return vec4(sRGBToLinear(srgbIn.xyz), srgbIn.w); +} + +// Uncharted 2 tone map +// see: http://filmicworlds.com/blog/filmic-tonemapping-operators/ +inline vec3 toneMapUncharted2Impl(vec3 color) { + const float A = 0.15; + const float B = 0.50; + const float C = 0.10; + const float D = 0.20; + const float E = 0.02; + const float F = 0.30; + return ((color*(A*color+C*B)+D*E)/(color*(A*color+B)+D*F))-E/F; +} + +inline vec3 toneMapUncharted(vec3 color) { + const float W = 11.2; + color = toneMapUncharted2Impl(2 * color); + vec3 whiteScale = 1 / toneMapUncharted2Impl(W); + return linearTosRGB(color * whiteScale); +} + +// Hejl Richard tone map +// see: http://filmicworlds.com/blog/filmic-tonemapping-operators/ +inline vec3 toneMapHejlRichard(vec3 color) { + color = max(vec3(0.0), color - vec3(0.004)); + return (color * (6.2f * color + .5f)) / + (color * (6.2f * color + 1.7f) + 0.06f); +} + +// ACES tone map +// see: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ +inline vec3 toneMapACES(vec3 color) { + const float A = 2.51; + const float B = 0.03; + const float C = 2.43; + const float D = 0.59; + const float E = 0.14; + return linearTosRGB(clamp((color * (A * color + B)) / (color * (C * color + D) + E), 0.f, 1.f)); +} + +inline vec3 toneMap(vec3 color, float exposure) { + color *= exposure; +/* +#ifdef TONEMAP_UNCHARTED + return toneMapUncharted(color); +#endif + +#ifdef TONEMAP_HEJLRICHARD + return toneMapHejlRichard(color); +#endif + +#ifdef TONEMAP_ACES + return toneMapACES(color); +#endif +*/ + return linearTosRGB(color); +}