美文网首页
一篇雨滴效果实现的学习笔记

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

作者: waempty | 来源:发表于2019-11-28 20:09 被阅读0次

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

    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<float4> 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<T>或者 RWStructuredBuffer<T>来声明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<MeshRenderer>();
       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.动态合批需要将顶点全部转化为世界坐标,所以只有当这项消耗低于合批的时候才值得做动态合批。

    相关文章

      网友评论

          本文标题:一篇雨滴效果实现的学习笔记

          本文链接:https://www.haomeiwen.com/subject/ftnqwctx.html