【UnityShader】紋理基礎與屬性

紋理基礎知識

  • 紋理的最初目的是使用一張圖片來控制模型的外觀,使用紋理映射技術,可以把一張圖“黏”在模型的表面,逐紋素的控制模型的顏色。

  • UV座標通常被歸一化到[0, 1]範圍內。

  • 在 OpenGL 中,紋理空間的原點位於左下角,而在 DirectX 中,原點位於左上角。

  • samplar2D _MainTex;
    float4 _MainTex_ST; // 紋理的屬性,ST爲縮放和平移的縮寫,_MainTex_ST.xy縮放值,_MainTex_ST.zw 偏移值。可以在材質面板中調節,對應的是 Tiling 和 Offset。

  • o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    // 等價於,先縮放,後偏移
    o.uv = v.texcoord * _MainTex_ST.xy + _MainTex_ST.zw;

紋理屬性

  • Texture Type 紋理類型

    • Default 普通貼圖
    • Normal map 法線貼圖
    • Editor GUI and Legacy GUI UI貼圖
    • Sprite(2D and UI) 精靈
    • Cursor 鼠標指針
    • Cookie 遮罩貼圖
    • Lightmap 烘焙貼圖
  • Texture Shape

    • 2D 默認類型,2D紋理
    • Cube 立方體貼圖,用於Skyboxes或者Reflection Probes
  • sRGB(Color Texture)(標準顏色的紋理)
    如果不需要標準RGB顏色的紋理,可以不勾選。

  • Alpha Source:紋理Alpha通道

    • None 無,不論紋理是否有Alpha,都設置爲沒有Alpha
    • Input Texture Alpha 導入紋理自帶Alpha
    • From Gray Scale 透明通道的值會由每個像素的灰度值生成
  • Alpha Is Transparency
    當alpha用於透明處理時,我們要勾上該選項,可以防止不透明邊緣鋸齒現象,如果Alpha有它用,可以不勾選。

  • Advanced

    • Non power of 2
      Unity默認導入的最大紋理尺寸爲8k,當紋理大小不是2的冪次方時,Unity會對其進行縮放,影響效率。
    • Read/Write Enabled
      從腳本中讀取Texture數據
      1.會製作一個Texture的副本,導致Texture的內存增加
      2.未壓縮和DXT壓縮紋理纔可讀取,其他壓縮的不能讀取
    • Generate Mip Maps
      生成mipmap,使遠離相機的物體使用較小的Texture。mipmap 啓用後內存增加33%,但是可以提高性能,優化實時3D渲染性能。
    • Border mipmaps:使用較小圖像時防止顏色溢出
    • MipmapFiltering:優化圖像質量的過濾方式
        box:平滑改變尺寸
        Kaiser:銳化算法,少用
    • Fadeout MipMaps:淡出功能,在Mip更換級別的時候漸變爲灰色,可用於地圖
  • Wrap Mode
    它決定了當紋理座標超過[0, 1]範圍後將如何平鋪。

    • repeat 表示重複,當紋理座標超過1時,整數部分被捨棄,直接使用小數部分進行採樣,紋理會不斷重複。
    • clamp 紋理座標大於1時會截取到1,小於0時會截取到0。
  • Filter Mode
    它決定了當紋理由於變換產生拉伸時會採用哪種濾波模式。有3種模式:Point,Bilinear,Trilinear。它們得到的圖片濾波效果依次提升,但是需要耗費的性能也依次增大。

    • Point模式:最近點採樣,當紋理座標沒有剛好對應Texture上的一個採樣點時,它會選擇最近的一個採樣點作爲該座標的採樣值,當紋理沒有拉伸變形時,這樣還不錯,因爲速度是最快的,但如果拉伸變形了,會出現馬賽克現象。

    • Bilinear模式:雙線性過濾,以像素對應的紋理座標爲中心,採樣它周圍4個texel(紋素)的像素,取平均值作爲該座標採樣值。這是Unity默認的模式,過渡效果相對平滑,當然速度會比最近點採樣有一定下降。

    • Trilinear模式:三線性過濾,會對像素大小和紋素大小最接近的兩層Mipmap level分別進行雙線性過濾,再對結果進行線性插值。由於使用了兩次雙線性過濾,也就是計算2x4=8個像素的值,速度會更加下降,當然濾波效果更好。

  • Generate Mip Maps
    紋理縮小的過程比放大更加複雜一些,此時原紋理中的多個像素將會對應一個目標像素。這樣就會出現鋸齒現象。一個最常用的方法就是多級漸遠紋理(mipmapping)技術。該技術將原紋理提前用濾波處理來得到很多更小的圖像,形成一個圖像金字塔,每一層都是對上一層圖像降採樣的結果。在實時運行時,比如物體遠離攝像機時,可以直接用較小的紋理採樣。比如一張128x128的圖,會生成64x64,32x32,16x16,8x8,4x4,2x2,1x1的圖片,當然,因爲要多存儲這些圖片,通常會多佔用33%的空間。這是典型的空間換取時間的方法。所以使用時如果因爲攝像機遠近會放大縮小的圖,比如場景資源,我們儘量用mipmap,否則取消mipmap,比如ui元素。

  • Max Size
    紋理最大尺寸,在紋理屬性裏我們看最後的部分,因爲沒選所以是灰色的,它會根據不同平臺進行設置,如果我們導入的紋理大小超過了Max Size,那麼Unity會把該紋理縮放成這個最大分辨率。導入的紋理可以是非正方形的,但長寬該是2的冪。如果使用了非2的冪大小(Non Power of Two, NPOT)的紋理,這些紋理往往會佔用更多的內存空間,GPU讀取速度也會下降。所以Unity在內部會把它縮放成最近的2的冪。出於性能和空間考慮,我們應儘量使用2的冪的紋理。

  • Format
    Unity內部使用哪種格式存儲該紋理。打開其下拉條,我們看到一捆格式,紋理的精度越高,佔用的內存越大,得到的效果也越好,最好的是RGBA 32 bit,如果沒有alpha通道,RGB 24 bit也是效果最好的,但帶寬太大不是好事,對於不需要使用高精度的紋理,我們要適當的用壓縮格式,這對手機平臺尤其重要。

