UnityShader(二)
光照模型:
1.漫反射模型(Lambert)
公式如下:
C反指的是反射光线的强度,C光指的是入射光线的强度,m物指的是物体的材质反射系数,l向量指的是反射光线对应的向量,n向量指的是法线向量。因为,l向量与n向量都是单位向量,所以点乘积得到的结果实际上是cosθ。
放在顶点着色器,代码如下:
Shader "Unlit/005"
{
Properties
{
_Diffuse("Diffuse",Color) = (1,1,1,1)
}
SubShader
{
Tags
{
"RenderType" = "Opaque"
"LightMode"= "Vertex"
}
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
fixed4 _Diffuse;
struct v2f
{
float4 vertex:SV_POSITION;
fixed3 color : Color;
};
v2f vert (appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
//环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//需要在同一个空间 点积 才有效,光源是在世界空间,法线是在模型空间,因此
//需要将法线从模型空间转换到世界空间,法线的转换并不是简单的用mvp矩阵转换
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
//这个地方不确定_WorldSpaceLightPos0.xyz是否是单位向量,所以需要单位化
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = unity_LightColor0.rgb * _Diffuse.rgb * max(0,dot(worldNormal, worldLight));
o.color = diffuse + ambient;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
return fixed4(i.color,1);
}
ENDCG
}
}
}
放在片元着色器,代码如下:
Shader "Unlit/006"
{
Properties
{
_Diffuse("Diffuse",Color) = (1,1,1,1)
}
SubShader
{
Tags
{
"RenderType" = "Opaque"
//这个地方可以是Deferred ForwardBase vertex
//Deferred不写LightMode即是这种模式,在这种模式下unity_LightColor0为(0,0,0)
//Vertex这种模式用于顶点光照渲染,如果没有光照映射,所有顶点光照被应用
//ForwardBase用于正向渲染,环境主要方向灯和定点光/SH 等的应用 ,第一个像素光所在的反射通道标记为Forward
"LightMode" = "Vertex"
}
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
fixed4 _Diffuse;
struct v2f
{
float4 vertex:SV_POSITION;
fixed3 worldNormal : TEXCOORD0;
};
v2f vert (appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldNormal = worldNormal;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse =unity_LightColor0.rgb* _Diffuse.rgb*max(0,dot(worldLightDir,i.worldNormal));
fixed3 color = ambient + diffuse;
return fixed4(color,1);
}
ENDCG
}
}
}
2.Phong高光反射
公式如下:
其中m只与物体的材质有关,m次是一个常数。
代码如下:
顶点高光反射:
Shader "Unlit/008"
{
Properties
{
_Diffuse("Diffuse",Color) = (1,1,1,1)
_Specular("Specular",Color) = (1,1,1,1)
_Gloss("Gloss",Range(1, 20)) = 5
}
SubShader
{
Tags
{
"RenderType" = "Opaque"
"LightMode"= "Vertex"
}
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
fixed _Gloss;
struct v2f
{
float4 vertex:SV_POSITION;
fixed3 color : Color;
};
v2f vert (appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
//环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//需要在同一个空间 点积 才有效,光源是在世界空间,法线是在模型空间,因此
//需要将法线从模型空间转换到世界空间,法线的转换并不是简单的用mvp矩阵转换
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
//这个地方不确定_WorldSpaceLightPos0.xyz是否是单位向量,所以需要单位化
//worldLight代表反射光线 可以自己用_WorldSpaceLightPos0单位化自己写
//也可以用UnityWorldSpaceLightDir获得,建议使用这个API获得
//fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
fixed3 worldLight = UnityWorldSpaceLightDir(UnityObjectToWorldDir(v.vertex));
fixed3 diffuse = unity_LightColor0.rgb * _Diffuse.rgb * max(0,dot(worldNormal, worldLight));
//这个地方算出来的是r向量也就是反射光线的向量
fixed3 reflectDir = normalize(reflect(-worldLight,worldNormal));
//fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - UnityObjectToWorldDir(v.vertex));
//但是这样子求出来的不是世界坐标而是向量,所以要用其他方法
//fixed3 worldPos = UnityObjectToWorldDir(v.vertex);
//模型空间坐标变换到世界坐标需要左乘unity_ObjectToWorld矩阵,但这个矩阵应该是4维的,所以下面要去掉3乘3
//这个地方求位置要用4乘4的,求方向用3乘3的
//fixed3 worldPos = mul((float3x3)unity_ObjectToWorld,v.vertex);
fixed3 worldPos = mul(unity_ObjectToWorld,v.vertex);
//前置带Unity的需要传入世界坐标,而且会单位化
//fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
//也可以不把点转到世界坐标,直接由本地坐标得到一个viewDir
//fixed3 viewDir = normalize(WorldSpaceViewDir(v.vertex));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
fixed3 specular = unity_LightColor0.rgb * _Specular.rgb * pow(max(0,dot(viewDir,reflectDir)),_Gloss);
o.color = diffuse + ambient + specular;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
return fixed4(i.color,1);
}
ENDCG
}
}
}
片元高光反射:
Shader "Unlit/009"
{
Properties
{
_Diffuse("Diffuse",Color) = (1,1,1,1)
_Specular("Specular",Color) = (1,1,1,1)
_Gloss("Gloss",Range(1, 20)) = 5
}
SubShader
{
Tags
{
"RenderType" = "Opaque"
//这个地方可以是Deferred ForwardBase vertex
//Deferred不写LightMode即是这种模式,在这种模式下unity_LightColor0为(0,0,0)
//Vertex这种模式用于顶点光照渲染,如果没有光照映射,所有顶点光照被应用
//ForwardBase用于正向渲染,环境主要方向灯和定点光/SH 等的应用 ,第一个像素光所在的反射通道标记为Forward
"LightMode" = "Vertex"
}
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct v2f
{
float4 vertex:SV_POSITION;
fixed3 worldNormal : TEXCOORD0;
float3 worldPos:TEXCOORD1;
};
v2f vert (appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldNormal = worldNormal;
o.worldPos = mul(unity_ObjectToWorld,v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//漫反射
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = unity_LightColor0.rgb* _Diffuse.rgb*max(0,dot(worldLightDir,i.worldNormal));
//phong高光反射
//计算反射向量
fixed3 reflectDir = normalize(reflect(-worldLightDir,i.worldNormal));
//fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
//计算高光颜色
fixed3 specular = unity_LightColor0.rgb * _Specular.rgb * pow(max(0,dot(viewDir,reflectDir)),_Gloss);
fixed3 color = ambient + diffuse + specular;
return fixed4(color,1);
}
ENDCG
}
}
}
3.Blinn-Phong高光反射
参数同Phong,只是计算的方式不一样。
Shader "Unlit/010"
{
Properties
{
_Diffuse("Diffuse",Color) = (1,1,1,1)
_Specular("Specular",Color) = (1,1,1,1)
_Gloss("Gloss",Range(1, 20)) = 5
}
SubShader
{
Tags
{
"RenderType" = "Opaque"
//这个地方可以是Deferred ForwardBase vertex
//Deferred不写LightMode即是这种模式,在这种模式下unity_LightColor0为(0,0,0)
//Vertex这种模式用于顶点光照渲染,如果没有光照映射,所有顶点光照被应用
//ForwardBase用于正向渲染,环境主要方向灯和定点光/SH 等的应用 ,第一个像素光所在的反射通道标记为Forward
"LightMode" = "Vertex"
}
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct v2f
{
float4 vertex:SV_POSITION;
fixed3 worldNormal : TEXCOORD0;
float3 worldPos:TEXCOORD1;
};
v2f vert (appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldNormal = worldNormal;
o.worldPos = mul(unity_ObjectToWorld,v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//漫反射
//获得反射光线 可以自己用对应变量单位化 也可以调用API,建议调用API,这样会有宏判断 跨平台不容易出问题
//fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 worldLightDir = UnityWorldSpaceLightDir(UnityObjectToWorldDir(i.worldPos));
fixed3 diffuse = unity_LightColor0.rgb* _Diffuse.rgb*max(0,dot(worldLightDir,i.worldNormal));
//blinnphong高光反射需要求半角向量
//这个地方计算视角坐标 可以直接相机的位置-点的位置计算
//fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
//也可以使用unity的内置函数计算,推荐使用内置函数计算,内置函数里会进行一些宏判断
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
//计算半角向量 半角向量等于光源方向加视角方向
fixed3 halfDir = normalize(worldLightDir + viewDir);
//计算高光颜色
fixed3 specular = unity_LightColor0.rgb * _Specular.rgb * pow(max(0,dot(halfDir,i.worldNormal)),_Gloss);
fixed3 color = ambient + diffuse + specular;
return fixed4(color,1);
}
ENDCG
}
}
}