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