LightingCommon.bslinc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. Technique
  2. : base("LightingCommon") =
  3. {
  4. Language = "HLSL11";
  5. Pass =
  6. {
  7. Common =
  8. {
  9. // Arbitrary limit, increase if needed
  10. #define MAX_LIGHTS 512
  11. #define PI 3.1415926
  12. #define HALF_PI 1.5707963
  13. struct LightData
  14. {
  15. float3 position;
  16. float radius;
  17. float3 direction;
  18. float intensity;
  19. float3 spotAngles;
  20. float radiusSqrdInv;
  21. float3 color;
  22. };
  23. float3 calcMicrofacetFresnelShlick(float3 F0, float LoH)
  24. {
  25. return F0 + (1.0f - F0) * pow(1.0f - LoH, 5.0f);
  26. }
  27. float calcMicrofacetShadowingSmithGGX(float roughness, float NoV, float NoL)
  28. {
  29. // Note: It's probably better to use the joint shadowing + masking version of this function
  30. // Note: Pull these multiplies out, since they're used by the distribution function as well?
  31. float roughness2 = roughness * roughness;
  32. float roughness4 = roughness2 * roughness2;
  33. // Note: Original GGX G1 multiplied by NoV & NoL (respectively), so that the microfacet function divisor gets canceled out
  34. // Original formula being (ignoring the factor for masking negative directions):
  35. // G1(v) = 2 / (1 + sqrt(1 + roughness^4 * tan^2(v)))
  36. //
  37. // Using trig identities: tan = sin/cos & sin^2 + cos^2 = 1
  38. // G1(v) = 2 / (1 + sqrt(1 + roughness^4 * (1 - cos^2(v))/cos^2(v)))
  39. //
  40. // Multiply by cos(v) so that we cancel out the (NoL * NoV) factor in the microfacet formula divisor
  41. // G1(v) = 2 * cos(v) / (cos^2(v) + sqrt(cos^2 + roughness^4 - roughness^4 * cos^2(v)))
  42. //
  43. // Actually do the cancellation:
  44. // G1(v) = 2 / (cos^2(v) + sqrt(cos^2 + roughness^4 - roughness^4 * cos^2(v)))
  45. //
  46. // Also cancel out the 2 and the 4:
  47. // G1(v) = 1 / (cos^2(v) + sqrt(cos^2 + roughness^4 - roughness^4 * cos^2(v)))
  48. //
  49. // Final equation being:
  50. // G(v, l) = G1(v) * G1(l)
  51. //
  52. // Where cos(v) is NoV or NoL
  53. float g1V = NoV + sqrt(NoV * (NoV - NoV * roughness4) + roughness4);
  54. float g1L = NoL + sqrt(NoL * (NoL - NoL * roughness4) + roughness4);
  55. return rcp(g1V * g1L);
  56. }
  57. float calcMicrofacetDistGGX(float roughness, float NoH)
  58. {
  59. float roughness2 = roughness * roughness;
  60. float roughness4 = roughness2 * roughness2;
  61. float d = (NoH * roughness4 - NoH) * NoH + 1.0f;
  62. return roughness4 / (PI * d * d);
  63. }
  64. float3 calcDiffuseLambert(float3 color)
  65. {
  66. return color * (1.0f / PI);
  67. }
  68. float getSpotAttenuation(float3 worldPosToLight, float3 direction, float3 angles)
  69. {
  70. float output = saturate((dot(-worldPosToLight, direction) - angles.y) * angles.z);
  71. return output * output;
  72. }
  73. float3 getDirLightContibution(SurfaceData surfaceData, LightData lightData)
  74. {
  75. return lightData.color * lightData.intensity;
  76. }
  77. float3 getPointLightContribution(float3 L, float3 worldPosition, SurfaceData surfaceData, LightData lightData)
  78. {
  79. float3 N = surfaceData.worldNormal.xyz;
  80. float distanceSqrd = dot(L, L);
  81. float distanceAttenuation = 1/(distanceSqrd + 1);
  82. float radiusAttenuation = distanceSqrd * lightData.radiusSqrdInv;
  83. radiusAttenuation *= radiusAttenuation;
  84. radiusAttenuation = saturate(1.0f - radiusAttenuation);
  85. radiusAttenuation *= radiusAttenuation;
  86. float attenuation = distanceAttenuation * radiusAttenuation;
  87. return lightData.color * lightData.intensity * attenuation;
  88. }
  89. float3 getSpotLightContribution(float3 L, float3 worldPosition, SurfaceData surfaceData, LightData lightData)
  90. {
  91. float3 pointLightContribution = getPointLightContribution(L, worldPosition, surfaceData, lightData);
  92. float spotFalloff = getSpotAttenuation(L, lightData.direction, lightData.spotAngles);
  93. return pointLightContribution * spotFalloff;
  94. }
  95. float3 getSurfaceShading(float3 V, float3 L, SurfaceData surfaceData)
  96. {
  97. float3 N = surfaceData.worldNormal.xyz;
  98. float3 H = normalize(V + L);
  99. float LoH = saturate(dot(L, H));
  100. float NoH = saturate(dot(N, H));
  101. float NoV = saturate(dot(N, V));
  102. float NoL = saturate(dot(N, L));
  103. float3 diffuseColor = lerp(surfaceData.albedo.rgb, float3(0.0f, 0.0f, 0.0f), 1.0f - surfaceData.metalness);
  104. // Note: Using a fixed F0 value of 0.04 (plastic) for dielectrics, and using albedo as specular for conductors.
  105. // For more customizability allow the user to provide separate albedo/specular colors for both types.
  106. float3 specularColor = lerp(float3(0.04f, 0.04f, 0.04f), surfaceData.albedo.rgb, surfaceData.metalness);
  107. float3 diffuse = calcDiffuseLambert(diffuseColor);
  108. float3 specular = calcMicrofacetFresnelShlick(specularColor, LoH) *
  109. calcMicrofacetDistGGX(surfaceData.roughness, NoH) *
  110. calcMicrofacetShadowingSmithGGX(surfaceData.roughness, NoV, NoL);
  111. // Note: Need to add energy conservation between diffuse and specular terms?
  112. return diffuse + specular;
  113. }
  114. StructuredBuffer<LightData> gLights;
  115. #ifdef USE_COMPUTE_INDICES
  116. groupshared uint gLightIndices[MAX_LIGHTS];
  117. #else
  118. Buffer<uint> gLightIndices;
  119. #endif
  120. float4 getDirectLighting(float3 worldPos, SurfaceData surfaceData, uint4 lightOffsets)
  121. {
  122. float3 N = surfaceData.worldNormal;
  123. float3 lightContribution = 0;
  124. float alpha = 0.0f;
  125. if(surfaceData.worldNormal.w > 0.0f)
  126. {
  127. for(uint i = 0; i < lightOffsets.x; ++i)
  128. {
  129. float3 L = -gLights[i].direction;
  130. float3 lightContrib = getDirLightContibution(surfaceData, gLights[i]);
  131. float NoL = saturate(dot(N, L));
  132. lightContribution += lightContrib * NoL;
  133. }
  134. for (uint i = lightOffsets.y; i < lightOffsets.z; ++i)
  135. {
  136. uint lightIdx = gLightIndices[i];
  137. float3 L = gLights[lightIdx].position - worldPos;
  138. L = normalize(L);
  139. float3 lightContrib = getPointLightContribution(L, worldPos, surfaceData, gLights[lightIdx]);
  140. float NoL = saturate(dot(N, L));
  141. lightContribution += lightContrib * NoL;
  142. }
  143. for(uint i = lightOffsets.z; i < lightOffsets.w; ++i)
  144. {
  145. uint lightIdx = gLightIndices[i];
  146. float3 L = gLights[lightIdx].position - worldPos;
  147. L = normalize(L);
  148. float3 lightContrib = getSpotLightContribution(L, worldPos, surfaceData, gLights[lightIdx]);
  149. float NoL = saturate(dot(N, L));
  150. lightContribution += lightContrib * NoL;
  151. }
  152. lightContribution += surfaceData.albedo.rgb * gAmbientFactor;
  153. // Note: Only possible because we are using Lambert only for lights
  154. lightContribution *= surfaceData.albedo.rgb / PI;
  155. alpha = 1.0f;
  156. }
  157. return float4(lightContribution, alpha);
  158. }
  159. };
  160. };
  161. };
  162. Technique
  163. : base("LightingCommon") =
  164. {
  165. Language = "GLSL";
  166. Pass =
  167. {
  168. Common =
  169. {
  170. #define PI 3.1415926
  171. #define HALF_PI 1.5707963
  172. struct LightData
  173. {
  174. vec3 position;
  175. float radius;
  176. vec3 direction;
  177. float intensity;
  178. vec3 spotAngles;
  179. float radiusSqrdInv;
  180. vec3 color;
  181. };
  182. vec3 calcMicrofacetFresnelShlick(vec3 F0, float LoH)
  183. {
  184. return F0 + (1.0f - F0) * pow(1.0f - LoH, 5.0f);
  185. }
  186. float calcMicrofacetShadowingSmithGGX(float roughness, float NoV, float NoL)
  187. {
  188. // Note: It's probably better to use the joint shadowing + masking version of this function
  189. // Note: Pull these multiplies out, since they're used by the distribution function as well?
  190. float roughness2 = roughness * roughness;
  191. float roughness4 = roughness2 * roughness2;
  192. // Note: Original GGX G1 multiplied by NoV & NoL (respectively), so that the microfacet function divisor gets canceled out
  193. // See HLSL code for derivation
  194. float g1V = NoV + sqrt(NoV * (NoV - NoV * roughness4) + roughness4);
  195. float g1L = NoL + sqrt(NoL * (NoL - NoL * roughness4) + roughness4);
  196. return 1.0f / (g1V * g1L);
  197. }
  198. float calcMicrofacetDistGGX(float roughness, float NoH)
  199. {
  200. float roughness2 = roughness * roughness;
  201. float roughness4 = roughness2 * roughness2;
  202. float d = (NoH * roughness4 - NoH) * NoH + 1.0f;
  203. return roughness4 / (PI * d * d);
  204. }
  205. float calcDiffuseLambert(float color)
  206. {
  207. return color * (1.0f / PI);
  208. }
  209. float getSpotAttenuation(vec3 worldPosToLight, vec3 direction, vec3 angles)
  210. {
  211. float atten = clamp((dot(-worldPosToLight, direction) - angles.y) * angles.z, 0.0, 1.0);
  212. return atten * atten;
  213. }
  214. vec3 getDirLightContibution(SurfaceData surfaceData, LightData lightData)
  215. {
  216. vec3 N = surfaceData.worldNormal.xyz;
  217. vec3 L = -lightData.direction;
  218. float NoL = clamp(dot(N, L), 0.0, 1.0); // TODO - Add bias here?
  219. return lightData.color * lightData.intensity * NoL;
  220. }
  221. vec3 getPointLightContribution(vec3 L, vec3 worldPosition, SurfaceData surfaceData, LightData lightData)
  222. {
  223. vec3 N = surfaceData.worldNormal.xyz;
  224. float distanceSqrd = dot(L, L);
  225. float distanceAttenuation = 1/(distanceSqrd + 1);
  226. L = normalize(L);
  227. float NoL = clamp(dot(N, L), 0.0, 1.0); // TODO - Add bias here?
  228. float radiusAttenuation = distanceSqrd * lightData.radiusSqrdInv;
  229. radiusAttenuation *= radiusAttenuation;
  230. radiusAttenuation = clamp(1.0f - radiusAttenuation, 0.0, 1.0);
  231. radiusAttenuation *= radiusAttenuation;
  232. float attenuation = distanceAttenuation * radiusAttenuation;
  233. return lightData.color * lightData.intensity * ((NoL * attenuation));
  234. }
  235. vec3 getPointLightContribution(vec3 worldPosition, SurfaceData surfaceData, LightData lightData)
  236. {
  237. vec3 L = lightData.position - worldPosition;
  238. return getPointLightContribution(L, worldPosition, surfaceData, lightData);
  239. }
  240. vec3 getSpotLightContribution(vec3 worldPosition, SurfaceData surfaceData, LightData lightData)
  241. {
  242. vec3 L = lightData.position - worldPosition;
  243. vec3 pointLightContribution = getPointLightContribution(L, worldPosition, surfaceData, lightData);
  244. float spotFalloff = getSpotAttenuation(L, lightData.direction, lightData.spotAngles);
  245. return pointLightContribution * spotFalloff;
  246. }
  247. };
  248. };
  249. };