【Aladdin Unity3D Shader编程】之四 贴图纹理

简介: 关于纹理贴图介绍纹理坐标也叫UV坐标,UV坐标都是0~1,并不是我们所理解的像素坐标,相当于是一个百分比。编写shader映射纹理将纹理的颜色取代漫反射的颜色Shader "AladdinShader/11 Single Texture Shader"...

关于纹理贴图介绍

纹理坐标也叫UV坐标,UV坐标都是0~1,并不是我们所理解的像素坐标,相当于是一个百分比。

编写shader映射纹理

将纹理的颜色取代漫反射的颜色

Shader "AladdinShader/11 Single Texture Shader"
{
    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 才能获取跟光相关的属性
            Tags { "LightMode"="ForwardBase" }
            CGPROGRAM
#include "Lighting.cginc" 
#pragma vertex vert 
#pragma fragment frag 

            // fixed4 _Diffuse;
            sampler2D _MainTex;
            fixed4 _Color;
            fixed4 _Specular;
            half _Gloss;
            //顶点函数参数
            struct a2v{
                float4 vertex:POSITION; //顶点位置
                float3 normal:NORMAL; //模型空间下的法线

                //纹理坐标只能在定点函数取到
                float4 texcoord:TEXCOORD0; 
            };

            struct v2f{
                float4 svPos:SV_POSITION;
                fixed3 worldNormal:TEXCOORD0;//世界空间下的法线
                float4 worldVertex:TEXCOORD1;

                //将取到的纹理坐标传递到片元函数
                float4 uv:TEXCOORD2;
            };

            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{
                //漫反射
                //漫反射颜色 先不管透明度
                //_LightColor0 平行光的颜色 cos夹角 光的方向和视野的夹角
                fixed3 normalDir = normalize(f.worldNormal);
                //光的方向
                fixed3 lightDir = normalize(WorldSpaceLightDir(f.worldVertex));

                //返回贴图上某像素颜色的值
                fixed3 texColor = tex2D(_MainTex, f.uv.xy) * _Color.rgb;

                //漫反射的颜色
                // fixed3 diffuse = _LightColor0.rgb *_Diffuse.rgb * max(dot(normalDir, lightDir),0);
                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; 

                //使用了纹理之后 一般环境光也跟纹理做一个融合
                fixed3 tempColor = diffuse + specular + UNITY_LIGHTMODEL_AMBIENT.rbg * texColor;
                return fixed4(tempColor,1);
            }
            ENDCG
        }
    }
    FallBack  "Specular"
}

我们找一个纹理贴图

然后将贴图赋值给_MainTex属性,调整一下高光颜色和整体贴图颜色属性并且查看效果
第一组效果:


第二组效果:

给纹理添加偏移和缩放

将一个贴图拖到模型上会自动创建跟名字一样的材质,Shader中创建一个2D属性会发现在Inspector面板上会有一个Tiling和Offset两个属性,Tiling是缩放属性,Offset是偏移属性。
将上面的例子修改一下Shader,支持纹理的旋转和缩放:

Shader "AladdinShader/11 Single Texture Shader"
{
    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 才能获取跟光相关的属性
            Tags { "LightMode"="ForwardBase" }
            CGPROGRAM
#include "Lighting.cginc" 
#pragma vertex vert 
#pragma fragment frag 

            // fixed4 _Diffuse;
            sampler2D _MainTex;
            fixed4 _Color;
            fixed4 _Specular;
            //Tiling 和Offset  ST 就是偏移和缩放的值
            float4 _MainTex_ST;
            half _Gloss;
            //顶点函数参数
            struct a2v{
                float4 vertex:POSITION; //顶点位置
                float3 normal:NORMAL; //模型空间下的法线

                //纹理坐标只能在定点函数取到
                float4 texcoord:TEXCOORD0; 
            };

            struct v2f{
                float4 svPos:SV_POSITION;
                fixed3 worldNormal:TEXCOORD0;//世界空间下的法线
                float4 worldVertex:TEXCOORD1;

                //将取到的纹理坐标传递到片元函数
                float2 uv:TEXCOORD2;
            };

            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.xy * _MainTex_ST.xy +_MainTex_ST.zw; //乘以Tiling缩放 + Offset旋转
                return f;
            } 

            //片元函数返回颜色
            fixed4 frag(v2f f):SV_Target{
                //漫反射
                //漫反射颜色 先不管透明度
                //_LightColor0 平行光的颜色 cos夹角 光的方向和视野的夹角
                fixed3 normalDir = normalize(f.worldNormal);
                //光的方向
                fixed3 lightDir = normalize(WorldSpaceLightDir(f.worldVertex));

                //返回贴图上某像素颜色的值
                fixed3 texColor = tex2D(_MainTex, f.uv.xy) * _Color.rgb;

                //漫反射的颜色
                // fixed3 diffuse = _LightColor0.rgb *_Diffuse.rgb * max(dot(normalDir, lightDir),0);
                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; 

                //使用了纹理之后 一般环境光也跟纹理做一个融合
                fixed3 tempColor = diffuse + specular + UNITY_LIGHTMODEL_AMBIENT.rbg * texColor;
                return fixed4(tempColor,1);
            }
            ENDCG
        }
    }
    FallBack  "Specular"
}

