????????勿于浮沙筑高台。回顾总结一下Unity前向渲染路径。
一、前置知识:光照的基本框架
????????光照主要分为:直接光照、间接光照、(环境光是间接光的一部分)。
????????这其中直接光照占主导地位。而为了减少间接光照的计算,有很多种技术来表达间接光照。
比如:LightMap,Reflection probe(反射球,IBL基于图像的照明),Light Probe(球谐光照SH)
????????目前的光照计算中最完整的是BRDF(Bidirectional Reflectance Distribution Function)光照模型。但是这个模型较为复杂,所以有一些简化后的光照模型。比如:标准光照模型
二、标准光照模型(Phong光照模型)
????????只关心直接光照。把进入摄像机的光线分为4个部分:
????????i.自发光emissive
????????ii.漫反射diffuse。
????????????????Lambert定律, half Lambert定律。
????????以Lambert为例,就是求光照方向与法线夹角的cos值,注意这里的光照方向是从顶点指向光源,这里只是约定俗称的。
???????
????????iii.高光反射specular
?????????????? ??????? Phong光照模型和Blinn-Phong光照模型。
????????以Phone光照为例,这里是求反射光与视线的夹角的cos值。smoothness值来控制光线的衰减,也就是光斑的大小。
????????iv.环境光ambient(表示所有的间接光照)。
??????? 具体实现在实现前向渲染的光照shader中实现。
三、前向渲染路径
?????? 渲染路径是决定灯光数据传递方式的。
??????? 1.实时光的处理
????????对于前向渲染,在场景里有多少盏实时灯光。物体就会被渲染多少次。而在含光照的shader中,分为两个Pass。
????????一个Pass为ForwardBase。这个Pass主要渲染主1盏平行光(逐像素)(以及小于等于4盏的顶点光,和SH球谐光。)
??????? 另一个Pass为ForwardAdd,这个Pass里面会渲染其他逐像素光源(逐像素)。多少盏由下面这个设置决定。
??????? 其他光源的处理方式:1.ForwardBase的顶点光。2.SH球谐光照
????? 光源重要等级排序:forwardBase像素光源 > forwardAdd像素光源 > forwardBase顶点光源 > SH球谐光源
?? 2.怎么区分不同的光源
?
??????????????? 1.RenderMode的设置。Not Important 的光源为顶点光或 SH 光源。Important 的光源为每像素光源。
??????????????? 2.按照光的强度排序。
如果像素光源少于设置,还有其他光源的话,其他光源可以依次升级。同理,像素光源多了就会依次降级。
?
四、实现简单的光照Shader
??????? 使用phong光照模型和Lambert光照模型。
Shader "Awen_Unlit/LambertPhoneSimple"
{
Properties
{
//最原始的数值上的光照模型
_Shininess("Shininess",Range(0.01,100)) = 1.0
_AmbientColor("Ambient Color",COLOR) = (0.0,0.0,0.0,0.0)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Tags{ "LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "AutoLight.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
float3 normal_dir : TEXCOORD1;
float3 pos_world : TEXCOORD2;
};
float4 _LightColor0;//定义了才能拿到光照颜色
float _Shininess;//光照衰减
float4 _AmbientColor;//环境光单纯使用的纯色
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
o.normal_dir = normalize(mul(float4(v.normal,0.0),unity_WorldToObject).xyz);
o.pos_world = mul(unity_ObjectToWorld,v.vertex).xyz;
//o.tangent_dir = normalize(mul(float4(v.tangent,0.0),))
return o;
}
half4 frag (v2f i) : SV_Target
{
half3 normal_dir = normalize(i.normal_dir);
//观察方向
half3 view_dir = normalize(_WorldSpaceCameraPos - i.pos_world);
//平行光的灯光方向
half3 light_dir = normalize(_WorldSpaceLightPos0.xyz);
//Lambert漫反射
half NdotL = dot(normal_dir,light_dir);
//获取灯光颜色,需要定义_LightColor0变量
half3 diffuse_color = max(0.0,NdotL) * _LightColor0.xyz;
//Phone高光
//注意这里的光照方向要加负号,因为灯光方向求的是顶点指向灯光
half3 reflect_dir = reflect(-light_dir,normal_dir);
half RdotV = dot(reflect_dir,view_dir);
half3 spec_color = pow(max(0.0,RdotV),_Shininess) * _LightColor0.xyz;
half3 final_color = diffuse_color + spec_color + _AmbientColor.xyz;
// sample the texture
// fixed4 col = tex2D(_MainTex, i.uv);
return float4(final_color,1.0);
}
ENDCG
}
Pass
{
Tags{ "LightMode" = "ForwardAdd"}
Blend One One
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdadd
#include "UnityCG.cginc"
#include "AutoLight.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
float3 normal_dir : TEXCOORD1;
float3 pos_world : TEXCOORD2;
};
float4 _LightColor0;//定义了才能拿到光照颜色
float _Shininess;//光照衰减
float4 _AmbientColor;//环境光单纯使用的纯色
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
o.normal_dir = normalize(mul(float4(v.normal,0.0),unity_WorldToObject).xyz);
o.pos_world = mul(unity_ObjectToWorld,v.vertex).xyz;
//o.tangent_dir = normalize(mul(float4(v.tangent,0.0),))
return o;
}
half4 frag (v2f i) : SV_Target
{
half3 normal_dir = normalize(i.normal_dir);
//观察方向
half3 view_dir = normalize(_WorldSpaceCameraPos - i.pos_world);
#if defined (DIRECTIONAL)
half3 light_dir = normalize(_WorldSpaceLightPos0.xyz);//平行光
half attenuation = 1.0;
#elif defined (POINT)
half3 light_dir = normalize(_WorldSpaceLightPos0.xyz - i.pos_world);
half distance = length(_WorldSpaceLightPos0.xyz - i.pos_world);
half range = 1.0 / unity_WorldToLight[0][0];
//最原始简单的点光源计算衰减值的做法,现在Unity是使用渐变图来计算的衰减
half attenuation = saturate((range - distance)/ range);
#endif
//Lambert漫反射
half NdotL = dot(normal_dir,light_dir);
//获取灯光颜色,需要定义_LightColor0变量
half3 diffuse_color = max(0.0,NdotL) * _LightColor0.xyz * attenuation;
//Phone高光
//注意这里的光照方向要加负号,因为灯光方向求的是顶点指向灯光
half3 reflect_dir = reflect(-light_dir,normal_dir);
half RdotV = dot(reflect_dir,view_dir);
half3 spec_color = pow(max(0.0,RdotV),_Shininess) * _LightColor0.xyz * attenuation;
half3 final_color = diffuse_color + spec_color + _AmbientColor.xyz;
return float4(final_color,1.0);
}
ENDCG
}
}
}
|