unity post processing stack v2——代码结构分析

PostProcessLayer.cs
它必须在Camera上添加。

[RequireComponent(typeof(Camera))]
public sealed class PostProcessLayer : MonoBehaviour
{
	……
}
#if UNITY_2019_1_OR_NEWER
        // We always use a CommandBuffer to blit to the final render target
        // OnRenderImage is used only to avoid the automatic blit from the RenderTexture of Camera.forceIntoRenderTexture to the actual target
        [ImageEffectUsesCommandBuffer]
        void OnRenderImage(RenderTexture src, RenderTexture dst)
        {
            if (finalBlitToCameraTarget)
                RenderTexture.active = dst; // silence warning
            else
                Graphics.Blit(src, dst);
        }
#endif

属性:ImageEffectUsesCommandBuffer
https://docs.unity3d.com/2019.1/Documentation/ScriptReference/ImageEffectUsesCommandBuffer.html
在这里插入图片描述
Camera强制输出到:RenderTexture
Camera.forceIntoRenderTexture
https://docs.unity3d.com/ScriptReference/Camera-forceIntoRenderTexture.html
在这里插入图片描述

UpdateVolumeSystem——设置参数
public void Render(PostProcessRenderContext context)——绘制
绘制分为三个步骤:
1、内置的
2、嵌入的
3、最后一个pass

在这里插入图片描述

Color grading 中文翻译:颜色校正

判断是否有激活的后处理效果:

public bool HasActiveEffects(PostProcessEvent evt, PostProcessRenderContext context)
{
    var list = sortedBundles[evt];
    foreach (var item in list)
    {
        bool enabledAndSupported = item.bundle.settings.IsEnabledAndSupported(context);
        if (context.isSceneView) //如果sceneview下需要后处理
        {
            if (item.bundle.attribute.allowInSceneView && enabledAndSupported)
                return true;
        }
        else if (enabledAndSupported)
        {
            return true;
        }
    }
    return false;
}

后处理基础类:
PostProcessEffectSettings

绘制后处理的插入命令:

int RenderInjectionPoint(PostProcessEvent evt, PostProcessRenderContext context, string marker, int releaseTargetAfterUse = -1)
{
	int tempTarget = m_TargetPool.Get(); //从池子里拿一个序号,这个序号是一个int
	var finalDestination = context.destination;
	
	var cmd = context.command;
	context.GetScreenSpaceTemporaryRT(cmd, tempTarget, 0, context.sourceFormat); //GetScreenSpaceTemporaryRT这个函数见下:
	context.destination = tempTarget;
	RenderList(sortedBundles[evt], context, marker); //这个函数见下:
	context.source = tempTarget;
	context.destination = finalDestination;
	
	if (releaseTargetAfterUse > -1)
	    cmd.ReleaseTemporaryRT(releaseTargetAfterUse);
	return tempTarget;
}

在这里插入图片描述

m_TargetPool是一个很简单的池子:
我其实很想解释的每行,但是太简单了,就不做详细一行一行注释了。

class TargetPool
{
   readonly List<int> m_Pool;
     int m_Current;
     internal TargetPool()
     {
         m_Pool = new List<int>();
         Get(); // Pre-warm with a default target to avoid black frame on first frame
     }
     internal int Get()
     {
         int ret = Get(m_Current);
         m_Current++;
         return ret;
     }
     int Get(int i)
     {
         int ret;

         if (m_Pool.Count > i)
         {
             ret = m_Pool[i];
         }
         else
         {
             // Avoid discontinuities
             while (m_Pool.Count <= i)
                 m_Pool.Add(Shader.PropertyToID("_TargetPool" + i));

             ret = m_Pool[i];
         }
         return ret;
     }
     internal void Reset()
     {
         m_Current = 0;
     }
}

开始绘制:

void RenderList(List<SerializedBundleRef> list, PostProcessRenderContext context, string marker)
{
    var cmd = context.command;
    cmd.BeginSample(marker);

    // First gather active effects - we need this to manage render targets more efficiently
    m_ActiveEffects.Clear();
    for (int i = 0; i < list.Count; i++)
    {
        var effect = list[i].bundle;
        if (effect.settings.IsEnabledAndSupported(context))
        {
            if (!context.isSceneView || (context.isSceneView && effect.attribute.allowInSceneView))
                m_ActiveEffects.Add(effect.renderer);
        }
    }

    int count = m_ActiveEffects.Count;

    // If there's only one active effect, we can simply execute it and skip the rest
    if (count == 1) //如果只有一个后处理效果:
    {
        m_ActiveEffects[0].Render(context);
    }
    else
    {
        // Else create the target chain
        m_Targets.Clear();
        m_Targets.Add(context.source); // First target is always source,添加source第一个

        int tempTarget1 = m_TargetPool.Get(); //申请第一个id
        int tempTarget2 = m_TargetPool.Get(); //申请第二个id

        for (int i = 0; i < count - 1; i++) //for循环遍历count-1个
            m_Targets.Add(i % 2 == 0 ? tempTarget1 : tempTarget2); //现在有count个了

        m_Targets.Add(context.destination); // Last target is always destination,添加destination最后一个,目前有count+1个了

        // Render
        context.GetScreenSpaceTemporaryRT(cmd, tempTarget1, 0, context.sourceFormat);
        if (count > 2)
            context.GetScreenSpaceTemporaryRT(cmd, tempTarget2, 0, context.sourceFormat);

        for (int i = 0; i < count; i++)
        {
            context.source = m_Targets[i];
            context.destination = m_Targets[i + 1];
            m_ActiveEffects[i].Render(context);
        }

        cmd.ReleaseTemporaryRT(tempTarget1);
        if (count > 2)
            cmd.ReleaseTemporaryRT(tempTarget2);
    }

    cmd.EndSample(marker);
}

