最近在研究如何把overdraw统计成准确的数据值,让美术同学能直观的知道自己做的特效、场景、角色到底有多费,再加上好久没有写文章了,自己也有点心得今天就分享给大家并且希望可以后面一起讨论。
首先Unity的overdraw是不准确的,如下图所示,我们放两个不透明的物体,在SceneView切到overdraw菜单,居然产生了overdraw。
不透明物体会写深度,默认是从后往前画的。那么按照上面的例子是不可能产生overdraw的。原因是当切到overdraw模式下unity会强制(无论透明还是不透明)都用另一个overdraw的shader渲染,直接 Blend One One然后关闭深度写入,所以就会出现上面的错误结果。
不透明物体大部分情况下是不会产生overdraw的,除非是自己改了RenderQueue。比如后面(白色)的物体深度设置RenderQueue=1999,前面(红色)的物体深度设置2000,那么此时才会真正产生overdraw。如果两个(白色、红色)RenderQueue一样都等于默认值2000的话是不会产生overdraw的。
在说说透明物体、透明物体一般是不写深度的,而且透明物体遵守“画家算法”是从后向前画的,如果只渲染透明物体那么在切到overdraw页签时unity渲染出来的区域就是准确的。
回到文章考开篇,我想做的是统计特效、场景、角色的渲染,实际应用场景存在透明物体+不透明物体混合排列的情况,所以计算出准确的overdraw就更重要了,于是我想起了SRP可编程渲染管线。
我们首先需要准备2个画overdraw的shader,一个是给不透明物体用的(写深度),另一个是给透明物体用的(关闭写深度)
OverDraw/OverDraw-Queue
Shader "OverDraw/OverDraw-Queue"
{
SubShader
{
Tags { "RenderType" = "Transparent" "Queue" = "Transparent" }
Fog { Mode Off }
ZTest Always
ZTest LEqual
Blend One One
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 vertex : SV_POSITION;
};
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
return fixed4(0.1, 0.04, 0.02, 0);
}
ENDCG
}
}
}
OverDraw/OverDraw-Transparent
Shader "OverDraw/OverDraw-Transparent"
{
SubShader
{
Tags { "RenderType" = "Transparent" "Queue" = "Transparent" }
Fog { Mode Off }
ZWrite Off
ZTest Always
ZTest LEqual
Blend One One
Cull Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 vertex : SV_POSITION;
};
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
return fixed4(0.1, 0.04, 0.02, 0);
}
ENDCG
}
}
}
代码比较好理解就是 Blend One One 一层叠一层,然后fixed4(0.1, 0.04, 0.02, 0);就是那个看起来暗红色的颜色,如果blend多了就越来越红。
OverDrawRenderPipelineAsset.cs
using UnityEngine;
using UnityEngine.Rendering;
[CreateAssetMenu(menuName = "Rendering/OverDraw Render Pipeline")]
public class OverDrawRenderPipelineAsset : RenderPipelineAsset
{
protected override RenderPipeline CreatePipeline()
{
return new OverDrawRenderPipeline();
}
}
Create->Rendering->OverDraw Render Pipeline 创建一个自定义的渲染配置文件,如下图所示,将新创建的配置文件绑定到SRPSetting中。
OverDrawRenderPipeline.cs
using UnityEngine;
using UnityEngine.Rendering;
public class OverDrawRenderPipeline : RenderPipeline
{
protected override void Render(ScriptableRenderContext context, Camera[] cameras)
{
foreach (Camera camera in cameras)
{
Render(context, camera);
}
}
const string BUFF_NAME = "Render Camera";
static CommandBuffer s_CommandBuffer = new CommandBuffer
{
name = BUFF_NAME
};
CullingResults cullingResults;
static Material s_OverQueue = new Material(Shader.Find("OverDraw/OverDraw-Queue"));
static Material s_OverTransparent = new Material(Shader.Find("OverDraw/OverDraw-Transparent"));
//支持的ShaderTagID
static ShaderTagId[] s_ShaderTagIds =
{
new ShaderTagId("SRPDefaultUnlit"),
new ShaderTagId("Always"),
new ShaderTagId("ForwardBase"),
new ShaderTagId("PrepassBase"),
new ShaderTagId("Vertex"),
new ShaderTagId("VertexLMRGBM"),
new ShaderTagId("VertexLM")
};
public void Render(ScriptableRenderContext context, Camera camera)
{
//处理裁切
if (camera.TryGetCullingParameters(out ScriptableCullingParameters p))
{
cullingResults = context.Cull(ref p);
}
else
{
return;
}
context.SetupCameraProperties(camera);
s_CommandBuffer.ClearRenderTarget(true, true, Color.clear);
s_CommandBuffer.BeginSample(BUFF_NAME);
context.ExecuteCommandBuffer(s_CommandBuffer);
s_CommandBuffer.Clear();
//排序设置-SortingCriteria.CommonOpaque表示默认不透明物体排序
var sortingSettings = new SortingSettings(camera) { criteria = SortingCriteria.CommonOpaque};
var drawingSettings = new DrawingSettings(s_ShaderTagIds[0], sortingSettings);
var filteringSettings = new FilteringSettings(RenderQueueRange.opaque);
for (int i = 1; i < s_ShaderTagIds.Length; i++)
{
drawingSettings.SetShaderPassName(i, s_ShaderTagIds[i]);
}
//开始绘制不透明物体,SceneView视图中还用物体自身的Shader
if (camera.cameraType != CameraType.SceneView)
drawingSettings.overrideMaterial = s_OverQueue;//换shader
context.DrawRenderers(
cullingResults, ref drawingSettings, ref filteringSettings
);
//排序设置-SortingCriteria.CommonTransparent表示默认透明物体排序
sortingSettings = new SortingSettings(camera) { criteria = SortingCriteria.CommonTransparent};
drawingSettings = new DrawingSettings(s_ShaderTagIds[0], sortingSettings);
filteringSettings = new FilteringSettings(RenderQueueRange.transparent);
for (int i = 1; i < s_ShaderTagIds.Length; i++)
{
drawingSettings.SetShaderPassName(i, s_ShaderTagIds[i]);
}
//开始绘制透明物体,SceneView视图中还用物体自身的Shader
if (camera.cameraType != CameraType.SceneView)
drawingSettings.overrideMaterial = s_OverTransparent;//换shader
context.DrawRenderers(
cullingResults, ref drawingSettings, ref filteringSettings
);
s_CommandBuffer.EndSample(BUFF_NAME);
context.ExecuteCommandBuffer(s_CommandBuffer);
s_CommandBuffer.Clear();
context.Submit();
}
}
现在还有2个问题
1.如何把准确的overdraw统计出来?
我的做法是做了一个全屏的RT(避免太卡还是要乘系数),分别取一张overdraw的图,在取一张不带overdraw的图,在遍历两张图的像素值就可以计算出来填充率了。
2.如果项目已经是lwrp、urp、hdrp、怎么处理呢?
其实也简单,就是两个做一个合并就可以了,也可以在创建一个新的SRPSetting,统计overdraw的时候动态覆盖到setting上面,统计完了在还原回来。
不管怎么说,这两个问题我会在下篇文章里好好讨论,我们下篇见!衷心的希望大家可以一起讨论。
- 本文固定链接: https://www.xuanyusong.com/archives/4669
- 转载请注明: 雨松MOMO于 雨松MOMO程序研究院 发表




不透明物体是从前往后
不透明物体默认不是从前往后画的吗?
不透明物体会写深度,默认是从后往前画的?????
请问下,有完整工程示例么?