PBR and Subsurface Scattering
The Basis of this project was to implement basic Physically Based Rendering and Subsurface Scattering
PBR Implementation
I implemented Metallic Roughness Workflow based PBR.
Based upon Unreal Engine
Normal Distribution Function: Trowbridge-Reitz GGX
Geometry Function: Smith's method with Schlick-GGX
Fresnel Equation: Fresnel Schlick Approximation
One core idea is to maintain energy conservation when calculating the BRDF.
Shown in the code I wrote below you can see that. The function’s overall purpose is to calculate the ending BRDF value. Energy is not supposed to be added or removed from this function.
Sources:
https://learnopengl.com/PBR/Theory (Main Guide)
http://www.rorydriscoll.com/2008/08/24/lighting-the-rendering-equation/ (Rendering Equation)
https://learnopengl.com/Advanced-Lighting/Normal-Mapping (Implementing Tangent Space Normal Maps)
vec3 calculateBDRF(vec3 i_albedoColor, float i_roughness, float i_metallic, vec3 i_normalDir,vec3 i_toViewDir, vec3 i_toLightDir) { // Inputs Needed vec3 halfAngleDir = normalize(i_toLightDir + i_toViewDir); // ---------------------- Ratio Reflected Light float normalDistributionF = distributionGGX(i_normalDir,halfAngleDir, i_roughness);// D float geometry = geometrySmith(i_normalDir,i_toViewDir,i_toLightDir, i_roughness); //G vec3 baseR = vec3(0.04); // base dielectric value held for most surfaces vec3 baseReflectivity = mix(baseR, i_albedoColor, i_metallic); vec3 fresnel = fresnelSchlickApproximation(halfAngleDir,i_toViewDir,baseReflectivity); //F This includes Ks already vec3 numerator = normalDistributionF * geometry * fresnel; float denominator = 4.0 * max(dot(i_normalDir, i_toViewDir),0.0) * max(dot(i_normalDir, i_toLightDir),0.0); vec3 cookTorrance = numerator / max(denominator, 0.001); vec3 reflectedLight = cookTorrance; // Also known as Specular. Already includes ks due to fresnel // ---------------------- Ratio Refracted Light vec3 ks = fresnel; vec3 kd = vec3(1.0) - ks; //Ratio of refracted light kd = mix(kd, vec3(0.0), i_metallic); vec3 fLambert = i_albedoColor / PI; // Surface Color of light absorbed and released to the eye vec3 refractedLight = kd * fLambert; // Also known as Diffuse return refractedLight + reflectedLight; }
PBR Scenes I Generated
Failed Subsurface Scattering Implementation
I initially tried to implement SSS via a back light method.
The idea is to fake SSS by essentially shining a back light on the dark side of a model. The angle at which this light is visible is done through modifying the dot product between the View vector and Normal Vector.
However, the look I ended up with didn’t look right in comparison to reference images that implemented the same thing. I believe the following led to this failure:
Lack of Proper Ambient and Global Illumination
Having a basic PBR implementation
Problems calculating the Thickness Map.
And so I decided to try a different method shown later.
Sources:
float _Distortion = 0.10f; // 0.0 - 1.0 float _Power = 1.02f; // 0.0 - 10.0 float _Scale = 2.92f;// 0.0 - 10.0 float _Attenuation = 0.1f; float thickness = abs(1.0 - i_thickness); vec3 L = fragToLightDir; vec3 V = fragToView; vec3 N = i_normalDir; vec3 H = normalize(L + N * _Distortion); float VdotH = pow(clamp(dot(V, -H),0.0,1.0), _Power) * _Scale; vec3 I = _Attenuation * (VdotH + (calculateAmbientLighting(i_albedoColor)) ) * thickness ; // Final add vec3 translucentColor = (incomingLightColor) * I ; lightOut += (BRDFColor * incomingLightColor * lambertCosLaw) + translucentColor;
Subsurface Scattering Implementation
The second attempt I made was to take one from the GPU gems book. I would use a light depth map along with properties about the known fragment to calculate light penetration into an object. Using this value, I would map it to an exponential function determining scattering light. The results were far more successful then the prior method.
Problems:
The angle at which you can see the Scattering Color isn’t physically accurate. I’m think I can maybe combine the prior method with this one to get a more successful look.
Texture noise. I have issues getting rid of some texture noise that is showing up on the depth map that I need to resolve.
Sources: