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