说明:
1.pass中定义的新的变量 float4 _MainTex_ST; 是用来获取贴图的Tiling和Offset的值,因为是一个xyzw的四个值,所以我们用float4来定义,变量名_MainTex_ST是一个固定格式的变量,_MainTex是跟纹理贴图变量保持一致,_ST是缩放Scale和移动Translate的首字母。
2. f.uv = v.texcoord.xy * _MainTex_ST.xy +_MainTex_ST.zw; 这句是核心,意思是将纹理*Tiling的缩放系数加上偏移量就得到想要的效果。
效果图:
原图
X缩放
Y缩放
偏移

纹理的属性

Texture Type

  • Default 就是默认的贴图/Texture
  • Normal map 法线贴图
  • Editor GUI and Legacy GUI 编辑器贴图
  • Sprite(2D and UI) 就是2D图
  • Cursor 就是鼠标贴图
  • Cubemap就是用来做环境盒子
  • Cookie 就是影子贴图
  • Lightmap 就是光照贴图,场景烘焙之后的光照贴图,就不用实时计算光照

Advanced

  • Read/Write Enabled 是否可读写
  • Generate Mip Maps适配贴图 会计算生成若干个贴图 更大效果更好

Wrap Mode

  • Repeat 当超过0-1的范围会周期循环读取贴图
  • Clap 当超过0-1的范围会使用边界像素

Filter Mode

滤波模式,当纹理拉伸之后图片显示的模式
* Point(no filter) 像素风格
* Bilinear 二线性
* Triliner 三线行
从上到下一次越来越耗费性能,但效果会越来越好,一般选用中间的格式。Triliner会经过插值计算,效果更好,但计算量也最大,如果选择Point格式,比较适合像素风格,但远处看的话效果还行,近看就会看出像素效果。

关于凹凸映射和法线映射

使用情况:我们在减少模型面的情况下使用凹凸映射会让模型看起来更加精细。会修改模型的法线,让模型看起来凹凸不平。
举例:比如我们生活中常见的石头模型,如果模型本身不是精细,因为精细的模型会显得顶点和面数会比较多,但这时候可以采用法线贴图来实现表面坑洼的效果。
石头原效果图:

使用我们自定义的Shader看到模型本身的棱角效果图会感觉石头模型比较光滑

图二就感觉效果跟图一相差很大,但模型是用的同一个模型,这就体现了法线贴图的重要性。

法线贴图

那么何为发现贴图,看下图:

法线贴图就是将贴图的法线用颜色值表现出来,但法线是-1~1,颜色是0~1是怎么对应上的呢?这里就需要做一个处理:
pixel(像素值) = (normal(法线值) + 1) / 2
经过这样的变化就能存到颜色里面,所以我们拿到一个发现贴图我们需要反向运算才能获得正确的法线值。
normal = pixel * 2 - 1

切线空间

切换空间最重要的用途之一,即法线映射(Normal Mapping)。在这个空间里,我们不需要考虑该模型在场景中可能出现的位置、朝向等众多因素,而专注于模型本身。详细的参见文章:https://www.cnblogs.com/naturelight/articles/5486469.html
我们称tangant轴(T)、bitangent轴(B)及法线轴(N)所组成的坐标系,即切线空间(TBN)。

在立方体中,每个面都有对应的切线空间,每个面由两个三角形组成,该两个三角形中的纹理坐标就基于相应的切线空间。

编写法线贴图Shader

