镜面反射,是指若反射面比较光滑,当平行入射的光线射到这个反射面时,仍会平行地向一个方向反射出来,这种反射就属于镜面反射,其反射波的方向与反射平面的法线夹角(反射角),与入射波方向与该反射平面法线的夹角(入射角)相等,且入射波、反射波,及平面法线同处于一个平面内。
算法
Shader "ApcShader/DiffusePerVetex" { //属性 Properties{ _Diffuse("Diffuse", Color) = (1,1,1,1) } //子着色器 SubShader { Pass { //定义Tags Tags{ "RenderType" = "Opaque" } CGPROGRAM //引入头文件 #include "Lighting.cginc" //定义Properties中的变量 fixed4 _Diffuse; //定义结构体:应用阶段到vertex shader阶段的数据,若是定义了 struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; }; //定义结构体:vertex shader阶段输出的内容 struct v2f { float4 pos : SV_POSITION; fixed4 color : COLOR; }; //定义顶点shader v2f vert(a2v v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); //把法线转化到世界空间 float3 worldNormal = mul(v.normal, (float3x3)_World2Object); //归一化法线 worldNormal = normalize(worldNormal); //把光照方向归一化 fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); //根据兰伯特模型计算顶点的光照信息,dot可能有负值,小于0的部分能够理解为看不见,直接取0 fixed3 lambert = max(0.0, dot(worldNormal, worldLightDir)); //最终输出颜色为lambert光强*材质diffuse颜色*光颜色 o.color = fixed4(lambert * _Diffuse.xyz * _LightColor0.xyz, 1.0); return o; } //定义片元shader fixed4 frag(v2f i) : SV_Target { return i.color; } //使用vert函数和frag函数 #pragma vertex vert #pragma fragment frag ENDCG } } //前面的Shader失效的话,使用默认的Diffuse FallBack "Diffuse" }咱们放置两个基本几何体,看一下shader的效果:
Shader "ApcShader/DiffusePerPixel" { //属性 Properties{ _Diffuse("Diffuse", Color) = (1,1,1,1) } //子着色器 SubShader { Pass { //定义Tags Tags{ "RenderType" = "Opaque" } CGPROGRAM //引入头文件 #include "Lighting.cginc" //定义Properties中的变量 fixed4 _Diffuse; //定义结构体:应用阶段到vertex shader阶段的数据 struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; }; //定义结构体:vertex shader阶段输出的内容 struct v2f { float4 pos : SV_POSITION; float3 worldNormal : TEXCOORD0; }; //定义顶点shader v2f vert(a2v v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); //把法线转化到世界空间 o.worldNormal = mul(v.normal, (float3x3)_World2Object); return o; } //定义片元shader fixed4 frag(v2f i) : SV_Target { //归一化法线,即便在vert归一化也不行,从vert到frag阶段有差值处理,传入的法线方向并非vertex shader直接传出的 fixed3 worldNormal = normalize(i.worldNormal); //把光照方向归一化 fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); //根据兰伯特模型计算像素的光照信息,小于0的部分理解为看不见,置为0 fixed3 lambert = max(0.0, dot(worldNormal, worldLightDir)); //最终输出颜色为lambert光强*材质diffuse颜色*光颜色 fixed3 diffuse = lambert * _Diffuse.xyz * _LightColor0.xyz; return fixed4(diffuse, 1.0); } //使用vert函数和frag函数 #pragma vertex vert #pragma fragment frag ENDCG } } //前面的Shader失效的话,使用默认的Diffuse FallBack "Diffuse" }仍是一个立方体和一个圆柱体,采用了逐像素着色后的结果:
Shader "ApcShader/HalfLambert" { //属性 Properties{ _Diffuse("Diffuse", Color) = (1,1,1,1) } //子着色器 SubShader { Pass { //定义Tags Tags{ "RenderType" = "Opaque" } CGPROGRAM //引入头文件 #include "Lighting.cginc" //定义Properties中的变量 fixed4 _Diffuse; //定义结构体:应用阶段到vertex shader阶段的数据 struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; }; //定义结构体:vertex shader阶段输出的内容 struct v2f { float4 pos : SV_POSITION; float3 worldNormal : TEXCOORD0; }; //定义顶点shader v2f vert(a2v v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); //把法线转化到世界空间 o.worldNormal = mul(v.normal, (float3x3)_World2Object); return o; } //定义片元shader fixed4 frag(v2f i) : SV_Target { //归一化法线,即便在vert归一化也不行,从vert到frag阶段有差值处理,传入的法线方向并非vertex shader直接传出的 fixed3 worldNormal = normalize(i.worldNormal); //把光照方向归一化 fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); //半兰伯特光照,将原来(-1,1)区间的光照条件转化到了(0,1)区间,既保证告终果的正确,又总体提高了亮度,保证非受光面也能有光,而不是全黑 fixed3 lambert = 0.5 * dot(worldNormal, worldLightDir) + 0.5; //最终输出颜色为lambert光强*材质diffuse颜色*光颜色 fixed3 diffuse = lambert * _Diffuse.xyz * _LightColor0.xyz; return fixed4(diffuse, 1.0); } //使用vert函数和frag函数 #pragma vertex vert #pragma fragment frag ENDCG } } //前面的Shader失效的话,使用默认的Diffuse FallBack "Diffuse" }看一下兰伯特光照模型和半兰伯特光照模型的对比:
Shader "ApcShader/DiffuseWithTex" { //属性 Properties{ _Diffuse("Diffuse", Color) = (1,1,1,1) _MainTex("Base 2D", 2D) = "white"{} } //子着色器 SubShader { Pass { //定义Tags Tags{ "RenderType" = "Opaque" } CGPROGRAM //引入头文件 #include "Lighting.cginc" //定义Properties中的变量 fixed4 _Diffuse; sampler2D _MainTex; //使用了TRANSFROM_TEX宏就须要定义XXX_ST float4 _MainTex_ST; //定义结构体:应用阶段到vertex shader阶段的数据 struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; }; //定义结构体:vertex shader阶段输出的内容 struct v2f { float4 pos : SV_POSITION; float3 worldNormal : TEXCOORD0; //转化纹理坐标 float2 uv : TEXCOORD1; }; //定义顶点shader v2f vert(a2v v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); //把法线转化到世界空间 o.worldNormal = mul(v.normal, (float3x3)_World2Object); //经过TRANSFORM_TEX宏转化纹理坐标,主要处理了Offset和Tiling的改变,默认时等同于o.uv = v.texcoord.xy; o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); return o; } //定义片元shader fixed4 frag(v2f i) : SV_Target { //unity自身的diffuse也是带了环境光,这里咱们也增长一下环境光 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Diffuse.xyz; //归一化法线,即便在vert归一化也不行,从vert到frag阶段有差值处理,传入的法线方向并非vertex shader直接传出的 fixed3 worldNormal = normalize(i.worldNormal); //把光照方向归一化 fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); //根据半兰伯特模型计算像素的光照信息 fixed3 lambert = 0.5 * dot(worldNormal, worldLightDir) + 0.5; //最终输出颜色为lambert光强*材质diffuse颜色*光颜色 fixed3 diffuse = lambert * _Diffuse.xyz * _LightColor0.xyz + ambient; //进行纹理采样 fixed4 color = tex2D(_MainTex, i.uv); return fixed4(diffuse * color.rgb, 1.0); } //使用vert函数和frag函数 #pragma vertex vert #pragma fragment frag ENDCG } } //前面的Shader失效的话,使用默认的Diffuse FallBack "Diffuse" }
Shader "ApcShader/DiffuseWithTexX" { //属性 Properties{ _Diffuse("Diffuse", Color) = (1,1,1,1) _MainTex("Base 2D", 2D) = "white"{} } //子着色器 SubShader { Pass { //定义Tags Tags{ "RenderType" = "Opaque" } CGPROGRAM //引入头文件 #include "Lighting.cginc" //定义Properties中的变量 fixed4 _Diffuse; sampler2D _MainTex; //使用了TRANSFROM_TEX宏就须要定义XXX_ST float4 _MainTex_ST; //定义结构体:应用阶段到vertex shader阶段的数据,若是定义了 struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; }; //定义结构体:vertex shader阶段输出的内容 struct v2f { float4 pos : SV_POSITION; fixed4 color : COLOR; //转化纹理坐标 float2 uv : TEXCOORD1; }; //定义顶点shader v2f vert(a2v v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); //unity自身的diffuse也是带了环境光,这里咱们也增长一下环境光 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Diffuse.xyz; //把法线转化到世界空间 float3 worldNormal = mul(v.normal, (float3x3)_World2Object); //归一化法线 worldNormal = normalize(worldNormal); //把光照方向归一化 fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); //根据兰伯特模型计算顶点的光照信息,dot可能有负值,小于0的部分能够理解为看不见,直接取0 fixed3 lambert = 0.5 * dot(worldNormal, worldLightDir) + 0.5; //最终输出颜色为lambert光强*材质diffuse颜色*光颜色 o.color = fixed4(lambert * _Diffuse.xyz * _LightColor0.xyz + ambient, 1.0); //经过TRANSFORM_TEX宏转化纹理坐标,主要处理了Offset和Tiling的改变,默认时等同于o.uv = v.texcoord.xy; o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); return o; } //定义片元shader fixed4 frag(v2f i) : SV_Target { return i.color * tex2D(_MainTex, i.uv); } //使用vert函数和frag函数 #pragma vertex vert #pragma fragment frag ENDCG } } //前面的Shader失效的话,使用默认的Diffuse FallBack "Diffuse" }
// Transforms 2D UV by scale/bias property #define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)若是咱们使用了这个宏,就须要在shader中定义咱们要采样的纹理的一个系数,命名方式为 纹理名_ST,float4类型。那么这个值是什么呢?