Shader

Shader

normalize() 用来把一个向量,单位化(原来方向保持不变,长度变为1)
max() 用来取得函数中最大的一个
dot 用来取得两个向量的点积
_WorldSpaceLightPos0 取得平行光的位置
_LightColor0取得平行光的颜色
UNITY_MATRIX_MVP 这个矩阵用来把一个坐标从模型空间转换到剪裁空间
_World2Object 这个矩阵用来把一个方向从世界空间转换到模型空间
UNITY_LIGHTMODEL_AMBIENT用来获取环境光
_WorldSpaceCameraPos可以获得相机的位置

shader简介

Mesh Filter

存储一个Mesh(网格,模型的网格,就是模型的由哪些三角面组成,组成一个什么样子的模型,三角面的一些顶点信息)

Mesh Renderer

用来渲染一个模型的外观,就是样子,按照 mesh给它皮肤,给它颜色,通过Material(材质)控制模型渲染的样子

Material

贴图(可以没有,可以是一个单纯的颜色)
Shader

OpenGL、DirectX(DX)

shader可以认为是一种渲染命令 ,由opengl 或者dx进行解析,来控制渲染丰富多彩的图形

OpenGL 使用GLSL 编写shader
DirectX 使用HSSL 编写shader
英伟达 CG 编写shader(跨平台)

unity shader的分类

使用的是ShaderLab(对CG进行了封装)编写Unity中的Shader

表面着色器Surface Shader

unity独有的着色器

顶点/片元着色器Vertex/Fragment Shader

比较基本的着色器,表面着色器对顶点/片元着色器进行封装,最后还被解析成顶点片元着色器

固定函数着色器Fixed Function Shader(已经被弃用)

创建一个unity shader

基本结构

Shader "LianBai/01-myshader" //引号内为shader的路径+名字,可以跟文件名字不一样
{ 
    Properties	//属性(类似于C#脚本中的public属性,通过外界可以调节),一个shader只可以有一个
    {
        //颜色,变量名字为_Color,面板上显示的名字为MyColor,类型为color,
        _Color("MyColor",Color) = (1,1,1,1)
    }

    SubShader	//子shader块,可以有很多个,编写渲染的代码。显卡运行效果的时候,从第一个SubShader,如果第一个SubShader中有部分无法实现,就会自动运行下一个SubShader
    {
        Pass	//必须有一个pass块,可以有很多pass块,一个pass块代表的一个方法
        {
            //编写shader代码 可以用CGPROGRAM 或者HLSLPROGRAM
            CGPROGRAM
            //使用CG语言编写代码
            ENDCG
        }
    }
    Fallback "VertexLit"	//当设备发现上面的SubShader都无法执行的时候,就会执行后这个后备方案
}

属性可以有哪些数据类型

//颜色,变量名字为_Color,面板上显示的名字为MyColor,类型为color,
_Color("MyColor",Color) = (1,1,1,1)
//向量,四维向量,syzw,Color和Vector本质上是一种类型
_Vector("MyVector",Vector) = (1,2,3,4)
//整数
_Int("MyInt",Int) = 1234
//小数
_Float("MyFloat",Float) = 4.5
//范围类型
_Range("MyRange",Range(1,11)) = 6
//图片,当不指定图片的时候,用"red",可以是其他颜色
_2D("MyTexture",2D) = "red"{}
//立方体纹理,天空盒子可以用到
_Cube("MyCube",Cube) = "white"{}
//3D纹理
_3D("MyTexture",3D) = "black"{}

如何使用属性

float4 _Color;
float4 _Vector;
float _Int;
float _Float;
float _Range;
sampler2D _2D;
samplerCube _Cube;
sampler3D _3D;

float = half = fixed 都可以代替
float 32位存储
half 16位存储(-6万 ~ 6万)
fixed 11位存储(-2 ~ 2) //color一般用fixed存储

创建vertex和fragment

