SubShader { ${Tags} ${Blending} ${Culling} ${ZTest} ${ZWrite} LOD ${LOD} CGPROGRAM #include "UnityCG.cginc" //#include "AdvancedBRDF.cginc" //#include "AdvancedShading.cginc" //#include "AdvancedLighting.cginc" ${MaterialID} // ------------------------------------------------------------------ // Diffuse // From UE4 - Used for Cloth (Deprecated) float3 Diffuse_Lambert(float3 DiffuseColor) { return DiffuseColor * (1 / UNITY_PI); } // ------------------------------------------------------------------ // Fresnel // From UE4 - Used for Cloth // [Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"] float3 F_Schlick(float3 SpecularColor, float VoH) { float Fc = Pow5(1 - VoH); // 1 sub, 3 mul //return Fc + (1 - Fc) * SpecularColor; // 1 add, 3 mad // Anything less than 2% is physically impossible and is instead considered to be shadowing return saturate(50.0 * SpecularColor.g) * Fc + (1 - Fc) * SpecularColor; } // ------------------------------------------------------------------ // Distribution // From UE4 - USed for Cloth // GGX / Trowbridge-Reitz // [Walter et al. 2007, "Microfacet models for refraction through rough surfaces"] float D_GGX(float roughness, float NdotH) { float a = roughness * roughness; float a2 = a * a; float d = (NdotH * a2 - NdotH) * NdotH + 1; // 2 mad return a2 / (UNITY_PI*d*d); // 4 mul, 1 rcp } // Anisotropic GGX // Taken from HDRenderPipeline float D_GGXAnisotropic(float TdotH, float BdotH, float NdotH, float roughnessT, float roughnessB) { float f = TdotH * TdotH / (roughnessT * roughnessT) + BdotH * BdotH / (roughnessB * roughnessB) + NdotH * NdotH; return 1.0 / (roughnessT * roughnessB * f * f); } // From UE4 - Used for Cloth float D_InvGGX(float roughness, float NdotH) { float a = roughness * roughness; float a2 = a * a; float A = 4; float d = (NdotH - a2 * NdotH) * NdotH + a2; return 1/(UNITY_PI * (1 + A*a2)) * (1 + 4 * a2*a2 / (d*d)); //RCP } // ------------------------------------------------------------------ // Visibility // From UE4 - Used for Cloth // Appoximation of joint Smith term for GGX // [Heitz 2014, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs"] float Vis_SmithJointApprox(float Roughness, float NoV, float NoL) { float a = (Roughness*Roughness); float Vis_SmithV = NoL * (NoV * (1 - a) + a); float Vis_SmithL = NoV * (NoL * (1 - a) + a); // Note: will generate NaNs with Roughness = 0. MinRoughness is used to prevent this return 0.5 * 1/(Vis_SmithV + Vis_SmithL); //RCP } // From UE4 - Used for Cloth float Vis_Cloth(float NoV, float NoL) { return 1/(4 * (NoL + NoV - NoL * NoV)); //RCP } // ------------------------------------------------------------------ // SORT THESE // Smith Joint GGX Anisotropic Visibility // Taken from https://cedec.cesa.or.jp/2015/session/ENG/14698.html float SmithJointGGXAnisotropic(float TdotV, float BdotV, float NdotV, float TdotL, float BdotL, float NdotL, float roughnessT, float roughnessB) { float aT = roughnessT; float aT2 = aT * aT; float aB = roughnessB; float aB2 = aB * aB; float lambdaV = NdotL * sqrt(aT2 * TdotV * TdotV + aB2 * BdotV * BdotV + NdotV * NdotV); float lambdaL = NdotV * sqrt(aT2 * TdotL * TdotL + aB2 * BdotL * BdotL + NdotL * NdotL); return 0.5 / (lambdaV + lambdaL); } // Convert Anistropy to roughness void ConvertAnisotropyToRoughness(float roughness, float anisotropy, out float roughnessT, out float roughnessB) { // (0 <= anisotropy <= 1), therefore (0 <= anisoAspect <= 1) // The 0.9 factor limits the aspect ratio to 10:1. float anisoAspect = sqrt(1.0 - 0.9 * anisotropy); roughnessT = roughness / anisoAspect; // Distort along tangent (rougher) roughnessB = roughness * anisoAspect; // Straighten along bitangent (smoother) } // Schlick Fresnel float FresnelSchlick(float f0, float f90, float u) { float x = 1.0 - u; float x5 = x * x; x5 = x5 * x5 * x; return (f90 - f0) * x5 + f0; // sub mul mul mul sub mad } //Clamp roughness float ClampRoughnessForAnalyticalLights(float roughness) { return max(roughness, 0.000001); } //Calculate tangent warp for IBL (Reference Version - not used) float3 SpecularGGXIBLRef(float3 viewDir, float3 normalDir, float3 tangentDir, float3 bitangentDir, float roughnessT, float roughnessB) { return float3(1, 1, 1); //Hidden in UnityAnisotropicLighting.cginc } // Sample Anisotropic Direction for IBL (Reference Version - not used) void SampleAnisoGGXDir(float2 u, float3 viewDir, float3 normalDir, float3 tangent, float3 bitangent, float roughnessT, float roughnessB, out float3 halfDir, out float3 lightDir) { // AnisoGGX NDF sampling halfDir = sqrt(u.x / (1.0 - u.x)) * (roughnessT * cos((UNITY_PI * 2) * u.y) * tangent + roughnessB * sin((UNITY_PI * 2) * u.y) * bitangent) + normalDir; halfDir = normalize(halfDir); // Convert sample from half angle to incident angle lightDir = 2.0 * saturate(dot(viewDir, halfDir)) * halfDir - viewDir; } // Ref: Donald Revie - Implementing Fur Using Deferred Shading (GPU Pro 2) // The grain direction (e.g. hair or brush direction) is assumed to be orthogonal to the normal. // The returned normal is NOT normalized. float3 ComputeGrainNormal(float3 grainDir, float3 V) { float3 B = cross(-V, grainDir); return cross(B, grainDir); } //Modify Normal for Anisotropic IBL (Realtime version) // Fake anisotropic by distorting the normal. // The grain direction (e.g. hair or brush direction) is assumed to be orthogonal to N. // Anisotropic ratio (0->no isotropic; 1->full anisotropy in tangent direction) float3 GetAnisotropicModifiedNormal(float3 grainDir, float3 N, float3 V, float anisotropy) { float3 grainNormal = ComputeGrainNormal(grainDir, V); // TODO: test whether normalizing 'grainNormal' is worth it. return normalize(lerp(N, grainNormal, anisotropy)); } /// REGION END - ANISOTROPY /// REGION START - SUBSURFACE SCATTERING half Fresnel(half3 H, half3 V, half F0) { half base = 1.0 - dot(V, H); half exponential = pow(base, 5.0); return exponential + F0 * (1.0 - exponential); } /* inline half3 KelemenSzirmayKalosSpecular(half3 normal, half3 lightDir, half3 viewDir, float roughness, float rho_s) { half3 result = half3(0, 0, 0); half NdotL = dot(normal, lightDir); if (NdotL > 0.0) { half3 h = lightDir + viewDir; half3 H = normalize(h); half NdotH = dot(normal, H); half PH = pow(2.0 * tex2D(_BeckmannPrecomputedTex, half2(NdotH, roughness)).r, 10.0); half F = Fresnel(H, viewDir, 0.028); half frSpec = max(PH * F / dot(h, h), 0); half term = NdotL * rho_s * frSpec; result = half3(term, term, term); } return result; }*/ /* half3 SkinDiffuse(float curv, float3 NdotL) { float3 lookup = NdotL * 0.5 + 0.5; float3 diffuse; diffuse.r = tex2D(_DiffusionProfileTexture, float2(lookup.r, curv)).r; diffuse.g = tex2D(_DiffusionProfileTexture, float2(lookup.g, curv)).g; diffuse.b = tex2D(_DiffusionProfileTexture, float2(lookup.b, curv)).b; return diffuse; }*/ /// REGION END - SUBSURFACE SCATTERING // Upgrade NOTE: replaced 'defined SHADINGMODELID_CLEARCOAT' with 'defined (SHADINGMODELID_CLEARCOAT)' // Upgrade NOTE: replaced 'defined SHADINGMODELID_CLOTH' with 'defined (SHADINGMODELID_CLOTH)' // Upgrade NOTE: replaced 'defined SHADINGMODELID_EYE' with 'defined (SHADINGMODELID_EYE)' // Upgrade NOTE: replaced 'defined SHADINGMODELID_FOLIAGE' with 'defined (SHADINGMODELID_FOLIAGE)' // Upgrade NOTE: replaced 'defined SHADINGMODELID_HAIR' with 'defined (SHADINGMODELID_HAIR)' // Upgrade NOTE: replaced 'defined SHADINGMODELID_SKIN' with 'defined (SHADINGMODELID_SKIN)' // Upgrade NOTE: replaced 'defined SHADINGMODELID_SUBSURFACE' with 'defined (SHADINGMODELID_SUBSURFACE)' // ------------------------------------------------------------------ // Shading models //#pragma multi_compile SHADINGMODELID_UNLIT SHADINGMODELID_STANDARD SHADINGMODELID_SUBSURFACE SHADINGMODELID_SKIN SHADINGMODELID_FOLIAGE SHADINGMODELID_CLEARCOAT SHADINGMODELID_CLOTH SHADINGMODELID_EYE // ------------------------------------------------------------------ // Input half _ShadingModel; sampler2D _AnisotropyMap; half _Anisotropy; sampler2D _TangentMap; half4 _TranslucentColor; sampler2D _TranslucencyMap; sampler2D _FuzzTex; half3 _FuzzColor; half _Cloth; sampler2D _IrisNormal; sampler2D _IrisMask; half _IrisDistance; half _TDistortion; half _TScale; half _TAmbient; half _TPower; half _TAttenuation; half _TransmissionOverallStrength; // ------------------------------------------------------------------ // Maths helpers // Octahedron Normal Vectors // [Cigolle 2014, "A Survey of Efficient Representations for Independent Unit Vectors"] // Mean Max // oct 8:8 0.33709 0.94424 // snorm 8:8:8 0.17015 0.38588 // oct 10:10 0.08380 0.23467 // snorm 10:10:10 0.04228 0.09598 // oct 12:12 0.02091 0.05874 float2 UnitVectorToOctahedron(float3 N) { N.xy /= dot(float3(1,1,1), abs(N)); if (N.z <= 0) { N.xy = (1 - abs(N.yx)) * (N.xy >= 0 ? float2(1, 1) : float2(-1, -1)); } return N.xy; } float3 OctahedronToUnitVector(float2 Oct) { float3 N = float3(Oct, 1 - dot(float2(1,1), abs(Oct))); if (N.z < 0) { N.xy = (1 - abs(N.yx)) * (N.xy >= 0 ? float2(1, 1) : float2(-1, -1)); } return float3(1, 1, 1); return normalize(N); } // ------------------------------------------------------------------ // Surface helpers half Anisotropy(float2 uv) { return tex2D(_AnisotropyMap, uv) * _Anisotropy; } half3 Fuzz(float2 uv) { return tex2D(_FuzzTex, uv) * _FuzzColor; } half Cloth() { return _Cloth; } half4 Iris(float2 uv) { float2 n = UnitVectorToOctahedron(normalize(UnpackNormal(tex2D(_IrisNormal, uv)).rgb)) * 0.5 + 0.5; float m = saturate(tex2D(_IrisMask, uv).r); // Iris Mask float d = saturate(_IrisDistance); // Iris Distance return float4(n.x, n.y, m, d); } half3 Translucency(float2 uv) { return tex2D(_TranslucencyMap, uv).rgb * _TranslucentColor.rgb; } // ------------------------------------------------------------------ // Unlit Shading Function float4 UnlitShading(float3 diffColor) { return half4(diffColor, 1); } // ------------------------------------------------------------------ // Standard Shading Function float4 StandardShading(float3 diffColor, float3 specColor, float oneMinusReflectivity, float smoothness, float3 normal, float3x3 worldVectors, float anisotropy, float metallic, float3 viewDir, UnityLight light, UnityIndirect gi) { //Unpack world vectors float3 tangent = worldVectors[0]; float3 bitangent = worldVectors[1]; //Normal shift float shiftAmount = dot(normal, viewDir); normal = shiftAmount < 0.0f ? normal + viewDir * (-shiftAmount + 1e-5f) : normal; //Regular vectors float NdotL = saturate(dot(normal, light.dir)); //sat? float NdotV = abs(dot(normal, viewDir)); //abs? float LdotV = dot(light.dir, viewDir); float3 H = Unity_SafeNormalize(light.dir + viewDir); float invLenLV = rsqrt(abs(2 + 2 * normalize(LdotV))); //float invLenLV = rsqrt(abs(2 + 2 * LdotV)); //float NdotH = (NdotL + normalize(NdotV)) * invLenLV; float NdotH = saturate(dot(normal, H)); //float NdotH = saturate((NdotL + normalize(NdotV)) * invLenLV); //float H = (light.dir + viewDir) * invLenLV; float LdotH = saturate(dot(light.dir, H)); //Tangent vectors float TdotH = dot(tangent, H); float TdotL = dot(tangent, light.dir); float BdotH = dot(bitangent, H); float BdotL = dot(bitangent, light.dir); float TdotV = dot(viewDir, tangent); float BdotV = dot(viewDir, bitangent); //Fresnels half grazingTerm = saturate(smoothness + (1 - oneMinusReflectivity)); float3 F = FresnelLerp(specColor, grazingTerm, NdotV); //Original Schlick - Replace from SRP? //float3 fresnel0 = lerp(specColor, diffColor, metallic); //float3 F = FresnelSchlick(fresnel0, 1.0, LdotH); //Calculate roughness float roughnessT; float roughnessB; float perceptualRoughness = SmoothnessToPerceptualRoughness(smoothness); float roughness = PerceptualRoughnessToRoughness(perceptualRoughness); ConvertAnisotropyToRoughness(roughness, anisotropy, roughnessT, roughnessB); //Clamp roughness //roughness = ClampRoughnessForAnalyticalLights(roughness); roughnessT = ClampRoughnessForAnalyticalLights(roughnessT); roughnessB = ClampRoughnessForAnalyticalLights(roughnessB); //Visibility & Distribution terms float V = SmithJointGGXAnisotropic(TdotV, BdotV, NdotV, TdotL, BdotL, NdotL, roughnessT, roughnessB); float D = D_GGXAnisotropic(TdotH, BdotH, NdotH, roughnessT, roughnessB); //Specular term float3 specularTerm = V * D; //*UNITY_PI; # ifdef UNITY_COLORSPACE_GAMMA specularTerm = sqrt(max(1e-4h, specularTerm)); # endif // specularTerm * nl can be NaN on Metal in some cases, use max() to make sure it's a sane value specularTerm = max(0, specularTerm * NdotL); #if defined(_SPECULARHIGHLIGHTS_OFF) specularTerm = 0.0; #endif //Diffuse term float diffuseTerm = DisneyDiffuse(NdotV, NdotL, LdotH, perceptualRoughness) * NdotL;// - Need this NdotL multiply? //Reduction half surfaceReduction; # ifdef UNITY_COLORSPACE_GAMMA surfaceReduction = 1.0 - 0.28*roughness*perceptualRoughness; // 1-0.28*x^3 as approximation for (1/(x^4+1))^(1/2.2) on the domain [0;1] # else surfaceReduction = 1.0 / (roughness*roughness + 1.0); // fade \in [0.5;1] # endif //Final half3 color = (diffColor * (gi.diffuse + light.color * diffuseTerm)) + specularTerm * light.color * FresnelTerm(specColor, LdotH) + surfaceReduction * gi.specular * FresnelLerp(specColor, grazingTerm, NdotV); return half4(color, 1); } // ------------------------------------------------------------------ // Cloth Shading Function //float3 ClothShading(FGBufferData GBuffer, float3 LobeRoughness, float3 LobeEnergy, float3 L, float3 V, half3 N) float4 ClothShading(float3 diffColor, float3 specColor, float3 fuzzColor, float cloth, float oneMinusReflectivity, float smoothness, float3 normal, float3 viewDir, UnityLight light, UnityIndirect gi, float3x3 worldVectors, float anisotropy) { const float3 FuzzColor = saturate(fuzzColor); const float Cloth = saturate(cloth); //Regular vectors float NdotL = saturate(dot(normal, light.dir)); //sat? float NdotV = abs(dot(normal, viewDir)); //abs? float LdotV = dot(light.dir, viewDir); //float invLenLV = rsqrt(abs(2 + 2 * normalize(LdotV))); ////float invLenLV = rsqrt(abs(2 + 2 * LdotV)); //float NdotH = (NdotL + normalize(NdotV)) * invLenLV; //float NdotH = saturate((NdotL + normalize(NdotV)) * invLenLV); float3 H = Unity_SafeNormalize(light.dir + viewDir); //float H = (light.dir + viewDir) * invLenLV; float LdotH = saturate(dot(light.dir, H)); //float3 H = normalize(viewDir + light.dir); //float NdotL = saturate(dot(normal, light.dir)); //float NdotV = saturate(abs(dot(normal, viewDir)) + 1e-5); float NdotH = saturate(dot(normal, H)); float VdotH = saturate(dot(viewDir, H)); //float LdotH = saturate(dot(light.dir, H)); half grazingTerm = saturate(smoothness + (1 - oneMinusReflectivity)); // Diffuse float perceptualRoughness = SmoothnessToPerceptualRoughness(smoothness); float roughness = PerceptualRoughnessToRoughness(perceptualRoughness); float diffuseTerm = DisneyDiffuse(NdotV, NdotL, LdotH, perceptualRoughness) * NdotL;// - Need this NdotL multiply? // Cloth - Asperity Scattering - Inverse Beckmann Layer float3 F1 = FresnelTerm(fuzzColor, LdotH);// FresnelLerp(fuzzColor, grazingTerm, NdotV);// FresnelTerm(FuzzColor, LdotH);// F_Schlick(FuzzColor, VdotH); float D1 = D_InvGGX(roughness, NdotH); float V1 = Vis_Cloth(NdotV, NdotL); //Specular term float3 specularTerm1 = V1 * D1; //*UNITY_PI; # ifdef UNITY_COLORSPACE_GAMMA specularTerm1 = sqrt(max(1e-4h, specularTerm1)); # endif // specularTerm * nl can be NaN on Metal in some cases, use max() to make sure it's a sane value // specularTerm1 = max(0, specularTerm1 * NdotL); #if defined(_SPECULARHIGHLIGHTS_OFF) specularTerm1 = 0.0; #endif float3 Spec1 = specularTerm1 * light.color * FresnelTerm(fuzzColor, LdotH); // Generalized microfacet specular /*float3 F2 = F_Schlick(specColor, VdotH); float D2 = D_GGX(roughness, NdotH); float V2 = Vis_SmithJointApprox(roughness, NdotV, NdotL); float3 Spec2 = D2 * V2 * F2 * light.color;*/ //Unpack world vectors float3 tangent = worldVectors[0]; float3 bitangent = worldVectors[1]; //Tangent vectors float TdotH = dot(tangent, H); float TdotL = dot(tangent, light.dir); float BdotH = dot(bitangent, H); float BdotL = dot(bitangent, light.dir); float TdotV = dot(viewDir, tangent); float BdotV = dot(viewDir, bitangent); //Fresnels float3 F2 = FresnelLerp(specColor, grazingTerm, NdotV);// FresnelTerm(specColor, LdotH);// FresnelLerp(specColor, grazingTerm, NdotV); //Original Schlick - Replace from SRP? float roughnessT; float roughnessB; //float perceptualRoughness = SmoothnessToPerceptualRoughness(smoothness); //float roughness = PerceptualRoughnessToRoughness(perceptualRoughness); ConvertAnisotropyToRoughness(roughness, anisotropy, roughnessT, roughnessB); //Clamp roughness //roughness = ClampRoughnessForAnalyticalLights(roughness); roughnessT = ClampRoughnessForAnalyticalLights(roughnessT); roughnessB = ClampRoughnessForAnalyticalLights(roughnessB); //Visibility & Distribution terms float V2 = SmithJointGGXAnisotropic(TdotV, BdotV, NdotV, TdotL, BdotL, NdotL, roughnessT, roughnessB); float D2 = D_GGXAnisotropic(TdotH, BdotH, NdotH, roughnessT, roughnessB); //Specular term float3 specularTerm2 = V2 * D2; //*UNITY_PI; # ifdef UNITY_COLORSPACE_GAMMA specularTerm2 = sqrt(max(1e-4h, specularTerm2)); # endif // specularTerm * nl can be NaN on Metal in some cases, use max() to make sure it's a sane value specularTerm2 = max(0, specularTerm2 * NdotL); #if defined(_SPECULARHIGHLIGHTS_OFF) specularTerm2 = 0.0; #endif float3 Spec2 = specularTerm2 * light.color * FresnelTerm(specColor, LdotH); float3 Spec = lerp(Spec2, Spec1, Cloth); //Reduction half surfaceReduction; # ifdef UNITY_COLORSPACE_GAMMA surfaceReduction = 1.0 - 0.28*roughness*perceptualRoughness; // 1-0.28*x^3 as approximation for (1/(x^4+1))^(1/2.2) on the domain [0;1] # else surfaceReduction = 1.0 / (roughness*roughness + 1.0); // fade \in [0.5;1] # endif //Final //half grazingTerm = saturate(smoothness + (1 - oneMinusReflectivity)); half3 color = (diffColor * (gi.diffuse + light.color * diffuseTerm)) + Spec + surfaceReduction * gi.specular * FresnelLerp(specColor, grazingTerm, NdotV); return half4(color, 1); } // ------------------------------------------------------------------ // Eye Shading Function //float3 EyeShading(FGBufferData GBuffer, float3 LobeRoughness, float3 LobeEnergy, float3 L, float3 V, half3 N) float4 EyeShading(float3 diffColor, float3 specColor, float3 viewDir, half3 normal, float smoothness, float oneMinusReflectivity, UnityLight light, UnityIndirect gi) { float3 H = normalize(viewDir + light.dir); float NdotL = saturate(dot(normal, light.dir)); float NdotV = saturate(abs(dot(normal, viewDir)) + 1e-5); float NdotH = saturate(dot(normal, H)); float VdotH = saturate(dot(viewDir, H)); float LdotH = saturate(dot(light.dir, H)); // Generalized microfacet specular float perceptualRoughness = SmoothnessToPerceptualRoughness(smoothness); float roughness = PerceptualRoughnessToRoughness(perceptualRoughness); float D = D_GGX(roughness, NdotH);// *LobeEnergy[1]; float V = Vis_SmithJointApprox(roughness, NdotV, NdotL); float3 F = F_Schlick(specColor, VdotH); float3 specularTerm = V * D; //*UNITY_PI; # ifdef UNITY_COLORSPACE_GAMMA specularTerm = sqrt(max(1e-4h, specularTerm)); # endif // specularTerm * nl can be NaN on Metal in some cases, use max() to make sure it's a sane value specularTerm = max(0, specularTerm * NdotL); #if defined(_SPECULARHIGHLIGHTS_OFF) specularTerm = 0.0; #endif half grazingTerm = saturate(smoothness + (1 - oneMinusReflectivity)); half surfaceReduction; # ifdef UNITY_COLORSPACE_GAMMA surfaceReduction = 1.0 - 0.28*roughness*perceptualRoughness; // 1-0.28*x^3 as approximation for (1/(x^4+1))^(1/2.2) on the domain [0;1] # else surfaceReduction = 1.0 / (roughness*roughness + 1.0); // fade \in [0.5;1] # endif float diffuseTerm = DisneyDiffuse(NdotV, NdotL, LdotH, perceptualRoughness) * NdotL; // TODO - Unreal does not apply diffuse in Shading function //Final half3 color = (diffColor * (gi.diffuse + light.color * diffuseTerm)) + specularTerm * light.color * FresnelTerm(specColor, LdotH) + surfaceReduction * gi.specular * FresnelLerp(specColor, grazingTerm, NdotV); return half4(color, 1); } // ------------------------------------------------------------------ // Subsurface Shading Function float3 SubsurfaceShadingSimple(float3 diffColor, float3 normal, float3 viewDir, float3 thickness, UnityLight light) { half3 vLTLight = light.dir + normal * 1; half fLTDot = pow(saturate(dot(viewDir, -vLTLight)), 3.5) * 1.5; half3 fLT = 1 * (fLTDot + 1.2) * (thickness); return diffColor * ((light.color * fLT) * 0.4); } // ------------------------------------------------------------------ // Eye Subsurface Shading Function //float3 EyeSubsurfaceShading(FGBufferData GBuffer, float3 L, float3 V, half3 N) float3 EyeSubsurfaceShading(float3 diffColor, float3 specColor, float3 viewDir, half3 normal, float smoothness, float4 iris, UnityLight light) { float2 irisNormal = iris.rg; float irisMask = iris.z; float irisDistance = iris.w; float3 H = normalize(viewDir + light.dir); float VdotH = saturate(dot(viewDir, H)); float NdotV = saturate(abs(dot(normal, viewDir)) + 1e-5); float LdotH = saturate(dot(light.dir, H)); // F_Schlick //float F0 = GBuffer.Specular * 0.08; //float Fc = Pow5(1 - VoH); //float F = Fc + (1 - Fc) * F0; float3 fresnel0 = lerp(specColor, diffColor, smoothness); float3 F = FresnelSchlick(fresnel0, 1.0, LdotH); //float IrisDistance = GBuffer.CustomData.w; //float IrisMask = GBuffer.CustomData.z; float3 IrisNormal; IrisNormal = OctahedronToUnitVector(irisNormal * 2 - 1); // Blend in the negative intersection normal to create some concavity // Not great as it ties the concavity to the convexity of the cornea surface // No good justification for that. On the other hand, if we're just looking to // introduce some concavity, this does the job. float3 CausticNormal = normalize(lerp(IrisNormal, -normal, irisMask*irisDistance)); float NdotL = saturate(dot(IrisNormal, light.dir)); float Power = lerp(12, 1, NdotL); float Caustic = 0.6 + 0.2 * (Power + 1) * pow(saturate(dot(CausticNormal, light.dir)), Power); float Iris = NdotL * Caustic; // http://blog.stevemcauley.com/2011/12/03/energy-conserving-wrapped-diffuse/ float Wrap = 0.15; float Sclera = saturate((dot(normal, light.dir) + Wrap) / (1 + Wrap) * (1 + Wrap)); return (1 - F) * lerp(Sclera, Iris, irisMask) * diffColor / UNITY_PI; } // ------------------------------------------------------------------ // Shading function selectors //float3 SurfaceShading(/*FGBufferData GBuffer,*/ float3 LobeRoughness, float3 LobeEnergy, float3 L, float3 V, half3 N, uint2 Random) float4 SurfaceShading(float3 diffColor, float3 specColor, float oneMinusReflectivity, float smoothness, float3 normal, float3x3 worldVectors, float anisotropy, float4 customData, float metallic, float3 viewDir, UnityLight light, UnityIndirect gi) { #if defined(SHADINGMODELID_UNLIT) { return UnlitShading(diffColor); } #elif defined(SHADINGMODELID_STANDARD) || defined(SHADINGMODELID_SUBSURFACE) || defined(SHADINGMODELID_SKIN) || defined(SHADINGMODELID_FOLIAGE) { return StandardShading(diffColor, specColor, oneMinusReflectivity, smoothness, normal, worldVectors, anisotropy, metallic, viewDir, light, gi); } #elif defined (SHADINGMODELID_CLEARCOAT) { return float4(1, 1, 1, 1); //ClearCoatShading(GBuffer, LobeRoughness, LobeEnergy, L, V, N); } #elif defined (SHADINGMODELID_CLOTH) { return ClothShading(diffColor, specColor, customData.rgb, customData.a, oneMinusReflectivity, smoothness, normal, viewDir, light, gi, worldVectors, anisotropy); } #elif defined (SHADINGMODELID_EYE) { return EyeShading(diffColor, specColor, viewDir, normal, smoothness, oneMinusReflectivity, light, gi); //EyeShading(GBuffer, LobeRoughness, LobeEnergy, L, V, N); } #endif return float4(0, 0, 0, 0); } //float3 SubsurfaceShading(/*FGBufferData GBuffer,*/ float3 L, float3 V, half3 N, float Shadow, uint2 Random) float3 SubsurfaceShading(float3 diffColor, float3 specColor, float3 normal, float smoothness, float3 viewDir, float4 customData, UnityLight light) { #if defined (SHADINGMODELID_SUBSURFACE) { return SubsurfaceShadingSimple(diffColor, normal, viewDir, customData.rgb, light); } #elif defined (SHADINGMODELID_SKIN) { return float3(0, 0, 0); //SubsurfaceShadingPreintegratedSkin(GBuffer, L, V, N); } #elif defined (SHADINGMODELID_FOLIAGE) { return float3(0, 0, 0); //SubsurfaceShadingTwoSided(SubsurfaceColor, L, V, N); } #elif defined (SHADINGMODELID_HAIR) { return float3(0, 0, 0); //HairShading(GBuffer, L, V, N, Shadow, 1, 0, Random); } #elif defined (SHADINGMODELID_EYE) { return EyeSubsurfaceShading(diffColor, specColor, viewDir, normal, smoothness, customData, light); //EyeSubsurfaceShading(GBuffer, L, V, N); } #endif return float3(0, 0, 0); } //#endif UNITY_ADVANCED_SHADINGMODELS_INCLUDED //------------------------------------------------------------------------------------- // Lighting Helpers // Glossy Environment half3 Unity_AnisotropicGlossyEnvironment(UNITY_ARGS_TEXCUBE(tex), half4 hdr, Unity_GlossyEnvironmentData glossIn, half anisotropy) //Reference IBL from HD Pipe (Add half3 L input and replace R) { half perceptualRoughness = glossIn.roughness /* perceptualRoughness */; // TODO: CAUTION: remap from Morten may work only with offline convolution, see impact with runtime convolution! // For now disabled #if 0 float m = PerceptualRoughnessToRoughness(perceptualRoughness); // m is the real roughness parameter const float fEps = 1.192092896e-07F; // smallest such that 1.0+FLT_EPSILON != 1.0 (+1e-4h is NOT good here. is visibly very wrong) float n = (2.0 / max(fEps, m*m)) - 2.0; // remap to spec power. See eq. 21 in --> https://dl.dropboxusercontent.com/u/55891920/papers/mm_brdf.pdf n /= 4; // remap from n_dot_h formulatino to n_dot_r. See section "Pre-convolved Cube Maps vs Path Tracers" --> https://s3.amazonaws.com/docs.knaldtech.com/knald/1.0.0/lys_power_drops.html perceptualRoughness = pow(2 / (n + 2), 0.25); // remap back to square root of real roughness (0.25 include both the sqrt root of the conversion and sqrt for going from roughness to perceptualRoughness) #else // MM: came up with a surprisingly close approximation to what the #if 0'ed out code above does. perceptualRoughness = perceptualRoughness*(1.7 - 0.7*perceptualRoughness); #endif half mip = perceptualRoughnessToMipmapLevel(perceptualRoughness); half3 R = glossIn.reflUVW;// -half3(anisotropy, 0, 0); half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(tex, R, mip); return DecodeHDR(rgbm, hdr); } // Indirect Specular inline half3 UnityGI_AnisotropicIndirectSpecular(UnityGIInput data, half occlusion, Unity_GlossyEnvironmentData glossIn, half anisotropy, half3x3 worldVectors) { half3 specular; float3 tangentX = worldVectors[0]; float3 tangentY = worldVectors[1]; float3 N = worldVectors[2]; float3 V = data.worldViewDir; float3 iblNormalWS = GetAnisotropicModifiedNormal(tangentY, N, V, anisotropy); float3 iblR = reflect(-V, iblNormalWS); #ifdef UNITY_SPECCUBE_BOX_PROJECTION // we will tweak reflUVW in glossIn directly (as we pass it to Unity_GlossyEnvironment twice for probe0 and probe1), so keep original to pass into BoxProjectedCubemapDirection half3 originalReflUVW = glossIn.reflUVW; glossIn.reflUVW = BoxProjectedCubemapDirection(iblR, data.worldPos, data.probePosition[0], data.boxMin[0], data.boxMax[0]); #endif #ifdef _GLOSSYREFLECTIONS_OFF specular = unity_IndirectSpecColor.rgb; #else half3 env0 = Unity_AnisotropicGlossyEnvironment(UNITY_PASS_TEXCUBE(unity_SpecCube0), data.probeHDR[0], glossIn, anisotropy); //half3 env0 = Unity_AnisotropicGlossyEnvironment(UNITY_PASS_TEXCUBE(unity_SpecCube0), data.probeHDR[0], glossIn, anisotropy, L); //Reference IBL from HD Pipe #ifdef UNITY_SPECCUBE_BLENDING const float kBlendFactor = 0.99999; float blendLerp = data.boxMin[0].w; UNITY_BRANCH if (blendLerp < kBlendFactor) { #ifdef UNITY_SPECCUBE_BOX_PROJECTION glossIn.reflUVW = BoxProjectedCubemapDirection(iblR, data.worldPos, data.probePosition[1], data.boxMin[1], data.boxMax[1]); #endif half3 env1 = Unity_AnisotropicGlossyEnvironment(UNITY_PASS_TEXCUBE_SAMPLER(unity_SpecCube1, unity_SpecCube0), data.probeHDR[1], glossIn, anisotropy); //half3 env1 = Unity_AnisotropicGlossyEnvironment(UNITY_PASS_TEXCUBE_SAMPLER(unity_SpecCube1, unity_SpecCube0), data.probeHDR[1], glossIn, anisotropy, L); //Reference IBL from HD Pipe specular = lerp(env1, env0, blendLerp); } else { specular = env0; } #else specular = env0; #endif #endif return specular * occlusion;// *weightOverPdf; //Reference IBL from HD Pipe //return specular * occlusion * weightOverPdf; //Reference IBL from HD Pipe } // Global Illumination inline UnityGI UnityAnisotropicGlobalIllumination(UnityGIInput data, half occlusion, half3 normalWorld, Unity_GlossyEnvironmentData glossIn, half anisotropy, half3x3 worldVectors) { UnityGI o_gi = UnityGI_Base(data, occlusion, normalWorld); o_gi.indirect.specular = UnityGI_AnisotropicIndirectSpecular(data, occlusion, glossIn, anisotropy, worldVectors); return o_gi; } //------------------------------------------------------------------------------------- // Lighting Functions //Surface Description struct SurfaceOutputAdvanced { fixed3 Albedo; // base (diffuse or specular) color fixed3 Normal; // tangent space normal, if written half3 Emission; half Metallic; // 0=non-metal, 1=metal // Smoothness is the user facing name, it should be perceptual smoothness but user should not have to deal with it. // Everywhere in the code you meet smoothness it is perceptual smoothness half Smoothness; // 0=rough, 1=smooth half Occlusion; // occlusion (default 1) fixed Alpha; // alpha for transparencies half3 Tangent; half Anisotropy; half4 CustomData; float3x3 WorldVectors; //half ShadingModel; }; inline half4 LightingAdvanced(SurfaceOutputAdvanced s, half3 viewDir, UnityGI gi) { s.Normal = normalize(s.Normal); half oneMinusReflectivity; half3 specColor; s.Albedo = DiffuseAndSpecularFromMetallic(s.Albedo, s.Metallic, /*out*/ specColor, /*out*/ oneMinusReflectivity); // shader relies on pre-multiply alpha-blend (_SrcBlend = One, _DstBlend = OneMinusSrcAlpha) // this is necessary to handle transparency in physically correct way - only diffuse component gets affected by alpha half outputAlpha; s.Albedo = PreMultiplyAlpha(s.Albedo, s.Alpha, oneMinusReflectivity, /*out*/ outputAlpha); half4 c = SurfaceShading(s.Albedo, specColor, oneMinusReflectivity, s.Smoothness, s.Normal, s.WorldVectors, s.Anisotropy, s.CustomData, s.Metallic, viewDir, gi.light, gi.indirect); c.rgb += SubsurfaceShading(s.Albedo, specColor, s.Normal, s.Smoothness, viewDir, s.CustomData, gi.light); //c.rgb += UNITY_BRDF_GI(s.Albedo, specColor, oneMinusReflectivity, s.Smoothness, s.Normal, viewDir, s.Occlusion, gi); c.a = outputAlpha; return c; } //This is pointless as always forward? inline half4 LightingAdvanced_Deferred(SurfaceOutputAdvanced s, half3 viewDir, UnityGI gi, out half4 outGBuffer0, out half4 outGBuffer1, out half4 outGBuffer2) { half oneMinusReflectivity; half3 specColor; s.Albedo = DiffuseAndSpecularFromMetallic(s.Albedo, s.Metallic, /*out*/ specColor, /*out*/ oneMinusReflectivity); half4 c = SurfaceShading(s.Albedo, specColor, oneMinusReflectivity, s.Smoothness, s.Normal, s.WorldVectors, s.Anisotropy, s.CustomData, s.Metallic, viewDir, gi.light, gi.indirect); c.rgb += SubsurfaceShading(s.Albedo, specColor, s.Normal, s.Smoothness, viewDir, s.CustomData, gi.light); UnityStandardData data; data.diffuseColor = s.Albedo; data.occlusion = s.Occlusion; data.specularColor = specColor; data.smoothness = s.Smoothness; data.normalWorld = s.Normal; UnityStandardDataToGbuffer(data, outGBuffer0, outGBuffer1, outGBuffer2); half4 emission = half4(s.Emission + c.rgb, 1); return emission; } inline void LightingAdvanced_GI(SurfaceOutputAdvanced s, UnityGIInput data, inout UnityGI gi) { #if defined(UNITY_PASS_DEFERRED) && UNITY_ENABLE_REFLECTION_BUFFERS gi = UnityGlobalIllumination(data, s.Occlusion, s.Normal); #else Unity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup(s.Smoothness, data.worldViewDir, s.Normal, lerp(unity_ColorSpaceDielectricSpec.rgb, s.Albedo, s.Metallic)); gi = UnityAnisotropicGlobalIllumination(data, s.Occlusion, s.Normal, g, s.Anisotropy, s.WorldVectors); #endif } ///END //#pragma target 5.0 #pragma surface surf ${LightingFunctionName} ${VertexShaderDecl} #pragma glsl #pragma debug ${ShaderFunctions} ${ShaderPropertyUsages} struct Input { ${ShaderInputs} }; void vert (inout appdata_full v, out Input o) { UNITY_INITIALIZE_OUTPUT(Input,o); ${VertexShaderBody} } void surf (Input IN, inout ${SurfaceOutputStructureName} o) { ${PixelShaderBody} } ENDCG }