// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
Shader "Custom/vFShader" {
/****************声明各属性含义,并与面板上名称相关联**********************/
Properties {
_Diffuse ("Diffuse", Color) = (1,1,1,1) //漫反射系数
_SpecColors("SpecColor", Color) = (1,1,1,1) //高光系数
_MainTex ("Albedo (RGB)", 2D) = "white" {} //颜色贴图
_SpecularTex ("Specular (RGB)", 2D) = "white" {} //高光贴图
_NormalTex ("Bump (RGB)", 2D) = "white" {} //法线贴图(即凹凸程度)
_BumpFactor ("Bump Scale", Range(-10.0, 10.0)) = 0.0 //法线贴图系数(缩放比例)
_SpecularMapScale ("SpecularMapScale", Range(0, 10)) = 0 //光照程度
_SpecShininess("Specular Shininess", Range(-10.3, 10.3)) = 2.0 //亮度
_ColorDepth("ColorDepth", Range(-10.0, 10.0)) = 2.0 //颜色深度
_Gloss("Gloss", Range(-10.0, 10.0)) = 4 //光滑度
_StartCutoff("StartCutoff", Range(-360, 360)) = 0 //开始切剖角度
_EndCutoff("EndCutoff", Range(-360, 360)) = 0 //切剖完成角度
_Cutoff("Cutoff", Range(0.0, 1.0)) = 1 //切剖系数
}
SubShader {
Tags { "RenderType"="Transparent" "IgnoreProjector"="True" "Queue"="Transparent-20"}
Pass
{
Tags { "LightMode" = "ForwardBase"}
Cull Off
LOD 2400
//ZWrite Front
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "unitycg.cginc"
#include "lighting.cginc"
#include "HLSLSupport.cginc"
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
/***********定义各变量并说明其类型***************/
fixed4 _Diffuse; //漫反射系数
fixed4 _SpecColors; //高光系数
sampler2D _MainTex; //颜色贴图
float4 _MainTex_ST;
sampler2D _SpecularTex; //高光贴图
float4 _SpecularTex_ST;
sampler2D _NormalTex; //法线贴图
float _BumpFactor; //法线贴图系数
float4 _NormalTex_ST;
float _SpecularMapScale; //光照程度
float _SpecShininess; //亮度
fixed _ColorDepth; //颜色深度
float _Gloss; //光滑度
float _StartCutoff; //开始切剖角度
float _EndCutoff; //切剖完成角度
float _Cutoff; //切剖系数
struct v2f /****************声明一个顶点着色器的输出结构,它同时也是片段着色器的输入结构:每个点的各个属性********************/
//稍微解释一下什么是纹理坐标:纹理坐标也就是uv坐标,通常美术在制作模型的时候,为了把用2D贴图贴到一个3D模型上,就会为模型的每一个顶点指定一个纹理坐标,而这个纹理坐标是一个二维坐标,根据这个坐标去贴图上查找相应的颜色就可以了,在光栅化的时候纹理坐标又会被差值,最终我们只通过对几个顶点指定纹理坐标却让整个模型都添加了纹理颜色
{
float4 pos :SV_POSITION; //顶点投影坐标 1
float4 col :COLOR; //颜色信息 2
float3 xyz :TEXCOORD1; //输出纹理坐标:TEXCOORD0--7 3
float4 uv:TEXCOORD2; //缩放,偏移 4
float2 uv_Normal:TEXCOORD3; // 5
float4 uv_SpeTex:TEXCOORD4; //高光 6
float3 lightDir : TEXCOORD5; //点对光的方向 7
float3 viewDir : TEXCOORD6; //点对视角的方向 8
float3 normal : NORMAL; //法线向量坐标 9
};
v2f vert(appdata_full v): POSITION /****************顶点着色器********************/
//顶点着色器接受的参数都是模型最原始的参数,也就是美术同学指定的模型本身的一些参数,包括顶点的位置,法线,纹理坐标,颜色等等
{
v2f o; //因为要返回一个输出结构所以我们先定义一个结构变量.然后分别为里面的子变量赋值
o.pos = UnityObjectToClipPos(v.vertex); //1.位置信息(在写Instanced Shader时,通常情况下并不用在意顶点空间转换,因为所有内建的矩阵名字在Instanced Shader中都是被重定义过的,如果直接使用UNITY_MATRIX_MVP,会引入一个额外的矩阵乘法运算,所以推荐使用UnityObjectToClipPos / UnityObjectToViewPos函数,它们会把这一次额外的矩阵乘法优化为向量-矩阵乘法)
o.xyz = v.vertex.xyz; //3.纹理坐标
o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; //4. xy缩放。使用纹理属性值_MainTex_ST对顶点纹理坐标进行变换,得到最后的纹理坐标。首先使用缩放属性 _MainTex_ST.xy对顶点纹理坐标进行缩放,然后使用偏移属性_MainTex_ST.zw对结果进行偏移。
o.uv.zw = v.texcoord.xy * _NormalTex_ST.xy + _NormalTex_ST.zw; // zw偏移
o.uv_SpeTex.xy = v.texcoord.xy * _SpecularTex_ST.xy + _SpecularTex_ST.zw; //6. 高光
//定义这两个变量,是为了在切线空间下的入射光方向以及视线方向。选择切线空间来计算:主要是考虑的计算量对效率的影响,
TANGENT_SPACE_ROTATION;
/*float3x3 Object2TangentMatrix = float3x3(tangent,binormal,normal); 矩阵第一行是T(切线)在本地坐标的表示,第二行是B(负法线)在本地坐标的表示,第三行是N(法线)在本地坐标中的表示。将这三个都单位化之后,假设T为(Tx,Ty,Tz),当我们将一个模型空间的向量以列向量(设起为(a,b,c))的方式左乘这个变换矩阵的时候。那么得到的变换后的在切线空间坐标的x = Tx*a + Ty*b + Tz *c
在原本的模型空间中x轴正方向的单位向量为n(1,0,0),可以理解为在一个对于给定的向量v=(a,b,c),那么如果我们将两者点乘则得到v·n = 1*a + b*0 + c*0=a。其实因为n是单位向量摸为1,所以v·n = |v||n|cosθ=|v|cosθ
实际上当n为单位向量时候v·n得到的是v在n方向上的投影大小.那么如果我们把一个本地空间的向量转换到切线空间,实际上也就是要求这个向量在切线空间三个坐标轴单位方向上的投影。*/
o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)); //7. 将入射光方向从本地空间转换到切线空间传给片段着色器用来参与计算光照;
// ObjSpaceLightDir(float4 v)是Unity为我们封装好的用来将得到光线在本地空间中的方向向量。
// 定义了一个float3变量来保存入射光的方向(反方向)。ObjectSpaceLightDir可以把光线方向转化到模型空间,然后通过rotation再转化到切线空间。
o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz; //8. 将视线方向从本地空间转换到切线空间传给片段着色器用来参与计算光照;
//ObjSpaceViewDir(float4 v)是Unity为我们封装好的用来将得到视线在本地空间中的方向向量。
o.normal = UnityObjectToWorldNormal(v.normal); //9. 把模型本身自带的顶点法线属性,先进行空间变换将其变换到世界空间中去(为什么要换到世界空间中呢,其实只要保证要进行操作的两个向量在同一个空间,那么具体是哪个空间并不重要,不过由于Unity为我们提供的大量参数都是在世界空间中的,所以我们就变换到世界空间中去)
return o;
}
float4 frag(v2f i) : SV_TARGET /****************片元着色器********************/
//片段着色器的任务就是最后计算出一个颜色提供给渲染设备进行最后的处理,所以它的返回值为float4(存的是rgba)。而最后面还跟了个:SV_TARGET,这是为了把返回结果提供给:SV_TARGET语义关联的寄存器,到时候渲染设备最后进行处理的时候去这里取片段着色器计算出来的结果就可以了。
{
float3 albedo = tex2D(_MainTex, i.uv).rgb; //返回该点的颜色。基础颜色;使用tex2D函数对纹理进行采样,第一个参数是需要被采样的纹理,第二个是float2类型的纹理坐标,将返回计算得到的纹素值。
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Diffuse.xyz; //环境色
fixed3 tangentLightDir = normalize(i.lightDir); //归一化后的入射光方向
fixed3 tangentViewDir = normalize(i.viewDir); //归一化后的视角方向
float3 worldNormal = normalize(i.normal); //归一化后的法线方向
fixed4 packedNormal = tex2D(_NormalTex, i.uv.zw); //返回该点的法线。tex2D函数,这是一个根据纹理坐标查询2D贴图颜色的函数:第一个参数是贴图,就是我们在上面声明的_NormalTex,而后面是我们要查询的坐标,也直接把顶点输出结构里我们赋值的纹理坐标填上去就可以了,最终将为我们返回一个贴图上的颜色。
fixed3 tangentNormal;
tangentNormal = UnpackNormal(packedNormal); //构建一个float3向量,用于与法线相乘得到更改后的法线值
tangentNormal.xy *= _BumpFactor; //利用_BumpFactor控制凹凸程度
tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));//由于法线都是单位矢量,因此tangentNormal.z 可以由tangentNormal.xy计算而得
float3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir)); //漫反射:“diffuse = Kd * lightColor * max(N•L,0)”;光源颜色_LightColor0.rgb,环境色albedo进行色彩处理,dot是来计算两个光源的点积的,最后一点就是max函数,之所以将点积计算出来之后还要和0去取一个最大值,主要是为了避免当normal和lightdir的夹角大于90度的时候点积计算出现负值,会导致整个漫反射颜色计算出来是个负值进而导致整体的光照计算出现错误,当大于90度的时候漫反射颜色已经失去意义,所以需要用max函数保证不会出现负值。
fixed3 halfDir = normalize(tangentLightDir + tangentViewDir);
fixed3 specular = _LightColor0.rgb *_SpecColors * pow(max(0, dot(tangentNormal, halfDir)), (10 -_Gloss));//Blinn-Phong高光光照模型,相对于普通的Phong高光模型,会更加光
float4 s = tex2D(_SpecularTex, i.uv_SpeTex); //返回该点的高光。使用tex2D函数对纹理进行采样,第一个参数是需要被采样的纹理,第二个是float4类型的纹理坐标
specular = specular * pow(s.r * _SpecularMapScale, _SpecShininess); //优化高光(与光线程度、亮度有关)
fixed3 c = pow(albedo, _ColorDepth) ; //优化基础颜色
float4 diffuseSpecularAmbient;
diffuseSpecularAmbient = float4(diffuse + specular, 1.0)+ UNITY_LIGHTMODEL_AMBIENT; //“漫反射+高光”环境
i.col = fixed4(diffuseSpecularAmbient * c, 1.0);//给传入的片元颜色信息赋值。与“漫反射、高光、基础颜色”均有关
float vy = acos(i.xyz.z / sqrt(i.xyz.z * i.xyz.z + i.xyz.x * i.xyz.x)) / 3.1415926 * 180;//俯视图中,z方向的对应角度
if(i.xyz.x < 0)
vy = 360 - vy;
if(_StartCutoff < _EndCutoff && _Cutoff < 1.0)
{
if(vy>= _StartCutoff && vy <=_EndCutoff && _Cutoff < 1.0) //如果采样点在切剖起始和末尾点之间:start<vy<end,将被切剖。
{
i.col.a = _Cutoff; //将颜色信息的RGBA通道的a通道赋值为切剖系数(cutoff=0时为透明)
if(i.col.a <= 0.05)
discard;
}
}
else
{
if(vy<= _StartCutoff && vy >=_EndCutoff && _Cutoff < 1.0) //如果采样点不在切剖起始和末尾点之间:vy<start时或vy>end时,也会被切剖。
{
i.col.a = _Cutoff;
if(i.col.a <= 0.05)
discard;
}
}
return i.col; //返回采样点颜色信息
}
ENDCG
}
}
FallBack "Specular"
}