您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
688 行
30 KiB
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
|