//顶点函数 函数名vert 这里只是声明了顶点函数的函数名
//基本作用是 完成顶点坐标从模型空间到剪裁空间的转换(从游戏环境转换到视野相机屏幕上)
#pragma vertex vert
//片元函数 
//基本作用是 返回模型对应的屏幕上的每一个像素的颜色值
#pragma fragment frag
//返回值和参数都可以不固定 
//通过语义告诉系统: POSITION标明要把顶点值传进v、SV_POSITION标明函数返回值是剪裁空间下的顶点坐标
float4 vert(float4 v : POSITION) : SV_POSITION
{
    return UnityObjectToClipPos(v);	//把模型空间坐标转换到剪裁空间坐标,UNITY_MATRIX_MVP(4x4矩阵)
}
float4 frag(): SV_Target
{
    return fixed4(0.5,0.5,1,1);
}

顶点其他信息

struct a2v
{
    float4 vertex : POSITION;
    //获取法线
    float3 normal : NORMAL;
    //纹理坐标	0代表第一套纹理坐标,纹理坐标一般0~1
    float4 texcoord : TEXCOORD0;
};

语义总结

从应用程序传递到顶点函数的语义有哪些a2v

POSITION 顶点坐标(模型空间下的)
NORMAL 法线( 模型空间下)
TANGENT 切线(模型空间)
TEXCOORD0 ~n 纹理坐标
COLOR 顶点颜色

从顶点函数传递给片元函数的时候可以使用的语义

SV_POSITION 剪裁空间中的顶点坐标(一般是系统直接使用)
COLOR0 可以传递一组值 4个
COLOR1 可以传递一组值 4个
TEXCOORD0~7 传递纹理坐标

片元函数传递给系统

SV_Target 颜色值,显示到屏幕上的颜色

什么是光照模型

光照模型就是一个公式,使用这个公式来计算在某个点的光照效果
只有定义了正确的LightMode才能得到一些Unity的内置光照变量

标准光照模型

在标准光照模型里面,我们把进入摄像机的光分为下面四个部分:

自发光
高光反射
漫反射
环境光

漫反射

Diffuse = 直射光颜色 * max(0,cos夹角(光和法线的夹角) )
cosθ = 光方向· 法线方向(单位向量的点积)

Shader "LianBai/04-Diffuse"
{
    SubShader
    {

        Pass
        {
            Tags{"LightMode" = "ForwardBase"}	//只有定义了正确的LightMode才能得到一些Unity的内置光照变量
            CGPROGRAM
#include "Lighting.cginc"		//引入定义好的命名空间内的变量
            //顶点函数 函数名vert 这里只是声明了顶点函数的函数名
            //基本作用是 完成顶点坐标从模型空间到剪裁空间的转换(从游戏环境转换到视野相机屏幕上)
#pragma vertex vert
            //片元函数 
            //基本作用是 返回模型对应的屏幕上的每一个像素的颜色值
#pragma fragment frag
        //返回值和参数都可以不固定 
        //通过语义告诉系统: POSITION标明要把顶点值传进v、SV_POSITION标明函数返回值是剪裁空间下的顶点坐标

            //application to wertex
            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 position : SV_POSITION;
                fixed3 color : COLOR0;
            };
            v2f vert(a2v v)
            {
                v2f f;
                f.position = UnityObjectToClipPos(v.vertex);	//把模型空间坐标转换到剪裁空间坐标,UNITY_MATRIX_MVP(4x4矩阵)

                //在Lighting.cginc命名空间下 _LightColor0取得第一个直射光的颜色 _WorldSpaceLightPos0第一个直射光的位置
                //dot用于计算点乘  normalize用来把一个向量单位化 _World2Object这个矩阵用来把一个方向从世界空间转换到模型空间
                float3 normalDir = normalize(mul(v.normal, (float3x3)unity_WorldToObject));	//变量位置交换,就是从模型空间转换到世界空间 float3x3强制把矩阵变成3*3的矩阵
                fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); //对于每一个点来说光的位置就是光的方向,因为光是平行光
                fixed3 diffuse = _LightColor0.rgb * max(0, dot(normalDir, lightDir)); //取得漫反射的颜色
                f.color = diffuse;
                return f;
            }
            float4 frag(v2f f) : SV_Target
            {
                return fixed4(f.color,1);
            }

            ENDCG

        }

    }
        FallBack "Diffuse"
}

