| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- Technique
- : base("LightingCommon") =
- {
- Language = "HLSL11";
-
- Pass =
- {
- Common =
- {
- // Arbitrary limit, increase if needed
- #define MAX_LIGHTS 512
-
- #define PI 3.1415926
- #define HALF_PI 1.5707963
-
- struct LightData
- {
- float3 position;
- float radius;
- float3 direction;
- float intensity;
- float3 spotAngles;
- float radiusSqrdInv;
- float3 color;
- };
-
- float3 calcMicrofacetFresnelShlick(float3 F0, float LoH)
- {
- return F0 + (1.0f - F0) * pow(1.0f - LoH, 5.0f);
- }
- float calcMicrofacetShadowingSmithGGX(float roughness, float NoV, float NoL)
- {
- // Note: It's probably better to use the joint shadowing + masking version of this function
- // Note: Pull these multiplies out, since they're used by the distribution function as well?
- float roughness2 = roughness * roughness;
- float roughness4 = roughness2 * roughness2;
- // Note: Original GGX G1 multiplied by NoV & NoL (respectively), so that the microfacet function divisor gets canceled out
- // Original formula being (ignoring the factor for masking negative directions):
- // G1(v) = 2 / (1 + sqrt(1 + roughness^4 * tan^2(v)))
- //
- // Using trig identities: tan = sin/cos & sin^2 + cos^2 = 1
- // G1(v) = 2 / (1 + sqrt(1 + roughness^4 * (1 - cos^2(v))/cos^2(v)))
- //
- // Multiply by cos(v) so that we cancel out the (NoL * NoV) factor in the microfacet formula divisor
- // G1(v) = 2 * cos(v) / (cos^2(v) + sqrt(cos^2 + roughness^4 - roughness^4 * cos^2(v)))
- //
- // Actually do the cancellation:
- // G1(v) = 2 / (cos^2(v) + sqrt(cos^2 + roughness^4 - roughness^4 * cos^2(v)))
- //
- // Also cancel out the 2 and the 4:
- // G1(v) = 1 / (cos^2(v) + sqrt(cos^2 + roughness^4 - roughness^4 * cos^2(v)))
- //
- // Final equation being:
- // G(v, l) = G1(v) * G1(l)
- //
- // Where cos(v) is NoV or NoL
-
- float g1V = NoV + sqrt(NoV * (NoV - NoV * roughness4) + roughness4);
- float g1L = NoL + sqrt(NoL * (NoL - NoL * roughness4) + roughness4);
- return rcp(g1V * g1L);
- }
-
- float calcMicrofacetDistGGX(float roughness, float NoH)
- {
- float roughness2 = roughness * roughness;
- float roughness4 = roughness2 * roughness2;
-
- float d = (NoH * roughness4 - NoH) * NoH + 1.0f;
- return roughness4 / (PI * d * d);
- }
-
- float3 calcDiffuseLambert(float3 color)
- {
- return color * (1.0f / PI);
- }
-
- float getSpotAttenuation(float3 worldPosToLight, float3 direction, float3 angles)
- {
- float output = saturate((dot(-worldPosToLight, direction) - angles.y) * angles.z);
- return output * output;
- }
-
- float3 getDirLightContibution(SurfaceData surfaceData, LightData lightData)
- {
- return lightData.color * lightData.intensity;
- }
-
- float3 getPointLightContribution(float3 L, float3 worldPosition, SurfaceData surfaceData, LightData lightData)
- {
- float3 N = surfaceData.worldNormal.xyz;
-
- float distanceSqrd = dot(L, L);
- float distanceAttenuation = 1/(distanceSqrd + 1);
-
- float radiusAttenuation = distanceSqrd * lightData.radiusSqrdInv;
- radiusAttenuation *= radiusAttenuation;
- radiusAttenuation = saturate(1.0f - radiusAttenuation);
- radiusAttenuation *= radiusAttenuation;
- float attenuation = distanceAttenuation * radiusAttenuation;
- return lightData.color * lightData.intensity * attenuation;
- }
-
- float3 getSpotLightContribution(float3 L, float3 worldPosition, SurfaceData surfaceData, LightData lightData)
- {
- float3 pointLightContribution = getPointLightContribution(L, worldPosition, surfaceData, lightData);
- float spotFalloff = getSpotAttenuation(L, lightData.direction, lightData.spotAngles);
-
- return pointLightContribution * spotFalloff;
- }
-
- float3 getSurfaceShading(float3 V, float3 L, SurfaceData surfaceData)
- {
- float3 N = surfaceData.worldNormal.xyz;
- float3 H = normalize(V + L);
- float LoH = saturate(dot(L, H));
- float NoH = saturate(dot(N, H));
- float NoV = saturate(dot(N, V));
- float NoL = saturate(dot(N, L));
-
- float3 diffuseColor = lerp(surfaceData.albedo.rgb, float3(0.0f, 0.0f, 0.0f), 1.0f - surfaceData.metalness);
-
- // Note: Using a fixed F0 value of 0.04 (plastic) for dielectrics, and using albedo as specular for conductors.
- // For more customizability allow the user to provide separate albedo/specular colors for both types.
- float3 specularColor = lerp(float3(0.04f, 0.04f, 0.04f), surfaceData.albedo.rgb, surfaceData.metalness);
-
- float3 diffuse = calcDiffuseLambert(diffuseColor);
- float3 specular = calcMicrofacetFresnelShlick(specularColor, LoH) *
- calcMicrofacetDistGGX(surfaceData.roughness, NoH) *
- calcMicrofacetShadowingSmithGGX(surfaceData.roughness, NoV, NoL);
-
- // Note: Need to add energy conservation between diffuse and specular terms?
- return diffuse + specular;
- }
-
- StructuredBuffer<LightData> gLights;
-
- #ifdef USE_COMPUTE_INDICES
- groupshared uint gLightIndices[MAX_LIGHTS];
- #else
- Buffer<uint> gLightIndices;
- #endif
-
- float4 getDirectLighting(float3 worldPos, SurfaceData surfaceData, uint4 lightOffsets)
- {
- float3 N = surfaceData.worldNormal;
-
- float3 lightContribution = 0;
- float alpha = 0.0f;
- if(surfaceData.worldNormal.w > 0.0f)
- {
- for(uint i = 0; i < lightOffsets.x; ++i)
- {
- float3 L = -gLights[i].direction;
- float3 lightContrib = getDirLightContibution(surfaceData, gLights[i]);
-
- float NoL = saturate(dot(N, L));
- lightContribution += lightContrib * NoL;
- }
-
- for (uint i = lightOffsets.y; i < lightOffsets.z; ++i)
- {
- uint lightIdx = gLightIndices[i];
-
- float3 L = gLights[lightIdx].position - worldPos;
- L = normalize(L);
-
- float3 lightContrib = getPointLightContribution(L, worldPos, surfaceData, gLights[lightIdx]);
- float NoL = saturate(dot(N, L));
- lightContribution += lightContrib * NoL;
- }
- for(uint i = lightOffsets.z; i < lightOffsets.w; ++i)
- {
- uint lightIdx = gLightIndices[i];
-
- float3 L = gLights[lightIdx].position - worldPos;
- L = normalize(L);
-
- float3 lightContrib = getSpotLightContribution(L, worldPos, surfaceData, gLights[lightIdx]);
-
- float NoL = saturate(dot(N, L));
- lightContribution += lightContrib * NoL;
- }
-
- lightContribution += surfaceData.albedo.rgb * gAmbientFactor;
-
- // Note: Only possible because we are using Lambert only for lights
- lightContribution *= surfaceData.albedo.rgb / PI;
-
- alpha = 1.0f;
- }
-
- return float4(lightContribution, alpha);
- }
- };
- };
- };
- Technique
- : base("LightingCommon") =
- {
- Language = "GLSL";
-
- Pass =
- {
- Common =
- {
- #define PI 3.1415926
- #define HALF_PI 1.5707963
-
- struct LightData
- {
- vec3 position;
- float radius;
- vec3 direction;
- float intensity;
- vec3 spotAngles;
- float radiusSqrdInv;
- vec3 color;
- };
-
- vec3 calcMicrofacetFresnelShlick(vec3 F0, float LoH)
- {
- return F0 + (1.0f - F0) * pow(1.0f - LoH, 5.0f);
- }
- float calcMicrofacetShadowingSmithGGX(float roughness, float NoV, float NoL)
- {
- // Note: It's probably better to use the joint shadowing + masking version of this function
- // Note: Pull these multiplies out, since they're used by the distribution function as well?
- float roughness2 = roughness * roughness;
- float roughness4 = roughness2 * roughness2;
- // Note: Original GGX G1 multiplied by NoV & NoL (respectively), so that the microfacet function divisor gets canceled out
- // See HLSL code for derivation
-
- float g1V = NoV + sqrt(NoV * (NoV - NoV * roughness4) + roughness4);
- float g1L = NoL + sqrt(NoL * (NoL - NoL * roughness4) + roughness4);
- return 1.0f / (g1V * g1L);
- }
-
- float calcMicrofacetDistGGX(float roughness, float NoH)
- {
- float roughness2 = roughness * roughness;
- float roughness4 = roughness2 * roughness2;
-
- float d = (NoH * roughness4 - NoH) * NoH + 1.0f;
- return roughness4 / (PI * d * d);
- }
-
- float calcDiffuseLambert(float color)
- {
- return color * (1.0f / PI);
- }
-
- float getSpotAttenuation(vec3 worldPosToLight, vec3 direction, vec3 angles)
- {
- float atten = clamp((dot(-worldPosToLight, direction) - angles.y) * angles.z, 0.0, 1.0);
- return atten * atten;
- }
-
- vec3 getDirLightContibution(SurfaceData surfaceData, LightData lightData)
- {
- vec3 N = surfaceData.worldNormal.xyz;
- vec3 L = -lightData.direction;
-
- float NoL = clamp(dot(N, L), 0.0, 1.0); // TODO - Add bias here?
- return lightData.color * lightData.intensity * NoL;
- }
-
- vec3 getPointLightContribution(vec3 L, vec3 worldPosition, SurfaceData surfaceData, LightData lightData)
- {
- vec3 N = surfaceData.worldNormal.xyz;
- float distanceSqrd = dot(L, L);
- float distanceAttenuation = 1/(distanceSqrd + 1);
-
- L = normalize(L);
- float NoL = clamp(dot(N, L), 0.0, 1.0); // TODO - Add bias here?
- float radiusAttenuation = distanceSqrd * lightData.radiusSqrdInv;
- radiusAttenuation *= radiusAttenuation;
- radiusAttenuation = clamp(1.0f - radiusAttenuation, 0.0, 1.0);
- radiusAttenuation *= radiusAttenuation;
-
- float attenuation = distanceAttenuation * radiusAttenuation;
- return lightData.color * lightData.intensity * ((NoL * attenuation));
- }
-
- vec3 getPointLightContribution(vec3 worldPosition, SurfaceData surfaceData, LightData lightData)
- {
- vec3 L = lightData.position - worldPosition;
- return getPointLightContribution(L, worldPosition, surfaceData, lightData);
- }
-
- vec3 getSpotLightContribution(vec3 worldPosition, SurfaceData surfaceData, LightData lightData)
- {
- vec3 L = lightData.position - worldPosition;
- vec3 pointLightContribution = getPointLightContribution(L, worldPosition, surfaceData, lightData);
- float spotFalloff = getSpotAttenuation(L, lightData.direction, lightData.spotAngles);
-
- return pointLightContribution * spotFalloff;
- }
- };
- };
- };
|