whoimi

A geek blog

View on GitHub

准备阴影Atlas:

// 在PrepareLightsForGPU当中分配阴影,PrepareLightsForGPU是用来CPU准备光照数据的

// 给阴影分配渲染请求,设置摄像机参数,阴影分辨率。
additionalData.ReserveShadows(camera, m_ShadowManager, m_ShadowInitParameters, cullResults, m_FrameSettings, lightIndex);

...
// Now that all the lights have requested a shadow resolution, we can layout them in the atlas
// And if needed rescale the whole atlas
m_ShadowManager.LayoutShadowMaps(debugDisplaySettings.data.lightingDebugSettings);

当我设置多个方向光阴影的时候会报错,分配阴影贴图布局的方法:

public void LayoutShadowMaps(LightingDebugSettings lightingDebugSettings)
{
    m_Atlas.UpdateDebugSettings(lightingDebugSettings);

    if (m_CascadeAtlas != null)
        m_CascadeAtlas.UpdateDebugSettings(lightingDebugSettings);

    m_AreaLightShadowAtlas.UpdateDebugSettings(lightingDebugSettings);

    if (lightingDebugSettings.shadowResolutionScaleFactor != 1.0f)
    {
        foreach (var shadowResolutionRequest in m_ShadowResolutionRequests)
        {
            // We don't rescale the directional shadows with the global shadow scale factor
            // because there is no dynamic atlas rescale when it overflow.
            if (shadowResolutionRequest.shadowMapType != ShadowMapType.CascadedDirectional)
                shadowResolutionRequest.resolution *= lightingDebugSettings.shadowResolutionScaleFactor;
        }
    }

    // Assign a position to all the shadows in the atlas, and scale shadows if needed
    if (m_CascadeAtlas != null && !m_CascadeAtlas.Layout(false))
        Debug.LogError("Cascade Shadow atlasing has failed, only one directional light can cast shadows at a time");
    m_Atlas.Layout();
    m_AreaLightShadowAtlas.Layout();
}

渲染阴影的位置是在HDShadowAtlas类当中:

// HDShadowAtlas类
// 所有需要渲染阴影的灯光都会组织成一个HDShadowAtlas类。
public void RenderShadows(ScriptableRenderContext renderContext, CommandBuffer cmd, ShadowDrawingSettings dss)
{
    // m_ShadowRequests是渲染阴影需要的所有的参数。
    if (m_ShadowRequests.Count == 0)
        return;
	
    // shadow mask 纹理id
    cmd.SetRenderTarget(identifier);
    // shadow 大小 
    cmd.SetGlobalVector(m_AtlasSizeShaderID, new Vector4(width, height, 1.0f / width, 1.0f / height));

    // debug设置
    if (m_LightingDebugSettings.clearShadowAtlas)
        CoreUtils.DrawFullScreen(cmd, m_ClearMaterial, null, 0);
	
    // 渲染所以阴影请求,直线光渲染次数为级联阴影数。
    foreach (var shadowRequest in m_ShadowRequests)
    {
        // 设置渲染区域。
        cmd.SetViewport(shadowRequest.atlasViewport);
		// 根据阴影设置是否启用zClip
        /*
        zClip的设置:
        shadowRequest.zClip = (legacyLight.type != LightType.Directional);
        为什么关闭直线光的ZClip
        */
        cmd.SetGlobalFloat(HDShaderIDs._ZClip, shadowRequest.zClip ? 1.0f : 0.0f);
        if (!m_LightingDebugSettings.clearShadowAtlas)
        {
            CoreUtils.DrawFullScreen(cmd, m_ClearMaterial, null, 0);
        }
		
        // 读取灯光信息和剪裁信息
        dss.lightIndex = shadowRequest.lightIndex;
        dss.splitData = shadowRequest.splitData;

        // 设置摄像机参数
        // Setup matrices for shadow rendering:
        Matrix4x4 viewProjection = shadowRequest.deviceProjectionYFlip * shadowRequest.view;
        cmd.SetGlobalMatrix(HDShaderIDs._ViewMatrix, shadowRequest.view);
        cmd.SetGlobalMatrix(HDShaderIDs._InvViewMatrix, shadowRequest.view.inverse);
        cmd.SetGlobalMatrix(HDShaderIDs._ProjMatrix, shadowRequest.deviceProjectionYFlip);
        cmd.SetGlobalMatrix(HDShaderIDs._InvProjMatrix, shadowRequest.deviceProjectionYFlip.inverse);
        cmd.SetGlobalMatrix(HDShaderIDs._ViewProjMatrix, viewProjection);
        cmd.SetGlobalMatrix(HDShaderIDs._InvViewProjMatrix, viewProjection.inverse);
        cmd.SetGlobalVectorArray(HDShaderIDs._ShadowClipPlanes, shadowRequest.frustumPlanes);

        // TODO: remove this execute when DrawShadows will use a CommandBuffer
        // 使用Command来绘制阴影,目前还没有实现??
        renderContext.ExecuteCommandBuffer(cmd);
        cmd.Clear();

        renderContext.DrawShadows(ref dss);
    }
	
    // 设置Clip启用
    cmd.SetGlobalFloat(HDShaderIDs._ZClip, 1.0f);   // Re-enable zclip globally
}