漫反射颜色

properties
{
    _DiffuseColor("MyDiffuseColor",Color) = (1,1,1,1)
}

fixed4 _DiffuseColor;
fixed3 diffuse = _LightColor0.rgb * max(0, dot(normalDir, lightDir)) * _DiffuseColor.rgb; //把两种颜色融合

环境光

fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;	//得到系统的环境光
f.color = diffuse + ambient;					// + 代表颜色叠加

逐像素漫反射

Shader "LianBai/05-Diffuse Fragment"
{
    properties
    {
        _DiffuseColor("MyDiffuseColor",Color) = (1,1,1,1)
    }
    SubShader
    {

        Pass
        {
            

            Tags{"LightMode" = "ForwardBase"}	//只有定义了正确的LightMode才能得到一些Unity的内置光照变量
            CGPROGRAM
#include "Lighting.cginc"		//引入定义好的命名空间内的变量
            //顶点函数 函数名vert 这里只是声明了顶点函数的函数名
            //基本作用是 完成顶点坐标从模型空间到剪裁空间的转换(从游戏环境转换到视野相机屏幕上)
#pragma vertex vert
            //片元函数 
            //基本作用是 返回模型对应的屏幕上的每一个像素的颜色值
#pragma fragment frag
        //返回值和参数都可以不固定 
        //通过语义告诉系统: POSITION标明要把顶点值传进v、SV_POSITION标明函数返回值是剪裁空间下的顶点坐标

            fixed4 _DiffuseColor;
            //application to wertex
            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 position : SV_POSITION;
                fixed3 worldnormaldir : COLOR0;
            };
            v2f vert(a2v v)
            {
                v2f f;
                f.position = UnityObjectToClipPos(v.vertex);	//把模型空间坐标转换到剪裁空间坐标,UNITY_MATRIX_MVP(4x4矩阵)

                f.worldnormaldir = mul(v.normal, (float3x3)unity_WorldToObject);
                return f;
            }
            float4 frag(v2f f) : SV_Target
            {
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;	//得到系统的环境光

                //在Lighting.cginc命名空间下 _LightColor0取得第一个直射光的颜色 _WorldSpaceLightPos0第一个直射光的位置
                //dot用于计算点乘  normalize用来把一个向量单位化 _World2Object这个矩阵用来把一个方向从世界空间转换到模型空间
                float3 normalDir = normalize(f.worldnormaldir);	//变量位置交换,就是从模型空间转换到世界空间 float3x3强制把矩阵变成3*3的矩阵
                fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); //对于每一个点来说光的位置就是光的方向,因为光是平行光
                fixed3 diffuse = _LightColor0.rgb * max(0, dot(normalDir, lightDir)) * _DiffuseColor.rgb; //取得漫反射的颜色
                fixed3 tempcolor = diffuse + ambient;
                return fixed4(tempcolor,1);
            }

            ENDCG

        }

    }
        FallBack "Diffuse"
}

半兰伯特光照模型

Diffuse = 直射光颜色 *( cosθ *0.5 +0.5 )

float halfLambert = dot(normalDir, lightDir) * 0.5 + 0.5;
fixed3 diffuse = _LightColor0.rgb * halfLambert * _DiffuseColor.rgb; //取得漫反射的颜色

高光反射

Blinn光照模型

Specular(高光反射) =直射光  * pow( max(cosθ,0),高光参数)  
θ:是法线和x的夹角  x 是平行光和视野方向的平分线
高光参数可以控制高光范围,值越小,范围越大,一般情况是高光参数>=10

Blinn-Phong光照模型(改进)

Specular=直射光  * pow( max(cosθ,0),10)  
θ:是法线和x的夹角  x 是平行光和视野方向的平分线

高光反射(逐顶点)