Shader "AladdinShader/13 Rock Normal Map Shader"
{
    Properties
    {
        _MainTex("Main Tex", 2D) = "white"{} //纹理贴图 默认白色
        _Color("Color", Color) = (1,1,1,1) //整体一个色调
        _NormalMap("NormalMap", 2D) = "bump"{} //bump这个位置没有使用法线贴图的时候就使用模型自带的法线  使用的是切线空间
    }
    SubShader {
        Pass{
            //只有正确定义Tags 才能获取跟光相关的属性
            Tags { "LightMode"="ForwardBase" }
            CGPROGRAM
#include "Lighting.cginc" 
#pragma vertex vert 
#pragma fragment frag 

            // fixed4 _Diffuse;
            sampler2D _MainTex;
            fixed4 _Color;
            fixed4 _Specular;
            float4 _MainTex_ST;
            sampler2D _NormalMap;
            float4 _NormalMap_ST; //法线贴图的ST

            //顶点函数参数
            struct a2v{
                float4 vertex:POSITION; //顶点位置
                //切线空间的确定是通过(存储到模型里面的)法线和(存储到模型里面的)切线确定的
                float3 normal:NORMAL; 
                float4 tangent:TANGENT; //TANGENT.w是用来确定切线空间中坐标轴的方向的
                //纹理坐标只能在定点函数取到
                float4 texcoord:TEXCOORD0; 
            };

            struct v2f{
                float4 svPos:SV_POSITION;
                //fixed3 worldNormal:TEXCOORD0;//世界空间下的法线
                //切线空间下 平行光的方向
                float3 lightDir:TEXCOORD0; 
                float4 worldVertex:TEXCOORD1;
                //将取到的纹理坐标传递到片元函数
                float4 uv:TEXCOORD2;  //xy 用来存储MainTex的纹理坐标 zw用来存存储NormalMap的纹理坐标
            };

            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.xy = v.texcoord.xy * _MainTex_ST.xy +_MainTex_ST.zw; //乘以Tiling缩放 + Offset旋转
                f.uv.zw = v.texcoord.xy * _NormalMap_ST.xy + _NormalMap_ST.zw;

                TANGENT_SPACE_ROTATION;//调用这个之后会得到一个矩阵 rotation 这个矩阵用来把模型空间下的方向转换成切线空间下  调用这个还必须要求a2v 的变量是V 并且它里面要有切线空间下的法线normal和切线tangent这两个变量 在这个方法里面会使用
                //得到模型空间下的光的方向
                //ObjSpaceLightDir(v.vertex) //得到模型空间下的平行光方向
                f.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));
                return f;
            } 

            //把所有跟法线方向有关的运算都放在切线空间下
            //从法线贴图里取得的法线方向都是在切线空间下的
            fixed4 frag(v2f f):SV_Target{
                //取得贴图里面获得的法线
                //获取颜色值
                fixed4 normalColor = tex2D(_NormalMap,f.uv.zw);

                //切线空间下的法线
                //fixed3 tangentNormal = normalize(normalColor.xyz * 2 - 1);  //unity里面直接将法线贴图设置成NormalMap就会做一些处理,然后用系统方法提取法线 而不用我们自己计算
                //提取法线
                fixed3 tangentNormal = UnpackNormal(normalColor);
                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.rbg * texColor;
                return fixed4(tempColor,1);
            }
            ENDCG
        }
    }
    FallBack  "Specular"
}

效果图:

最左边是插件中的模型效果,最右边是自己实现的模型效果

法线贴图添加凹凸参数

添加凹凸参数属性控制凹凸力度

