一篇雨滴效果实现的学习笔记

前两天看到一篇关于雨滴效果制作的文章,学习实践了一下。这里贴一下涉及到的知识点。

1.CommandBuffer

用来存储一系列渲染指令,可以通过Camera(Camera.AddCommandBuffer)
,Light( Light.AddCommandBuffer
)及Graphics(Graphics.ExecuteCommandBuffer
)来调用执行。

2.ComputeShader

ComputeShader运行在GPU上,可以执行大量并行算法,或者用来加速游戏渲染。为了更高效的使用Compute Shader,需要额外了解GPU的架构及常见并行算法。
使用ComputerShader之前需要先使用SystemInfo.supportsComputeShaders测试可用性。下面是基本的ComputeShader格式

// test.compute
// 一个Compute Shader必须至少有一个 kernel, #pragma kernel后可添加额外的宏,如OTHER_DEFINE
#pragma kernel FillWithRed OTHER_DEFINE

RWTexture2D res;

[numthreads(100,1,1)]
void FillWithRed (uint3 dtid : SV_DispatchThreadID)
{
    res[dtid.xy] = float4(1,0,0,1);
}

之后可以通过 ComputeShader.Dispatch来执行shader。方式如下:

ComputeShader shader;
int kernal = shader.FindKernel("CSMain");

shader.Dispatch(kernal, 100, 1, 1);

3. ComputeBuffer

GPU数据缓存,通常是在ComputeShader中使用,可以填充数据,也可以获取数据。
在HLSL格式里,使用StructuredBuffer或者 RWStructuredBuffer来声明ComputeBuffer。

4. GPU Instance

GPU Instance是一种将相同Mesh的多个拷贝在少量的draw call中完成绘制的技术。经常在绘制建筑,树木,草木或者其他一些在场景中重复出现的物件中使用。可以明显的提升渲染性能。
GPU Instance在每个draw call中绘制的必须是完全相同的mesh,但是每个mesh可以拥有不同的参数(形如颜色,缩放值)。
为了使用GPU Instance,需要开启材质的 Enable Instancing选项。


Enable Instancing

除了以上方式,也可以在脚本中调用Graphics.DrawMeshInstanced 或者 Graphics.DrawMeshInstancedIndirect执行GPU Instance。

通常,Unity只会对仅有Transform变化的GameObject做批处理,为了为每个实例添加更多的变体,需要在Shader中添加per-instance 属性,示例如下:

Shader "SimplestInstancedShader"
{
    Properties
    {
        _Color ("Color", Color) = (1, 1, 1, 1)
    }

    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_instancing
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                UNITY_VERTEX_INPUT_INSTANCE_ID // necessary only if you want to access instanced properties in __fragment Shader__.
            };

            //声明实例属性_Color
            UNITY_INSTANCING_BUFFER_START(Props)
                UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
            UNITY_INSTANCING_BUFFER_END(Props)
           
            v2f vert(appdata v)
            {
                v2f o;

                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_TRANSFER_INSTANCE_ID(v, o); // necessary only if you want to access instanced properties in the fragment Shader.

                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }
           
            fixed4 frag(v2f i) : SV_Target
            {
                UNITY_SETUP_INSTANCE_ID(i); // necessary only if any instanced properties are going to be accessed in the fragment Shader.
                return UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
            }
            ENDCG
        }
    }
}

上面声明了_Color属性,Unity会从MaterialPropertyBlock中采集_Color属性,设置到GameObject的Shader中,并将GameObject合并在一个DrawCall中

MaterialPropertyBlock props = new MaterialPropertyBlock();
MeshRenderer renderer;

foreach (GameObject obj in objects)
{
   float r = Random.Range(0.0f, 1.0f);
   float g = Random.Range(0.0f, 1.0f);
   float b = Random.Range(0.0f, 1.0f);
   props.SetColor("_Color", new Color(r, g, b));
   
   renderer = obj.GetComponent();
   renderer.SetPropertyBlock(props);
}

一些高级Tips
1.当Unity进行batch的时候,Static batching
优先级要比instancing高,也就是说,如果将某个GameObject设置为static batching,Unity 会禁掉instancing,即使使用了instancing的shader,但是会完成static batching。这时,Inspector窗口会显示警告提示关闭static batch。
GPU Instancing的优先级会比dynamic batching高,当执行instancing的时候,dynamic batching会被禁用。
2.某些情况如材质改变或者深度排序会导致阻止Unity进行自动的instancing,这是可以调用Graphics.DrawMeshInstanced
强制执行GPU instancing。
3.从Unity2018.1开始,Unity支持GI 渲染时,在GPU Instancing使用光照探针及occlusion探针。可以在MaterialPropertyBlock中提供 light probe 及 occlusion probe数据(LightProbeUsage
参数中设置).
4.使用UnityObjectToClipPos(v.vertex) 而不是 mul(UNITY_MATRIX_MVP,v.vertex),前者更高效。

其他需要注意的地方:
1.SurfaceShader默认会生成示例变量,但是可以用#pragma noinstancing禁止这一行为。
2.Graphics.DrawMeshInstanced需要在材质设置中开启GPU Instancing设定,但是Graphics.DrawMeshInstancedIndirect不需要。
3.instanced draw call在Frame Debugger中以 Draw Mesh (instanced)形式出现。
4.在前向渲染的时候,只有base pass会执行instancing,add pass不会
5.如果有两个以上的pass,只有第一个pass会被instanced,因为后面的pass需要强制在一起渲染,这个操作会是材质发生变化.

5.Draw call batching

Dynamic batching:通常用于特别小的mesh,在cpu层面做顶点变换,之后将相似的顶点合批在一个draw call中绘制。会耗费大量cpu时间。
Static batching: 将静态的GameObject合并成一个大的mesh,从而提高渲染的速度。缺点是会耗费大量内存和硬盘空间。

只有使用相同的材质的GameObjects才会被合批处理。
1.如果除了纹理,两个材质完全相同,可以将两个纹理合在一张大的纹理中。
2.Renderer.material会创建一份材质的拷贝,而不是共享。需要调用Renderer.sharedMaterial共享材质
3.Shadow casters在材质不同的情况下也会被合批处理,只要供Shadow casters使用的材质参数一样即可。

4.一次动态合批不能超过900个顶点属性,不能超过300个顶点。
5.镜面GameObject(scale = -1)不能被合批
6.除了Shadow casters外,使用不同material的实例的对象不能被合批
7.动态合批需要将顶点全部转化为世界坐标,所以只有当这项消耗低于合批的时候才值得做动态合批。

你可能感兴趣的:(一篇雨滴效果实现的学习笔记)