纹理基础——单张纹理&纹理属性笔记

纹理基础——单张纹理&纹理属性


使用 纹理映射(texture mapping)技术,可以把一张图“黏”在模型表面,逐 纹素(texel)(纹素的名字是为了和像素进行区分)地控制模型的颜色。
美术在建模时会在建模软件中利用纹理展开技术把 纹理映射坐标(texture-mapping coordinates) 存储在每个顶点上。
纹理映射坐标(texture-mapping coordinates) 定义了该顶点在纹理中对应的2D坐标,通常使用二位变量 (u, v) 来表示,u横坐标,v纵坐标,纹理映射坐标也被称为 UV 坐标。
OpenGL纹理空间的原点位于左下角,DirextX纹理空间的原点位于左上角,Unity使用的纹理空间是符合OpenGL,原点位于左下角。

单张纹理

通常使用一张纹理来代替物体的漫反射颜色。实现在Unity Shader 中使用单张纹理来作为模拟的颜色。

实现

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

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Shader Test/Chapter 7/Single Texture"
{
    Properties
    {
        // 控制物体的整体色调
        _Color ("Color Tint", Color) = (1,1,1,1)

        // 2D是纹理属性,"white"是内置纹理的名字也就是一个纯白纹理
        _MainTex ("Main Tex", 2D) = "white" {}

        _Specular("Specular", Color) = (1,1,1,1)
        _Gloss("Gloss", Range(8.0,256)) = 20
    }

    SubShader
    {
        Pass
        {
            // 指明该Pass的光照模式
            Tags { "LightMode" = "ForwardBase" }

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            #include "Lighting.cginc"

            fixed4 _Color;
            sampler2D _MainTex;

            // Unity 需使用 纹理名_ST 的方式来声明某个纹理的属性,ST是缩放(scale)和平移(translation)的缩写
            // _MainTex_ST得到该纹理的缩放和平移(偏移)值,_MainTex_ST.xy 存储的是缩放值,_MainTex_ST.zw 存储的是偏移值,这些值可在材质面板的纹理属性中调节
            float4 _MainTex_ST;

            fixed4 _Specular;
            float _Gloss;

            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;

                // Unity就会将模型的第一组纹理坐标存储到texcoord变量中
                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 = UnityObjectToClipPos(v.vertex);

                o.worldNormal = UnityObjectToWorldNormal(v.normal);

                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

                // 使用纹理的属性值 _MainTex_ST 来对顶点纹理坐标进行变换得到最终的纹理坐标
                // 先使用缩放属性 _MainTex_ST.xy 对地点纹理坐标进行缩放,然后再使用 _MainTex_ST.zw 对结果进行偏移
                // Unity 提供了一个内置宏 TRANSFORM_TEX(在 UnityCG.cginc 中定义) 计算:
                // #define TRANSFORM_TEX(tex, name) (tex.xy * name##_ST.xy + name##_ST.zw)
                // 第一个参数是顶点纹理坐标,第二个参数是纹理名,在它的实现中将利用 纹理名_ST 的方式来计算变换后的纹理坐标
                // 下方代码可替换为宏:
                // o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;

                return o;
            }

            // 实现片元着色器,并在计算漫反射时使用纹理中的纹理值
            fixed4 frag(v2f i) : SV_Target
            {
                // 计算世界空间下的法线方向和光照方向
                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

                // CG的tex2D函数对纹理进行采样,
                // 第一个参数是需要被采样的纹理,
                // 第二个参数是一个float2类型的纹理坐标,
                // 它将返回计算得到的纹理值
                // 使用采样结果和颜色属性_Color 的乘积来作为材质的反射率albedo
                fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;

                // 材质的反射率albedo与环境光照相乘得到环境光部分
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;

                // 使用albedo来计算漫反射光照的结果
                fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));

                /// 计算高光反射部分
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
                fixed3 halfDir = normalize(worldLightDir + viewDir);
                fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);

                // 环境光、漫反射部分和高光反射光照相加后返回
                return fixed4(ambient + diffuse + specular, 1.0);
            }

            ENDCG
        }
    }
    Fallback "Specular"
}

纹理的属性


Texture Type:纹理属性,
选择合适的纹理类型,在一些情况下可让Unity对该纹理进行优化;
正常来说按默认设置的Default就行,Default是所有纹理最常见的类型,可用于访问导入的大多数的纹理属性;
除了Default之外还有,Normal Map——法线贴图;Sprite——精灵和UI的图;Cursor——鼠标指针的样子图等等;

Texture Shape:纹理形状
可选的有最常用的2D纹理;3D纹理;Cube(立方体贴图)等,默认是2D纹理。

Alpha Source:指定如何生成Alpha通道

  • Input Texture Alpha 输入纹理Alpha
  • From Gray Scale 来自灰度,选中透明通道值就会有每个像素的灰度值生成
    Alpha Is TransParency :如果指定的Alpha通道为透明度,这个属性可以配合着使用。

wrap mode:封装模式
这是纹理比较重要的一个属性,其实就是在纹理管线中的提到的纹理采样会用到的映射函数。
它决定了当纹理坐标超过 [0, 1] 范围后将会如何被平铺。

  • Repeat 模式
  • Clamp 模式
  • Mirror 模式

