Unity shader学习笔记(五)纹理

发表于2018-04-28
评论0 2.5k浏览
纹理映射

纹理坐标(也称为u/v坐标,横坐标是u,纵坐标是v,长宽是1)
一般将纹理的颜色代替漫反射的颜色,这样就会看到图像,一般是片元函数处理

纹理的类型有
Normalmap            法线贴图
EditorGUI andLegacy  菜单栏图标
Sprite               2D图片和UI界面
Cubemap              立方体纹理,做环境盒子
cookie               制作阴影,使手电筒的光从圆形变成另一种形状,类似蝙蝠灯的中间阴影,
lightmap             光照贴图,使用避免实时计算
Advanced             自定义

如何使用纹理呢,代码如下
Shader "Custom/10-FirstShader" {  
    Properties{
        //_Diffuse("Diffuse Color",Color) = (1,1,1,1)
        _MainTex("Main Tex",2D) = "white"{}     
        _Color("Color",Color) = (1,1,1,1)
        _Specular("Specular Color",Color) = (1,1,1,1)
        _Gloss("Gloss",Range(10,200)) = 20
    }
        SubShader{
        Pass{
        Tags{ "LightMode" = "ForwardBase" }
            CGPROGRAM
    #include "Lighting.cginc"
    #pragma vertex vert
    #pragma fragment frag
            //fixed4 _Diffuse;
        fixed4 _Specular;
        half _Gloss;
        sampler2D _MainTex;
        fixed4 _Color;
        struct a2v {
            float4 vertex:POSITION;
            float3 normal:NORMAL;
            float4 texcoord:TEXCOORD0;//获取纹理坐标点
        };
        struct v2f {
            float4 svPos:SV_POSITION;
            float3 worldNormal:TEXCOORD0;//世界空间下法线向量
            float4 worldVertex:TEXCOORD1;//用于传递世界空间下顶点坐标
            float4 uv:TEXCOORD2;//用于传递uv坐标
        };
        v2f vert(a2v v) {
            v2f f;
            f.svPos = mul(UNITY_MATRIX_MVP, v.vertex);
            f.worldNormal = UnityObjectToWorldNormal(v.normal);
            f.worldVertex = mul(v.vertex, unity_WorldToObject);
            f.uv = v.texcoord;//将纹理坐标传递到片元函数
            return f;
        }
        fixed4 frag(v2f f) :SV_Target{
        //法线向量
        fixed3 normalDir = normalize(f.worldNormal);
        //灯光向量
        fixed3 lightDir = normalize(WorldSpaceLightDir(f.worldVertex));
        //内置函数,可以获取到纹理上某个纹理坐标点的颜色值
        //这里只用到了u,v两个值
        fixed3 texColor = tex2D(_MainTex,f.uv.xy)*_Color.rgb;
        //使用获取到的颜色值替换掉原先漫反射光产生的颜色值
        fixed3 diffuse = _LightColor0.rgb * texColor * max(dot(normalDir, lightDir), 0);
        //相机视野向量
        fixed3 viewDir = normalize(UnityWorldSpaceViewDir(f.worldVertex));
        //获取半分线
        fixed3 halfDir = normalize(lightDir + viewDir);
        //获取高光
        fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(dot(normalDir, halfDir), 0), _Gloss);
        //最后环境光和纹理做了一个融合   这样修改环境光纹理也不会受到太大影响,不会使得纹理模糊
        fixed3 tempColor = diffuse + specular + UNITY_LIGHTMODEL_AMBIENT.rgb*texColor;
        return fixed4(tempColor, 1);
        }
            ENDCG
        }
        }
        Fallback "Specular"
}
上述代码中,定义了一个纹理参数_MainTex,还有一个_Color用来与纹理叠加,能够控制纹理的颜色。

再看Inspector面板,可以看到纹理属性不仅可以选择纹理图案,还有Tiling属性和Offset属性。
Tilling 是控制纹理进行压缩,Tilling值越大,那么模型上显示的纹理数量也就越多,x控制纹理的宽度,y控制纹理的高度。
Offset属性是控制材质的偏移,通过修改Offset下的x的值来使纹理进行左右移动,通过修改Offset下的y的值来使纹理进行上下的移动。

那么如何使用这两个值呢,Tilling和Offset是纹理自带的属性,可以直接使用:
定义变量
fixed4 _MainTex_ST;
变量名称是固定的,后面加_ST,其中前两个属性代表S 理解为Scale,后两个属性代表T 理解为Transform

使用缩放变量的话,将
f.uv = v.texcoord;
替换为
f.uv = v.texcoord*_MainTex_ST.xy
使用平移变量的话,将
f.uv = v.texcoord;
f.uv = v.texcoord+_MainTex_ST.zw;映射
可以使得物体有凹凸效果。

法线贴图

