【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"
}

你可能感兴趣的:(UnityShader)