Shader "LianBai/07-Specular Vertex"
{
    properties
    {
        _DiffuseColor("MyDiffuseColor",Color) = (1,1,1,1)
    }
    SubShader
    {

        Pass
        {
            

            Tags{"LightMode" = "ForwardBase"}	//只有定义了正确的LightMode才能得到一些Unity的内置光照变量
            CGPROGRAM
#include "Lighting.cginc"		//引入定义好的命名空间内的变量
            //顶点函数 函数名vert 这里只是声明了顶点函数的函数名
            //基本作用是 完成顶点坐标从模型空间到剪裁空间的转换(从游戏环境转换到视野相机屏幕上)
#pragma vertex vert
            //片元函数 
            //基本作用是 返回模型对应的屏幕上的每一个像素的颜色值
#pragma fragment frag
        //返回值和参数都可以不固定 
        //通过语义告诉系统: POSITION标明要把顶点值传进v、SV_POSITION标明函数返回值是剪裁空间下的顶点坐标

            fixed4 _DiffuseColor;
            //application to wertex
            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 position : SV_POSITION;
                fixed3 color : COLOR0;
            };
            v2f vert(a2v v)
            {
                v2f f;
                f.position = UnityObjectToClipPos(v.vertex);	//把模型空间坐标转换到剪裁空间坐标,UNITY_MATRIX_MVP(4x4矩阵)

                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;	//得到系统的环境光

                //在Lighting.cginc命名空间下 _LightColor0取得第一个直射光的颜色 _WorldSpaceLightPos0第一个直射光的位置
                //dot用于计算点乘  normalize用来把一个向量单位化 _World2Object这个矩阵用来把一个方向从世界空间转换到模型空间
                float3 normalDir = normalize(mul(v.normal, (float3x3)unity_WorldToObject));	//变量位置交换,就是从模型空间转换到世界空间 float3x3强制把矩阵变成3*3的矩阵
                fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); //对于每一个点来说光的位置就是光的方向,因为光是平行光
                fixed3 diffuse = _LightColor0.rgb * max(0, dot(normalDir, lightDir)) * _DiffuseColor.rgb; //取得漫反射的颜色

                fixed3 reflectDir = normalize(reflect(-lightDir, normalDir));	//reflect 用来求反射光方向向量,lightDir是指向平行光的方向,所以需要带符号变成入射方向,
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(v.vertex, unity_WorldToObject).xyz); //_WorldSpaceCameraPos.xyz相机的位置,
                fixed3 specular = _LightColor0.rgb * pow(max(0, dot(reflectDir, viewDir)), 10);

                f.color = diffuse + ambient + specular;
                return f;
            }
            float4 frag(v2f f) : SV_Target
            {
                return fixed4(f.color,1);
            }

            ENDCG

        }

    }
        FallBack "Diffuse"
}

高光反射(逐像素)