Shader "AladdinShader/13 Rock Normal Map Shader"
{
    Properties
    {
        _MainTex("Main Tex", 2D) = "white"{} //纹理贴图 默认白色
        _Color("Color", Color) = (1,1,1,1) //整体一个色调
        _NormalMap("NormalMap", 2D) = "bump"{} //bump这个位置没有使用法线贴图的时候就使用模型自带的法线  使用的是切线空间
        _BumpScale("Bump Scale", float) = 1
    }
    SubShader {
        Pass{
            //只有正确定义Tags 才能获取跟光相关的属性
            Tags { "LightMode"="ForwardBase" }
            CGPROGRAM
#include "Lighting.cginc" 
#pragma vertex vert 
#pragma fragment frag 

            // fixed4 _Diffuse;
            sampler2D _MainTex;
            fixed4 _Color;
            fixed4 _Specular;
            float4 _MainTex_ST;
            sampler2D _NormalMap;
            float4 _NormalMap_ST; //法线贴图的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;
                //fixed3 worldNormal:TEXCOORD0;//世界空间下的法线
                //切线空间下 平行光的方向
                float3 lightDir:TEXCOORD0; 
                float4 worldVertex:TEXCOORD1;
                //将取到的纹理坐标传递到片元函数
                float4 uv:TEXCOORD2;  //xy 用来存储MainTex的纹理坐标 zw用来存存储NormalMap的纹理坐标
            };

            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.xy = v.texcoord.xy * _MainTex_ST.xy +_MainTex_ST.zw; //乘以Tiling缩放 + Offset旋转
                f.uv.zw = v.texcoord.xy * _NormalMap_ST.xy + _NormalMap_ST.zw;

                TANGENT_SPACE_ROTATION;//调用这个之后会得到一个矩阵 rotation 这个矩阵用来把模型空间下的方向转换成切线空间下  调用这个还必须要求a2v 的变量是V 并且它里面要有切线空间下的法线normal和切线tangent这两个变量 在这个方法里面会使用
                //得到模型空间下的光的方向
                //ObjSpaceLightDir(v.vertex) //得到模型空间下的平行光方向
                f.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));
                return f;
            } 

            //把所有跟法线方向有关的运算都放在切线空间下
            //从法线贴图里取得的法线方向都是在切线空间下的
            fixed4 frag(v2f f):SV_Target{
                //取得贴图里面获得的法线
                //获取颜色值
                fixed4 normalColor = tex2D(_NormalMap,f.uv.zw);

                //切线空间下的法线
                //fixed3 tangentNormal = normalize(normalColor.xyz * 2 - 1);  //unity里面直接将法线贴图设置成NormalMap就会做一些处理,然后用系统方法提取法线 而不用我们自己计算
                //提取法线
                fixed3 tangentNormal = UnpackNormal(normalColor);
                tangentNormal = normalize(tangentNormal);
                tangentNormal.xy = tangentNormal.xy * _BumpScale;

                //光的方向
                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.rbg * texColor;
                return fixed4(tempColor,1);
            }
            ENDCG
        }
    }
    FallBack  "Specular"
}

调整凹凸属性:


编写透明Shader

Shader "AladdinShader/14 Rock Alpha Shader"
{
    Properties
    {
        _MainTex("Main Tex", 2D) = "white"{} //纹理贴图 默认白色
        _Color("Color", Color) = (1,1,1,1) //整体一个色调
        _NormalMap("NormalMap", 2D) = "bump"{} //bump这个位置没有使用法线贴图的时候就使用模型自带的法线  使用的是切线空间
        _BumpScale("Bump Scale", float) = 1
        _Alpha("Alpha", Range(0,1)) = 1
    }
    SubShader {
        Tags{"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}
        Pass{
            //只有正确定义Tags 才能获取跟光相关的属性
            Tags { "LightMode"="ForwardBase" }

            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
#include "Lighting.cginc" 
#pragma vertex vert 
#pragma fragment frag 

            // fixed4 _Diffuse;
            sampler2D _MainTex;
            fixed4 _Color;
            fixed4 _Specular;
            float4 _MainTex_ST;
            sampler2D _NormalMap;
            float4 _NormalMap_ST; //法线贴图的ST
            float _BumpScale;
            float _Alpha;

            //顶点函数参数
            struct a2v{
                float4 vertex:POSITION; //顶点位置
                //切线空间的确定是通过(存储到模型里面的)法线和(存储到模型里面的)切线确定的
                float3 normal:NORMAL; 
                float4 tangent:TANGENT; //TANGENT.w是用来确定切线空间中坐标轴的方向的
                //纹理坐标只能在定点函数取到
                float4 texcoord:TEXCOORD0; 
            };

            struct v2f{
                float4 svPos:SV_POSITION;
                //fixed3 worldNormal:TEXCOORD0;//世界空间下的法线
                //切线空间下 平行光的方向
                float3 lightDir:TEXCOORD0; 
                float4 worldVertex:TEXCOORD1;
                //将取到的纹理坐标传递到片元函数
                float4 uv:TEXCOORD2;  //xy 用来存储MainTex的纹理坐标 zw用来存存储NormalMap的纹理坐标
            };

            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.xy = v.texcoord.xy * _MainTex_ST.xy +_MainTex_ST.zw; //乘以Tiling缩放 + Offset旋转
                f.uv.zw = v.texcoord.xy * _NormalMap_ST.xy + _NormalMap_ST.zw;

                TANGENT_SPACE_ROTATION;//调用这个之后会得到一个矩阵 rotation 这个矩阵用来把模型空间下的方向转换成切线空间下  调用这个还必须要求a2v 的变量是V 并且它里面要有切线空间下的法线normal和切线tangent这两个变量 在这个方法里面会使用
                //得到模型空间下的光的方向
                //ObjSpaceLightDir(v.vertex) //得到模型空间下的平行光方向
                f.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));
                return f;
            } 

            //把所有跟法线方向有关的运算都放在切线空间下
            //从法线贴图里取得的法线方向都是在切线空间下的
            fixed4 frag(v2f f):SV_Target{
                //取得贴图里面获得的法线
                //获取颜色值
                fixed4 normalColor = tex2D(_NormalMap,f.uv.zw);

                //切线空间下的法线
                //fixed3 tangentNormal = normalize(normalColor.xyz * 2 - 1);  //unity里面直接将法线贴图设置成NormalMap就会做一些处理,然后用系统方法提取法线 而不用我们自己计算
                //提取法线
                fixed3 tangentNormal = UnpackNormal(normalColor);
                tangentNormal = normalize(tangentNormal);
                tangentNormal.xy = tangentNormal.xy * _BumpScale;

                //光的方向
                fixed3 lightDir = normalize(f.lightDir);

                //返回贴图上某像素颜色的值
                fixed4 texColor = tex2D(_MainTex, f.uv.xy) * _Color;

                fixed3 diffuse = _LightColor0.rgb * texColor.rgb * max(dot(tangentNormal, lightDir), 0);

                fixed3 tempColor = diffuse + UNITY_LIGHTMODEL_AMBIENT.rbg * texColor;
                return fixed4(tempColor,texColor.a);
            }
            ENDCG
        }
    }
    FallBack  "Specular"
}

