1.纹理映射(texture mapping)
将纹理空间中的纹理元素映射到屏幕空间中的像素的过程。(表象就是把图像映射到模型三角形表面)
ex:左边是两个三角面,右边是一个贴图,把一张图片映射到2个三角形上。
在纹理映射过程中,我们使用纹理坐标的方式(或称u、v坐标),把纹理进行划分。UV(纹理映射坐标)它定义了图片上每个点的位置的信息,将图像上每一个点精确对应到模型表面。无论模型贴图大小如何,UV纹理一般都被归一化到[0,1]。
UV坐标算法 ex: 模型贴图1024 * 1024,贴图某个点的UV坐标 = (x/1024,y/1024) 例如(512,512) UV坐标= (0.5,0.5)。
对于一个三角形,我们要为每一个顶点指定uv坐标,如上图的2个三角形,我们按照图上标的纹理坐标指定uv坐标,则左边的2个三角形则会被完整的映射成右边的纹理形状。
2.纹理采样有关的概念:
- Magnification 纹理放大
纹理大小为256*256,但我们的两个三角形组成的quad却是512*512,则一个纹理单元(纹素)要对应quad上的四个像素,一个纹理单元对应多个光栅化后的像素的情况通常称作Magnification。
在magnification情况下如何进行纹理映射呢?通常是用双线性插值的方式,比如下面左边的图中红色的pixel,没有对应的纹理texel,则它的颜色值用它的上下左右的pixel颜色插值得到,当然我们也可以选择最接近的纹理单元颜色做为其颜色,
具体要视Filter Mode纹理滤波中设置的MAG值。
- Minification 纹理缩小
和Magnification相反,三角面小,纹理大。例如128 * 128的面片大小,纹理大小 256 * 256。
这时可以选择最接近的纹理单元,也可以把上下左右纹理单元进行双线性插值,再和pixel映射起来。
具体要视Filter Mode纹理滤波中设置的MAG值。
- Mipmap 多级渐远纹理技术
选择创建mipmp层,系统就自动创建一系列采样的图像,每个图像都是前面一个图像的1/4(如下图所示)。
比如纹理原始大小是256*256,我们的三角形是178*178,那么这时会做三线性插值,四边形先和128*128纹理执行magnificaiton插值得到一个结果,再和256*256纹理做minification插值得到一个结果,最后再对这2个结果进行线性插值,得到的颜色为最终的pixel颜色。
- Wrap Mode纹理的寻址模式:
1.Repeat方式:
对于不在[0,1]之间的纹理坐标,采用缠绕的方式来对应纹理单元,比如下面图所示,这样的方式特别适合用小纹理来贴一个大的平面,比如在地面铺瓷砖,在地面上铺草地等等。这也要求我们设计纹理图片时,把左右、上下的边缘部分,最好能无缝的连接起来。纹理坐标[3.5,2.7] 整数部分被抛弃,变成采样[0.5,0.7]的纹理。
2.Border方式:
把不在[0,1]范围的纹理坐标设置为一些指定的颜色,比如下图,指定为红色
3.Clamp方式:
把不在[0,1]范围的纹理坐标指定为离其最近的纹理单元颜色。纹理坐标大于1,截取到1。纹理坐标小于0。截取到0。
采样纹理坐标[-0.5,1.3]处的纹理,变成采样[0,0.3]。
4.Mirror方式:
就是把不在[0,1]范围的纹理坐标按镜像的方式指定纹理单元,如下图所示:
Unity中纹理属性:
1.TextureType 纹理类型,让Shader得到正确的纹理,并在某些情况下对该纹理进行优化。
2.Wrap Mode,纹理的寻址方式。见上边所述
3.Filter Mode,纹理由于变换产生拉伸时,图片采用哪种滤波方式。
- Point - 只采样一个像素点 (马赛克风格)
- Bilinear - 采样4个邻近像素,然后线性插值得到最终像素 (感觉图片模糊了)
- Trilinear - 跟Bilinear差不多,只是如果采用了Mippap,会在多级渐远纹理之间进行混合。如上所述。
三种方式,得到的图片效果依次提升。但是效率依次下降。
4.Format,决定了Unity使用哪种格式存储该纹理。效率越好,内存占用越大。
如下图。图片下边可看到纹理格式和内存占用
Shader纹理采样显示
Shader "Chan/Chapter7_SingleTexture" {
Properties
{
_Color("Color Tint",Color) = (1,1,1,1)
_MainTex("Mai Tex",2D) = "white"{}
_Specular("Specular",Color) = (1,1,1,1)
_Gloss("Gloss",Range(8,256)) = 20
}
SubShader
{
Pass
{
//LightMode定义该Pass在Unity光照流水线中的角色。
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
//纹理名_ST 获取纹理的缩放和平移 ST scale和translation
//纹理名_ST.xy = 缩放值 纹理名_ST.zw = 偏移
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;
};
v2f vert(a2v i)
{
v2f o;
o.pos = UnityObjectToClipPos(i.vertex);
o.worldNormal = UnityObjectToWorldNormal(i.normal);
o.worldPos = mul(unity_ObjectToWorld,i.vertex).xyz;
//得到顶点纹理坐标
o.uv = i.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
return o;
}
fixed4 frag(v2f i):SV_Target
{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
//tex2D采样得到纹理颜色 作为材质反射率
fixed3 albedo = tex2D(_MainTex,i.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * _Color.rgb;
//兰伯特 漫反射
fixed3 diffuse = _LightColor0.rgb * albedo * max(0,dot(worldNormal,worldLightDir));
//Blinn-Phong 高光反射
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);
}
ENDCG
}
}
Fallback "Specular"
}
凹凸映射(bump mapping)
使用纹理修改模型表面法线,让模型看起来凹凸不平,更有型。
1.实现凹凸映射有两种方法:
- 高度映射(height mapping) - 高度纹理(height map) 模拟表面位移,得到修改后的法线值。
高度图中存储的是强度值,颜色越深,表明越往里凹,颜色越浅,表明越往外凸。优点很直观的看出模型表面凹凸情况,缺点要通过像素灰度值计算得到。消耗更多性能。
- 法线映射(normal mapping) - 使用法线纹理(normal map)存储模型表面法线。
法线纹理存储的就是表面法线方向,由于法线方向分量范围在[-1,1]之间,像素分量范围在[0,1]之间。因此需要做映射:
Shader使用时候,对法线纹理采样后,需要对结果进行反映射得到真实的法线方向。
normal = pixel * 2 - 1
2.法线纹理有两种存储方式:
- 模型空间 下存储的顶点表面法线叫做模型空间下的法线纹理。
1.实现简单,直观。2.边角处,由于使用的同一坐标系下,因此边界处插值得到的法线比较平滑。
- 切线空间 基于每个模型顶点的切线空间存储的顶点表面法线叫做切线空间下的法线纹理。
自由度高,由于使用的是模型定点的切线空间生成的法线纹理,因此变换顶点,一样可以得到合理的结果。由于是相对法线信息,因此可以做很多效果,例如UV动画,重用法线纹理,比如一张法线纹理用在一个砖块模型的6个面。
纹理可压缩,切线空间下的法线纹理Z总是正方向,因此可以只存储X(切线)Y(副切线)方向,推导出Z。
切线空间 下的切线方向怎么确定?
实践:
1.切线空间下计算光照模型
Shader "Chan/Chapter7_NormalMapTangentSpace" {
Properties
{
_Color("Color Tint",Color) = (1,1,1,1)
_MainTex("Main Tex",2D) = "white"{}
_BumpMap("Bump Map",2D) = "white"{}
_BumpScale("Bump Scale",float) = 1.0
_Specular("Specular",Color) = (1,1,1,1)
_Gloss("Gloss",Range(8,255)) = 10
}
SubShader
{
Pass
{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
float4 _BumpMap_ST;
float _BumpScale;
fixed4 _Specular;
float _Gloss;
struct a2v
{
float4 vertex:POSITION;
float3 normal:NORMAL;
float4 tangent:TANGENT;//语义 获取顶点切线方向
float4 texcoord:TEXCOORD0;
};
struct v2f
{
float4 pos:SV_POSITION;
float4 uv:TEXCOORD0;
float3 lightDir:TEXCOORD1;
float3 viewDir:TEXCOORD2;
};
v2f vert(a2v i)
{
v2f o;
o.pos = UnityObjectToClipPos(i.vertex);
//1.xy分量存储_MainTex纹理坐标,xw分量存储_BumpMap纹理坐标
//2.通常_MainTex,_BumpMap通常使用同一组纹理坐标,为了减少插值寄存器使用数量
//一般只计算和存储一个纹理坐标就可。
o.uv.xy = i.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
o.uv.zw = i.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
//cross求得两个3D向量的叉积,求得两个向量垂直这两个向量 * v.tangent.w 确定出副切线方向
float3 binormal = cross(normalize(i.normal),normalize(i.tangent.xyz)) * i.tangent.w;
//(切线,副切线,法线)按行排列 得到模型空间到切线空间的变换矩阵 ????
float3x3 rotaition = float3x3(i.tangent.xyz,binormal,i.normal);
//模型空间下光照和视觉方向转化到切线空间
o.lightDir = mul(rotaition,ObjSpaceLightDir(i.vertex)).xyz;
o.viewDir = mul(rotaition,ObjSpaceViewDir(i.vertex)).xyz;
return o;
}
fixed4 frag(v2f o):SV_Target
{
fixed3 tangentLightDir = normalize(o.lightDir);
fixed3 tangentViewDir = normalize(o.viewDir);
fixed4 packedNormal = tex2D(_BumpMap,o.uv.zw);
fixed3 tangentNormal;
/*图片格式不是 “Normal map”时候, 法线纹理rgb分量是切线空间下的法线方向的xyz
像素分量映射法线分量 normal = pixel * 2 - 1 具体看上面讲述
tangentNormal.xy = (packedNormal.xy * 2 - 1) * _BumpScale;*/
//图片格式是 “Normal map”时候
//通过Shader内置函数UnpackNormal获得
tangentNormal = UnpackNormal(packedNormal);
tangentNormal.xy *= _BumpScale;
//法线是单位向量,因此分量z可通过xy求得
tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy,tangentNormal.xy)));
fixed3 albedo = tex2D(_MainTex,o.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0,dot(tangentNormal,tangentLightDir));
fixed3 halfDir = normalize(tangentLightDir + tangentViewDir);
fixed3 specualr = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(tangentNormal,halfDir)),_Gloss);
return fixed4(ambient + diffuse + specualr,1.0);
}
ENDCG
}
}
Fallback "Specular"
}
2.世界空间下计算光照模型(Unity5.x内置Shader使用世界空间)
Shader "Chan/Chapter7_NormalMapWorldSpace" {
Properties
{
_Color("Color Tint",Color) = (1,1,1,1)
_MainTex("Main Tex",2D) = "white"{}
_BumpMap("Bump Map",2D) = "white"{}
_BumpScale("Bump Scale",float) = 1.0
_Specular("Specular",Color) = (1,1,1,1)
_Gloss("Gloss",Range(8,255)) = 10
}
SubShader
{
Pass
{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
float4 _BumpMap_ST;
float _BumpScale;
fixed4 _Specular;
float _Gloss;
struct a2v
{
float4 vertex:POSITION;
float3 normal:NORMAL;
float4 tangent:TANGENT;//语义 获取顶点切线方向
float4 texcoord:TEXCOORD0;
};
struct v2f
{
float4 pos:SV_POSITION;
float4 uv:TEXCOORD0;
float4 TtoW0:TEXCOORD1;
float4 TtoW1:TEXCOORD2;
float4 TtoW2:TEXCOORD3;
};
v2f vert(a2v i)
{
v2f o;
o.pos = UnityObjectToClipPos(i.vertex);
//1.xy分量存储_MainTex纹理坐标,xw分量存储_BumpMap纹理坐标
o.uv.xy = i.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
o.uv.zw = i.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
//世界空间坐标
float3 wolrdPos = mul(unity_ObjectToWorld,i.vertex).xyz;
fixed3 worldNormal = UnityObjectToWorldNormal(i.normal);
fixed3 worldTangent = UnityObjectToWorldDir(i.tangent.xyz);
//cross求得两个3D向量的叉积,求得两个向量垂直这两个向量 * v.tangent.w 确定出副切线方向
fixed3 worldBinormal = cross(worldNormal,worldTangent) * i.tangent.w;
//世界空间下的 切线(o.TtoW0.x,o.TtoW1.x,o.TtoW2.x),副切线,法线,坐标
//这么存,减少插值寄存器的使用
o.TtoW0 = float4(worldTangent.x,worldBinormal.x,worldNormal.x,wolrdPos.x);
o.TtoW1 = float4(worldTangent.y,worldBinormal.y,worldNormal.y,wolrdPos.y);
o.TtoW2 = float4(worldTangent.z,worldBinormal.z,worldNormal.z,wolrdPos.z);
return o;
}
fixed4 frag(v2f o):SV_Target
{
float3 worldPos = float3(o.TtoW0.w,o.TtoW1.w,o.TtoW2.w);
//世界空间下的光源方向和视觉方向
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
//法线是单位向量,根据法线纹理uv采样法线纹理。因此分量z可通过xy求得
fixed3 bump = UnpackNormal(tex2D(_BumpMap,o.uv.zw));
bump.xy *= _BumpScale;
bump.z = sqrt(1.0 - saturate(dot(bump.xy,bump.xy)));
//法线从切线空间变换到世界空间 ???
bump = normalize(half3(dot(o.TtoW0.xyz,bump),dot(o.TtoW1.xyz,bump),dot(o.TtoW2.xyz,bump)));
fixed3 albedo = tex2D(_MainTex,o.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0,dot(bump,lightDir));
fixed3 halfDir = normalize(lightDir + viewDir);
fixed3 specualr = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(bump,halfDir)),_Gloss);
return fixed4(ambient + diffuse + specualr,1.0);
}
ENDCG
}
}
Fallback "Specular"
}
//不乘以_BumpScale,就不需要求z了,UnpackNormal已经求好了
bump.z = sqrt(1.0 - saturate(dot(bump.xy,bump.xy)));
PS:
"UnityCG.cginc"中内部实现 UnpackNormal
// Unpack normal as DXT5nm (1, y, 1, x) or BC5 (x, y, 0, 1)
// Note neutral texture like "bump" is (0, 0, 1, 1) to work with both plain RGB normal and DXT5nm/BC5
fixed3 UnpackNormalmapRGorAG(fixed4 packednormal)
{
// This do the trick
packednormal.x *= packednormal.w;
fixed3 normal;
normal.xy = packednormal.xy * 2 - 1;
normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));
return normal;
}
inline fixed3 UnpackNormal(fixed4 packednormal)
{
#if defined(UNITY_NO_DXT5nm)
return packednormal.xyz * 2 - 1;
#else
return UnpackNormalmapRGorAG(packednormal);
#endif
}
上述代码中:
传入法线纹理采样后的纹理信息fixed4 (R,G,B,A),由于存储法线向量时都是以单位向量去存储的,也就是说x*x + y*y + z*z = 1。同时法线向量的分量Z永远是正值,因此只需要存储法线向量的xy2个分量,z分量通过计算可得到。
normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));
不同纹理格式,xy分量存在纹理信息中的位置不同,
DXT5nm:(1,y,1,x) - >法线x分量存在a通道中,法线y分量存在G通道。
BC5:(x,y,0,1)- >法线x分量存在R通道中,法线y分量存在G通道。
为了适配不同平台:
UnpackNormal函数中: packednormal.x *= packednormal.w;
将DxT5nm(1,y,1,x) 变成(x,y,1,x),就可两个格式都使用后边的语句: normal.xy = packednormal.xy * 2 - 1;
当把图片设置为Normal map后,有个Create from Grayscale,这是凹凸映射另一个种方法高度图中使用的。有两个参数:
1.Bumpiness = 控制凹凸程度 2.Filtering = 如何计算凹凸程度。
渐变纹理
控制漫反射光照
Shader "Chan/Chapter7_RampTexture" {
Properties
{
_Color("Color Tint",Color) = (1,1,1,1)
_RampTex("Tamp Tex",2D) = "white"{}
_Specular("Specular",Color) = (1,1,1,1)
_Gloss("Gloss",Range(8.0,256)) = 20
}
SubShader
{
Pass
{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _RampTex;
float4 _RampTex_ST;
fixed4 _Specular;
float _Gloss;
struct a2v
{
float4 vertex:POSITION;
float3 normal:NORMAL;
float3 texcoord:TEXCOORD0;
};
struct v2f
{
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
float2 uv:TEXCOORD2;
};
v2f vert(a2v i)
{
v2f o;
o.pos = UnityObjectToClipPos(i.vertex);
o.worldNormal = UnityObjectToWorldNormal(i.normal);
o.worldPos = mul(unity_ObjectToWorld,i.vertex).xyz;
//下同 o.uv.xy = texcoord.xy * _RampTex_ST.xy + _RampTex_ST.zw;
o.uv = TRANSFORM_TEX(i.texcoord, _RampTex);
return o;
}
fixed4 frag(v2f i):SV_Target
{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
//环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//半兰伯特光照计算漫反射
fixed halfLamert = 0.5 * dot(worldNormal,worldLightDir) + 0.5;
//_RampTex采样得到的颜色 * _Color.rgb = 最终的漫反射颜色
fixed3 diffuseColor = tex2D(_RampTex,fixed2(halfLamert,halfLamert)).rgb * _Color.rgb;
fixed3 diffuse = _LightColor0.rgb * diffuseColor;
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0 * _Specular.rgb * pow(max(0,dot(worldNormal,halfDir)),_Gloss);
return fixed4(ambient + specular + diffuse,1.0);
}
ENDCG
}
}
Fallback "Specular"
}
使用渐变纹理有时候会出现上图左侧的情况,这是因为纹理信息值理论在[0,1]之间,但是可能会出现1.0001,这是float浮点精度造成的。使用Repeat纹理寻址模式会把1.0001的整数部分去掉,取0.0001采样纹理。用Clamp。具体看上边WrapMode所述。
遮罩纹理
采样遮罩纹理的纹素值,然后使用其中的RGBA通道中一个或几个通道值,来与某种表面属性进行相乘,这样当该通道的值为0时,可以保护表面不受该属性影响。例如强化和弱化某些区域的高光反射强度。或制作地形,表现草地,石子等。
Shader "Chan/Chapter7_MaskTexture" {
Properties
{
_Color("Color Tint",Color) = (1,1,1,1)
_MainTex("Main Tex",2D) = "white"{}
_BumpMap("Bump Map",2D) = "white"{}
_BumpScale("Bump Scale",float) = 1
_SpecularMask("Specular Mask",2D) = "white"{}//高光反射遮罩纹理
_SpecualrScale("Specular Scale",float) = 1//控制遮罩影响度系数
_Specular("Specular",Color) = (1,1,1,1)
_Gloss("Gloss",Range(8,256)) = 20
}
SubShader
{
Pass
{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;//此处 _MainTex,_BumpMap,_SpecularMask共同使用纹理属性变量_MainTex_ST
sampler2D _BumpMap;
float _BumpScale;
sampler2D _SpecularMask;
float _SpecualrScale;
fixed4 _Specular;
float _Gloss;
struct a2v
{
float4 vertex:POSITION;
float3 normal:NORMAL;
float4 tangent:TANGENT;
float4 texcoord:TEXCOORD0;
};
struct v2f
{
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0;
float3 lightDir:TEXCOORD1;
float3 viewDir:TEXCOORD2;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
TANGENT_SPACE_ROTATION;
o.lightDir = mul(rotation,ObjSpaceLightDir(v.vertex)).xyz;
o.viewDir = mul(rotation,ObjSpaceViewDir(v.vertex)).xyz;
return o;
}
fixed4 frag(v2f i):SV_Target
{
fixed3 tangentLightDir = normalize(i.lightDir);
fixed3 tangentViewDir = normalize(i.viewDir);
fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap,i.uv));
tangentNormal.xy *= _BumpScale;
tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy,tangentNormal.xy)));
fixed3 albedo = tex2D(_MainTex,i.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0,dot(tangentNormal,tangentLightDir));
fixed3 halfDir = normalize(tangentLightDir + tangentViewDir);
//采样遮罩纹理的R通道,跟specular高光反射颜色相乘,控制颜色
fixed specularMask = tex2D(_SpecularMask,i.uv).r * _SpecualrScale;
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(tangentNormal,halfDir)),_Gloss) * specularMask;
return fixed4(ambient + diffuse + specular,1.0);
}
ENDCG
}
}
Fallback "Specular"
}
Unity中的宏定义,自己定义的参数名称和结构体的变量必须和Unity内置的.cginc文件中的函数和宏中使用的对应。不然结果不对。所以参数名和输入输出结构体的变量名称别随便改。。。。
#define TANGENT_SPACE_ROTATION
float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w;
float3x3 rotation = float3x3( v.tangent.xyz, binormal, v.normal )
参考:
https://www.cnblogs.com/mikewolf2002/archive/2012/04/07/2436063.html