[UnityShader学习日志]2.第六章实验代码记录
目录
提要
- 本章节进行了最基本的光照实验
实验1-1:漫反射光照模型·逐顶点
- 首先给出漫反射光照的计算公式(基本光照模型中)
$C_{diffuse} = (C_{light}·m_{diffuse})max(0,\hat{n}·\hat{l})$
也就是说:我们需要入射光线的强度和颜色,材质的漫反射系数,以及根据兰伯特定律所得的表面法线与光源方向的点积(并且截取在[0,1]内,防止其为负值) - 有关颜色的我们往往可以通过
.rgb
属性来获取
获取入射光线的强度和颜色
- 在Pass语义块中,我们利用Tag{}来指明光照模式
Tags{ "LightMode" = "ForwardBase" }
- 其次,为了获取这样光照的入射光线信息,我们需要引入Unity的内置文件
#include "Lighting.cginc"
- 使用内置变量_LightColor0()来访问相关信息
_LightColor0.rgb
获取材质的漫反射系数
- 在Properties语义块中,声明一个Color类型的属性,将初始值设为白色
Properties
{
_Diffuse ("Diffuse",Color) = (1,1,1,1)
}
- 在pass中声明对应变量后,用同样的方式获取颜色
_Diffuse.rgb
编写相关相关代码
- 完成了需要信息的获取后,就可以开始编写代码了。还要注意的一点是,我们需要把法线方向和光线方向都转到世界空间中计算。
Shader "Unlit/diffuseVertex"
{
Properties
{
_Diffuse ("Diffuser",Color) = (1,1,1,1)
}
SubShader
{
Pass
{
Tags{ "LightMode" = "ForwardBase" }
CGPROGRAM
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse;
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
fixed3 color : COLOR;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;//环境光
fixed3 worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject));//把法线转换到世界空间(顶点变换矩阵的逆转置矩阵)
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,worldLight));
o.color = ambient + diffuse;
return o;
}
fixed4 frag(v2f i) : SV_TARGET0{
return fixed4(i.color,1.0);
}
ENDCG
}
}
}
实验1-2:漫反射光照模型·逐像素
- 此时,顶点着色器不再需要计算光照模型,只需要传递给片元着色器即可
Shader "Unlit/diffuseFrag"
{
Properties
{
_Diffuse("DIFFUSE",Color) = (1,1,1,1)
}
SubShader
{
Tags { "LightMode" = "ForwardBase" }
Pass
{
CGPROGRAM
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag
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;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = mul(v.normal,(float3x3)unity_WorldToObject);
return o;
}
fixed4 frag(v2f i) : SV_TARGET0
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,worldLightDir));
float3 color = ambient + diffuse;
return fixed4(color,1.0);
}
ENDCG
}
}
}
半兰伯特光照模型
- 引入:不管是使用逐顶点还是逐像素来实现,我们都会发现模型背面(光照无法到达的区域)都是全黑的,为了改善这个问题,V社的《半条命2》提出了一种技术:半兰伯特光照模型
- 半兰伯特光照模型对之前的公式进行了一些更改:
$C_{diffuse} = (C_{light}·m_{diffuse})(α·(\hat{n}·\hat{l})$ +β)
多数情况下,α和β的值都是0.5,这样就将原先向量点积的结果[-1,1]映射到哦了[0,1]范围内,对于背光面,原来的结果都是映射到0,而在这个模型公式下,背光面也可以有明暗变化 - 使用半兰伯特光照模型,我们只需要修改一些计算的代码:
fixed halfLambert = dot(worldNormal,worldLightDir) * 0.5 + 0.5;
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * halfLambert;
- 背面效果
实验2-1,2-2:高光反射光照模型·逐顶点 与 逐像素
- 高光反射的计算公式:
$C_{specular} = (C_{light}·m_{specular})max(0,\hat{v}·\hat{r})^{m_{gloss}}$ - 高光反射需要的参数较多,比如反射方向和视角方向,Cg提供了计算反射方向的函数reflect,需要法线方向和入射方向
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unlit/SpeecularFrag"
{
Properties
{
_Diffuse("DIFFUSE",COLOR) = (1,1,1,1)
_Specular("SPECULAR",COLOR) = (1,1,1,1)
_Gloss("GLOSS",Range(8.0,256)) = 20
}
SubShader
{
Tags { "LightMode" = "ForwardBase"}
Pass
{
CGPROGRAM
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
v2f vert(a2v v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject));
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
return o;
}
fixed4 frag(v2f i) : SV_TARGET0
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(i.worldNormal,worldLightDir));
fixed3 reflectDir = normalize(reflect(-worldLightDir,i.worldNormal));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir,viewDir)),_Gloss);
fixed3 color = ambient + diffuse + specular;
return fixed4(color , 1.0);
}
ENDCG
}
}
}