Unity_Shader初级篇_7_Unity Shader入门精要

本文介绍了Unity中纹理映射的概念,包括UV坐标、纹理平铺模式、滤波模式以及纹理类型的选择。此外,详细讨论了凹凸纹理的两种方法:高度映射和法线映射,特别强调了切线空间法线纹理的优势。通过实例演示了如何在Unity中使用法线纹理进行光照计算。最后,提到了渐变纹理和遮罩纹理在控制漫反射和表面属性中的应用。
摘要由CSDN通过智能技术生成

纹理最初的目的就是使用一张图片来控制模型的外观。使用纹理映射(texture mapping)技术,我们可以把一张图“黏”在模型表面,逐妏素(texel)(妏素的名字是为了和像素进行区分)地控制模型的颜色。
在美术人员建模的时候,通常会在建模软件中利用纹理展开技术把纹理映射坐标(texture-mapping coordinates)存储在每个顶点上。纹理映射坐标定义了该顶点在纹理中对应的2D坐标。通常,这些坐标使用一个二维变量(u,v)来表示,其中u是横向坐标,而v是纵向坐标。因此,纹理映射坐标也被称为UV坐标。
尽管纹理的大小可以是多种多样的,例如可以是256×256或者1028×1028,但顶点UV坐标的范围通常都被归一化到[0,1]范围内。需要注意的是,纹理采样时使用的纹理坐标不一定是在[0,1]范围内。实际上,这种不在[0,1]范围的纹理坐标有时会非常有用。与之关系紧密的是纹理的平铺模式,它将决定渲染引擎在遇到[0,1]范围内的纹理坐标时如何进行纹理采样。
在OpenGL里,纹理空间的原点位于左下角,而在DirectX中,原点位于左上角。Unity在绝大多数情况下(特殊情况参见5.6节)为我们处理好了这个差异问题,也就是说,即便游戏的目标平台可能既有OpenGL风格的,也有DirectX风格的,但我们在Unity中使用的通常只有一种坐标系。Unity使用的纹理空间是符合OpenGL的传统的,也就是说,原点位于左下角。
这里写图片描述
本章将介绍如何在Unity中利用纹理采样来实现更加丰富的视觉效果。7.1中,我们将学习如何在Unity SHader中进行最基本的纹理采样,并介绍纹理的属性等基本概念。7.2节将介绍游戏中应用广泛的凹凸纹理,还会解释Unity中法线纹理的一些实现细节。7.3节和7.4节将分别介绍两类特殊的纹理类型,即渐变纹理和遮罩纹理,这些纹理在游戏中的应用非常广泛。

7.1 单张纹理
我们通常会使用一张纹理来代替物体的漫反射颜色。
这里写图片描述
在本例中,仍然使用Blinn-Phong光照模型来计算光照。准备工作如下。
(1)创建新场景。(Scene_7_1)。
(2)新建材质。(SingleTextureMat)
(3)新建 Unity Shader 。(Chapter7-SingleTexture)。把新的Unity Shader赋给第二步中创建的材质。
(4)创建一个胶囊体,并把第二步中的材质赋给该胶囊体。
(5)保存场景。

