SpriteSpecular.cginc 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. #ifndef SPRITE_SPECULAR_INCLUDED
  2. #define SPRITE_SPECULAR_INCLUDED
  3. #include "ShaderMaths.cginc"
  4. ////////////////////////////////////////
  5. // Specular functions
  6. //
  7. #if defined(_SPECULAR) || defined(_SPECULAR_GLOSSMAP)
  8. #define SPECULAR
  9. //ALL THESE FUNCTIONS ARE TAKEN AND ADAPTED FROM UNITY'S OWN PHYSICS BASED STANDARD SHADER
  10. uniform float _Metallic;
  11. uniform float _Glossiness;
  12. uniform float _GlossMapScale;
  13. uniform sampler2D _MetallicGlossMap;
  14. struct SpecularLightData
  15. {
  16. half3 lighting;
  17. half3 specular;
  18. };
  19. struct SpecularCommonData
  20. {
  21. half3 diffColor, specColor;
  22. // Note: smoothness & oneMinusReflectivity for optimization purposes, mostly for DX9 SM2.0 level.
  23. // Most of the math is being done on these (1-x) values, and that saves a few precious ALU slots.
  24. half oneMinusReflectivity, smoothness;
  25. half alpha;
  26. };
  27. inline half2 getMetallicGloss(float2 uv)
  28. {
  29. half2 mg;
  30. #ifdef _SPECULAR_GLOSSMAP
  31. mg = tex2D(_MetallicGlossMap, uv).ra;
  32. mg.g *= _GlossMapScale;
  33. #else
  34. mg.r = _Metallic;
  35. mg.g = _Glossiness;
  36. #endif
  37. return mg;
  38. }
  39. inline half getOneMinusReflectivityFromMetallic(half metallic)
  40. {
  41. // We'll need oneMinusReflectivity, so
  42. // 1-reflectivity = 1-lerp(dielectricSpec, 1, metallic) = lerp(1-dielectricSpec, 0, metallic)
  43. // store (1-dielectricSpec) in unity_ColorSpaceDielectricSpec.a, then
  44. // 1-reflectivity = lerp(alpha, 0, metallic) = alpha + metallic*(0 - alpha) =
  45. // = alpha - metallic * alpha
  46. half oneMinusDielectricSpec = unity_ColorSpaceDielectricSpec.a;
  47. return oneMinusDielectricSpec - metallic * oneMinusDielectricSpec;
  48. }
  49. inline SpecularCommonData getSpecularData(float2 uv, half4 texureColor, fixed4 color)
  50. {
  51. half2 metallicGloss = getMetallicGloss(uv);
  52. half metallic = metallicGloss.x;
  53. half smoothness = metallicGloss.y; // this is 1 minus the square root of real roughness m.
  54. fixed4 albedo = calculatePixel(texureColor, color);
  55. half3 specColor = lerp (unity_ColorSpaceDielectricSpec.rgb, albedo, metallic);
  56. half oneMinusReflectivity = getOneMinusReflectivityFromMetallic(metallic);
  57. half3 diffColor = albedo * oneMinusReflectivity;
  58. SpecularCommonData o = (SpecularCommonData)0;
  59. o.diffColor = diffColor;
  60. o.specColor = specColor;
  61. o.oneMinusReflectivity = oneMinusReflectivity;
  62. o.smoothness = smoothness;
  63. #if defined(_ALPHAPREMULTIPLY_ON) && (SHADER_TARGET >= 30)
  64. // Reflectivity 'removes' from the rest of components, including Transparency
  65. // outAlpha = 1-(1-alpha)*(1-reflectivity) = 1-(oneMinusReflectivity - alpha*oneMinusReflectivity) =
  66. // = 1-oneMinusReflectivity + alpha*oneMinusReflectivity
  67. //o.alpha = 1-oneMinusReflectivity + albedo.a*oneMinusReflectivity;
  68. o.alpha = albedo.a;
  69. #else
  70. o.alpha = albedo.a;
  71. #endif
  72. return o;
  73. }
  74. inline half SmoothnessToPerceptualRoughness(half smoothness)
  75. {
  76. return (1 - smoothness);
  77. }
  78. inline half PerceptualRoughnessToRoughness(half perceptualRoughness)
  79. {
  80. return perceptualRoughness * perceptualRoughness;
  81. }
  82. // Ref: http://jcgt.org/published/0003/02/03/paper.pdf
  83. inline half SmithJointGGXVisibilityTerm (half NdotL, half NdotV, half roughness)
  84. {
  85. #if 0
  86. // Original formulation:
  87. // lambda_v = (-1 + sqrt(a2 * (1 - NdotL2) / NdotL2 + 1)) * 0.5f;
  88. // lambda_l = (-1 + sqrt(a2 * (1 - NdotV2) / NdotV2 + 1)) * 0.5f;
  89. // G = 1 / (1 + lambda_v + lambda_l);
  90. // Reorder code to be more optimal
  91. half a = roughness;
  92. half a2 = a * a;
  93. half lambdaV = NdotL * sqrt((-NdotV * a2 + NdotV) * NdotV + a2);
  94. half lambdaL = NdotV * sqrt((-NdotL * a2 + NdotL) * NdotL + a2);
  95. // Simplify visibility term: (2.0f * NdotL * NdotV) / ((4.0f * NdotL * NdotV) * (lambda_v + lambda_l + 1e-5f));
  96. return 0.5f / (lambdaV + lambdaL + 1e-5f); // This function is not intended to be running on Mobile,
  97. // therefore epsilon is smaller than can be represented by half
  98. #else
  99. // Approximation of the above formulation (simplify the sqrt, not mathematically correct but close enough)
  100. half a = roughness;
  101. half lambdaV = NdotL * (NdotV * (1 - a) + a);
  102. half lambdaL = NdotV * (NdotL * (1 - a) + a);
  103. return 0.5f / (lambdaV + lambdaL + 1e-5f);
  104. #endif
  105. }
  106. inline half GGXTerm (half NdotH, half roughness)
  107. {
  108. half a2 = roughness * roughness;
  109. half d = (NdotH * a2 - NdotH) * NdotH + 1.0f; // 2 mad
  110. return UNITY_INV_PI * a2 / (d * d + 1e-7f); // This function is not intended to be running on Mobile,
  111. // therefore epsilon is smaller than what can be represented by half
  112. }
  113. inline half3 FresnelTerm (half3 F0, half cosA)
  114. {
  115. half t = pow5 (1 - cosA); // ala Schlick interpoliation
  116. return F0 + (1-F0) * t;
  117. }
  118. inline half3 FresnelLerp (half3 F0, half F90, half cosA)
  119. {
  120. half t = pow5 (1 - cosA); // ala Schlick interpoliation
  121. return lerp (F0, F90, t);
  122. }
  123. // Note: Disney diffuse must be multiply by diffuseAlbedo / PI. This is done outside of this function.
  124. inline half DisneyDiffuse(half NdotV, half NdotL, half LdotH, half perceptualRoughness)
  125. {
  126. half fd90 = 0.5 + 2 * LdotH * LdotH * perceptualRoughness;
  127. // Two schlick fresnel term
  128. half lightScatter = (1 + (fd90 - 1) * pow5(1 - NdotL));
  129. half viewScatter = (1 + (fd90 - 1) * pow5(1 - NdotV));
  130. return lightScatter * viewScatter;
  131. }
  132. // Main Physically Based BRDF
  133. // Derived from Disney work and based on Torrance-Sparrow micro-facet model
  134. //
  135. // BRDF = kD / pi + kS * (D * V * F) / 4
  136. // I = BRDF * NdotL
  137. //
  138. // * NDF (depending on UNITY_BRDF_GGX):
  139. // a) Normalized BlinnPhong
  140. // b) GGX
  141. // * Smith for Visiblity term
  142. // * Schlick approximation for Fresnel
  143. SpecularLightData calculatePhysicsBasedSpecularLight(half3 specColor, half oneMinusReflectivity, half smoothness, half3 normal, half3 viewDir, half3 lightdir, half3 lightColor, half3 indirectDiffuse, half3 indirectSpecular)
  144. {
  145. half perceptualRoughness = SmoothnessToPerceptualRoughness (smoothness);
  146. half3 halfDir = safeNormalize (lightdir + viewDir);
  147. // NdotV should not be negative for visible pixels, but it can happen due to perspective projection and normal mapping
  148. // In this case normal should be modified to become valid (i.e facing camera) and not cause weird artifacts.
  149. // but this operation adds few ALU and users may not want it. Alternative is to simply take the abs of NdotV (less correct but works too).
  150. // Following define allow to control this. Set it to 0 if ALU is critical on your platform.
  151. // This correction is interesting for GGX with SmithJoint visibility function because artifacts are more visible in this case due to highlight edge of rough surface
  152. // Edit: Disable this code by default for now as it is not compatible with two sided lighting used in SpeedTree.
  153. #define UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV 0
  154. #if UNITY_HANDLE_CORRECTLY_NEGATIVE_NDOTV
  155. // The amount we shift the normal toward the view vector is defined by the dot product.
  156. half shiftAmount = dot(normal, viewDir);
  157. normal = shiftAmount < 0.0f ? normal + viewDir * (-shiftAmount + 1e-5f) : normal;
  158. // A re-normalization should be applied here but as the shift is small we don't do it to save ALU.
  159. //normal = normalize(normal);
  160. half nv = saturate(dot(normal, viewDir)); // TODO: this saturate should no be necessary here
  161. #else
  162. half nv = abs(dot(normal, viewDir)); // This abs allow to limit artifact
  163. #endif
  164. half nl = saturate(dot(normal, lightdir));
  165. half nh = saturate(dot(normal, halfDir));
  166. half lv = saturate(dot(lightdir, viewDir));
  167. half lh = saturate(dot(lightdir, halfDir));
  168. // Diffuse term
  169. half diffuseTerm = DisneyDiffuse(nv, nl, lh, perceptualRoughness) * nl;
  170. // Specular term
  171. // HACK: theoretically we should divide diffuseTerm by Pi and not multiply specularTerm!
  172. // BUT 1) that will make shader look significantly darker than Legacy ones
  173. // and 2) on engine side "Non-important" lights have to be divided by Pi too in cases when they are injected into ambient SH
  174. half roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
  175. half V = SmithJointGGXVisibilityTerm (nl, nv, roughness);
  176. half D = GGXTerm (nh, roughness);
  177. half specularTerm = V*D * UNITY_PI; // Torrance-Sparrow model, Fresnel is applied later
  178. # ifdef UNITY_COLORSPACE_GAMMA
  179. specularTerm = sqrt(max(1e-4h, specularTerm));
  180. # endif
  181. // specularTerm * nl can be NaN on Metal in some cases, use max() to make sure it's a sane value
  182. specularTerm = max(0, specularTerm * nl);
  183. // surfaceReduction = Int D(NdotH) * NdotH * Id(NdotL>0) dH = 1/(roughness^2+1)
  184. half surfaceReduction;
  185. # ifdef UNITY_COLORSPACE_GAMMA
  186. surfaceReduction = 1.0 - 0.28f * roughness * perceptualRoughness; // 1-0.28*x^3 as approximation for (1/(x^4+1))^(1/2.2) on the domain [0;1]
  187. # else
  188. surfaceReduction = 1.0 / (roughness*roughness + 1.0); // fade \in [0.5;1]
  189. # endif
  190. // To provide true Lambert lighting, we need to be able to kill specular completely.
  191. specularTerm *= any(specColor) ? 1.0 : 0.0;
  192. half grazingTerm = saturate(smoothness + (1-oneMinusReflectivity));
  193. SpecularLightData outData = (SpecularLightData)0;
  194. outData.lighting = indirectDiffuse + lightColor * diffuseTerm;
  195. outData.specular = (specularTerm * lightColor * FresnelTerm (specColor, lh)) + (surfaceReduction * indirectSpecular * FresnelLerp (specColor, grazingTerm, nv));
  196. return outData;
  197. }
  198. #endif // _SPECULAR && _SPECULAR_GLOSSMAP
  199. #endif // SPRITE_SPECULAR_INCLUDED