您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

688 行
30 KiB

// Copyright (c) Valve Corporation, All rights reserved. ======================================================================================================
#ifndef VALVE_VR_LIGHTING_INCLUDED
#define VALVE_VR_LIGHTING_INCLUDED
#include "UnityCG.cginc"
#include "UnityStandardBRDF.cginc"
#define Tex2DLevel( name, uv, flLevel ) name.SampleLevel( sampler##name, ( uv ).xy, flLevel )
#define Tex2DLevelFromSampler( texturename, samplername, uv, flLevel ) texturename.SampleLevel( sampler##samplername, ( uv ).xy, flLevel )
//---------------------------------------------------------------------------------------------------------------------------------------------------------
#define DEBUG_SHADOWS_SPLIT 0
//---------------------------------------------------------------------------------------------------------------------------------------------------------
#define MAX_LIGHTS 10
#define MAX_SHADOWMAP_PER_LIGHTS 6
#define MAX_DIRECTIONAL_SPLIT 4
#define LIGHT_TYPE_SPOT 0
#define LIGHT_TYPE_DIRECTIONAL 1
#define LIGHT_TYPE_POINT 2
#define CUBEMAPFACE_POSITIVE_X 0
#define CUBEMAPFACE_NEGATIVE_X 1
#define CUBEMAPFACE_POSITIVE_Y 2
#define CUBEMAPFACE_NEGATIVE_Y 3
#define CUBEMAPFACE_POSITIVE_Z 4
#define CUBEMAPFACE_NEGATIVE_Z 5
CBUFFER_START(ValveVrLighting)
int g_nNumLights;
bool g_bIndirectLightmaps = false;
float4 g_vLightColor[MAX_LIGHTS];
float4 g_vLightPosition_flInvRadius[MAX_LIGHTS];
float4 g_vLightDirection[MAX_LIGHTS];
float4 g_vLightShadowIndex_vLightParams[MAX_LIGHTS]; // x = Shadow index, y = Light cookie index, z = Diffuse enabled, w = Specular enabled
float4 g_vLightFalloffParams[MAX_LIGHTS]; // x = Linear falloff, y = Quadratic falloff, z = Radius squared for culling, w = type
float4 g_vSpotLightInnerOuterConeCosines[MAX_LIGHTS];
float4 g_vDirShadowSplitSpheres[MAX_DIRECTIONAL_SPLIT];
//float4x4 g_matWorldToLightCookie[ MAX_LIGHTS ];
float4x4 g_matWorldToShadow[MAX_LIGHTS * MAX_SHADOWMAP_PER_LIGHTS];
float4 g_vShadow3x3PCFTerms0;
float4 g_vShadow3x3PCFTerms1;
float4 g_vShadow3x3PCFTerms2;
float4 g_vShadow3x3PCFTerms3;
CBUFFER_END
// Override lightmap
sampler2D g_tOverrideLightmap;
uniform float3 g_vOverrideLightmapScale;
float g_flCubeMapScalar = 1.0;
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
struct LightingTerms_t
{
float3 vDiffuse;
float3 vSpecular;
float3 vIndirectDiffuse;
float3 vIndirectSpecular;
float3 vTransmissiveSunlight;
};
//---------------------------------------------------------------------------------------------------------------------------------------------------------
float CalculateGeometricRoughnessFactor(float3 vGeometricNormalWs)
{
float3 vNormalWsDdx = ddx(vGeometricNormalWs.xyz);
float3 vNormalWsDdy = ddy(vGeometricNormalWs.xyz);
float flGeometricRoughnessFactor = pow(saturate(max(dot(vNormalWsDdx.xyz, vNormalWsDdx.xyz), dot(vNormalWsDdy.xyz, vNormalWsDdy.xyz))), 0.333);
return flGeometricRoughnessFactor;
}
float2 AdjustRoughnessByGeometricNormal(float2 vRoughness, float3 vGeometricNormalWs)
{
float flGeometricRoughnessFactor = CalculateGeometricRoughnessFactor(vGeometricNormalWs.xyz);
//if ( Blink( 1.0 ) )
//vRoughness.xy = min( vRoughness.xy + flGeometricRoughnessFactor.xx, float2( 1.0, 1.0 ) );
//else
vRoughness.xy = max(vRoughness.xy, flGeometricRoughnessFactor.xx);
return vRoughness.xy;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
void RoughnessEllipseToScaleAndExp(float2 vRoughness, out float2 o_vDiffuseExponentOut, out float2 o_vSpecularExponentOut, out float2 o_vSpecularScaleOut)
{
o_vDiffuseExponentOut.xy = ((1.0 - vRoughness.xy) * 0.8) + 0.6; // 0.8 and 0.6 are magic numbers
o_vSpecularExponentOut.xy = exp2(pow(float2(1.0, 1.0) - vRoughness.xy, float2(1.5, 1.5)) * float2(14.0, 14.0)); // Outputs 1-16384
o_vSpecularScaleOut.xy = 1.0 - saturate(vRoughness.xy * 0.5); // This is an energy conserving scalar for the roughness exponent.
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
// Used for ( N.H^k ) * ( N.L )
//---------------------------------------------------------------------------------------------------------------------------------------------------------
float BlinnPhongModifiedNormalizationFactor(float k)
{
float flNumer = (k + 2.0) * (k + 4.0);
float flDenom = 8 * (exp2(-k * 0.5) + k);
return flNumer / flDenom;
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
float DistanceFalloff(float flDistToLightSq, float flLightInvRadius, float2 vFalloffParams)
{
// AV - My approximation to Unity's falloff function (I'll experiment with putting this into a texture later)
return lerp(1.0, (1.0 - pow(flDistToLightSq * flLightInvRadius * flLightInvRadius, 0.175)), vFalloffParams.x);
// AV - This is the VR Aperture Demo falloff function
//flDistToLightSq = max( flDistToLightSq, 8.0f ); // Can't be inside the light source (assuming radius^2 == 8.0f)
//
//float2 vInvRadiusAndInvRadiusSq = float2( flLightInvRadius, flLightInvRadius * flLightInvRadius );
//float2 vLightDistAndLightDistSq = float2( sqrt( flDistToLightSq ), flDistToLightSq );
//
//float flTruncation = dot( vFalloffParams.xy, vInvRadiusAndInvRadiusSq.xy ); // Constant amount to subtract to ensure that the light is zero past the light radius
//float flFalloff = dot( vFalloffParams.xy, vLightDistAndLightDistSq.xy );
//
//return saturate( ( 1.0f / flFalloff ) - flTruncation );
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
// Anisotropic diffuse and specular lighting based on 2D tangent-space axis-aligned roughness
//---------------------------------------------------------------------------------------------------------------------------------------------------------
float4 ComputeDiffuseAndSpecularTerms(bool bDiffuse, bool bSpecular,
float3 vNormalWs, float3 vEllipseUWs, float3 vEllipseVWs, float3 vPositionToLightDirWs, float3 vPositionToCameraDirWs,
float2 vDiffuseExponent, float2 vSpecularExponent, float2 vSpecularScale, float3 vReflectance, float flFresnelExponent)
{
float flNDotL = ClampToPositive(dot(vNormalWs.xyz, vPositionToLightDirWs.xyz));
// Diffuse
float flDiffuseTerm = 0.0;
if (bDiffuse)
{
/* Disabling anisotropic diffuse until we have a need for it. Isotropic diffuse should be enough.
// Project light vector onto each tangent plane
float3 vDiffuseNormalX = vPositionToLightDirWs.xyz - ( vEllipseUWs.xyz * dot( vPositionToLightDirWs.xyz, vEllipseUWs.xyz ) ); // Not normalized on purpose
float3 vDiffuseNormalY = vPositionToLightDirWs.xyz - ( vEllipseVWs.xyz * dot( vPositionToLightDirWs.xyz, vEllipseVWs.xyz ) ); // Not normalized on purpose
float flNDotLX = ClampToPositive( dot( vDiffuseNormalX.xyz, vPositionToLightDirWs.xyz ) );
flNDotLX = pow( flNDotLX, vDiffuseExponent.x * 0.5 );
float flNDotLY = ClampToPositive( dot( vDiffuseNormalY.xyz, vPositionToLightDirWs.xyz ) );
flNDotLY = pow( flNDotLY, vDiffuseExponent.y * 0.5 );
flDiffuseTerm = flNDotLX * flNDotLY;
flDiffuseTerm *= ( ( vDiffuseExponent.x * 0.5 + vDiffuseExponent.y * 0.5 ) + 1.0 ) * 0.5;
flDiffuseTerm *= flNDotL;
//*/
float flDiffuseExponent = (vDiffuseExponent.x + vDiffuseExponent.y) * 0.5;
flDiffuseTerm = pow(flNDotL, flDiffuseExponent) * ((flDiffuseExponent + 1.0) * 0.5);
}
// Specular
float3 vSpecularTerm = float3(0.0, 0.0, 0.0);
[branch] if (bSpecular)
{
float3 vHalfAngleDirWs = normalize(vPositionToLightDirWs.xyz + vPositionToCameraDirWs.xyz);
float flSpecularTerm = 0.0;
#if ( S_ANISOTROPIC_GLOSS ) // Adds 34 asm instructions compared to isotropic spec in #else below
{
float3 vSpecularNormalX = vHalfAngleDirWs.xyz - (vEllipseUWs.xyz * dot(vHalfAngleDirWs.xyz, vEllipseUWs.xyz)); // Not normalized on purpose
float3 vSpecularNormalY = vHalfAngleDirWs.xyz - (vEllipseVWs.xyz * dot(vHalfAngleDirWs.xyz, vEllipseVWs.xyz)); // Not normalized on purpose
float flNDotHX = ClampToPositive(dot(vSpecularNormalX.xyz, vHalfAngleDirWs.xyz));
float flNDotHkX = pow(flNDotHX, vSpecularExponent.x * 0.5);
flNDotHkX *= vSpecularScale.x;
float flNDotHY = ClampToPositive(dot(vSpecularNormalY.xyz, vHalfAngleDirWs.xyz));
float flNDotHkY = pow(flNDotHY, vSpecularExponent.y * 0.5);
flNDotHkY *= vSpecularScale.y;
flSpecularTerm = flNDotHkX * flNDotHkY;
}
#else
{
float flNDotH = saturate(dot(vNormalWs.xyz, vHalfAngleDirWs.xyz));
float flNDotHk = pow(flNDotH, dot(vSpecularExponent.xy, float2(0.5, 0.5)));
flNDotHk *= dot(vSpecularScale.xy, float2(0.33333, 0.33333)); // The 0.33333 is to match the spec of the aniso algorithm above with isotropic roughness values
flSpecularTerm = flNDotHk;
}
#endif
flSpecularTerm *= flNDotL; // This makes it modified Blinn-Phong
flSpecularTerm *= BlinnPhongModifiedNormalizationFactor(vSpecularExponent.x * 0.5 + vSpecularExponent.y * 0.5);
float flLDotH = ClampToPositive(dot(vPositionToLightDirWs.xyz, vHalfAngleDirWs.xyz));
float3 vMaxReflectance = vReflectance.rgb / (Luminance(vReflectance.rgb) + 0.0001);
float3 vFresnel = lerp(vReflectance.rgb, vMaxReflectance.rgb, pow(1.0 - flLDotH, flFresnelExponent));
vSpecularTerm.rgb = flSpecularTerm * vFresnel.rgb;
}
return float4(flDiffuseTerm, vSpecularTerm.rgb);
}
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// Filter weights: 20 33 20
// 33 55 33
// 20 33 20
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
#define VALVE_DECLARE_SHADOWMAP( tex ) Texture2D tex; SamplerComparisonState sampler##tex
#define VALVE_SAMPLE_SHADOW( tex, coord ) tex.SampleCmpLevelZero( sampler##tex, (coord).xy, (coord).z )
VALVE_DECLARE_SHADOWMAP(g_tShadowBuffer);
float ComputeShadow_PCF_3x3_Gaussian(float3 vPositionWs, float4x4 matWorldToShadow)
{
float4 vPositionTextureSpace = mul(float4(vPositionWs.xyz, 1.0), matWorldToShadow);
vPositionTextureSpace.xyz /= vPositionTextureSpace.w;
float2 shadowMapCenter = vPositionTextureSpace.xy;
//if ( ( frac( shadowMapCenter.x ) != shadowMapCenter.x ) || ( frac( shadowMapCenter.y ) != shadowMapCenter.y ) )
if ((shadowMapCenter.x < 0.0f) || (shadowMapCenter.x > 1.0f) || (shadowMapCenter.y < 0.0f) || (shadowMapCenter.y > 1.0f))
return 1.0f;
//float objDepth = saturate( vPositionTextureSpace.z - 0.000001 );
float objDepth = 1 - vPositionTextureSpace.z;
/* // Depth texture visualization
if ( 1 )
{
#define NUM_SAMPLES 128.0
float flSum = 0.0;
for ( int j = 0; j < NUM_SAMPLES; j++ )
{
flSum += ( 1.0 / NUM_SAMPLES ) * ( VALVE_SAMPLE_SHADOW( g_tShadowBuffer, float3( shadowMapCenter.xy, j / NUM_SAMPLES ) ).r );
}
return flSum;
}
//*/
//float flTexelEpsilonX = 1.0 / 4096.0;
//float flTexelEpsilonY = 1.0 / 4096.0;
//g_vShadow3x3PCFTerms0 = float4( 20.0 / 267.0, 33.0 / 267.0, 55.0 / 267.0, 0.0 );
//g_vShadow3x3PCFTerms1 = float4( flTexelEpsilonX, flTexelEpsilonY, -flTexelEpsilonX, -flTexelEpsilonY );
//g_vShadow3x3PCFTerms2 = float4( flTexelEpsilonX, flTexelEpsilonY, 0.0, 0.0 );
//g_vShadow3x3PCFTerms3 = float4( -flTexelEpsilonX, -flTexelEpsilonY, 0.0, 0.0 );
float4 v20Taps;
v20Taps.x = VALVE_SAMPLE_SHADOW(g_tShadowBuffer, float3(shadowMapCenter.xy + g_vShadow3x3PCFTerms1.xy, objDepth)).x; // 1 1
v20Taps.y = VALVE_SAMPLE_SHADOW(g_tShadowBuffer, float3(shadowMapCenter.xy + g_vShadow3x3PCFTerms1.zy, objDepth)).x; // -1 1
v20Taps.z = VALVE_SAMPLE_SHADOW(g_tShadowBuffer, float3(shadowMapCenter.xy + g_vShadow3x3PCFTerms1.xw, objDepth)).x; // 1 -1
v20Taps.w = VALVE_SAMPLE_SHADOW(g_tShadowBuffer, float3(shadowMapCenter.xy + g_vShadow3x3PCFTerms1.zw, objDepth)).x; // -1 -1
float flSum = dot(v20Taps.xyzw, float4(0.25, 0.25, 0.25, 0.25));
if ((flSum == 0.0) || (flSum == 1.0))
return flSum;
flSum *= g_vShadow3x3PCFTerms0.x * 4.0;
float4 v33Taps;
v33Taps.x = VALVE_SAMPLE_SHADOW(g_tShadowBuffer, float3(shadowMapCenter.xy + g_vShadow3x3PCFTerms2.xz, objDepth)).x; // 1 0
v33Taps.y = VALVE_SAMPLE_SHADOW(g_tShadowBuffer, float3(shadowMapCenter.xy + g_vShadow3x3PCFTerms3.xz, objDepth)).x; // -1 0
v33Taps.z = VALVE_SAMPLE_SHADOW(g_tShadowBuffer, float3(shadowMapCenter.xy + g_vShadow3x3PCFTerms3.zy, objDepth)).x; // 0 -1
v33Taps.w = VALVE_SAMPLE_SHADOW(g_tShadowBuffer, float3(shadowMapCenter.xy + g_vShadow3x3PCFTerms2.zy, objDepth)).x; // 0 1
flSum += dot(v33Taps.xyzw, g_vShadow3x3PCFTerms0.yyyy);
flSum += VALVE_SAMPLE_SHADOW(g_tShadowBuffer, float3(shadowMapCenter.xy, objDepth)).x * g_vShadow3x3PCFTerms0.z;
return flSum;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
float3 ComputeOverrideLightmap(float2 vLightmapUV)
{
float4 vLightmapTexel = tex2D(g_tOverrideLightmap, vLightmapUV.xy);
// This path looks over-saturated
//return g_vOverrideLightmapScale * ( unity_Lightmap_HDR.x * pow( vLightmapTexel.a, unity_Lightmap_HDR.y ) ) * vLightmapTexel.rgb;
// This path looks less broken
return g_vOverrideLightmapScale * (unity_Lightmap_HDR.x * vLightmapTexel.a) * sqrt(vLightmapTexel.rgb);
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
/**
* Gets the cascade weights based on the world position of the fragment and the positions of the split spheres for each cascade.
* Returns an invalid split index if past shadowDistance (ie 4 is invalid for cascade)
*/
float GetSplitSphereIndexForDirshadows(float3 wpos)
{
float3 fromCenter0 = wpos.xyz - g_vDirShadowSplitSpheres[0].xyz;
float3 fromCenter1 = wpos.xyz - g_vDirShadowSplitSpheres[1].xyz;
float3 fromCenter2 = wpos.xyz - g_vDirShadowSplitSpheres[2].xyz;
float3 fromCenter3 = wpos.xyz - g_vDirShadowSplitSpheres[3].xyz;
float4 distances2 = float4(dot(fromCenter0, fromCenter0), dot(fromCenter1, fromCenter1), dot(fromCenter2, fromCenter2), dot(fromCenter3, fromCenter3));
float4 vDirShadowSplitSphereSqRadii;
vDirShadowSplitSphereSqRadii.x = g_vDirShadowSplitSpheres[0].w;
vDirShadowSplitSphereSqRadii.y = g_vDirShadowSplitSpheres[1].w;
vDirShadowSplitSphereSqRadii.z = g_vDirShadowSplitSpheres[2].w;
vDirShadowSplitSphereSqRadii.w = g_vDirShadowSplitSpheres[3].w;
fixed4 weights = float4(distances2 < vDirShadowSplitSphereSqRadii);
weights.yzw = saturate(weights.yzw - weights.xyz);
return 4 - dot(weights, float4(4, 3, 2, 1));
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
void OverrideLightColorWithSplitDebugInfo(inout float3 lightColor, int shadowSplitIndex)
{
#if DEBUG_SHADOWS_SPLIT
// Slightly intensified colors of ShadowCascadeSplitGUI + 2 new for point light (ie splitIndex 4 and 5)
const fixed3 kSplitColors[6] =
{
fixed3(0.5, 0.5, 0.7),
fixed3(0.5, 0.7, 0.5),
fixed3(0.7, 0.7, 0.5),
fixed3(0.7, 0.5, 0.5),
fixed3(0.7, 0.5, 0.7),
fixed3(0.5, 0.7, 0.7),
};
lightColor = kSplitColors[shadowSplitIndex];
#endif
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
LightingTerms_t ComputeLighting(float3 vPositionWs, float3 vNormalWs, float3 vTangentUWs, float3 vTangentVWs, float2 vRoughness,
float3 vReflectance, float flFresnelExponent, float4 vLightmapUV)
{
LightingTerms_t o;
o.vDiffuse = float3(0.0, 0.0, 0.0);
o.vSpecular = float3(0.0, 0.0, 0.0);
o.vIndirectDiffuse = float3(0.0, 0.0, 0.0);
o.vIndirectSpecular = float3(0.0, 0.0, 0.0);
o.vTransmissiveSunlight = float3(0.0, 0.0, 0.0);
// Convert roughness to scale and exp
float2 vDiffuseExponent;
float2 vSpecularExponent;
float2 vSpecularScale;
RoughnessEllipseToScaleAndExp(vRoughness.xy, vDiffuseExponent.xy, vSpecularExponent.xy, vSpecularScale.xy);
// Get positions between the clip planes of the frustum in 0..1 coordinates
//float3 vPositionCs = float3( vPosition4Cs.xy / vPosition4Cs.w, vPosition4Cs.z );
//vPositionCs.xy = ( vPositionCs.xy * 0.5 ) + 0.5;
float3 vPositionToCameraDirWs = CalculatePositionToCameraDirWs(vPositionWs.xyz);
// Compute tangent frame relative to per-pixel normal
float3 vEllipseUWs = normalize(cross(vTangentVWs.xyz, vNormalWs.xyz));
float3 vEllipseVWs = normalize(cross(vNormalWs.xyz, vTangentUWs.xyz));
//-------------------------------------//
// Point, spot, and directional lights //
//-------------------------------------//
int nNumLightsUsed = 0;
[loop] for (int i = 0; i < g_nNumLights; i++)
{
float3 vPositionToLightRayWs = g_vLightPosition_flInvRadius[i].xyz - vPositionWs.xyz;
float flDistToLightSq = dot(vPositionToLightRayWs.xyz, vPositionToLightRayWs.xyz);
if (flDistToLightSq > g_vLightFalloffParams[i].z) // .z stores radius squared of light
{
// Outside light range
continue;
}
if (dot(vNormalWs.xyz, vPositionToLightRayWs.xyz) <= 0.0)
{
// Backface cull pixel to this light
continue;
}
float3 vPositionToLightDirWs = normalize(vPositionToLightRayWs.xyz);
float flOuterConeCos = g_vSpotLightInnerOuterConeCosines[i].y;
float flTemp = dot(vPositionToLightDirWs.xyz, -g_vLightDirection[i].xyz) - flOuterConeCos;
if (flTemp <= 0.0)
{
// Outside spotlight cone
continue;
}
float3 vSpotAtten = saturate(flTemp * g_vSpotLightInnerOuterConeCosines[i].z).xxx;
nNumLightsUsed++;
//[branch] if ( g_vLightShadowIndex_vLightParams[ i ].y != 0 ) // If has light cookie
//{
// // Light cookie
// float4 vPositionTextureSpace = mul( float4( vPositionWs.xyz, 1.0 ), g_matWorldToLightCookie[ i ] );
// vPositionTextureSpace.xyz /= vPositionTextureSpace.w;
// vSpotAtten.rgb = Tex3DLevel( g_tVrLightCookieTexture, vPositionTextureSpace.xyz, 0.0 ).rgb;
//}
float flLightFalloff = DistanceFalloff(flDistToLightSq, g_vLightPosition_flInvRadius[i].w, g_vLightFalloffParams[i].xy);
float flShadowScalar = 1.0;
int shadowSplitIndex = 0;
if (g_vLightShadowIndex_vLightParams[i].x != 0.0)
{
if (g_vLightFalloffParams[i].w == LIGHT_TYPE_DIRECTIONAL)
{
shadowSplitIndex = GetSplitSphereIndexForDirshadows(vPositionWs);
}
if (g_vLightFalloffParams[i].w == LIGHT_TYPE_POINT)
{
float3 absPos = abs(vPositionToLightDirWs);
shadowSplitIndex = (vPositionToLightDirWs.z > 0) ? CUBEMAPFACE_NEGATIVE_Z : CUBEMAPFACE_POSITIVE_Z;
if (absPos.x > absPos.y)
{
if (absPos.x > absPos.z)
{
shadowSplitIndex = (vPositionToLightDirWs.x > 0) ? CUBEMAPFACE_NEGATIVE_X : CUBEMAPFACE_POSITIVE_X;
}
}
else
{
if (absPos.y > absPos.z)
{
shadowSplitIndex = (vPositionToLightDirWs.y > 0) ? CUBEMAPFACE_NEGATIVE_Y : CUBEMAPFACE_POSITIVE_Y;
}
}
}
flShadowScalar = ComputeShadow_PCF_3x3_Gaussian(vPositionWs.xyz, g_matWorldToShadow[i * MAX_SHADOWMAP_PER_LIGHTS + shadowSplitIndex]);
if (flShadowScalar <= 0.0)
continue;
}
float4 vLightingTerms = ComputeDiffuseAndSpecularTerms(g_vLightShadowIndex_vLightParams[i].z != 0.0, g_vLightShadowIndex_vLightParams[i].w != 0.0,
vNormalWs.xyz, vEllipseUWs.xyz, vEllipseVWs.xyz,
vPositionToLightDirWs.xyz, vPositionToCameraDirWs.xyz,
vDiffuseExponent.xy, vSpecularExponent.xy, vSpecularScale.xy, vReflectance.rgb, flFresnelExponent);
float3 vLightColor = g_vLightColor[i].rgb;
OverrideLightColorWithSplitDebugInfo(vLightColor, shadowSplitIndex);
float3 vLightMask = vLightColor.rgb * flShadowScalar * flLightFalloff * vSpotAtten.rgb;
o.vDiffuse.rgb += vLightingTerms.xxx * vLightMask.rgb;
o.vSpecular.rgb += vLightingTerms.yzw * vLightMask.rgb;
}
/* // Visualize number of lights for the first 7 as RGBCMYW
if ( nNumLightsUsed == 0 )
o.vDiffuse.rgb = float3( 0.0, 0.0, 0.0 );
else if ( nNumLightsUsed == 1 )
o.vDiffuse.rgb = float3( 1.0, 0.0, 0.0 );
else if ( nNumLightsUsed == 2 )
o.vDiffuse.rgb = float3( 0.0, 1.0, 0.0 );
else if ( nNumLightsUsed == 3 )
o.vDiffuse.rgb = float3( 0.0, 0.0, 1.0 );
else if ( nNumLightsUsed == 4 )
o.vDiffuse.rgb = float3( 0.0, 1.0, 1.0 );
else if ( nNumLightsUsed == 5 )
o.vDiffuse.rgb = float3( 1.0, 0.0, 1.0 );
else if ( nNumLightsUsed == 6 )
o.vDiffuse.rgb = float3( 1.0, 1.0, 0.0 );
else
o.vDiffuse.rgb = float3( 1.0, 1.0, 1.0 );
o.vDiffuse.rgb *= float3( 2.0, 2.0, 2.0 );
o.vSpecular.rgb = float3( 0.0, 0.0, 0.0 );
return o;
//*/
// Apply specular reflectance to diffuse term (specular term already accounts for this in the fresnel equation)
o.vDiffuse.rgb *= (float3(1.0, 1.0, 1.0) - vReflectance.rgb);
//------------------//
// Indirect diffuse //
//------------------//
#if ( S_OVERRIDE_LIGHTMAP )
{
o.vIndirectDiffuse.rgb += ComputeOverrideLightmap(vLightmapUV.xy);
}
#elif ( LIGHTMAP_ON )
{
// Baked lightmaps
float4 bakedColorTex = Tex2DLevel(unity_Lightmap, vLightmapUV.xy, 0.0);
float3 bakedColor = DecodeLightmap(bakedColorTex);
#if ( DIRLIGHTMAP_OFF ) // Directional Mode = Non Directional
{
o.vIndirectDiffuse.rgb += bakedColor.rgb;
//o_gi.indirect.diffuse = bakedColor;
//
//#ifdef SHADOWS_SCREEN
// o_gi.indirect.diffuse = MixLightmapWithRealtimeAttenuation (o_gi.indirect.diffuse, data.atten, bakedColorTex);
//#endif // SHADOWS_SCREEN
}
#elif ( DIRLIGHTMAP_COMBINED ) // Directional Mode = Directional
{
//o.vIndirectDiffuse.rgb = float3( 0.0, 1.0, 0.0 );
float4 bakedDirTex = Tex2DLevelFromSampler(unity_LightmapInd, unity_Lightmap, vLightmapUV.xy, 0.0);
//float flHalfLambert = dot( vNormalWs.xyz, bakedDirTex.xyz - 0.5 ) + 0.5;
//o.vIndirectDiffuse.rgb += bakedColor.rgb * flHalfLambert / bakedDirTex.w;
float flHalfLambert = dot(vNormalWs.xyz, normalize(bakedDirTex.xyz - 0.5));// + ( 1.0 - length( bakedDirTex.xyz - 0.5 ) );
o.vIndirectDiffuse.rgb += bakedColor.rgb * flHalfLambert / (bakedDirTex.w);
//#ifdef SHADOWS_SCREEN
// o_gi.indirect.diffuse = MixLightmapWithRealtimeAttenuation (o_gi.indirect.diffuse, data.atten, bakedColorTex);
//#endif // SHADOWS_SCREEN
}
#elif ( DIRLIGHTMAP_SEPARATE ) // Directional Mode = Directional Specular
{
// Left halves of both intensity and direction lightmaps store direct light; right halves store indirect.
float2 vUvDirect = vLightmapUV.xy;
float2 vUvIndirect = vLightmapUV.xy + float2(0.5, 0.0);
// Direct Diffuse
float4 bakedDirTex = float4(0.0, 0.0, 0.0, 0.0);
if (!g_bIndirectLightmaps)
{
bakedDirTex = Tex2DLevelFromSampler(unity_LightmapInd, unity_Lightmap, vUvDirect.xy, 0.0);
//float flHalfLambert = dot( vNormalWs.xyz, bakedDirTex.xyz - 0.5 ) + 0.5;
//o.vDiffuse.rgb += bakedColor.rgb * flHalfLambert / bakedDirTex.w;
float flHalfLambert = ClampToPositive(dot(vNormalWs.xyz, normalize(bakedDirTex.xyz - 0.5)));// + ( 1.0 - length( bakedDirTex.xyz - 0.5 ) );
o.vDiffuse.rgb += bakedColor.rgb * flHalfLambert / (bakedDirTex.w);
}
// Indirect Diffuse
float4 bakedIndirTex = float4(0.0, 0.0, 0.0, 0.0);
float3 vBakedIndirectColor = float3(0.0, 0.0, 0.0);
if (1)
{
vBakedIndirectColor.rgb = DecodeLightmap(Tex2DLevel(unity_Lightmap, vUvIndirect.xy, 0.0));
bakedIndirTex = Tex2DLevelFromSampler(unity_LightmapInd, unity_Lightmap, vUvIndirect.xy, 0.0);
//float flHalfLambert = dot( vNormalWs.xyz, bakedIndirTex.xyz - 0.5 ) + 0.5;
//o.vIndirectDiffuse.rgb += vBakedIndirectColor.rgb * flHalfLambert / bakedIndirTex.w;
float flHalfLambert = dot(vNormalWs.xyz, normalize(bakedIndirTex.xyz - 0.5));// + ( 1.0 - length( bakedIndirTex.xyz - 0.5 ) );
o.vIndirectDiffuse.rgb += vBakedIndirectColor.rgb * flHalfLambert / (bakedIndirTex.w);
}
// Direct Specular
if (!g_bIndirectLightmaps)
{
UnityLight o_light;
o.vIndirectDiffuse.rgb += DecodeDirectionalSpecularLightmap(bakedColor, bakedDirTex, vNormalWs, false, 0, o_light);
float4 vLightingTerms = ComputeDiffuseAndSpecularTerms(false, true,
vNormalWs.xyz, vEllipseUWs.xyz, vEllipseVWs.xyz,
o_light.dir.xyz, vPositionToCameraDirWs.xyz,
vDiffuseExponent.xy, vSpecularExponent.xy, vSpecularScale.xy, vReflectance.rgb, flFresnelExponent);
float3 vLightColor = o_light.color;
float3 vLightMask = vLightColor.rgb;
o.vSpecular.rgb += vLightingTerms.yzw * vLightMask.rgb;
}
// Indirect Specular
//if ( 1 )
//{
// UnityLight o_light;
// o.vIndirectSpecular.rgb += DecodeDirectionalSpecularLightmap( vBakedIndirectColor, bakedIndirTex, vNormalWs, false, 0, o_light );
//}
}
#endif
}
#elif ( UNITY_SHOULD_SAMPLE_SH )
{
// Light probe
o.vIndirectDiffuse.rgb += ShadeSH9(float4(vNormalWs.xyz, 1.0));
}
#endif
#if ( DYNAMICLIGHTMAP_ON )
{
float4 realtimeColorTex = Tex2DLevel(unity_DynamicLightmap, vLightmapUV.zw, 0.0);
float3 realtimeColor = DecodeRealtimeLightmap(realtimeColorTex);
#if ( DIRLIGHTMAP_OFF )
{
o.vIndirectDiffuse.rgb += realtimeColor.rgb;
}
#elif ( DIRLIGHTMAP_COMBINED )
{
float4 realtimeDirTex = Tex2DLevelFromSampler(unity_DynamicDirectionality, unity_DynamicLightmap, vLightmapUV.zw, 0.0);
o.vIndirectDiffuse.rgb += DecodeDirectionalLightmap(realtimeColor, realtimeDirTex, vNormalWs);
}
#elif ( DIRLIGHTMAP_SEPARATE )
{
float4 realtimeDirTex = Tex2DLevelFromSampler(unity_DynamicDirectionality, unity_DynamicLightmap, vLightmapUV.zw, 0.0);
o.vIndirectDiffuse.rgb += DecodeDirectionalLightmap(realtimeColor, realtimeDirTex, vNormalWs);
UnityLight o_light;
float4 realtimeNormalTex = Tex2DLevelFromSampler(unity_DynamicNormal, unity_DynamicLightmap, vLightmapUV.zw, 0.0);
o.vIndirectSpecular.rgb += DecodeDirectionalSpecularLightmap(realtimeColor, realtimeDirTex, vNormalWs, true, realtimeNormalTex, o_light);
float4 vLightingTerms = ComputeDiffuseAndSpecularTerms(false, true,
vNormalWs.xyz, vEllipseUWs.xyz, vEllipseVWs.xyz,
o_light.dir.xyz, vPositionToCameraDirWs.xyz,
vDiffuseExponent.xy, vSpecularExponent.xy, vSpecularScale.xy, vReflectance.rgb, flFresnelExponent);
float3 vLightColor = o_light.color;
float3 vLightMask = vLightColor.rgb;
o.vSpecular.rgb += vLightingTerms.yzw * vLightMask.rgb;
}
#endif
}
#endif
//-------------------//
// Indirect specular //
//-------------------//
#if ( 1 )
{
float flRoughness = dot(vRoughness.xy, float2(0.5, 0.5));
float3 vReflectionDirWs = CalculateCameraReflectionDirWs(vPositionWs.xyz, vNormalWs.xyz);
float3 vReflectionDirWs0 = vReflectionDirWs.xyz;
#if ( UNITY_SPECCUBE_BOX_PROJECTION )
{
vReflectionDirWs0.xyz = BoxProjectedCubemapDirection(vReflectionDirWs.xyz, vPositionWs.xyz, unity_SpecCube0_ProbePosition, unity_SpecCube0_BoxMin, unity_SpecCube0_BoxMax);
}
#endif
float3 vEnvMap0 = max(0.0, Unity_GlossyEnvironment(UNITY_PASS_TEXCUBE(unity_SpecCube0), unity_SpecCube0_HDR, vReflectionDirWs0, flRoughness));
#if ( 0 )
{
const float flBlendFactor = 0.99999;
float flBlendLerp = saturate(unity_SpecCube0_BoxMin.w);
UNITY_BRANCH
if (flBlendLerp < flBlendFactor)
{
float3 vReflectionDirWs1 = vReflectionDirWs.xyz;
#if ( UNITY_SPECCUBE_BOX_PROJECTION )
{
vReflectionDirWs1.xyz = BoxProjectedCubemapDirection(vReflectionDirWs.xyz, vPositionWs.xyz, unity_SpecCube1_ProbePosition, unity_SpecCube1_BoxMin, unity_SpecCube1_BoxMax);
}
#endif
float3 vEnvMap1 = max(0.0, Unity_GlossyEnvironment(UNITY_PASS_TEXCUBE(unity_SpecCube1), unity_SpecCube1_HDR, vReflectionDirWs1, flRoughness));
o.vIndirectSpecular.rgb += lerp(vEnvMap1.rgb, vEnvMap0.rgb, flBlendLerp);
}
else
{
o.vIndirectSpecular.rgb += vEnvMap0.rgb;
}
}
#else
{
o.vIndirectSpecular.rgb += vEnvMap0.rgb;
}
#endif
}
#endif
// Apply fresnel to indirect specular
float flVDotN = saturate(dot(vPositionToCameraDirWs.xyz, vNormalWs.xyz));
float3 vMaxReflectance = vReflectance.rgb / (Luminance(vReflectance.rgb) + 0.0001);
float3 vFresnel = lerp(vReflectance.rgb, vMaxReflectance.rgb, pow(1.0 - flVDotN, flFresnelExponent));
o.vIndirectSpecular.rgb *= vFresnel.rgb;
o.vIndirectSpecular.rgb *= g_flCubeMapScalar; // !!! FIXME: This also contains lightmap spec
// Since we have indirect specular, apply reflectance to indirect diffuse
o.vIndirectDiffuse.rgb *= (float3(1.0, 1.0, 1.0) - vReflectance.rgb);
return o;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
LightingTerms_t ComputeLightingDiffuseOnly(float3 vPositionWs, float3 vNormalWs, float3 vTangentUWs, float3 vTangentVWs, float2 vRoughness, float4 vLightmapUV)
{
LightingTerms_t lightingTerms = ComputeLighting(vPositionWs, vNormalWs, vTangentUWs, vTangentVWs, vRoughness, 0.0, 1.0, vLightmapUV.xyzw);
lightingTerms.vSpecular = float3(0.0, 0.0, 0.0);
lightingTerms.vIndirectSpecular = float3(0.0, 0.0, 0.0);
return lightingTerms;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
float3 CubeMapBoxProjection(float3 vPositionCubemapLocal, float3 vNormalCubemapLocal, float3 vCameraPositionCubemapLocal, float3 vBoxMins, float3 vBoxMaxs)
{
float3 vCameraToPositionRayCubemapLocal = vPositionCubemapLocal.xyz - vCameraPositionCubemapLocal.xyz;
float3 vCameraToPositionRayReflectedCubemapLocal = reflect(vCameraToPositionRayCubemapLocal.xyz, vNormalCubemapLocal.xyz);
float3 vIntersectA = (vBoxMaxs.xyz - vPositionCubemapLocal.xyz) / vCameraToPositionRayReflectedCubemapLocal.xyz;
float3 vIntersectB = (vBoxMins.xyz - vPositionCubemapLocal.xyz) / vCameraToPositionRayReflectedCubemapLocal.xyz;
float3 vIntersect = max(vIntersectA.xyz, vIntersectB.xyz);
float flDistance = min(vIntersect.x, min(vIntersect.y, vIntersect.z));
float3 vReflectDirectionWs = vPositionCubemapLocal.xyz + vCameraToPositionRayReflectedCubemapLocal.xyz * flDistance;
return vReflectDirectionWs;
}
#endif