以上展示了在纹理平铺(Tiling)属性为(3, 3)时分别使用不同模式的结果。

也可修改 Offset 属性实现偏移的效果,以下为(-1, -1)时的效果:

  • Repeat 模式
  • Clamp 模式
  • Mirror 模式

想让纹理得到这样的效果,必须使用纹理的属性(如之前用到的_MainTex_ST变量)在Unity Shader中对顶点纹理坐标进行相应的变换,代码中需要包含类似的代码:

                // 使用内置方法: o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;

Filter Mode: 过滤模式
它决定了当纹理由于变换而产生拉伸时将会采用哪种滤波模式,
支持3种模式:Point,Bilinear以及Trilinear。得到的图片滤波效果以此提升,但需要消耗的性能也依次增大。纹理滤波会影响放大或缩小纹理时得到的图片质量。
以下为 306 * 204 纹理图片贴在 12800 * 7200 平面上不同过滤模式的对比情况:

  • Point(no filter) 模式:
  • Bilinear 模式:
  • Trilinear 模式:

纹理缩小的过程比放大的更加复杂一些,此时原纹理中的多个像素将会对应一个目标像素。
纹理缩放更加复杂的原因在于需要处理抗锯齿问题,一个最常使用的方法就是使用 多级渐远纹理(mipmapping) 技术。

多级渐远纹理(mipmapping) 技术:

  • 原理:将原纹理提前用滤波处理来得到很多更小的图像,形成了一个图像金字塔,每一层都是对上一层图像降采样的结果。
  • 缺点:需要使用一定的空间用于存储这些多级渐远纹理,通常会多占用33%的内存空间,这是一种典型的用空间换时间的方法。
  • 开启步骤:在Unity 点开一张纹理图,Advanced 中勾选 Generate Mip Maps 就可以启用 Mipmap 了。

对于渐远地面,MipMap开启与不开启的效果对比(均采用Bilinear滤波)

  • Bilinear滤波+MipMap
    Bilinear滤波+MipMap
  • Bilinear滤波(不加MipMap)
    Bilinear滤波(不加MipMap)
    不同Filter Mode结合多级渐远纹理技术得到效果对比:
  • Point(no filter) 模式:使用了最近邻(nearest neighbor) 滤波,再放大缩小时它的采样像素数目通常只有一个,因此图像会看起来有种像素风格的效果。
  • Bilinear 模式:使用了线性滤波,对于每个目标像素,会找到4个邻近像素,然后对它们进行线性插值混合后得到最终像素,因此图像看起来像被模糊了。
  • Trilinear 模式:几乎和 Bilinear 模式一样,只是 Trilinear 还会在多级渐远纹理之间进行混合,如果一张纹理没有使用多级渐远纹理技术,那么 Trilinear 得到的结果是和 Bilinear 一样。

通常会选择 Bilinear 滤波模式,需注意如果不希望纹理看起来是模糊的,希望是像素风,这时可能会选择 Point 模式。

在为不同平台发布游戏时需考虑目标平台的纹理尺寸和质量问题,Unity允许我们为不同目标平台选择不同的分辨率。
如果导入的纹理大小超过了Max Texture Size中的设置值,那么 Unity 将会把该纹理缩放为这个最大分辨率。
理想情况下,导入的纹理可以是非正方形的,但长宽的大小应该是2的幂。
如果使用了非2的幂大小(Non PowerofTwo,NPOT)的纹理,那么这些纹理往往会占用更多的内存空间,而且GPU读取该纹理的速度也会有所下降。
有一些平台甚至不支持这种 NPOT纹理,这时 Unity 在内部会把它缩放成最近的2的幂大小。出于性能和空间的考虑应该尽量使用2的幂大小的纹理。

Resize Algorithm
当 Texture 尺寸大于指定的Max Size时,选择一种用于缩小 Texture 的算法。
1. Mitchell:使用 Mitchell 算法调整纹理大小。这是默认的调整大小算法。
2. Bilinear:使用双线性插值调整纹理大小。对于小而清晰的细节很重要的图像,这可以比 Mitchell 保留更多的这些细节。

Format 绕过自动系统来指定纹理使用的内部表示。可用格式列表取决于平台和纹理类型。

Compression 选择纹理的压缩类型
1. None:不要压缩纹理。
2. Low Quality:以低质量格式压缩纹理。这可能比Normal Quality使用更少的内存。
3. Normal Quality:使用标准格式压缩纹理。
4. High Quality:以高质量格式压缩纹理。这可能比Normal Quality使用更多的内存。

需要知道的是,使用的纹理格式精度越高,占用的内存空间越大但得到的效果也越好。因此对于一些不需要使用很高精度的纹理(例如用于漫反射颜色的纹理),应该尽量使用压缩格式。

Use Crunch Compression
如果适用,请使用紧缩压缩。Crunch 是一种基于 DXT 或 ETC 的有损压缩格式纹理压缩
Unity 在 CPU 上将纹理解压缩为 DXT 或 ETC,然后在运行时将它们上传到 GPU。
Crunch 压缩有助于纹理使用尽可能少的磁盘空间和下载空间。
Crunch Textures 可能需要很长时间来压缩,但在运行时解压非常快。

  • 33
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值