HDShadowResolutionRequest表示一个灯光需要渲染阴影的次数。目前通过灯光类型,和shadow级联数决定,下面是所有相关的方法:

// =================HDAdditionalLightData:ReserveShadows====
int count = HDAdditionalLightData:GetShadowRequestCount();
for (int index = 0; index < count; index++)
    m_ShadowRequestIndices[index] = shadowManager.ReserveShadowResolutions(viewportSize, shadowMapType);

// =================HDAdditionalLightData:GetShadowRequestCount====
int GetShadowRequestCount()
{
    return (legacyLight.type == LightType.Point 
            && lightTypeExtent == LightTypeExtent.Punctual) ? 
        	6 : 
    		// 如果是直线光,则渲染次数等于级联阴影数。
            (legacyLight.type == LightType.Directional) ? 				    m_ShadowSettings.cascadeShadowSplitCount.value : 1;
}

//==================HDShadowManager:ReserveShadowResolutions ======
switch (shadowMapType)
{
    case ShadowMapType.PunctualAtlas:
      m_Atlas.ReserveResolution(resolutionRequest);
        break;
    case ShadowMapType.AreaLightAtlas:
      m_AreaLightShadowAtlas.ReserveResolution(resolutionRequest);
        break;
    case ShadowMapType.CascadedDirectional:
      m_CascadeAtlas.ReserveResolution(resolutionRequest);
        break;
}

通过级联数据和剪裁结果计算剪裁平面。

 cullResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(lightIndex, (int)cascadeIndex, cascadeCount, ratios, (int)viewportSize.x, nearPlaneOffset, out view, out projection, out splitData);
            

ShadowAtlas的纹理声明:

//	HDShadowManager:
// The cascade atlas will be allocated only if there is a directional light
m_Atlas = new HDShadowAtlas(renderPipelineResources, punctualLightAtlasInfo.shadowAtlasResolution, punctualLightAtlasInfo.shadowAtlasResolution, HDShaderIDs._ShadowAtlasSize, clearMaterial, false, depthBufferBits: punctualLightAtlasInfo.shadowAtlasDepthBits, name: "Shadow Map Atlas");
// Cascade atlas render texture will only be allocated if there is a shadow casting directional light
bool useMomentShadows = GetDirectionalShadowAlgorithm() == DirectionalShadowAlgorithm.IMS;
m_CascadeAtlas = new HDShadowAtlas(renderPipelineResources, 1, 1, HDShaderIDs._CascadeShadowAtlasSize, clearMaterial, useMomentShadows, depthBufferBits: directionalShadowDepthBits, name: "Cascade Shadow Map Atlas");

m_AreaLightShadowAtlas = new HDShadowAtlas(renderPipelineResources, areaLightAtlasInfo.shadowAtlasResolution, areaLightAtlasInfo.shadowAtlasResolution, HDShaderIDs._AreaShadowAtlasSize, clearMaterial, false, BlurredEVSM: true, depthBufferBits: areaLightAtlasInfo.shadowAtlasDepthBits, name: "Area Light Shadow Map Atlas");

在HDRenderPipelineAsset当中可以设置每种Atlas的大小,以及每种纹理深度的精度。

// LightLoop
// 下面是阴影相关设置的使用位置。
void InitShadowSystem(HDRenderPipelineAsset hdAsset)
{
    m_ShadowInitParameters = hdAsset.currentPlatformRenderPipelineSettings.hdShadowInitParams;
    m_ShadowManager = new HDShadowManager(
        hdAsset.renderPipelineResources,
        m_ShadowInitParameters.directionalShadowsDepthBits,
        m_ShadowInitParameters.punctualLightShadowAtlas,
        m_ShadowInitParameters.areaLightShadowAtlas,
        m_ShadowInitParameters.maxShadowRequests,
        hdAsset.renderPipelineResources.shaders.shadowClearPS
    );
}

Unity支持的Soft Shadow 类型:

public static DirectionalShadowAlgorithm GetDirectionalShadowAlgorithm()
{
    var hdAsset = (GraphicsSettings.renderPipelineAsset as HDRenderPipelineAsset);
    switch (hdAsset.currentPlatformRenderPipelineSettings.hdShadowInitParams.shadowQuality)
    {
        case HDShadowQuality.Low:
            {
                return DirectionalShadowAlgorithm.PCF5x5;
            }
        case HDShadowQuality.Medium:
            {
                return DirectionalShadowAlgorithm.PCF7x7;
            }
        case HDShadowQuality.High:
            {
                return DirectionalShadowAlgorithm.PCSS;
            }
        case HDShadowQuality.VeryHigh:
            {
                return DirectionalShadowAlgorithm.IMS;
            }
    };
    return DirectionalShadowAlgorithm.PCF5x5;
}