Shader "LianBai/08-Specular Fragment"
{
    properties
    {
        _DiffuseColor("MyDiffuseColor",Color) = (1,1,1,1)
        _Gloss("MyGloss",Range(8,200)) = 10
        _SpecularColor("MySpecularColor",Color) = (1,1,1,1)
    }
    SubShader
    {

        Pass
        {
            

            Tags{"LightMode" = "ForwardBase"}	//只有定义了正确的LightMode才能得到一些Unity的内置光照变量
            CGPROGRAM
#include "Lighting.cginc"		//引入定义好的命名空间内的变量
            //顶点函数 函数名vert 这里只是声明了顶点函数的函数名
            //基本作用是 完成顶点坐标从模型空间到剪裁空间的转换(从游戏环境转换到视野相机屏幕上)
#pragma vertex vert
            //片元函数 
            //基本作用是 返回模型对应的屏幕上的每一个像素的颜色值
#pragma fragment frag
        //返回值和参数都可以不固定 
        //通过语义告诉系统: POSITION标明要把顶点值传进v、SV_POSITION标明函数返回值是剪裁空间下的顶点坐标

            fixed4 _DiffuseColor;
            fixed4 _SpecularColor;
            half _Gloss;

            //application to wertex
            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 position : SV_POSITION;
                float3 worldnormal : TEXCOORD0;
                float3 worldvertex : TEXCOORD1;
            };
            v2f vert(a2v v)
            {
                v2f f;
                f.position = UnityObjectToClipPos(v.vertex);	//把模型空间坐标转换到剪裁空间坐标,UNITY_MATRIX_MVP(4x4矩阵)
                f.worldnormal = mul(v.normal, (float3x3)unity_WorldToObject);
                f.worldvertex = mul(v.vertex, unity_WorldToObject).xyz;
                return f;
            }
            float4 frag(v2f f) : SV_Target
            {
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;	//得到系统的环境光

                //在Lighting.cginc命名空间下 _LightColor0取得第一个直射光的颜色 _WorldSpaceLightPos0第一个直射光的位置
                //dot用于计算点乘  normalize用来把一个向量单位化 _World2Object这个矩阵用来把一个方向从世界空间转换到模型空间
                float3 normalDir = normalize(f.worldnormal);	//变量位置交换,就是从模型空间转换到世界空间 float3x3强制把矩阵变成3*3的矩阵
                fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); //对于每一个点来说光的位置就是光的方向,因为光是平行光
                fixed3 diffuse = _LightColor0.rgb * max(0, dot(normalDir, lightDir)) * _DiffuseColor.rgb; //取得漫反射的颜色

                fixed3 reflectDir = normalize(reflect(-lightDir, normalDir));	//reflect 用来求反射光方向向量,lightDir是指向平行光的方向,所以需要带符号变成入射方向,
                fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - f.worldvertex); //_WorldSpaceCameraPos.xyz相机的位置,
                fixed3 specular = _LightColor0.rgb * pow(max(0, dot(reflectDir, viewDir)), _Gloss) * _SpecularColor.rgb;
                fixed3 tempcolor = diffuse + ambient + specular;

                return fixed4(tempcolor,1);
            }

            ENDCG

        }

    }
        FallBack "Diffuse"
}

unity内置函数

//摄像机方向(视角方向)
float3 WorldSpaceViewDir(float4 v)  	根据模型空间中的顶点坐标 得到 (世界空间)从这个点到摄像机的观察方向
float3 UnityWorldSpaceViewDir(float4 v) 世界空间中的顶点坐标==》世界空间从这个点到摄像机的观察方向
float3 ObjSpaceViewDir(float4 v) 		模型空间中的顶点坐标==》模型空间从这个点到摄像机的观察方向
//光源方向
float3 WorldSpaceLightDir(float4 v) 	模型空间中的顶点坐标==》世界空间中从这个点到光源的方向
float3 UnityWorldSpaceLightDir(float4 v) 	世界空间中的顶点坐标==》世界空间中从这个点到光源的方向
float3 ObjSpaceLightDir(float4 v) 	模型空间中的顶点坐标==》模型空间中从这个点到光源的方向
//方向转换
float3 UnityObjectToWorldNormal(float3 norm) 把法线方向 模型空间==》世界空间
float3 UnityObjectToWorldDir(float3 dir) 把方向 模型空间=》世界空间
float3 UnityWorldToObjectDir(float3 dir) 把方向 世界空间=》模型空间

纹理

使用纹理的颜色代替漫反射的颜色(需要逐像素,这样三角形内不会出现偏差)

纹理坐标只能在顶点函数里取到,所以需要传递

在使用纹理的时候,环境光与纹理颜色做一个融合比较好一点

fixed3 tempcolor = diffuse + specular + UNITY_LIGHTMODEL_AMBIENT.rgb * texColor;

示例脚本

