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

269 行
14 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