UnityShader入门精要学习笔记(七):基础纹理之单张纹理

时间:2021-01-25 10:23:20

一 .基础纹理应用

纹理的最初目的就是使用一张图片来控制模型的外观。使用纹理映射技术,我们可以吧一张图“黏”在模型表面,逐纹素(texel)(名字是为了和逐像素进行区分)地控制模型的颜色。

结合之前的光照模型的单张纹理应用:

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'

Shader "Custom/Edu/SingleTexture" {
    Properties 
    {
        _MainTex("MainTex",2D)= "white"{}
        _Diffuse("Diffuse",Color) = (1,1,1,1)
        _Specular("Specular",Color) = (1,1,1,1)
        _Gloss("Gloss",Range(8,256)) = 40
        _Color("ColorTint",Color) = (1,1,1,1)
    }
    SubShader 
    {
        Pass
        {
            Tags{"LightMode" = "ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Diffuse;
            fixed4 _Specular;
            fixed4 _Color;
            float _Gloss;

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

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

            v2f vert(a2v v)
            {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
                o.worldPos = mul(unity_ObjectToWorld,v.vertex);
                o.worldNormal = mul(v.normal,(float3x3)unity_WorldToObject);
                o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
                return o;
            }

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

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

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

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;

                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
                fixed3 halfDir = normalize(viewDir + worldLightDir);

                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal,halfDir)),_Gloss);

                return fixed4( diffuse + ambient + specular , 1.0);
            }

            ENDCG
        }
    }
    FallBack "Diffuse"
}

TRASFORM_TEX实在UnityCG.cginc中定义的:

//Transform 2D UV by scale/bias property
#define TRANSFORM_TEX(tex,name)(tex.xy * name##_ST.xy + name##_ST.zw)

它接受两个参数
第一个参数是顶点的纹理坐标
第二个参数是纹理的名称

看一下效果:

UnityShader入门精要学习笔记(七):基础纹理之单张纹理

二.纹理采样区间设置

下面为了阐释纹理的属性,编写一个更为纯粹的纹理属性应用shader

Shader "Custom/Edu/SimpleTextureProperties" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
    }
    SubShader 
    {
        Pass
        {
            Tags{"LightMode" = "ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Color;

            struct a2v
            {
                float4 vertex:POSITION;
                float2 texcoord:TEXCOORD0;
            };

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

            v2f vert(a2v v)
            {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
                return o;
            }

            fixed4 frag(v2f i):SV_Target
            {
                fixed3 color = tex2D(_MainTex,i.uv);
                return fixed4(color,1.0);
            }

            ENDCG
        }
    }
    FallBack "Diffuse"
}

利用这个shader在场景中创建一个Quad。

UnityShader入门精要学习笔记(七):基础纹理之单张纹理

此时我们对纹理使用的Wrap Mode的是Repeat——重复纹理采样。
这种模式下,如果纹理的坐标超过了1,那么整数部分将会被舍弃,而直接使用小数部分进行采用。
下面我们来看另外一种模式

UnityShader入门精要学习笔记(七):基础纹理之单张纹理

当使用Clamp模式时,超过的部分将会截取到边界值,从而形成一个条形结构。

三.3种滤波模式

纹理导入面板中的下一个属性是FilterMode属性,它决定了当纹理由于变换而产生拉伸是将会使用哪种滤波模式。

Point模式使用了最近邻滤波,在放大或缩小时,它的采样像素数目通常只有一个,因此图像看起来有种像素风格的效果。

Bilinear滤波采用了线性滤波,对于每个目标像素,它会找4个邻近的像素,然后对他们进行线性插值混合后得到最终的像素,因此图像看起来像被模糊了。

Trilinear滤波几乎和Bilinear是一样的,知识Trilinear还会在多级渐远纹理(mipmapping)之间进行混合。

四.纹理的最大尺寸和纹理模式

UnityShader入门精要学习笔记(七):基础纹理之单张纹理
【我只安装了安卓build的模块】
Unity允许我们为不同目标平选择不同的纹理尺寸和压缩格式。
如果导入的纹理超过了MaxTextureSize中的设置,那么Unity会把这个纹理所防伪这个最大分辨率。
导入的纹理的长宽大小应该是2的整数次幂。
如果使用了非2的整数次幂大小(Non Power of Two,NPOT)的纹理,那么这些纹理往往会占用更多的内存空间,而且GPU的读取该纹理的速度也会下降。甚至有一些平台不支持NPOY纹理。
对于一些不需要使用很高精度的纹理(例如漫反射颜色的纹理),我们应该尽量使用压缩格式!