或者属性面板控制:

Shader "AladdinShader/14 Rock Alpha Shader"
{
    Properties
    {
        _MainTex("Main Tex", 2D) = "white"{} //纹理贴图 默认白色
        _Color("Color", Color) = (1,1,1,1) //整体一个色调
        _NormalMap("NormalMap", 2D) = "bump"{} //bump这个位置没有使用法线贴图的时候就使用模型自带的法线  使用的是切线空间
        _BumpScale("Bump Scale", float) = 1
        _Alpha("Alpha", Range(0,1)) = 1
    }
    SubShader {
        Tags{"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}
        Pass{
            //只有正确定义Tags 才能获取跟光相关的属性
            Tags { "LightMode"="ForwardBase" }

            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
#include "Lighting.cginc" 
#pragma vertex vert 
#pragma fragment frag 

            // fixed4 _Diffuse;
            sampler2D _MainTex;
            fixed4 _Color;
            fixed4 _Specular;
            float4 _MainTex_ST;
            sampler2D _NormalMap;
            float4 _NormalMap_ST; //法线贴图的ST
            float _BumpScale;
            float _Alpha;

            //顶点函数参数
            struct a2v{
                float4 vertex:POSITION; //顶点位置
                //切线空间的确定是通过(存储到模型里面的)法线和(存储到模型里面的)切线确定的
                float3 normal:NORMAL; 
                float4 tangent:TANGENT; //TANGENT.w是用来确定切线空间中坐标轴的方向的
                //纹理坐标只能在定点函数取到
                float4 texcoord:TEXCOORD0; 
            };

            struct v2f{
                float4 svPos:SV_POSITION;
                //fixed3 worldNormal:TEXCOORD0;//世界空间下的法线
                //切线空间下 平行光的方向
                float3 lightDir:TEXCOORD0; 
                float4 worldVertex:TEXCOORD1;
                //将取到的纹理坐标传递到片元函数
                float4 uv:TEXCOORD2;  //xy 用来存储MainTex的纹理坐标 zw用来存存储NormalMap的纹理坐标
            };

            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.xy = v.texcoord.xy * _MainTex_ST.xy +_MainTex_ST.zw; //乘以Tiling缩放 + Offset旋转
                f.uv.zw = v.texcoord.xy * _NormalMap_ST.xy + _NormalMap_ST.zw;

                TANGENT_SPACE_ROTATION;//调用这个之后会得到一个矩阵 rotation 这个矩阵用来把模型空间下的方向转换成切线空间下  调用这个还必须要求a2v 的变量是V 并且它里面要有切线空间下的法线normal和切线tangent这两个变量 在这个方法里面会使用
                //得到模型空间下的光的方向
                //ObjSpaceLightDir(v.vertex) //得到模型空间下的平行光方向
                f.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));
                return f;
            } 

            //把所有跟法线方向有关的运算都放在切线空间下
            //从法线贴图里取得的法线方向都是在切线空间下的
            fixed4 frag(v2f f):SV_Target{
                //取得贴图里面获得的法线
                //获取颜色值
                fixed4 normalColor = tex2D(_NormalMap,f.uv.zw);

                //切线空间下的法线
                //fixed3 tangentNormal = normalize(normalColor.xyz * 2 - 1);  //unity里面直接将法线贴图设置成NormalMap就会做一些处理,然后用系统方法提取法线 而不用我们自己计算
                //提取法线
                fixed3 tangentNormal = UnpackNormal(normalColor);
                tangentNormal = normalize(tangentNormal);
                tangentNormal.xy = tangentNormal.xy * _BumpScale;

                //光的方向
                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.rbg * texColor;
                return fixed4(tempColor,_Alpha);
            }
            ENDCG
        }
    }
    FallBack  "Specular"
}