使用法线贴图的颜色值修改某一点的法线,以此来实现更真实的效果。ps:法线贴图应该和贴图大小相同,一一配套对应

需要注意的是:法线每个轴取值范围是-1到1,而颜色的值范围是0到1,因此会进行变换运算
像素值 = (法线某轴的值+1)/2

这样像素值就能存下法线的值,需要使用时只需要还原一下就行
法线某轴的值 = 像素值*2-1

法线贴图一般都是采用模型顶点的切线空间的坐标来存储法线,这种纹理被称为切线空间的法线纹理,而有些贴图是将模型空间中的表面进行直接修改,然后将法线存储在一张纹理中,这种纹理被称之为模型空间的法线纹理,不过该纹理只能应用于一种模型,不如切线空间的法线纹理应用范围广。

那么如何在shader中使用法线贴图呢
代码如下:
Shader "Custom/11-FirstShader"{
    Properties{
        //_Diffuse("Diffuse Color",Color) = (1,1,1,1)
        _Color("Color",Color) = (1,1,1,1)
        _MainTex("Main Tex",2D) = "white"{}
//定义一个法线贴图变量
//bump是一个内置模型,意思是当没有指定法线贴图时,使用bump的法线贴图
//一般指定的纹理是切线空间下的法线纹理,也可以使用模型空间下的法线纹理,只要注意坐标所在空间相同即可
    _NormalMap("Normal Map",2D) = "bump"{}
    _BumpScale("Bump Scale",Float)=1
    }
        SubShader{
        Pass{
        Tags{ "LightMode" = "ForwardBase" }
        CGPROGRAM
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag
        //fixed4 _Diffuse;
        fixed4 _Color;
        sampler2D _MainTex;
        float4 _MainTex_ST;
//引用法线贴图变量
        sampler2D _NormalMap;
        float4 _NormalMap_ST;
        float _BumpScale;
        struct a2v {
            float4 vertex:POSITION;
//切线空间的确定是通过存储到模型里面的法线和切线确定的
            float3 normal:NORMAL;
            float4 tangent:TANGENT;//tangent.w是用来确定切线空间中坐标轴的方向的 
            float4 texcoord:TEXCOORD0;
        };
        struct v2f {
            float4 svPos:SV_POSITION;
            //float3 worldNormal:TEXCOORD0;
            //float4 worldVertex:TEXCOORD1;
            float3 lightDir : TEXCOORD0;
            float4 uv:TEXCOORD1;//xy用来存储纹理的uv坐标,zw用来存储法线贴图的纹理坐标
        };
        //切线空间根据顶点一直在变化,因此只能在顶点函数中获取
        //因此该顶点光照的方向向量也在顶点函数中获取
        v2f vert(a2v v) {
            v2f f;
            f.svPos = mul(UNITY_MATRIX_MVP, v.vertex);
        //  f.worldNormal = UnityObjectToWorldNormal(v.normal);
        //  f.worldVertex = mul(v.vertex, _World2Object);
            f.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
            f.uv.zw = v.texcoord.xy * _NormalMap_ST.xy + _NormalMap_ST.zw;
            TANGENT_SPACE_ROTATION;//调用这个后之后,会得到一个矩阵 rotation 这个矩阵用来把模型空间下的方向转换成切线空间下
            //ObjSpaceLightDir(v.vertex)//得到模型空间下的平行光方向 
            f.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)); 
            return f; 
        }
        //把所有跟法线方向相关的运算都放在切线空间下
        //从法线贴图里取到的法线方向都是切线空间下的
        fixed4 frag(v2f f) :SV_Target{
            //fixed3 normalDir = normalize(f.worldNormal);
            //获取纹理坐标该点的颜色值  
            fixed4 normalColor = tex2D(_NormalMap,f.uv.zw);
            //将颜色值转换为法线的值
            //  fixed3 tangentNormal = normalize(  normalColor.xyz * 2 - 1 ) ; //切线空间下的法线
//UnpackNormal 是系统自带的转换方法           
fixed3 tangentNormal = UnpackNormal(normalColor);
            tangentNormal.xy = tangentNormal.xy*_BumpScale;
            tangentNormal = normalize(tangentNormal);
            fixed3 lightDir = normalize(f.lightDir);
            fixed3 texColor = tex2D(_MainTex, f.uv.xy)*_Color.rgb;
            fixed3 diffuse = _LightColor0.rgb * texColor * max(dot(tangentNormal, lightDir), 0);
            fixed3 tempColor = diffuse + UNITY_LIGHTMODEL_AMBIENT.rgb*texColor;
            return fixed4(tempColor, 1);
        }
        ENDCG
    }
    }
        Fallback "Specular"
}

有无法线贴图对比
来自:https://blog.csdn.net/u013106366/article/details/53353751

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引