Shader "Unity Shaders Book/Chapter 7/Single Texture" {
    Properties {
        //为了使用纹理,在Properties语义块中添加一个纹理属性
        _Color ("Color Tint", Color) = (1, 1, 1, 1)
        _MainTex ("Main Tex", 2D) = "white" {}
        _Specular ("Specular", Color) = (1, 1, 1, 1)
        _Gloss ("Gloss", Range(8.0, 256)) = 20
        //上面的代码声明了一个名为_MainTex的纹理,2D是纹理属性的声明方式。
        //我们使用一个字符串后跟一个花括号作为它的初始值,“white”是内置
        //纹理的名字,也是一个全白的纹理。为了控制物体的整体色调,我们还声明了一个_Color属性。
    }
    SubShader {     
        Pass { 
            //光照模式:
            Tags { "LightMode"="ForwardBase" }

            CGPROGRAM
            //定义顶点着色器和片元着色器叫什么名字。
            #pragma vertex vert
            #pragma fragment frag
            //为了使用Unity内置的一些变量,我们需要包含了;
            #include "Lighting.cginc"

            //在CG代码中声明和上述属性类型相匹配的变量,以便和材质面板中的属性建立联系
            //与其他属性类型不同的是,我们还需要为纹理类型的属性声明一个float4类型的变量
            //_MainTex_ST。其中,_MainTex_ST的名字不是任意起的。在Unity中,我们需要使用
            //纹理名_ST的方式来声明某个纹理的属性。其中,ST是缩放(scale)和平移(translation)的
            //缩写。_MainTex_ST可以让我们得到该纹理的缩放和平移(偏移)值,_MainTex_ST存储的是缩放值,
            //_MainTex_ST.xy存储的是缩放值,而_MainTex_ST.zw存储的是偏移值。这些值可以在材质面板的属性纹理
            //中调节,
            fixed4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Specular;
            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;
            };
            //在上面的代码中,我们首先在a2v结构体中使用TEXCOORD0语义块声明了一个新的变量texcoord,
            //这样Unity就会将模型的第一组纹理坐标存储到该变量中。然后,我们v2f结构体中添加了用于存储纹理坐标的变量uv,
            //以便子片元着色器中使用该坐标进行纹理采样。

            //然后,我们定义了顶点着色器:
            v2f vert(a2v v) {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

                o.worldNormal = UnityObjectToWorldNormal(v.normal);

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

                o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
                // Or just call the built-in function
//              o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

                return o;
            }
            //在顶点着色器中,我们使用纹理的属性值_MainTex_ST来对顶点纹理做坐标进行变换,得到
            //最终的纹理坐标。计算过程是,首先使用缩放属性_MainTex_ST.xy对顶点纹理坐标进行缩放,
            //然后在使用偏移属性_MainTex_ST.zw对结果进行偏移。Unity提供了一个内置宏TRANSFORM_TEX
            //来帮我们计算上述过程。TRANSFORM_TEX是在UnityCG.cginc中定义的:
            //Transforms 2D UV by scale/bias property
            //#define TRANSFORM_TEX(tex,name) (tex.xy * name ## _ST.xy + name ## _ST.zw)
            //他接受两个参数,第一个参数是顶点纹理坐标,第二参数是纹理名,在它的实现中,将利用纹理名_ST的
            //方式来计算变换后的纹理坐标。

            //我们还需要实现片元着色器,并在计算漫反射时使用纹理中的纹素值:
            fixed4 frag(v2f i) : SV_Target {
                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

                // Use the texture to sample the diffuse color
                fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * 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);
            }
            //上面的代码首先计算了世界空间下的法线方向和光照方向。然后,使用CG的tex2D
            //函数对纹理进行采样。它的第一参数是需要被采样的纹理,第二个参数是一个float2
            //类型的纹理坐标,它将返回计算得到的纹素值。我们使用采样结果和颜色属相_Color的
            //乘积来作为材质的反射率albedo,并把他和环境光照相乘得到环境部分。随后,我们使用albedo
            //来计算漫反射光照的结果,并和环境光照、高光反射光照想加后返回/
        ENDCG
        }
    } 
        //最后,我们为该Shader设置了合适的Fallback;
    FallBack "Specular"
}

纹理属性:
在我们向Unity中导入一张纹理资源后,可以在它的材质面板上调整期属性,如下图所示:
这里写图片描述
纹理面板上的第一个属性是纹理类型。在本节中,我们使用的是Texture类型,在下面的法线纹理一节中,我们会使用Normal map类型。而在后面的章节中,我们还会看到Cubemap等高级纹理类型。我们之所以要为导入的纹理选择合适的类型,是因为只有这样才能让Unity知道我们的意图,为Unity Shader传递正确的纹理,并在一些情况下可以让Unity对该纹理进行优化。
当把纹理类型设置成Texture后,下面会有一个Alpha from Grayscale 复选框,如果勾选了它,那么透明通道的值将会由每个像素的灰度值生成。关于透明效果(8)。
下面一个属性非常重要——Wrap Mode。他决定了当纹理坐标超过[0,1]范围后将会如何被平铺。Wrap Mode有两种模式:一种是Repeat,在这种模式下,如果纹理坐标超过了1,那么他的整数部分将会被舍弃,而直接使用小数部分进行采样,这样的结果是纹理将会不断重复;另一种是Clamp,在这种模式下,如果纹理坐标大于1,那么将会截取到1,如果小于0,那么将会截取到0。下图给出两种模式下平铺一张纹理的效果。
这里写图片描述
这里写图片描述
上图展示了在纹理的平铺(Tiling)属性为(3,3)时分别使用两种Wrap Mode的结果。第一张图使用了Repeat模式,在这种模式下纹理将会不断重复;第二张图使用了Clamp模式,在这种模式下超过范围的部分将会截取到边界值,形成一个条形结构。
需要注意的是,想要让纹理得到这样的效果,我们必须使用纹理的属性(例如上面的_MainTex_ST变量)在Unity Shader 中对顶点纹理坐标进行相应的变换。也就是说,代码中需要类似下面的代码:

o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
  // Or just call the built-in function
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

我们还可以在材质面板中调整纹理的偏移量,
这里写图片描述
纹理导入面板中的下一个属性是Filter Mode属性,他决定了当纹理由于变换而产生拉伸时将会采用哪种滤波模式。Filter Mode支持三种模式:Point,Bilinear以及Trilinear。它们得到的图片滤波效果依次提升,单需要耗费的性能也依次增大。纹理滤波会影响放大或缩小纹理时得到的图片那质量。例如,当我们把一张64×64大小的纹理贴在512×512大小的平面上时,就需要放大纹理。
在资源Scene_7_1_2_b中可以找到该场景,
这里写图片描述

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值