Billboards 技術(shù)在Unity 中的幾種使用方法

關(guān)于billboard技術(shù),原理就是計(jì)算出來(lái)一個(gè)始終朝向攝像機(jī)的面片,可以在CPU里計(jì)算,也可以在GPU里實(shí)現(xiàn)。應(yīng)用的場(chǎng)合很多:

  • 游戲角色的頭頂文字,血條
  • 場(chǎng)景的樹(shù),草
  • 特效粒子片
  • 3d場(chǎng)景里的2d角色

world space計(jì)算

思路:shader中傳入面片的中心點(diǎn)的世界坐標(biāo),以及攝像機(jī)的right和up在world space的方向,中心點(diǎn)直接沿著right和up方向計(jì)算四個(gè)頂點(diǎn)的世界坐標(biāo),定點(diǎn)數(shù)據(jù)中包含了每個(gè)頂點(diǎn)的偏移信息。然后乘以ViewProjection矩陣,作為輸出。
這里有個(gè)trick的地方,就是從object space 到world space是沒(méi)有旋轉(zhuǎn)的,只有偏移,所以攝像機(jī)在世界空間的right就是模型空間的right。MV的逆矩陣相當(dāng)于View到Object的變換,轉(zhuǎn)置是為了去列向量好取。

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float2 vertexOffset : TEXCOORD1;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;

            v2f vert (appdata v)
            {
                v2f o;
                
                //這里相當(dāng)于取列向量
                
                float3 right = UNITY_MATRIX_IT_MV[0].xyz;
                float3 up = UNITY_MATRIX_IT_MV[1].xyz;

                v.vertex.xyz += v.vertexOffset.x * right + v.vertexOffset.y * up;
                o.vertex = mul(UNITY_MATRIX_VP, float4(v.vertex.xyz, 1.0));

                o.uv = v.uv;

                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {

                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG

unity c#中生成mesh的代碼

        Vector3[] vertices = new Vector3[4]
        {
            worldPos,
            worldPos,
            worldPos,
            worldPos
        };

        int[] indices = new int[6]
        {
            0, 2, 1, 0, 3, 2
        };

        Vector2[] uvs = new Vector2[4]
        {
            new Vector2(0, 0),
            new Vector2(0, 1),
            new Vector2(1, 1),
            new Vector2(1, 0),
        };

        Vector2[] uv2s = new Vector2[4]
        {
            new Vector2(-0.5f, -0.5f),
            new Vector2(0.5f, -0.5f),
            new Vector2(0.5f, 0.5f),
            new Vector2(-0.5f, 0.5f),
        };

        meshFilter.mesh.vertices = vertices;
        meshFilter.mesh.triangles = indices;
        meshFilter.mesh.uv = uvs;
        meshFilter.mesh.uv2 = uv2s;

clip space計(jì)算

思路:還是傳入中心點(diǎn)的世界坐標(biāo),以及4個(gè)頂點(diǎn)的偏移信息。把中心點(diǎn)轉(zhuǎn)到
clip space,然后按照偏移信息縮放。這個(gè)適用的場(chǎng)合是面片不隨距離攝像機(jī)的遠(yuǎn)近而縮放。

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float2 vertexOffset : TEXCOORD1;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float3 color : TEXCOORD1;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;

            v2f vert (appdata v)
            {
                v2f o;

                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.vertex.xyz /= o.vertex.w;

                o.vertex.xy += v.vertexOffset.xy * float2(0.2, 0.05);
                o.vertex.w = 1;

                o.uv = v.uv;

                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {

                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG

view space計(jì)算

上面兩種方式都是通過(guò)傳入中心點(diǎn)坐標(biāo)到shader進(jìn)行計(jì)算,但是有時(shí)候并不能獲得面片的中心點(diǎn)坐標(biāo),比如Unity中的SpriteRenderer,這時(shí)候的思路是:把中心點(diǎn)即(0,0,0,1)(Object space)轉(zhuǎn)到View Space,然后頂點(diǎn)再做偏移。

                o.pos = mul(UNITY_MATRIX_P, 
                  mul(UNITY_MATRIX_MV, float4(0.0, 0.0, 0.0, 1.0))
                  + float4(i.vertex.x, i.vertex.y, 0.0, 0.0));

CPU計(jì)算

思路:面片的位置發(fā)生變化,或者攝像機(jī)發(fā)生變化的時(shí)候,重新計(jì)算一下面片的旋轉(zhuǎn),讓它始終朝向攝像機(jī)

    private void CalcBillboard()
    {
        if(instance != null)
        {
            instance.transform.rotation = Camera.main.transform.rotation;
        }
    }

參考資料

知乎專欄-程序員的自我修養(yǎng) 歡迎關(guān)注,交流游戲開(kāi)發(fā)相關(guān)技術(shù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 111. [動(dòng)畫(huà)系統(tǒng)]如何將其他類型的動(dòng)畫(huà)轉(zhuǎn)換成關(guān)鍵幀動(dòng)畫(huà)? 動(dòng)畫(huà)->點(diǎn)緩存->關(guān)鍵幀 112. [動(dòng)畫(huà)]Unit...
    胤醚貔貅閱讀 13,510評(píng)論 3 88
  • 原文:Quickstart tutorial 譯者:Reverland 來(lái)源:試驗(yàn)性NumPy教程(譯) 2.1 ...
    布客飛龍閱讀 1,485評(píng)論 5 52
  • 我挽著一面藤牌,趴在船沿上,看著自己的船隊(duì)在火海中做些最后的掙扎,遠(yuǎn)去…… 嗖!這聲音再熟悉不過(guò),我習(xí)慣性的抄起藤...
    清風(fēng)碎刀閱讀 468評(píng)論 0 6
  • 一定要充分暴露牙槽嵴頂,翻開(kāi)頰腭側(cè)瓣,直視下觀察牙槽骨厚度和形態(tài),預(yù)估鉆孔位點(diǎn)、角度和深度。在第一鉆時(shí)評(píng)估骨質(zhì)...
    練習(xí)人生閱讀 392評(píng)論 0 0

友情鏈接更多精彩內(nèi)容