跟透明相关的代码块
Tags{“Queue” = “Transparent” “IgnoreProjector” = “True” “RenderType” = “Transparent”}
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha

Shader学习交流群:
316977780

相关文章
|
20天前
|
图形学 数据可视化 开发者
超实用Unity Shader Graph教程:从零开始打造令人惊叹的游戏视觉特效,让你的作品瞬间高大上,附带示例代码与详细步骤解析!
【8月更文挑战第31天】Unity Shader Graph 是 Unity 引擎中的强大工具,通过可视化编程帮助开发者轻松创建复杂且炫酷的视觉效果。本文将指导你使用 Shader Graph 实现三种效果:彩虹色渐变着色器、动态光效和水波纹效果。首先确保安装最新版 Unity 并启用 Shader Graph。创建新材质和着色器图谱后,利用节点库中的预定义节点,在编辑区连接节点定义着色器行为。
71 0
|
1月前
|
缓存 图形学
Unity3D学习笔记12——渲染纹理
Unity3D学习笔记12——渲染纹理
29 2
|
1月前
|
API C# 图形学
Unity3D学习笔记9——加载纹理
Unity3D学习笔记9——加载纹理
26 2
|
20天前
|
图形学 开发者
【独家揭秘】Unity游戏开发秘籍:从基础到进阶,掌握材质与纹理的艺术,打造超现实游戏视效的全过程剖析——案例教你如何让每一面墙都会“说话”
【8月更文挑战第31天】Unity 是全球领先的跨平台游戏开发引擎,以其高效性能和丰富的工具集著称,尤其在提升游戏视觉效果方面表现突出。本文通过具体案例分析,介绍如何利用 Unity 中的材质与纹理技术打造逼真且具艺术感的游戏世界。材质定义物体表面属性,如颜色、光滑度等;纹理则用于模拟真实细节。结合使用两者可显著增强场景真实感。以 FPS 游戏为例,通过调整材质参数和编写脚本动态改变属性,可实现自然视觉效果。此外,Unity 还提供了多种高级技术和优化方法供开发者探索。
35 0
|
20天前
|
图形学 C# 开发者
全面掌握Unity游戏开发核心技术:C#脚本编程从入门到精通——详解生命周期方法、事件处理与面向对象设计,助你打造高效稳定的互动娱乐体验
【8月更文挑战第31天】Unity 是一款强大的游戏开发平台,支持多种编程语言,其中 C# 最为常用。本文介绍 C# 在 Unity 中的应用,涵盖脚本生命周期、常用函数、事件处理及面向对象编程等核心概念。通过具体示例,展示如何编写有效的 C# 脚本,包括 Start、Update 和 LateUpdate 等生命周期方法,以及碰撞检测和类继承等高级技巧,帮助开发者掌握 Unity 脚本编程基础,提升游戏开发效率。
32 0
|
1月前
|
测试技术 C# 图形学
Unity3D学习笔记10——纹理数组
Unity3D学习笔记10——纹理数组
37 0
|
1月前
|
C# 图形学 C语言
Unity3D学习笔记3——Unity Shader的初步使用
Unity3D学习笔记3——Unity Shader的初步使用
34 0
|
1月前
|
存储 算法 C#
Unity3D学习笔记2——绘制一个带纹理的面
Unity3D学习笔记2——绘制一个带纹理的面
24 0
|
1月前
|
数据可视化 图形学
小功能⭐️Unity2018 Shader Graph——全息影像、物体消融
小功能⭐️Unity2018 Shader Graph——全息影像、物体消融
|
3月前
|
图形学
【实现100个unity特效】shader实现3D物品闪光和描边效果
【实现100个unity特效】shader实现3D物品闪光和描边效果
57 0