Shader "LianBai/11-Single Texture"
{
    Properties
    {
        //_DiffuseColor("MyDiffuseColor",Color) = (1,1,1,1)
        _TexColor("MyTexColor",Color) = (1,1,1,1)
        _MainTex("MyMainTex",2D) = "white"{}
        _SpecularColor("MySpecularColor",Color) = (1,1,1,1)
        _Gloss("MyGloss",Range(10,200)) = 20
    }
        SubShader
        {
            Pass
            {
                Tags{"LightMode" = "ForwardBase"}
                CGPROGRAM
    #include "Lighting.cginc"
    #pragma vertex vert
    #pragma fragment frag
            //float4 _DiffuseColor;
            fixed4 _TexColor;
            sampler2D _MainTex;
            float4 _SpecularColor;
            float _Gloss;

            struct a2v
            {
                float4 vertex : POSITION;
                float4 normal : NORMAL;
                float4 texcoord :TEXCOORD0;		//得到纹理坐标
            };
            struct v2f
            {
                float4 svPos : SV_POSITION;
                float3 worldnormal : TEXCOORD0;
                float4 worldvertex : TEXCOORD1;
                float4 uv:TEXCOORD2;
            };
            v2f vert(a2v v)
            {
                v2f f;
                f.svPos = UnityObjectToClipPos(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));
                fixed3 viewDir = normalize(UnityWorldSpaceViewDir(f.worldvertex));
                fixed3 halfDir = normalize(viewDir + lightDir);

                fixed3 texColor = tex2D(_MainTex, f.uv.xy) * _TexColor;	//的到纹理上的颜色tex()

                fixed3 diffuse = _LightColor0.rgb * texColor * max(0,dot(normalDir,lightDir));
                fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0,dot(normalDir, halfDir)),_Gloss);

                fixed3 tempcolor = diffuse + specular + UNITY_LIGHTMODEL_AMBIENT.rgb * texColor;
                return fixed4(tempcolor,1);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

给纹理添加便宜和缩放

Offset:控制材质的偏移
Tiling:图片的压缩倍数

固定的,必须是:纹理的名字_ST

float4 _MainTex_ST;

示例脚本

f.uv = v.texcoord.xy *_MainTex_ST.xy + _MainTex_ST.zw;	//xy是缩放,zw是偏移

凹凸映射和法线映射

基础+示例脚本

pixel = (normal+1)/2

normal = pixel*2 - 1

把所有跟法线方向有关的运算都放在切线空间下
因为从法线贴图里面取得的法线方向是在切线空间下的

Shader "LianBai/13-Rock NormalMap"
{
    Properties
    {
        //_DiffuseColor("MyDiffuseColor",Color) = (1,1,1,1)
        _TexColor("MyTexColor",Color) = (1,1,1,1)
        _MainTex("MyMainTex",2D) = "white"{}
        _NorMap("MyNormap",2D) = "bump"{}
    }
    SubShader
    {
        Pass
        {
            Tags{"LightMode" = "ForwardBase"}
            CGPROGRAM
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag
            //float4 _DiffuseColor;
            fixed4 _TexColor;
            sampler2D _MainTex;
            float4 _MainTex_ST;		//固定的,必须是:纹理的名字_ST
            sampler2D _NorMap;
            float4 _NorMap_ST;

            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;
                float3 lightDir : TEXCOORD0;	//切线空间下平行光的方向
                float4 worldvertex : TEXCOORD1;
                float4 uv : TEXCOORD2;			//xy用来存储maintex zw用来存储法线贴图的纹理
            };
            v2f vert(a2v v)
            {
                v2f f;
                f.svPos = UnityObjectToClipPos(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;	//xy是缩放,zw是偏移
                f.uv.zw = v.texcoord.xy * _NorMap_ST.xy + _NorMap_ST.zw;

                TANGENT_SPACE_ROTATION;	//调用这个宏之后会得到一个矩阵 rotation 用来把模型空间下的方向转换成切线空间下
                /*float3 binormal = cross(v.normal, v.tangent.xyz) * v.tangent.w;
                float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);*/

                //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(_NorMap,f.uv.zw);
                //fixed3 tangentNormal = normalize(normalcolor.xyz * 2 - 1);
                fixed3 tangentNormal = UnpackNormal(normalcolor);
                tangentNormal = normalize(tangentNormal);

                fixed3 lightDir = normalize(f.lightDir);

                fixed3 texColor = tex2D(_MainTex, f.uv.xy) * _TexColor;	//的到纹理上的颜色tex()

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

                fixed3 tempcolor = diffuse + UNITY_LIGHTMODEL_AMBIENT.rgb * texColor;
                return fixed4(tempcolor,1);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

凹凸映射和法线映射添加参数

切线空间的z轴是和原来法线方向是一致的


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 841774407@qq.com

×

喜欢就点赞,疼爱就打赏