blit过程示意图:
在这里插入图片描述
执行之前保存dest保存的id:
finalID = dest;
dest = tempID;
blit(xxx);
source = dest;
dest = finalID;

需要的东西:
1、commandbuffer
2、rt的id和rt
3、源、目的
4、blit方法
5、shader,材质球,网格点
6、释放rt

1、commandbuffer的创建并添加到camera的指定点:
CommandBuffer m_LegacyCmdBuffer = new CommandBuffer { name = “Post-processing” };
并且为摄像机添加这个buffer到指定的插入点:
m_Camera.AddCommandBuffer(CameraEvent.BeforeImageEffects, m_LegacyCmdBuffer);

2、rt的id并且申请rt
int tempTarget = m_TargetPool.Get();
context.GetScreenSpaceTemporaryRT(cmd, tempTarget, 0, context.sourceFormat);
申请的代码:
cmd.GetTemporaryRT(nameID, actualWidth, actualHeight, depthBufferBits, filter, colorFormat, readWrite);

3、源、目的
源头肯定是看到的东西,目的可以是一个rt
var cameraTarget = new RenderTargetIdentifier(BuiltinRenderTextureType.CameraTarget);
这是摄像机看到的那个图

4、blit操作

cmd.SetGlobalTexture(ShaderIDs.MainTex, source);
cmd.SetRenderTargetWithLoadStoreAction(destination, viewport == null ? loadAction : LoadAction.Load, StoreAction.Store);
if (viewport != null)
	cmd.SetViewport(viewport.Value);
bool clear = (loadAction == LoadAction.Clear);
if(clear) loadAction = LoadAction.DontCare;
if (clear) cmd.ClearRenderTarget(true, true, Color.clear);
cmd.DrawMesh(fullscreenTriangle, Matrix4x4.identity, propertySheet.material, 0, pass, propertySheet.properties);

5、fullscreenTriangle和材质球
三角形网格:

s_FullscreenTriangle = new Mesh { name = "Fullscreen Triangle" };
s_FullscreenTriangle.SetVertices(new List<Vector3>
{
	new Vector3(-1f, -1f, 0f),
	new Vector3(-1f,  3f, 0f),
	new Vector3( 3f, -1f, 0f)
});
s_FullscreenTriangle.SetIndices(new [] { 0, 1, 2 }, MeshTopology.Triangles, 0, false);
s_FullscreenTriangle.UploadMeshData(false);

一个简单拷贝摄像机画面的shader:

struct AttributesDefault
{
    float3 vertex : POSITION;
};

struct VaryingsDefault
{
    float4 vertex : SV_POSITION;
    float2 texcoord : TEXCOORD0;
    float2 texcoordStereo : TEXCOORD1;
#if STEREO_INSTANCING_ENABLED
    uint stereoTargetEyeIndex : SV_RenderTargetArrayIndex;
#endif
};

float2 TransformTriangleVertexToUV(float2 vertex)
{
    float2 uv = (vertex + 1.0) * 0.5;
    return uv;
}

VaryingsDefault VertDefault(AttributesDefault v)
{
    VaryingsDefault o;
    o.vertex = float4(v.vertex.xy, 0.0, 1.0);
    o.texcoord = TransformTriangleVertexToUV(v.vertex.xy);

#if UNITY_UV_STARTS_AT_TOP
    o.texcoord = o.texcoord * float2(1.0, -1.0) + float2(0.0, 1.0);
#endif
    o.texcoordStereo = TransformStereoScreenSpaceTex(o.texcoord, 1.0);
    return o;
}

float4 Frag(VaryingsDefault i) : SV_Target
{
	float4 color = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoordStereo);
	return color;
}

6、释放rt
cmd.ReleaseTemporaryRT(tempTarget1);

ok,至此,我们已经将讲解一个完整的blit所需要的元素。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值