// Generated on <$_SCRIBE_DATE$> // // Created by Sam Gateau on 6/8/16. // Copyright 2016 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // <@if not SUBSURFACE_SCATTERING_SLH@> <@def SUBSURFACE_SCATTERING_SLH@> <@include render-utils/ShaderConstants.h@> <@func declareSubsurfaceScatteringProfileSource()@> float gaussian(float v, float r) { const float _PI = 3.14159265358979523846; return (1.0 / sqrt(2.0 * _PI * v)) * exp(-(r*r) / (2.0 * v)); } vec3 scatter(float r) { // r is the distance expressed in millimeter // returns the scatter reflectance // Values from GPU Gems 3 "Advanced Skin Rendering". // Originally taken from real life samples. const vec4 profile[6] = vec4[6]( vec4(0.0064, 0.233, 0.455, 0.649), vec4(0.0484, 0.100, 0.336, 0.344), vec4(0.1870, 0.118, 0.198, 0.000), vec4(0.5670, 0.113, 0.007, 0.007), vec4(1.9900, 0.358, 0.004, 0.000), vec4(7.4100, 0.078, 0.000, 0.000) ); const int profileNum = 6; vec3 ret = vec3(0.0); for (int i = 0; i < profileNum; i++) { float v = profile[i].x * 1.414; float g = gaussian(v, r); ret += g * profile[i].yzw; } return ret; } <@endfunc@> <@func declareSubsurfaceScatteringGenerateProfileMap()@> <$declareSubsurfaceScatteringProfileSource()$> vec3 generateProfile(vec2 uv) { return scatter(uv.x * 2.0); } <@endfunc@> <@func declareSubsurfaceScatteringProfileMap()@> LAYOUT(binding=RENDER_UTILS_TEXTURE_SSSC_PROFILE) uniform sampler2D scatteringProfile; vec3 scatter(float r) { return texture(scatteringProfile, vec2(r * 0.5, 0.5)).rgb; } <@endfunc@> <@func declareSubsurfaceScatteringIntegrate(NumIntegrationSteps)@> vec3 integrate(float cosTheta, float skinRadius) { // Angle from lighting direction. float theta = acos(cosTheta); vec3 totalWeights = vec3(0.0); vec3 totalLight = vec3(0.0); const float _PI = 3.14159265358979523846; const float step = 2.0 * _PI / float(<$NumIntegrationSteps$>); float a = -(_PI); while (a <= (_PI)) { float sampleAngle = theta + a; float diffuse = clamp(cos(sampleAngle), 0.0, 1.0); // Distance. float sampleDist = abs(2.0 * skinRadius * sin(a * 0.5)); // Profile Weight. vec3 weights = scatter(sampleDist); totalWeights += weights; totalLight += diffuse * weights; a += step; } vec3 result = (totalLight / totalWeights); return clamp(result, vec3(0.0), vec3(1.0)); } <@endfunc@> <@func declareSubsurfaceScatteringResource()@> LAYOUT(binding=RENDER_UTILS_TEXTURE_SSSC_LUT) uniform sampler2D scatteringLUT; vec3 fetchBRDF(float LdotN, float curvature) { return texture(scatteringLUT, vec2( clamp(LdotN * 0.5 + 0.5, 0.0, 1.0), clamp(2.0 * curvature, 0.0, 1.0))).xyz; } vec3 fetchBRDFSpectrum(vec3 LdotNSpectrum, float curvature) { return vec3( fetchBRDF(LdotNSpectrum.r, curvature).r, fetchBRDF(LdotNSpectrum.g, curvature).g, fetchBRDF(LdotNSpectrum.b, curvature).b); } // Subsurface Scattering parameters struct ScatteringParameters { vec4 normalBendInfo; // R, G, B, factor vec4 curvatureInfo;// Offset, Scale, level vec4 debugFlags; }; LAYOUT(binding=RENDER_UTILS_BUFFER_SSSC_PARAMS) uniform subsurfaceScatteringParametersBuffer { ScatteringParameters parameters; }; vec3 getBendFactor() { return parameters.normalBendInfo.xyz * parameters.normalBendInfo.w; } float getScatteringLevel() { return parameters.curvatureInfo.z; } bool showBRDF() { return parameters.curvatureInfo.w > 0.0; } bool showCurvature() { return parameters.debugFlags.x > 0.0; } bool showDiffusedNormal() { return parameters.debugFlags.y > 0.0; } float tuneCurvatureUnsigned(float curvature) { return abs(curvature) * parameters.curvatureInfo.y + parameters.curvatureInfo.x; } float unpackCurvature(float packedCurvature) { return (packedCurvature * 2.0 - 1.0); } vec3 evalScatteringBentNdotL(vec3 normal, vec3 midNormal, vec3 lowNormal, vec3 lightDir) { vec3 bendFactorSpectrum = getBendFactor(); // vec3 rN = normalize(mix(normal, lowNormal, bendFactorSpectrum.x)); vec3 rN = normalize(mix(midNormal, lowNormal, bendFactorSpectrum.x)); vec3 gN = normalize(mix(midNormal, lowNormal, bendFactorSpectrum.y)); vec3 bN = normalize(mix(midNormal, lowNormal, bendFactorSpectrum.z)); vec3 NdotLSpectrum = vec3(dot(rN, lightDir), dot(gN, lightDir), dot(bN, lightDir)); return NdotLSpectrum; } <@endfunc@> <@func declareSubsurfaceScatteringBRDF()@> <$declareSubsurfaceScatteringResource()$> vec3 evalSkinBRDF(vec3 lightDir, vec3 normal, vec3 midNormal, vec3 lowNormal, float curvature) { if (showDiffusedNormal()) { return lowNormal * 0.5 + vec3(0.5); } if (showCurvature()) { float check = float(curvature > 0.0); return vec3(check * curvature, 0.0, (1.0 - check) * -curvature); } vec3 bentNdotL = evalScatteringBentNdotL(normal, midNormal, lowNormal, lightDir); float tunedCurvature = tuneCurvatureUnsigned(curvature); vec3 brdf = fetchBRDFSpectrum(bentNdotL, tunedCurvature); return brdf; } <@endfunc@> <@endif@>