srp——後處理

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
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章