前部分知识复习03
一、光照模型
经验型:
1.Lambert光照模型
2.Phong光照模型
3.Blinn-Phong光照模型
物理型:
4.PBR光照模型
二、渲染路径
渲染路径:是为进行光照计算而设置的渲染方式
- 前向渲染路径
- 顶点照明渲染路径
- 延迟渲染路径
顶点照明渲染路径中的灯光渲染方式都是逐顶点渲染,延迟渲染路径中的灯光渲染方式都是逐像素渲染;而前向渲染路径中灯光的渲染的方式结合以上两种,即有逐顶点渲染还有逐像素的渲染方式
前向渲染路径:
包含两种渲染路径:ForWardBase 和 ForWardAdd
ForWardBase:用于主平行灯的逐像素渲染和所有的逐顶点灯光和球谐灯光渲染
ForWardAdd : 用于其他所有的逐像素灯光的渲染(包括除主平行灯光之外的所有灯光,但有数量限制,一旦超过限制的数量后,该灯光就会使用ForWardBase中的逐顶点和球谐的渲染方式)
三、利用Lambert光照模型实现主平行灯光和其他灯光类型的渲染
1.主平行灯光的实现
Diffuse=Ambient+LightColor*Kd*max(0,dot(N,L));
- float4 Ambient=unity_AmbientSky; //环境光
- float4 LightColor=_LightColor0; //_LightColor0在“Lighting.cginc”中定义
- float3 N=normalize(i.worldNormal);
- float3 L=_WorldSpaceLightPos0; //_WoldSpaceLightPos0为由模型指向灯光单位向量
_WorldSpaceLightPos0:
对于主平行光,它是一个指向光源的单位长度的方向向量,对于点光源,它正是名称所说的,指该光源的世界空间位置。也就是说当光源为平行光时,_WorldSpaceLightPos0代表归一化的单位方向向量
_LightColor0:
_LightColor0为float4类型,在''Lighting.cginc"中定义
对应程序灯光的【颜色】和【光强】属性
unity_AmbientSky 代表环境光(Gradient)中的 Sky Color
unity_AmbientEquator 代表环境光(Gradient)中的 Equator Color
unity_AmbientGround 代表环境光(Gradient)中的 Ground Color
UNITY_LIGHTMODEL_AMBIENT 代表环境光(Color)中的 Sky color
其中Tags{"LightMode"="ForWardBase"}
2.其他类型灯光的实现(例如点光源)
Diffuse=atten*LightColor*max(0,dot(N,L));
//atten为衰减贴图的采样结果
例如点光光源的实现,需要考虑光源的衰减情况,当光源逐渐远离物体时,光源的光也会随之衰减
衰减贴图 _LightTexture0 (衰减贴图_LightTexture0定义在"AutoLight.cginc"中)
当采样衰减贴图时,需要使用模型的灯光空间坐标来采样贴图
- 灯光空间的转换:
- 先将模型空间转换为世界空间 (利用矩阵mul(unity_ObjectToWorld,v.vertex) )
- 然后将世界空间转换为灯光空间 (利用矩阵mul(untiy_WorldToLight,i.worldPos) )
v2f vert(appdate v)
{
v2f o;
o.worldPos=mul(unity_ObjectToWorld,v.vertex);.......
}float4 frag(v2f i):SV_Target
{
float3 LightCoord=mul(unity_WorldToLight,i.worldPos).xyz; //转换到灯光空间
float atten =tex2D(_LightTexture0,dot(LightCoord,LightCoord));.......
}
之后使用灯光空间坐标的dot结果来采样衰减贴图得到衰减值
float atten =tex2D(_LightTexture0,dot(LightCoord,LightCoord));
最后将衰减值与漫反射值相乘输出结果
其中
- Tags{"LightMode"="ForWardAdd"}
- Blend One One //因为默认的混合效果为 One Zero
- * 同时需注意一定要在结构体中定义UV (因为衰减贴图的采样需要UV)
四、阴影的接收与投射
1.投射阴影
投射阴影如何作用:将需要投射阴影的物体加入到ShadowMap阴影映射纹理图的生成中,这样其他物体在生成自身阴影时就会采样到该物体所投射出来的阴影
简便方法实现投射阴影:
FallBack"Specular" //回调函数,需写在SubShader外
模型阴影的投射(本质上生成带有该模型信息的ShadowMap,然后其他物体通过采样ShadowMap形成了该模型的阴影效果)-CSDN博客
2.接收阴影
在Tags{"LightMode"="ForWardBase"}的Pass中实现
需要先定义与接收阴影有关的变体
#pragma multi_compile_fwdbase
或者
#pragma multi_compile SHADOWS_SCREEN DIRECTIONAL
#pragma multi_compile_fwdbase novertexlight nodynlightmap nodirlightmap
定义在LightMode = ForwardBase的Pass中DIRECTIONAL : 主平行灯下的效果开启, fowwardBase下必开宏
DIRLIGHTMAP_COMBINED : 烘焙界面中的DirecitonalMode设置为Directional
DYNAMICLIGHTMAP_ON : RealtimeGI是否开启
LIGHTMAP_ON:当对象标记为LightMap Static并且场景烘焙后开启
LIGHTMAP_SHADOW_MIXING: 当灯光设置为Mixed,光照烘焙模式设置为Subtractive或者shadowMask时开启,Baked Indirect情况下无效
LIGHTPROBE_SH: 开启光照探针,动态物体会受到LightProbe的影响,静态物体与此不相关
SHADOWS_SCREEN : 在硬件支持屏幕阴影的情况下,同时处理阴影的距离范围内时开启
SHADOWS_SHADOWMASK: 当灯光设置为Mixed,光照烘焙模式设置为shadowMask时开启
VERTEXLIGHT_ON :是否受到逐顶点的照明
- UNITY_SHADOW_COORDS(x) //定义一个名为_shadowCoord的阴影纹理坐标变量
- TRANSFER_SHADOW(o) //根据不同情况转换模型坐标
- UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos)
阴影映射纹理的实现有两种方式,传统阴影映射和屏幕空间阴影映射
1.传统阴影映射纹理的实现: 首先会在正常渲染的Pass中把顶点的坐标位置变换到光源空间下,以得到它在光源空间下的三维位置信息,然后我们使用光源空间下模型的XY方向坐标信息对阴影映射纹理进行采样,然后得到阴影映射纹理中该位置的深度信息,若该深度值小于该顶点的深度值(顶点的深度值由模型的Z轴方向坐标决定),说明该顶点位于阴影中。(ShadowMap阴影映射纹理其实就是深度图,记录了从该光源的位置出发、能看到的场景中距离它最近的表面位置(深度信息))
2.屏幕阴影映射纹理的实现:与传统阴影映射方法不同的是,Unity会在LightMode=ShadowCaster的Pass中得到可投射阴影的光源的阴影映射纹理和摄像机的深度纹理,由光源的阴影映射纹理和摄像机的深度纹理来得到屏幕空间的阴影图。如果当前位置下摄像机的深度图中的深度记录大于转换到光源的阴影映射纹理中的深度记录,说明这个位置虽然是可见的但是处于阴影中,这样就得到了包含屏幕空间中所有的阴影区域。如果我们想要一个物体接受来自其他物体产生的阴影,就需要在Shader中采样这张阴影图,由于阴影图是屏幕空间下的,所以我们在采样前需要将模型的坐标转换到屏幕空间中(利用ComputeScreenPos函数实现屏幕空间坐标的转换)
所以TRANFER_SHADOW(o)的实现会根据平台不同而有差异,如果当前平台可以使用屏幕空间的阴影映射技术,TRANSFER_SHADOWS(o)会调用内置的ComputeScreenPos函数来计算模型在屏幕下的坐标值(ComputeScreenPos函数:把裁剪空间齐次坐标转换到屏幕空间的齐次坐标),然后存储在_ShadowCoord中,供后续的采样屏幕空间阴影图使用。如果不支持屏幕空间阴影映射技术,就会使用传统的阴影映射方式,TRANSFER_SHADOW会将模型的顶点坐标转换到光源空间下存储到_ShadowCoord中,后面的SHADOW_ATTENUTION会根据_ShadowCoord来对相关纹理进行采样
UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos)
第二个参数用于采样阴影映射纹理图,这个参数会传递给SHADOW_ATTENUATION用来计算阴影值(SHADOW_ATTENUATION负责使用_ShadowCoord对相关的纹理进行采样然后计算阴影);
第三个参数用于采样衰减贴图
最后将衰减值和阴影值相乘存储到第一个参数中