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

272 行
15 KiB

namespace UnityEngine.Experimental.Rendering
{
public class ShadowUtilsConstants
{
// Matches ScriptableShadowsUtility.cpp
public enum CubemapEdge
{
kCubeEdgePX_PY = 0,
kCubeEdgePX_NY,
kCubeEdgePX_PZ,
kCubeEdgePX_NZ,
kCubeEdgeNX_PY,
kCubeEdgeNX_NY,
kCubeEdgeNX_PZ,
kCubeEdgeNX_NZ,
kCubeEdgePY_PZ,
kCubeEdgePY_NZ,
kCubeEdgeNY_PZ,
kCubeEdgeNY_NZ,
kCubeEdge_Count
};
public static readonly CubemapEdge[,] kCubemapEdgesPerFace = new CubemapEdge[6,4]
{
{ CubemapEdge.kCubeEdgePX_PY, CubemapEdge.kCubeEdgePX_NY, CubemapEdge.kCubeEdgePX_PZ, CubemapEdge.kCubeEdgePX_NZ }, // PX
{ CubemapEdge.kCubeEdgeNX_PY, CubemapEdge.kCubeEdgeNX_NY, CubemapEdge.kCubeEdgeNX_PZ, CubemapEdge.kCubeEdgeNX_NZ }, // NX
{ CubemapEdge.kCubeEdgePX_PY, CubemapEdge.kCubeEdgeNX_PY, CubemapEdge.kCubeEdgePY_PZ, CubemapEdge.kCubeEdgePY_NZ }, // PY
{ CubemapEdge.kCubeEdgePX_NY, CubemapEdge.kCubeEdgeNX_NY, CubemapEdge.kCubeEdgeNY_PZ, CubemapEdge.kCubeEdgeNY_NZ }, // NY
{ CubemapEdge.kCubeEdgePX_PZ, CubemapEdge.kCubeEdgeNX_PZ, CubemapEdge.kCubeEdgePY_PZ, CubemapEdge.kCubeEdgeNY_PZ }, // PZ
{ CubemapEdge.kCubeEdgePX_NZ, CubemapEdge.kCubeEdgeNX_NZ, CubemapEdge.kCubeEdgePY_NZ, CubemapEdge.kCubeEdgeNY_NZ } // NZ
};
const float oneOverSqr2 = 0.70710678118654752440084436210485f;
public static readonly Vector3[] kCubemapEdgeDirections = new Vector3[(int)CubemapEdge.kCubeEdge_Count]
{
new Vector3( oneOverSqr2, oneOverSqr2, 0 ),
new Vector3( oneOverSqr2, -oneOverSqr2, 0 ),
new Vector3( oneOverSqr2, 0, oneOverSqr2 ),
new Vector3( oneOverSqr2, 0, -oneOverSqr2 ),
new Vector3( -oneOverSqr2, oneOverSqr2, 0 ),
new Vector3( -oneOverSqr2, -oneOverSqr2, 0 ),
new Vector3( -oneOverSqr2, 0, oneOverSqr2 ),
new Vector3( -oneOverSqr2, 0, -oneOverSqr2 ),
new Vector3( 0, oneOverSqr2, oneOverSqr2 ),
new Vector3( 0, oneOverSqr2, -oneOverSqr2 ),
new Vector3( 0, -oneOverSqr2, oneOverSqr2 ),
new Vector3( 0, -oneOverSqr2, -oneOverSqr2 )
};
// Cubemap faces with flipped z coordinate.
// These matrices do NOT match what we have in Skybox.cpp.
// The C++ runtime flips y as well and requires patching up
// the culling state. Using these matrices keeps the winding
// order, but may need some special treatment if rendering
// into an actual cubemap.
public static readonly Matrix4x4[] kCubemapFaces = new Matrix4x4[]
{
new Matrix4x4( // pos X
new Vector4( 0.0f, 0.0f, -1.0f, 0.0f ),
new Vector4( 0.0f, 1.0f, 0.0f, 0.0f ),
new Vector4( -1.0f, 0.0f, 0.0f, 0.0f ),
new Vector4( 0.0f, 0.0f, 0.0f, 1.0f ) ),
new Matrix4x4( // neg x
new Vector4( 0.0f, 0.0f, 1.0f, 0.0f ),
new Vector4( 0.0f, 1.0f, 0.0f, 0.0f ),
new Vector4( 1.0f, 0.0f, 0.0f, 0.0f ),
new Vector4( 0.0f, 0.0f, 0.0f, 1.0f ) ),
new Matrix4x4( // pos y
new Vector4( 1.0f, 0.0f, 0.0f, 0.0f ),
new Vector4( 0.0f, 0.0f, -1.0f, 0.0f ),
new Vector4( 0.0f, -1.0f, 0.0f, 0.0f ),
new Vector4( 0.0f, 0.0f, 0.0f, 1.0f ) ),
new Matrix4x4( // neg y
new Vector4( 1.0f, 0.0f, 0.0f, 0.0f ),
new Vector4( 0.0f, 0.0f, 1.0f, 0.0f ),
new Vector4( 0.0f, 1.0f, 0.0f, 0.0f ),
new Vector4( 0.0f, 0.0f, 0.0f, 1.0f ) ),
new Matrix4x4( // pos z
new Vector4( 1.0f, 0.0f, 0.0f, 0.0f ),
new Vector4( 0.0f, 1.0f, 0.0f, 0.0f ),
new Vector4( 0.0f, 0.0f, -1.0f, 0.0f ),
new Vector4( 0.0f, 0.0f, 0.0f, 1.0f ) ),
new Matrix4x4( // neg z
new Vector4( -1.0f, 0.0f, 0.0f, 0.0f ),
new Vector4( 0.0f, 1.0f, 0.0f, 0.0f ),
new Vector4( 0.0f, 0.0f, 1.0f, 0.0f ),
new Vector4( 0.0f, 0.0f, 0.0f, 1.0f ) )
};
}
public class ShadowUtils
{
public static void InvertView( ref Matrix4x4 view, out Matrix4x4 invview )
{
invview = Matrix4x4.zero;
invview.m00 = view.m00; invview.m01 = view.m10; invview.m02 = view.m20;
invview.m10 = view.m01; invview.m11 = view.m11; invview.m12 = view.m21;
invview.m20 = view.m02; invview.m21 = view.m12; invview.m22 = view.m22;
invview.m33 = 1.0f;
invview.m03 = -(invview.m00 * view.m03 + invview.m01 * view.m13 + invview.m02 * view.m23);
invview.m13 = -(invview.m10 * view.m03 + invview.m11 * view.m13 + invview.m12 * view.m23);
invview.m23 = -(invview.m20 * view.m03 + invview.m21 * view.m13 + invview.m22 * view.m23);
}
public static void InvertOrthographic( ref Matrix4x4 proj, ref Matrix4x4 view, out Matrix4x4 vpinv )
{
Matrix4x4 invview;
InvertView( ref view, out invview );
Matrix4x4 invproj = Matrix4x4.zero;
invproj.m00 = 1.0f / proj.m00;
invproj.m11 = 1.0f / proj.m11;
invproj.m22 = 1.0f / proj.m22;
invproj.m33 = 1.0f;
invproj.m03 = proj.m03 * invproj.m00;
invproj.m13 = proj.m13 * invproj.m11;
invproj.m23 = - proj.m23 * invproj.m22;
vpinv = invview * invproj;
}
public static void InvertPerspective( ref Matrix4x4 proj, ref Matrix4x4 view, out Matrix4x4 vpinv )
{
Matrix4x4 invview;
InvertView(ref view, out invview);
Matrix4x4 invproj = Matrix4x4.zero;
invproj.m00 = 1.0f / proj.m00;
invproj.m03 = proj.m02 * invproj.m00;
invproj.m11 = 1.0f / proj.m11;
invproj.m13 = proj.m12 * invproj.m11;
invproj.m22 = 0.0f;
invproj.m23 = -1.0f;
invproj.m33 = proj.m22 / proj.m23;
invproj.m32 = invproj.m33 / proj.m22;
vpinv = invview * invproj;
}
public static Matrix4x4 ExtractSpotLightMatrix( VisibleLight vl, float guardAngle, out Matrix4x4 view, out Matrix4x4 proj, out Matrix4x4 deviceProj, out Matrix4x4 vpinverse, out Vector4 lightDir, out ShadowSplitData splitData )
{
splitData = new ShadowSplitData();
splitData.cullingSphere.Set( 0.0f, 0.0f, 0.0f, float.NegativeInfinity );
splitData.cullingPlaneCount = 0;
// get lightDir
lightDir = vl.light.transform.forward;
// calculate view
Matrix4x4 scaleMatrix = Matrix4x4.identity;
scaleMatrix.m22 = -1.0f;
view = scaleMatrix * vl.localToWorld.inverse;
// following code is from SharedLightData::GetNearPlaneMinBound
float percentageBound = 0.01f * vl.light.range;
float fixedBound = 0.1f;
float nearmin = fixedBound <= percentageBound ? fixedBound : percentageBound;
// calculate projection
float zfar = vl.range;
float znear = vl.light.shadowNearPlane >= nearmin ? vl.light.shadowNearPlane : nearmin;
float fov = vl.spotAngle + guardAngle;
proj = Matrix4x4.Perspective(fov, 1.0f, znear, zfar);
// and the compound (deviceProj will potentially inverse-Z)
deviceProj = GL.GetGPUProjectionMatrix( proj, false );
InvertPerspective( ref deviceProj, ref view, out vpinverse );
return deviceProj * view;
}
public static Matrix4x4 ExtractPointLightMatrix( VisibleLight vl, uint faceIdx, float guardAngle, out Matrix4x4 view, out Matrix4x4 proj, out Matrix4x4 deviceProj, out Matrix4x4 vpinverse, out Vector4 lightDir, out ShadowSplitData splitData )
{
if( faceIdx > (uint) CubemapFace.NegativeZ )
Debug.LogError( "Tried to extract cubemap face " + faceIdx + "." );
splitData = new ShadowSplitData();
splitData.cullingSphere.Set( 0.0f, 0.0f, 0.0f, float.NegativeInfinity );
splitData.cullingPlaneCount = 4;
// get lightDir
lightDir = vl.light.transform.forward;
// calculate the view matrices
Vector3 lpos = vl.light.transform.position;
view = ShadowUtilsConstants.kCubemapFaces[faceIdx];
Vector3 inverted_viewpos = ShadowUtilsConstants.kCubemapFaces[faceIdx].MultiplyPoint( -lpos );
view.SetColumn( 3, new Vector4( inverted_viewpos.x, inverted_viewpos.y, inverted_viewpos.z, 1.0f ) );
for( int i = 0; i < 4; ++i )
{
ShadowUtilsConstants.CubemapEdge cubemapEdge = ShadowUtilsConstants.kCubemapEdgesPerFace[faceIdx,i];
Vector3 cullingPlaneDirection = ShadowUtilsConstants.kCubemapEdgeDirections[(int)cubemapEdge];
splitData.SetCullingPlane( i, new Plane( cullingPlaneDirection, lpos ) );
}
// following code is from SharedLightData::GetNearPlaneMinBound
float percentageBound = 0.01f * vl.light.range;
float fixedBound = 0.1f;
float nearmin = fixedBound <= percentageBound ? fixedBound : percentageBound;
// calculate projection
float farPlane = vl.range;
float nearPlane = vl.light.shadowNearPlane >= nearmin ? vl.light.shadowNearPlane : nearmin;
proj = Matrix4x4.Perspective( 90.0f + guardAngle, 1.0f, nearPlane, farPlane );
// and the compound (deviceProj will potentially inverse-Z)
deviceProj = GL.GetGPUProjectionMatrix( proj, false );
InvertPerspective( ref deviceProj, ref view, out vpinverse );
return deviceProj * view;
}
public static Matrix4x4 ExtractDirectionalLightMatrix( VisibleLight vl, uint cascadeIdx, int cascadeCount, float[] splitRatio, float nearPlaneOffset, uint width, uint height, out Matrix4x4 view, out Matrix4x4 proj, out Matrix4x4 deviceProj, out Matrix4x4 vpinverse, out Vector4 lightDir, out ShadowSplitData splitData, CullResults cullResults, int lightIndex )
{
Debug.Assert( width == height, "Currently the cascaded shadow mapping code requires square cascades." );
splitData = new ShadowSplitData();
splitData.cullingSphere.Set(0.0f, 0.0f, 0.0f, float.NegativeInfinity);
splitData.cullingPlaneCount = 0;
// get lightDir
lightDir = vl.light.transform.forward;
// TODO: At some point this logic should be moved to C#, then the parameters cullResults and lightIndex can be removed as well
// For directional lights shadow data is extracted from the cullResults, so that needs to be somehow provided here.
// Check ScriptableShadowsUtility.cpp ComputeDirectionalShadowMatricesAndCullingPrimitives(...) for details.
Vector3 ratios = new Vector3();
for( int i = 0, cnt = splitRatio.Length < 3 ? splitRatio.Length : 3; i < cnt; i++ )
ratios[i] = splitRatio[i];
cullResults.ComputeDirectionalShadowMatricesAndCullingPrimitives( lightIndex, (int) cascadeIdx, cascadeCount, ratios, (int) width, nearPlaneOffset, out view, out proj, out splitData );
// and the compound (deviceProj will potentially inverse-Z)
deviceProj = GL.GetGPUProjectionMatrix( proj, false );
InvertOrthographic( ref deviceProj, ref view, out vpinverse );
return deviceProj * view;
}
public static float CalcGuardAnglePerspective( float angleInDeg, float resolution, float filterWidth, float normalBiasMax, float guardAngleMaxInDeg )
{
float angleInRad = angleInDeg * 0.5f * Mathf.Deg2Rad;
float res = 2.0f / resolution;
float texelSize = Mathf.Cos( angleInRad ) * res;
float beta = normalBiasMax * texelSize * 1.4142135623730950488016887242097f;
float guardAngle = Mathf.Atan( beta );
texelSize = Mathf.Tan( angleInRad + guardAngle ) * res;
guardAngle = Mathf.Atan( (resolution + Mathf.Ceil( filterWidth )) * texelSize * 0.5f ) * 2.0f * Mathf.Rad2Deg - angleInDeg;
guardAngle *= 2.0f;
return guardAngle < guardAngleMaxInDeg ? guardAngle : guardAngleMaxInDeg;
}
public static GPUShadowAlgorithm Pack( ShadowAlgorithm algo, ShadowVariant vari, ShadowPrecision prec )
{
int precshift = ShadowConstants.Bits.k_ShadowVariant + ShadowConstants.Bits.k_ShadowAlgorithm;
int algoshift = ShadowConstants.Bits.k_ShadowVariant;
return (GPUShadowAlgorithm) ( (int) prec << precshift | ((int) algo << algoshift) | (int)vari);
}
public static ShadowAlgorithm ExtractAlgorithm( GPUShadowAlgorithm gpuAlgo ) { return (ShadowAlgorithm) ( ShadowConstants.Masks.k_ShadowAlgorithm & ((int)gpuAlgo >> ShadowConstants.Bits.k_ShadowVariant) ); }
public static ShadowVariant ExtractVariant( GPUShadowAlgorithm gpuAlgo ) { return (ShadowVariant ) ( ShadowConstants.Masks.k_ShadowVariant & (int)gpuAlgo ); }
public static ShadowPrecision ExtractPrecision( GPUShadowAlgorithm gpuAlgo ) { return (ShadowPrecision) ( ShadowConstants.Masks.k_ShadowPrecision & ((int)gpuAlgo >> (ShadowConstants.Bits.k_ShadowVariant + ShadowConstants.Bits.k_ShadowAlgorithm)) ); }
public static void Unpack( GPUShadowAlgorithm gpuAlgo, out ShadowAlgorithm algo, out ShadowVariant vari, out ShadowPrecision prec )
{
algo = ExtractAlgorithm( gpuAlgo );
vari = ExtractVariant( gpuAlgo );
prec = ExtractPrecision( gpuAlgo );
}
public static GPUShadowAlgorithm ClearPrecision( GPUShadowAlgorithm gpuAlgo )
{
var algo = ExtractAlgorithm( gpuAlgo );
var vari = ExtractVariant( gpuAlgo );
return Pack( algo, vari, ShadowPrecision.Low );
}
public static float Asfloat( uint val ) { unsafe { return *((float*)&val); } }
public static float Asfloat( int val ) { unsafe { return *((float*)&val); } }
public static int Asint( float val ) { unsafe { return *((int*)&val); } }
public static uint Asuint( float val ) { unsafe { return *((uint*)&val); } }
}
} // end of namespace UnityEngine.Experimental.ScriptableRenderLoop