Wang Tile的Shader简易实现

在使用大面积的平铺纹理时,会导致重复感较强的贴图呈现在画面中。我们可以通过许多方法进行优化,WangTile就是其中一种。

WangTile(王浩瓷砖)方法通过对每条边标记颜色,并在平铺时将相同颜色的边拼接在一起,最终铺满整个平面。

参考《GPU Gems2》中的做法,但这里使用一组预先设定好的可循环组合值,以及一个4x4的瓷砖纹理。

 

4x4纹理图:

 

假设y坐标朝向为从下到上,x坐标朝向为从左到右,则索引(0,0)表示数字16的色块,索引(2,3)表示数字3的色块,以此类推。

这里首先配置好可循环的组合,他们的x轴和y轴不同的序列为:

X_seq: 1, 2, 2, 3, 0, 1, 3, 1, 2, 3, 0, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3
Y_seq: 0, 1, 2, 3, 0, 1, 3, 1, 2, 3, 1, 2, 2, 2, 3, 0, 1, 3, 0, 1, 3

 

这个序列可以给不同的瓷砖贴图重复使用,我们把这个作为数组传入Shader,数组长度写死为21,其Shader如下:

Shader "Unlit/WangTileTesGPU"
{
    Properties
    {
        _TileTex("Tile Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

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

            sampler2D _TileTex;

            uniform float _WangTile_X_Seq[21];
            uniform float _WangTile_Y_Seq[21];

#define MAIN_TEX_TILE_SIZE half2(21, 21)
#define TILE_TEX_SIZE half2(4, 4)

            v2f vert (appdata v)
            {
                v2f o = (v2f)0;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            fixed4 SampleTile(half2 uv, half2 index, half2 mainTexTileTexSize, half2 tileTexSize)
            {
                float xIndex = _WangTile_X_Seq[index.x % 21] / tileTexSize.x;
                float yIndex = _WangTile_Y_Seq[index.y % 21] / tileTexSize.y;

                half2 large_uv = frac(uv * mainTexTileTexSize);
                half2 sub_uv = large_uv / tileTexSize;

                return tex2D(_TileTex, half2(xIndex, yIndex) + sub_uv, ddx(uv), ddy(uv));
                //使用ddx,ddy参数去除接缝问题
            }

            fixed4 frag (v2f i) : SV_Target
            {
                half2 sub_uv_index = floor(i.uv * MAIN_TEX_TILE_SIZE);//Index: 1,2,3,4...

                return SampleTile(i.uv, sub_uv_index, MAIN_TEX_TILE_SIZE, TILE_TEX_SIZE);
            }
            ENDCG
        }
    }
}

 

传入数组的csharp脚本如下:

public class WangTileGPU : MonoBehaviour
{
    public Material mat;


    void Start()
    {
        float[] index_x_loop = new float[] { 1, 2, 2, 3, 0, 1, 3, 1, 2, 3, 0, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3 };
        float[] index_y_loop = new float[] { 0, 1, 2, 3, 0, 1, 3, 1, 2, 3, 1, 2, 2, 2, 3, 0, 1, 3, 0, 1, 3 };

        mat.SetFloatArray("_WangTile_X_Seq", index_x_loop);
        mat.SetFloatArray("_WangTile_Y_Seq", index_y_loop);
    }
}

 

最终效果:

 

带贴图的效果(未做连续处理):

 

posted @ 2020-04-19 17:52  HONT  阅读(868)  评论(0编辑  收藏  举报