實例:用一張紋理來代替物體的漫反射顏色

Shader "ZJT/Shader07-01"
{
    Properties
    {
        _Color ("Color", Color) = (1, 1, 1, 1)
        _MainTex ("Main Tex", 2D) = "white" {}
        _Specular ("Color", Color) = (1, 1, 1, 1)
        _Glossness ("Glossness", Range(1, 200)) = 10
    }
    SubShader
    {
        Tags{"RenderType"="Opaque"}
        LOD 200

        Pass
        {
            Tags{"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert 
            #pragma fragment frag 

            #include "Lighting.cginc"

            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;  // 紋理的屬性,ST爲縮放和平移的縮寫,_MainTex_ST.xy縮放值,_MainTex_ST.zw 偏移值
            fixed4 _Specular;
            float _Glossness;

            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float3 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 worldNormal : TEXCOORD1;
                float3 worldPos : TEXCOORD2;
            };

            v2f vert(a2v v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                // 等價於
                // o.uv = v.texcoord * _MainTex_ST.xy + _MainTex_ST.zw;
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);

                return o;
            }

            fixed4 frag(v2f v):SV_Target
            {
                fixed3 worldNormal = normalize(v.worldNormal);
                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(v.worldPos));
                fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(v.worldPos));

                fixed3 albedo = tex2D(_MainTex, v.uv).rgb * _Color.rgb;

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
                fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(worldNormal, worldLightDir));

                fixed3 halfDir = normalize(worldLightDir + worldViewDir);
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal, halfDir)), _Glossness);

                return fixed4(ambient + diffuse + specular, 1);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章