https://catlikecoding.com/unity/tutorials/scriptable-render-pipeline/post-processing/
using Unity.Collections;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Experimental.GlobalIllumination;
using LightType = UnityEngine.LightType;
using Conditional = System.Diagnostics.ConditionalAttribute;
public class MyPipeline : RenderPipeline
{
const int maxVisibleLights = 16;
const string cascadedShadowsHardKeyword = "_CASCADED_SHADOWS_HARD";
const string cascadedShadowsSoftKeyword = "_CASCADED_SHADOWS_SOFT";
const string shadowsHardKeyword = "_SHADOWS_HARD";
const string shadowsSoftKeyword = "_SHADOWS_SOFT";
const string shadowmaskKeyword = "_SHADOWMASK";
const string distanceShadowmaskKeyword = "_DISTANCE_SHADOWMASK";
const string subtractiveLightingKeyword = "_SUBTRACTIVE_LIGHTING";
static int visibleLightColorsId =
Shader.PropertyToID("_VisibleLightColors");
static int visibleLightDirectionsOrPositionsId =
Shader.PropertyToID("_VisibleLightDirectionsOrPositions");
static int visibleLightAttenuationsId =
Shader.PropertyToID("_VisibleLightAttenuations");
static int visibleLightSpotDirectionsId =
Shader.PropertyToID("_VisibleLightSpotDirections");
static int visibleLightOcclusionMasksId =
Shader.PropertyToID("_VisibleLightOcclusionMasks");
static int lightIndicesOffsetAndCountID =
Shader.PropertyToID("unity_LightIndicesOffsetAndCount");
static int shadowMapId = Shader.PropertyToID("_ShadowMap");
static int cascadedShadowMapId = Shader.PropertyToID("_CascadedShadowMap");
static int worldToShadowMatricesId =
Shader.PropertyToID("_WorldToShadowMatrices");
static int worldToShadowCascadeMatricesId =
Shader.PropertyToID("_WorldToShadowCascadeMatrices");
static int shadowBiasId = Shader.PropertyToID("_ShadowBias");
static int shadowDataId = Shader.PropertyToID("_ShadowData");
static int shadowMapSizeId = Shader.PropertyToID("_ShadowMapSize");
static int cascadedShadowMapSizeId =
Shader.PropertyToID("_CascadedShadowMapSize");
static int cascadedShadoStrengthId =
Shader.PropertyToID("_CascadedShadowStrength");
static int globalShadowDataId = Shader.PropertyToID("_GlobalShadowData");
static int cascadeCullingSpheresId =
Shader.PropertyToID("_CascadeCullingSpheres");
static int subtractiveShadowColorId =
Shader.PropertyToID("_SubtractiveShadowColor");
static int ditherTextureId = Shader.PropertyToID("_DitherTexture");
static int ditherTextureSTId = Shader.PropertyToID("_DitherTexture_ST");
static int cameraColorTextureId = Shader.PropertyToID("_CameraColorTexture");
static int cameraDepthTextureId = Shader.PropertyToID("_CameraDepthTexture");
//这里有5个元素,第一个是默认的
//有16个灯,每个灯用一个通道
//第一个元素:new Vector4(-1f, 0f, 0f, 0f)——是为了有的灯不使用shadowmask
static Vector4[] occlusionMasks = {
new Vector4(-1f, 0f, 0f, 0f),
new Vector4(1f, 0f, 0f, 0f),
new Vector4(0f, 1f, 0f, 0f),
new Vector4(0f, 0f, 1f, 0f),
new Vector4(0f, 0f, 0f, 1f)
};
Vector4[] visibleLightColors = new Vector4[maxVisibleLights];
Vector4[] visibleLightDirectionsOrPositions = new Vector4[maxVisibleLights];
Vector4[] visibleLightAttenuations = new Vector4[maxVisibleLights];
Vector4[] visibleLightSpotDirections = new Vector4[maxVisibleLights];
Vector4[] visibleLightOcclusionMasks = new Vector4[maxVisibleLights];
CullResults cull; //Culling results (visible objects, lights, reflection probes).
RenderTexture shadowMap, cascadedShadowMap;
Vector4[] shadowData = new Vector4[maxVisibleLights];
Matrix4x4[] worldToShadowMatrices = new Matrix4x4[maxVisibleLights];
Matrix4x4[] worldToShadowCascadeMatrices = new Matrix4x4[5];
Vector4[] cascadeCullingSpheres = new Vector4[4];
Material errorMaterial;
//CommandBuffer是new出来的
//List of graphics commands to execute.
CommandBuffer cameraBuffer = new CommandBuffer { name = "Render Camera" };
CommandBuffer shadowBuffer = new CommandBuffer { name = "Render Shadows" };
CommandBuffer postProcessingBuffer = new CommandBuffer { name = "Post-Processing" };
DrawRendererFlags drawFlags;
int shadowMapSize;
int shadowTileCount;
float shadowDistance;
int shadowCascades;
Vector3 shadowCascadeSplit;
bool mainLightExists;
Vector4 globalShadowData;
Texture2D ditherTexture;
float ditherAnimationFrameDuration;
Vector4[] ditherSTs;
float lastDitherTime;
int ditherSTIndex = -1;
MyPostProcessingStack defaultStack;
public MyPipeline(
bool dynamicBatching, bool instancing, MyPostProcessingStack defaultStack,
Texture2D ditherTexture, float ditherAnimationSpeed,
int shadowMapSize, float shadowDistance, float shadowFadeRange,
int shadowCascades, Vector3 shadowCascasdeSplit
)
{
GraphicsSettings.lightsUseLinearIntensity = true;
if (SystemInfo.usesReversedZBuffer)
{
worldToShadowCascadeMatrices[4].m33 = 1f;
}
if (dynamicBatching)
{
drawFlags = DrawRendererFlags.EnableDynamicBatching;
}
if (instancing)
{
drawFlags |= DrawRendererFlags.EnableInstancing;
}
this.defaultStack = defaultStack;
this.ditherTexture = ditherTexture;
if (ditherAnimationSpeed > 0f && Application.isPlaying)
{
ConfigureDitherAnimation(ditherAnimationSpeed);
}
//阴影贴图的大小
this.shadowMapSize = shadowMapSize;
//阴影的距离参考:https://catlikecoding.com/unity/tutorials/scriptable-render-pipeline/directional-shadows/ 1.3节 Shadow Distance
this.shadowDistance = shadowDistance;
//此参数不知道啥意思
globalShadowData.y = 1f / shadowFadeRange;
//使用几级级联阴影
this.shadowCascades = shadowCascades;
//每级的距离
this.shadowCascadeSplit = shadowCascasdeSplit;
//全局光照相关
#if UNITY_EDITOR
Lightmapping.SetDelegate(lightmappingLightsDelegate);
#endif
}
void ConfigureDitherAnimation(float ditherAnimationSpeed)
{
ditherAnimationFrameDuration = 1f / ditherAnimationSpeed;
ditherSTs = new Vector4[16];
Random.State state = Random.state;
Random.InitState(0);
for (int i = 0; i < ditherSTs.Length; i++)
{
ditherSTs[i] = new Vector4(
(i & 1) == 0 ? (1f / 64f) : (-1f / 64f),
(i & 2) == 0 ? (1f / 64f) : (-1f / 64f),
Random.value, Random.value
);
}
Random.state = state;
}
#if UNITY_EDITOR
public override void Dispose()
{
base.Dispose();
Lightmapping.ResetDelegate();
}
#endif
public override void Render(
ScriptableRenderContext renderContext, Camera[] cameras
)
{
base.Render(renderContext, cameras);
ConfigureDitherPattern(renderContext);
foreach (var camera in cameras)
{
Render(renderContext, camera);
}
}
void ConfigureDitherPattern(ScriptableRenderContext context)
{
if (ditherSTIndex < 0)
{
ditherSTIndex = 0;
lastDitherTime = Time.unscaledTime;
cameraBuffer.SetGlobalTexture(ditherTextureId, ditherTexture);
cameraBuffer.SetGlobalVector(
ditherTextureSTId, new Vector4(1f / 64f, 1f / 64f, 0f, 0f)
);
context.ExecuteCommandBuffer(cameraBuffer);
cameraBuffer.Clear();
}
else if (ditherAnimationFrameDuration > 0f)
{
float currentTime = Time.unscaledTime;
if (currentTime - lastDitherTime >= ditherAnimationFrameDuration)
{
lastDitherTime = currentTime;
ditherSTIndex = ditherSTIndex < 15 ? ditherSTIndex + 1 : 0;
cameraBuffer.SetGlobalVector(ditherTextureSTId, ditherSTs[ditherSTIndex]);
}
context.ExecuteCommandBuffer(cameraBuffer);
cameraBuffer.Clear();
}
}
//真正绘制的地方
void Render(ScriptableRenderContext context, Camera camera)
{
//每次绘制的时候,第一步都是获得剔除的参数
ScriptableCullingParameters cullingParameters;
if (!CullResults.GetCullingParameters(camera, out cullingParameters))
{
return;
}
//设置剔除参数——影子的距离
//在shadowDistance和camera的远平面取小的那个
cullingParameters.shadowDistance = Mathf.Min(shadowDistance, camera.farClipPlane);
#if UNITY_EDITOR
if (camera.cameraType == CameraType.SceneView)
{
ScriptableRenderContext.EmitWorldGeometryForSceneView(camera);
}
#endif
//参数设置好,之后就执行剔除操作
CullResults.Cull(ref cullingParameters, context, ref cull);
//设置光源,哪几个灯是可见的
if (cull.visibleLights.Count > 0)
{
ConfigureLights();
if (mainLightExists)
{
RenderCascadedShadows(context);
}
else
{
cameraBuffer.DisableShaderKeyword(cascadedShadowsHardKeyword);
cameraBuffer.DisableShaderKeyword(cascadedShadowsSoftKeyword);
}
if (shadowTileCount > 0)
{
RenderShadows(context);
}
else
{
cameraBuffer.DisableShaderKeyword(shadowsHardKeyword);
cameraBuffer.DisableShaderKeyword(shadowsSoftKeyword);
}
}
else
{
cameraBuffer.SetGlobalVector(
lightIndicesOffsetAndCountID, Vector4.zero
);
cameraBuffer.DisableShaderKeyword(cascadedShadowsHardKeyword);
cameraBuffer.DisableShaderKeyword(cascadedShadowsSoftKeyword);
cameraBuffer.DisableShaderKeyword(shadowsHardKeyword);
cameraBuffer.DisableShaderKeyword(shadowsSoftKeyword);
}
context.SetupCameraProperties(camera);
var myPipelineCamera = camera.GetComponent<MyPipelineCamera>();
MyPostProcessingStack activeStack = myPipelineCamera ?
myPipelineCamera.PostProcessingStack : defaultStack;
if (activeStack)
{
//Add a "get a temporary render texture" command.
cameraBuffer.GetTemporaryRT(
cameraColorTextureId, camera.pixelWidth, camera.pixelHeight, 0,
FilterMode.Bilinear
);
cameraBuffer.GetTemporaryRT(
cameraDepthTextureId, camera.pixelWidth, camera.pixelHeight, 24,
FilterMode.Point, RenderTextureFormat.Depth
);
//设置渲染的目前,设置渲染目标之前,要先申请texture
//Add a "set active render target" command.
//RenderBufferLoadAction->This enum describes what should be done on the render target when it is activated
/*
* // 摘要:RenderBufferLoadAction.DontCare
When this RenderBuffer is activated, the GPU is instructed not to care about
the existing contents of that RenderBuffer. On tile-based GPUs this means that
the RenderBuffer contents do not need to be loaded into the tile memory, providing
a performance boost.
*/
//RenderBufferStoreAction->This enum describes what should be done on the render target when the GPU is done rendering into it.
/*
* // 摘要:RenderBufferStoreAction.Store
The RenderBuffer contents need to be stored to RAM. If the surface has MSAA enabled,
this stores the non-resolved surface.
*/
cameraBuffer.SetRenderTarget(
cameraColorTextureId, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store,//关于color的设置
cameraDepthTextureId, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store //关于depth的设置
);
}
CameraClearFlags clearFlags = camera.clearFlags;
//Adds a "clear render target" command.
cameraBuffer.ClearRenderTarget(
(clearFlags & CameraClearFlags.Depth) != 0,
(clearFlags & CameraClearFlags.Color) != 0,
camera.backgroundColor);
//Adds a command to begin profile sampling.
cameraBuffer.BeginSample("Render Camera");
//Add a "set global shader vector array property" command.
cameraBuffer.SetGlobalVectorArray(visibleLightColorsId, visibleLightColors);
cameraBuffer.SetGlobalVectorArray(visibleLightDirectionsOrPositionsId, visibleLightDirectionsOrPositions);
cameraBuffer.SetGlobalVectorArray(visibleLightAttenuationsId, visibleLightAttenuations);
cameraBuffer.SetGlobalVectorArray(visibleLightSpotDirectionsId, visibleLightSpotDirections);
cameraBuffer.SetGlobalVectorArray(visibleLightOcclusionMasksId, visibleLightOcclusionMasks);
globalShadowData.z = 1f - cullingParameters.shadowDistance * globalShadowData.y;
cameraBuffer.SetGlobalVector(globalShadowDataId, globalShadowData);
//about commandbuffer:
/*
* https://catlikecoding.com/unity/tutorials/scriptable-render-pipeline/custom-pipeline/
* The context delays the actual rendering until we submit it.
* Before that, we configure it and add commands to it for later execution.
* Some tasks—like drawing the skybox—can be
* issued via a dedicated method, but other commands have to be issued indirectly,
* via a separate command buffer.
*/
//We can instruct the context to execute the buffer via its ExecuteCommandBuffer method. Once again,
//this doesn't immediately execute the commands, but copies them to the internal buffer of the context.
context.ExecuteCommandBuffer(cameraBuffer);
//Clear all commands in the buffer.
cameraBuffer.Clear();
//Settings for ScriptableRenderContext.DrawRenderers.
//使用什么样的shader
var drawSettings = new DrawRendererSettings(camera, new ShaderPassName("SRPDefaultUnlit"))
{
flags = drawFlags
};
if (cull.visibleLights.Count > 0)
{
drawSettings.rendererConfiguration = RendererConfiguration.PerObjectLightIndices8;
}
drawSettings.rendererConfiguration |=
RendererConfiguration.PerObjectReflectionProbes |
RendererConfiguration.PerObjectLightmaps |
RendererConfiguration.PerObjectLightProbe |
RendererConfiguration.PerObjectLightProbeProxyVolume |
RendererConfiguration.PerObjectShadowMask |
RendererConfiguration.PerObjectOcclusionProbe |
RendererConfiguration.PerObjectOcclusionProbeProxyVolume;
//使用什么样的排序
drawSettings.sorting.flags = SortFlags.CommonOpaque;
//渲染哪些东西
var filterSettings = new FilterRenderersSettings(true)
{
renderQueueRange = RenderQueueRange.opaque
};
//绘制可见物体
context.DrawRenderers(cull.visibleRenderers, ref drawSettings, filterSettings);
//绘制天空盒,这个单独的方法
/*
* Some tasks—like drawing the skybox—can be issued via a dedicated method,
* but other commands have to be issued indirectly, via a separate command buffer.
*/
context.DrawSkybox(camera);
if (activeStack)
{
//后处理
activeStack.RenderAfterOpaque(postProcessingBuffer, cameraColorTextureId, cameraDepthTextureId,
camera.pixelWidth, camera.pixelHeight);
context.ExecuteCommandBuffer(postProcessingBuffer);
postProcessingBuffer.Clear();
cameraBuffer.SetRenderTarget(
cameraColorTextureId, RenderBufferLoadAction.Load, RenderBufferStoreAction.Store,
cameraDepthTextureId, RenderBufferLoadAction.Load, RenderBufferStoreAction.Store
);
context.ExecuteCommandBuffer(cameraBuffer);
cameraBuffer.Clear();
}
//画透明物体
drawSettings.sorting.flags = SortFlags.CommonTransparent;
filterSettings.renderQueueRange = RenderQueueRange.transparent;
context.DrawRenderers(cull.visibleRenderers, ref drawSettings, filterSettings);
//画默认的
DrawDefaultPipeline(context, camera);
if (activeStack)
{
activeStack.RenderAfterTransparent(
postProcessingBuffer, cameraColorTextureId, cameraDepthTextureId,
camera.pixelWidth, camera.pixelHeight
);
context.ExecuteCommandBuffer(postProcessingBuffer);
postProcessingBuffer.Clear();
cameraBuffer.ReleaseTemporaryRT(cameraColorTextureId);
cameraBuffer.ReleaseTemporaryRT(cameraDepthTextureId);
}
cameraBuffer.EndSample("Render Camera");
context.ExecuteCommandBuffer(cameraBuffer);
cameraBuffer.Clear();
//正式提交
context.Submit();
if (shadowMap)
{
RenderTexture.ReleaseTemporary(shadowMap);
shadowMap = null;
}
if (cascadedShadowMap)
{
RenderTexture.ReleaseTemporary(cascadedShadowMap);
cascadedShadowMap = null;
}
}
void ConfigureLights()
{
mainLightExists = false;
bool shadowmaskExists = false;
bool subtractiveLighting = false;
shadowTileCount = 0;
for (int i = 0; i < cull.visibleLights.Count; i++)
{
if (i == maxVisibleLights) //最多有16个可见的灯,多了就不处理了
{
break;
}
VisibleLight light = cull.visibleLights[i]; //第i个可见的灯
visibleLightColors[i] = light.finalColor; //最终颜色=Light color multiplied by intensity.
Vector4 attenuation = Vector4.zero;
attenuation.w = 1f;
Vector4 shadow = Vector4.zero;
//设置每个灯使用的遮罩通道
LightBakingOutput baking = light.light.bakingOutput; // This property describes the output of the last Global Illumination bake.
visibleLightOcclusionMasks[i] = occlusionMasks[baking.occlusionMaskChannel + 1];
//如果灯的类型为mixed
if (baking.lightmapBakeType == LightmapBakeType.Mixed)
{
//是否有shadowmask
shadowmaskExists |= baking.mixedLightingMode == MixedLightingMode.Shadowmask;
if (baking.mixedLightingMode == MixedLightingMode.Subtractive)
{
subtractiveLighting = true;
cameraBuffer.SetGlobalColor(subtractiveShadowColorId, RenderSettings.subtractiveShadowColor.linear); //线性的颜色
}
}
//平行光
if (light.lightType == LightType.Directional)
{
Vector4 v = light.localToWorld.GetColumn(2); //第3列,表示z轴方向,取-,是因为我们shader用点到光源的方向
v.x = -v.x;
v.y = -v.y;
v.z = -v.z;
visibleLightDirectionsOrPositions[i] = v;
//配置阴影
shadow = ConfigureShadows(i, light.light);
shadow.z = 1f;
if (i == 0 && shadow.x > 0f && shadowCascades > 0)
{
mainLightExists = true;
shadowTileCount -= 1;
}
}
else
{
visibleLightDirectionsOrPositions[i] = light.localToWorld.GetColumn(3); //第四列,对于非平行光,存储的是位置
attenuation.x = 1f / Mathf.Max(light.range * light.range, 0.00001f); //点光源的衰减,存储在x分量
if (light.lightType == LightType.Spot) //聚光灯的设置
{
Vector4 v = light.localToWorld.GetColumn(2);
v.x = -v.x;
v.y = -v.y;
v.z = -v.z;
visibleLightSpotDirections[i] = v;
float outerRad = Mathf.Deg2Rad * 0.5f * light.spotAngle;
float outerCos = Mathf.Cos(outerRad);
float outerTan = Mathf.Tan(outerRad);
float innerCos = Mathf.Cos(Mathf.Atan((46f / 64f) * outerTan));
float angleRange = Mathf.Max(innerCos - outerCos, 0.001f);
attenuation.z = 1f / angleRange;
attenuation.w = -outerCos * attenuation.z; //衰减的公式后面再细算
shadow = ConfigureShadows(i, light.light);
}
else
{
visibleLightSpotDirections[i] = Vector4.one;
}
}
visibleLightAttenuations[i] = attenuation;
shadowData[i] = shadow;
}
bool useDistanceShadowmask = QualitySettings.shadowmaskMode == ShadowmaskMode.DistanceShadowmask;
CoreUtils.SetKeyword(cameraBuffer, shadowmaskKeyword, shadowmaskExists && !useDistanceShadowmask);
CoreUtils.SetKeyword(cameraBuffer, distanceShadowmaskKeyword, shadowmaskExists && useDistanceShadowmask);
CoreUtils.SetKeyword(cameraBuffer, subtractiveLightingKeyword, subtractiveLighting);
if (mainLightExists || cull.visibleLights.Count > maxVisibleLights)
{
int[] lightIndices = cull.GetLightIndexMap();
if (mainLightExists)
{
lightIndices[0] = -1;
}
for (int i = maxVisibleLights; i < cull.visibleLights.Count; i++)
{
lightIndices[i] = -1;
}
cull.SetLightIndexMap(lightIndices);
}
}
Vector4 ConfigureShadows(int lightIndex, Light shadowLight)
{
Vector4 shadow = Vector4.zero;
Bounds shadowBounds;
if (shadowLight.shadows != LightShadows.None && cull.GetShadowCasterBounds(lightIndex, out shadowBounds))
{
shadowTileCount += 1;
shadow.x = shadowLight.shadowStrength; //x分量记录影子的强度
shadow.y = shadowLight.shadows == LightShadows.Soft ? 1f : 0f;
}
return shadow;
}
void RenderCascadedShadows(ScriptableRenderContext context)
{
float tileSize = shadowMapSize / 2;
cascadedShadowMap = SetShadowRenderTarget();
shadowBuffer.BeginSample("Render Shadows");
context.ExecuteCommandBuffer(shadowBuffer);
shadowBuffer.Clear();
Light shadowLight = cull.visibleLights[0].light;
shadowBuffer.SetGlobalFloat(
shadowBiasId, shadowLight.shadowBias
);
var shadowSettings = new DrawShadowsSettings(cull, 0);
var tileMatrix = Matrix4x4.identity;
tileMatrix.m00 = tileMatrix.m11 = 0.5f;
for (int i = 0; i < shadowCascades; i++)
{
Matrix4x4 viewMatrix, projectionMatrix;
ShadowSplitData splitData;
cull.ComputeDirectionalShadowMatricesAndCullingPrimitives(
0, i, shadowCascades, shadowCascadeSplit, (int)tileSize,
shadowLight.shadowNearPlane,
out viewMatrix, out projectionMatrix, out splitData
);
Vector2 tileOffset = ConfigureShadowTile(i, 2, tileSize);
shadowBuffer.SetViewProjectionMatrices(viewMatrix, projectionMatrix);
context.ExecuteCommandBuffer(shadowBuffer);
shadowBuffer.Clear();
cascadeCullingSpheres[i] =
shadowSettings.splitData.cullingSphere = splitData.cullingSphere;
cascadeCullingSpheres[i].w *= splitData.cullingSphere.w;
context.DrawShadows(ref shadowSettings);
CalculateWorldToShadowMatrix(
ref viewMatrix, ref projectionMatrix,
out worldToShadowCascadeMatrices[i]
);
tileMatrix.m03 = tileOffset.x * 0.5f;
tileMatrix.m13 = tileOffset.y * 0.5f;
worldToShadowCascadeMatrices[i] =
tileMatrix * worldToShadowCascadeMatrices[i];
}
shadowBuffer.DisableScissorRect();
shadowBuffer.SetGlobalTexture(cascadedShadowMapId, cascadedShadowMap);
shadowBuffer.SetGlobalVectorArray(
cascadeCullingSpheresId, cascadeCullingSpheres
);
shadowBuffer.SetGlobalMatrixArray(
worldToShadowCascadeMatricesId, worldToShadowCascadeMatrices
);
float invShadowMapSize = 1f / shadowMapSize;
shadowBuffer.SetGlobalVector(
cascadedShadowMapSizeId, new Vector4(
invShadowMapSize, invShadowMapSize, shadowMapSize, shadowMapSize
)
);
shadowBuffer.SetGlobalFloat(
cascadedShadoStrengthId, shadowLight.shadowStrength
);
bool hard = shadowLight.shadows == LightShadows.Hard;
CoreUtils.SetKeyword(shadowBuffer, cascadedShadowsHardKeyword, hard);
CoreUtils.SetKeyword(shadowBuffer, cascadedShadowsSoftKeyword, !hard);
shadowBuffer.EndSample("Render Shadows");
context.ExecuteCommandBuffer(shadowBuffer);
shadowBuffer.Clear();
}
void RenderShadows(ScriptableRenderContext context)
{
int split;
if (shadowTileCount <= 1)
{
split = 1;
}
else if (shadowTileCount <= 4)
{
split = 2;
}
else if (shadowTileCount <= 9)
{
split = 3;
}
else
{
split = 4;
}
float tileSize = shadowMapSize / split;
float tileScale = 1f / split;
globalShadowData.x = tileScale;
shadowMap = SetShadowRenderTarget();
shadowBuffer.BeginSample("Render Shadows");
context.ExecuteCommandBuffer(shadowBuffer);
shadowBuffer.Clear();
int tileIndex = 0;
bool hardShadows = false;
bool softShadows = false;
for (int i = mainLightExists ? 1 : 0; i < cull.visibleLights.Count; i++)
{
if (i == maxVisibleLights)
{
break;
}
if (shadowData[i].x <= 0f)
{
continue;
}
Matrix4x4 viewMatrix, projectionMatrix;
ShadowSplitData splitData;
bool validShadows;
if (shadowData[i].z > 0f)
{
validShadows =
cull.ComputeDirectionalShadowMatricesAndCullingPrimitives(
i, 0, 1, Vector3.right, (int)tileSize,
cull.visibleLights[i].light.shadowNearPlane,
out viewMatrix, out projectionMatrix, out splitData
);
}
else
{
validShadows =
cull.ComputeSpotShadowMatricesAndCullingPrimitives(
i, out viewMatrix, out projectionMatrix, out splitData
);
}
if (!validShadows)
{
shadowData[i].x = 0f;
continue;
}
Vector2 tileOffset = ConfigureShadowTile(tileIndex, split, tileSize);
shadowData[i].z = tileOffset.x * tileScale;
shadowData[i].w = tileOffset.y * tileScale;
shadowBuffer.SetViewProjectionMatrices(viewMatrix, projectionMatrix);
shadowBuffer.SetGlobalFloat(
shadowBiasId, cull.visibleLights[i].light.shadowBias
);
context.ExecuteCommandBuffer(shadowBuffer);
shadowBuffer.Clear();
var shadowSettings = new DrawShadowsSettings(cull, i);
shadowSettings.splitData.cullingSphere = splitData.cullingSphere;
context.DrawShadows(ref shadowSettings);
CalculateWorldToShadowMatrix(
ref viewMatrix, ref projectionMatrix, out worldToShadowMatrices[i]
);
tileIndex += 1;
if (shadowData[i].y <= 0f)
{
hardShadows = true;
}
else
{
softShadows = true;
}
}
shadowBuffer.DisableScissorRect();
shadowBuffer.SetGlobalTexture(shadowMapId, shadowMap);
shadowBuffer.SetGlobalMatrixArray(
worldToShadowMatricesId, worldToShadowMatrices
);
shadowBuffer.SetGlobalVectorArray(shadowDataId, shadowData);
float invShadowMapSize = 1f / shadowMapSize;
shadowBuffer.SetGlobalVector(
shadowMapSizeId, new Vector4(
invShadowMapSize, invShadowMapSize, shadowMapSize, shadowMapSize
)
);
CoreUtils.SetKeyword(shadowBuffer, shadowsHardKeyword, hardShadows);
CoreUtils.SetKeyword(shadowBuffer, shadowsSoftKeyword, softShadows);
shadowBuffer.EndSample("Render Shadows");
context.ExecuteCommandBuffer(shadowBuffer);
shadowBuffer.Clear();
}
RenderTexture SetShadowRenderTarget()
{
RenderTexture texture = RenderTexture.GetTemporary(
shadowMapSize, shadowMapSize, 16, RenderTextureFormat.Shadowmap
);
texture.filterMode = FilterMode.Bilinear;
texture.wrapMode = TextureWrapMode.Clamp;
CoreUtils.SetRenderTarget(
shadowBuffer, texture,
RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store,
ClearFlag.Depth
);
return texture;
}
Vector2 ConfigureShadowTile(int tileIndex, int split, float tileSize)
{
Vector2 tileOffset;
tileOffset.x = tileIndex % split;
tileOffset.y = tileIndex / split;
var tileViewport = new Rect(
tileOffset.x * tileSize, tileOffset.y * tileSize, tileSize, tileSize
);
shadowBuffer.SetViewport(tileViewport);
shadowBuffer.EnableScissorRect(new Rect(
tileViewport.x + 4f, tileViewport.y + 4f,
tileSize - 8f, tileSize - 8f
));
return tileOffset;
}
void CalculateWorldToShadowMatrix(
ref Matrix4x4 viewMatrix, ref Matrix4x4 projectionMatrix,
out Matrix4x4 worldToShadowMatrix
)
{
if (SystemInfo.usesReversedZBuffer)
{
projectionMatrix.m20 = -projectionMatrix.m20;
projectionMatrix.m21 = -projectionMatrix.m21;
projectionMatrix.m22 = -projectionMatrix.m22;
projectionMatrix.m23 = -projectionMatrix.m23;
}
var scaleOffset = Matrix4x4.identity;
scaleOffset.m00 = scaleOffset.m11 = scaleOffset.m22 = 0.5f;
scaleOffset.m03 = scaleOffset.m13 = scaleOffset.m23 = 0.5f;
worldToShadowMatrix = scaleOffset * (projectionMatrix * viewMatrix);
}
[Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
void DrawDefaultPipeline(ScriptableRenderContext context, Camera camera)
{
if (errorMaterial == null)
{
Shader errorShader = Shader.Find("Hidden/InternalErrorShader");
errorMaterial = new Material(errorShader)
{
hideFlags = HideFlags.HideAndDontSave
};
}
var drawSettings = new DrawRendererSettings(
camera, new ShaderPassName("ForwardBase")
);
drawSettings.SetShaderPassName(1, new ShaderPassName("PrepassBase"));
drawSettings.SetShaderPassName(2, new ShaderPassName("Always"));
drawSettings.SetShaderPassName(3, new ShaderPassName("Vertex"));
drawSettings.SetShaderPassName(4, new ShaderPassName("VertexLMRGBM"));
drawSettings.SetShaderPassName(5, new ShaderPassName("VertexLM"));
drawSettings.SetOverrideMaterial(errorMaterial, 0);
var filterSettings = new FilterRenderersSettings(true);
context.DrawRenderers(cull.visibleRenderers, ref drawSettings, filterSettings);
}
#if UNITY_EDITOR
static Lightmapping.RequestLightsDelegate lightmappingLightsDelegate =
(Light[] inputLights, NativeArray<LightDataGI> outputLights) =>
{
LightDataGI lightData = new LightDataGI();
for (int i = 0; i < inputLights.Length; i++)
{
Light light = inputLights[i];
switch (light.type)
{
case LightType.Directional:
var directionalLight = new DirectionalLight();
LightmapperUtils.Extract(light, ref directionalLight);
lightData.Init(ref directionalLight);
break;
case LightType.Point:
var pointLight = new PointLight();
LightmapperUtils.Extract(light, ref pointLight);
lightData.Init(ref pointLight);
break;
case LightType.Spot:
var spotLight = new SpotLight();
LightmapperUtils.Extract(light, ref spotLight);
lightData.Init(ref spotLight);
break;
case LightType.Area:
var rectangleLight = new RectangleLight();
LightmapperUtils.Extract(light, ref rectangleLight);
lightData.Init(ref rectangleLight);
break;
default:
lightData.InitNoBake(light.GetInstanceID());
break;
}
lightData.falloff = FalloffType.InverseSquared;
outputLights[i] = lightData;
}
};
#endif
}