之前只是草草看了书,隔了一两个月回头看,很多都忘了,学习方法不对啊,希望利用写博客加深对知识的理解吧。
背景
渲染包含两大部分:像素可见性和光照计算。
今天就是介绍如何利用shader仿真现实世界中的漫反射。
漫反射计算公式如下
逐顶点光照
Shader "Test Shader/VertexLevel"{
Properties
{
_Diffuse ("DiffuseName",Color)=(1,1,1,1)
}
SubShader
{
Pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _Diffuse;
struct a2v
{
//模型空间的顶点坐标
float4 vertex:POSITION;
//模型空间的法线方向
float3 normal:NORMAL;
};
struct v2f
{
//输出的是裁剪空间的顶点坐标
float4 pos:SV_POSITION;
float3 color:COLOR;
};
v2f vert(a2v v){
v2f o;
//利用unity内置的模型-观察-投影矩阵将顶点坐标转换到裁剪空间
o.pos=UnityObjectToClipPos(v.vertex);
//通过内置变量获取环境光
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
//法线转换到世界坐标
//unity_WorldToObject为模型空间到世界空间的变换矩阵的逆矩阵
fixed3 worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
//获取光源方向
fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
//利用漫反射光照公式计算漫反射
fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*max(0,dot(worldNormal,worldLight));
//漫反射和环境光相加得到最终的光照结构
o.color=ambient+diffuse;
return o;
}
fixed4 frag(v2f i):SV_Target
{
return fixed4(i.color,1.0);
}
ENDCG
}
}
FallBack "DiffuseFallBack"
把这个shader文件添加给某个材质,再把该材质添加到角色中,就能得到简单的漫反射效果
不过这样的漫反射会出现一些视觉问题,背光面和光面交界处有锯齿
逐像素光照
Shader "Test Shader/DiffusePixelLevel"{
Properties
{
_Diffuse ("DiffuseName",Color)=(1,1,1,1)
}
SubShader
{
Pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _Diffuse;
struct a2v
{
//模型空间的顶点坐标
float4 vertex:POSITION;
//模型空间的法线方向
float3 normal:NORMAL;
};
struct v2f
{
//输出的是裁剪空间的顶点坐标
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD0 ;
};
v2f vert(a2v v){
v2f o;
//利用unity内置的模型-观察-投影矩阵将顶点坐标转换到裁剪空间
o.pos=UnityObjectToClipPos(v.vertex);
//法线转换到世界坐标
//unity_WorldToObject为模型空间到世界空间的变换矩阵的逆矩阵
o.worldNormal=mul(v.normal,(float3x3)unity_WorldToObject);
return o;
}
fixed4 frag(v2f i):SV_Target
{
//通过内置变量获取环境光
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal=normalize(i.worldNormal);
//获取光源方向
fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
//利用漫反射光照公式计算漫反射
//saturate(x)和max(0,x)能达到同样的效果,他的作用是把x截取到[0,1]范围内
fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal,worldLight));
fixed3 color=ambient+diffuse;
return fixed4(color,1.0);
}
ENDCG
}
}
FallBack "DiffuseFallBack"}
把计算挪到片元着色器中完成了逐像素光照,逐像素光照更平滑
不过这2种着色效果造成模型外观呈黑,
我们用半兰伯特光照模型解决这一问题
半兰伯特光照模型
Shader "Test Shader/HalfLambert"{
Properties
{
_Diffuse ("DiffuseName",Color)=(1,1,1,1)
}
SubShader
{
Pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _Diffuse;
struct a2v
{
//模型空间的顶点坐标
float4 vertex:POSITION;
//模型空间的法线方向
float3 normal:NORMAL;
};
struct v2f
{
//输出的是裁剪空间的顶点坐标
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD0 ;
};
v2f vert(a2v v){
v2f o;
//利用unity内置的模型-观察-投影矩阵将顶点坐标转换到裁剪空间
o.pos=UnityObjectToClipPos(v.vertex);
//法线转换到世界坐标
//unity_WorldToObject为模型空间到世界空间的变换矩阵的逆矩阵
o.worldNormal=mul(v.normal,(float3x3)unity_WorldToObject);
return o;
}
fixed4 frag(v2f i):SV_Target
{
//通过内置变量获取环境光
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal=normalize(i.worldNormal);
//获取光源方向
fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
fixed3 halflambert=dot(worldNormal,worldLight)*0.5+0.5;
//利用漫反射光照公式计算漫反射
fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*halflambert;
fixed3 color=ambient+diffuse;
return fixed4(color,1.0);
}
ENDCG
}
}
FallBack "DiffuseFallBack"}
我在下面的代码里使用半兰特伯模型取代了原来的半兰特伯模型
fixed3 halflambert=dot(worldNormal,worldLight)*0.5+0.5;
//利用漫反射光照公式计算漫反射
fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*halflambert;
fixed3 color=ambient+diffuse;
return fixed4(color,1.0);