您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
179 行
8.4 KiB
179 行
8.4 KiB
using System;
|
|
using UnityEngine.Rendering;
|
|
|
|
namespace UnityEngine.Experimental.Rendering.LightweightPipeline
|
|
{
|
|
public struct ShadowSliceData
|
|
{
|
|
public Matrix4x4 shadowTransform;
|
|
public int offsetX;
|
|
public int offsetY;
|
|
public int resolution;
|
|
|
|
public void Clear()
|
|
{
|
|
shadowTransform = Matrix4x4.identity;
|
|
offsetX = offsetY = 0;
|
|
resolution = 1024;
|
|
}
|
|
}
|
|
|
|
public class LightweightShadowUtils
|
|
{
|
|
public static bool ExtractDirectionalLightMatrix(ref CullResults cullResults, ref ShadowData shadowData, int shadowLightIndex, int cascadeIndex, int shadowResolution, float shadowNearPlane, out Vector4 cascadeSplitDistance, out ShadowSliceData shadowSliceData, out Matrix4x4 viewMatrix, out Matrix4x4 projMatrix)
|
|
{
|
|
ShadowSplitData splitData;
|
|
bool success = cullResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(shadowLightIndex,
|
|
cascadeIndex, shadowData.directionalLightCascadeCount, shadowData.directionalLightCascades, shadowResolution, shadowNearPlane, out viewMatrix, out projMatrix,
|
|
out splitData);
|
|
|
|
float cullingSphereRadius = splitData.cullingSphere.w;
|
|
cascadeSplitDistance = splitData.cullingSphere;
|
|
cascadeSplitDistance.w = cullingSphereRadius * cullingSphereRadius;
|
|
|
|
shadowSliceData.offsetX = (cascadeIndex % 2) * shadowResolution;
|
|
shadowSliceData.offsetY = (cascadeIndex / 2) * shadowResolution;
|
|
shadowSliceData.resolution = shadowResolution;
|
|
shadowSliceData.shadowTransform = GetShadowTransform(projMatrix, viewMatrix);
|
|
|
|
// If we have shadow cascades baked into the atlas we bake cascade transform
|
|
// in each shadow matrix to save shader ALU and L/S
|
|
if (shadowData.directionalLightCascadeCount > 1)
|
|
ApplySliceTransform(ref shadowSliceData, shadowData.directionalShadowAtlasWidth, shadowData.directionalShadowAtlasHeight);
|
|
|
|
return success;
|
|
}
|
|
|
|
public static bool ExtractSpotLightMatrix(ref CullResults cullResults, ref ShadowData shadowData, int shadowLightIndex, out Matrix4x4 shadowMatrix, out Matrix4x4 viewMatrix, out Matrix4x4 projMatrix)
|
|
{
|
|
ShadowSplitData splitData;
|
|
bool success = cullResults.ComputeSpotShadowMatricesAndCullingPrimitives(shadowLightIndex, out viewMatrix, out projMatrix, out splitData);
|
|
shadowMatrix = GetShadowTransform(projMatrix, viewMatrix);
|
|
return success;
|
|
}
|
|
|
|
public static void RenderShadowSlice(CommandBuffer cmd, ref ScriptableRenderContext context,
|
|
ref ShadowSliceData shadowSliceData,
|
|
Matrix4x4 proj, Matrix4x4 view, DrawShadowsSettings settings)
|
|
{
|
|
cmd.SetViewport(new Rect(shadowSliceData.offsetX, shadowSliceData.offsetY, shadowSliceData.resolution, shadowSliceData.resolution));
|
|
cmd.EnableScissorRect(new Rect(shadowSliceData.offsetX + 4, shadowSliceData.offsetY + 4, shadowSliceData.resolution - 8, shadowSliceData.resolution - 8));
|
|
|
|
cmd.SetViewProjectionMatrices(view, proj);
|
|
context.ExecuteCommandBuffer(cmd);
|
|
cmd.Clear();
|
|
context.DrawShadows(ref settings);
|
|
cmd.DisableScissorRect();
|
|
context.ExecuteCommandBuffer(cmd);
|
|
cmd.Clear();
|
|
}
|
|
|
|
public static int GetMaxTileResolutionInAtlas(int atlasWidth, int atlasHeight, int tileCount)
|
|
{
|
|
int resolution = Mathf.Min(atlasWidth, atlasHeight);
|
|
if (tileCount > Mathf.Log(resolution))
|
|
{
|
|
Debug.LogError(
|
|
String.Format(
|
|
"Cannot fit {0} tiles into current shadowmap atlas of size ({1}, {2}). ShadowMap Resolution set to zero.",
|
|
tileCount, atlasWidth, atlasHeight));
|
|
return 0;
|
|
}
|
|
|
|
int currentTileCount = atlasWidth / resolution * atlasHeight / resolution;
|
|
while (currentTileCount < tileCount)
|
|
{
|
|
resolution = resolution >> 1;
|
|
currentTileCount = atlasWidth / resolution * atlasHeight / resolution;
|
|
}
|
|
return resolution;
|
|
}
|
|
|
|
public static void ApplySliceTransform(ref ShadowSliceData shadowSliceData, int atlasWidth, int atlasHeight)
|
|
{
|
|
Matrix4x4 sliceTransform = Matrix4x4.identity;
|
|
float oneOverAtlasWidth = 1.0f / atlasWidth;
|
|
float oneOverAtlasHeight = 1.0f / atlasHeight;
|
|
sliceTransform.m00 = shadowSliceData.resolution * oneOverAtlasWidth;
|
|
sliceTransform.m11 = shadowSliceData.resolution * oneOverAtlasHeight;
|
|
sliceTransform.m03 = shadowSliceData.offsetX * oneOverAtlasWidth;
|
|
sliceTransform.m13 = shadowSliceData.offsetY * oneOverAtlasHeight;
|
|
|
|
// Apply shadow slice scale and offset
|
|
shadowSliceData.shadowTransform = sliceTransform * shadowSliceData.shadowTransform;
|
|
}
|
|
|
|
public static void SetupShadowCasterConstants(CommandBuffer cmd, ref VisibleLight visibleLight, Matrix4x4 proj, float cascadeResolution)
|
|
{
|
|
Light light = visibleLight.light;
|
|
float bias = 0.0f;
|
|
float normalBias = 0.0f;
|
|
|
|
// Use same kernel radius as built-in pipeline so we can achieve same bias results
|
|
// with the default light bias parameters.
|
|
const float kernelRadius = 3.65f;
|
|
|
|
if (visibleLight.lightType == LightType.Directional)
|
|
{
|
|
// Scale bias by cascade's world space depth range.
|
|
// Directional shadow lights have orthogonal projection.
|
|
// proj.m22 = -2 / (far - near) since the projection's depth range is [-1.0, 1.0]
|
|
// In order to be correct we should multiply bias by 0.5 but this introducing aliasing along cascades more visible.
|
|
float sign = (SystemInfo.usesReversedZBuffer) ? 1.0f : -1.0f;
|
|
bias = light.shadowBias * proj.m22 * sign;
|
|
|
|
// Currently only square POT cascades resolutions are used.
|
|
// We scale normalBias
|
|
double frustumWidth = 2.0 / (double)proj.m00;
|
|
double frustumHeight = 2.0 / (double)proj.m11;
|
|
float texelSizeX = (float)(frustumWidth / (double)cascadeResolution);
|
|
float texelSizeY = (float)(frustumHeight / (double)cascadeResolution);
|
|
float texelSize = Mathf.Max(texelSizeX, texelSizeY);
|
|
|
|
// Since we are applying normal bias on caster side we want an inset normal offset
|
|
// thus we use a negative normal bias.
|
|
normalBias = -light.shadowNormalBias * texelSize * kernelRadius;
|
|
}
|
|
else if (visibleLight.lightType == LightType.Spot)
|
|
{
|
|
float sign = (SystemInfo.usesReversedZBuffer) ? -1.0f : 1.0f;
|
|
bias = light.shadowBias * sign;
|
|
normalBias = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning("Only spot and directional shadow casters are supported in lightweight pipeline");
|
|
}
|
|
|
|
Vector3 lightDirection = -visibleLight.localToWorld.GetColumn(2);
|
|
cmd.SetGlobalVector("_ShadowBias", new Vector4(bias, normalBias, 0.0f, 0.0f));
|
|
cmd.SetGlobalVector("_LightDirection", new Vector4(lightDirection.x, lightDirection.y, lightDirection.z, 0.0f));
|
|
}
|
|
|
|
static Matrix4x4 GetShadowTransform(Matrix4x4 proj, Matrix4x4 view)
|
|
{
|
|
// Currently CullResults ComputeDirectionalShadowMatricesAndCullingPrimitives doesn't
|
|
// apply z reversal to projection matrix. We need to do it manually here.
|
|
if (SystemInfo.usesReversedZBuffer)
|
|
{
|
|
proj.m20 = -proj.m20;
|
|
proj.m21 = -proj.m21;
|
|
proj.m22 = -proj.m22;
|
|
proj.m23 = -proj.m23;
|
|
}
|
|
|
|
Matrix4x4 worldToShadow = proj * view;
|
|
|
|
var textureScaleAndBias = Matrix4x4.identity;
|
|
textureScaleAndBias.m00 = 0.5f;
|
|
textureScaleAndBias.m11 = 0.5f;
|
|
textureScaleAndBias.m22 = 0.5f;
|
|
textureScaleAndBias.m03 = 0.5f;
|
|
textureScaleAndBias.m23 = 0.5f;
|
|
textureScaleAndBias.m13 = 0.5f;
|
|
|
|
// Apply texture scale and offset to save a MAD in shader.
|
|
return textureScaleAndBias * worldToShadow;
|
|
}
|
|
}
|
|
}
|