unity shader:深入光照

常见的光源如下:
1.平行光:可以放置在场景中的任何一个位置,朝着指定的方向像太阳一样,光照强度是一样的而且不存在光照衰减。
2.点光源:放置在场景中固定的一个位置,朝着所有方向像球体一样,点光源中间强度最大,光照衰减也最弱,向着四周距离点光源中间越远,光照强度越弱,光照衰减也越大。
3.聚光灯:放置在场景中固定一个位置,朝着固定方向像一个锥形体一样,聚光灯顶点处强度最大,光照衰减最弱,再矩形体内远离聚光灯顶点处越远,光照强度越小,光照衰减越大。
4.面光源:仅在烘焙时使用。

渲染路径定义:就是告诉unity将光源的光照信息(如:光照方向,光照位置,光照衰减,光照颜色和光照强度等)数据存储到指定渲染路径对应的变量中。

渲染路径分类:在unity4.x中,主要存在前向渲染路径,延时渲染路径以及顶点光照渲染路径,而在unity5.x中,主要存在前向渲染路径,新版延迟渲染路径,旧版延时渲染路径以及旧版顶点光照渲染路径。

渲染路径设置:unity中可以在摄像机的RenderPath中进行设置,也可以在PlayerSetting的RenderPath中进行设置,并且摄像机中的设置会覆盖掉PlayerSetting中的设置。在代码里,主要是使用LightModel渲染标签进行设置,并且常见的渲染标签如下所示:
这里写图片描述

前向渲染路径实现原理:每一个光源光照处理时,都是将模型中的所有渲染图元进行深度测试,将不满足深度测试的图元进行舍弃,对满足深度测试的图元得到最终的颜色并更新到像素缓冲区中。所以如果光源多时,容易出现相同的操作多次执行而造成性能低下问题。

前向渲染路径的光照处理方式:unity中主要存在三种前向渲染路径光照处理方式,分别为逐顶点处理,逐像素处理,球谐函数处理,其中不同的光源类型和不同的光源渲染模式对应的光照处理方式也是不同的,分别如下:
1.平行光总是按照逐像素处理。
2.渲染模式为Important的光源会按照逐像素处理。
3.渲染模式为Not Important的光源会按照逐顶点处理或者球谐函数处理。
4.逐顶点处理最多只能处理4个,而逐像素处理最多不能超过Quality Setting中设置的逐像素光源数量。

前向渲染路径内置的变量和函数:前向渲染路径处理光照时,光源和光照信息会填充到对应的变量上,其中常用的变量如下:
这里写图片描述
并且unity针对这些变量也提供了常用的辅助函数,如下所示:
这里写图片描述

顶点光照渲染路径的光照处理方式:仅使用逐顶点光照处理方式,所以计算速度最快,硬件支持的最广,但是效果并不是最好(因为不支持逐像素处理光照方式),并且逐顶点光照渲染路径中处理光源最多不超过8个。

顶点光照渲染路径内置的变量和函数:顶点光照渲染路径处理光照时,光源和光照信息会填充到对应的变量上,其中常用的变量如下:
这里写图片描述
并且unity针对这些变量也提供了常用的辅助函数,如下所示:
这里写图片描述

延迟渲染路径实现原理:就是在一个pass渲染过程中,将模型的所有图元进行深度测试,将不满足深度测试的图元进行舍弃,将满足深度测试的图元信息保存在几何缓冲区(G Buffer)中。然后另外一个pass渲染过程中,将屏幕当中所有的像素对应从几何缓冲区中定位获取到对应的图元,得到最终颜色并更新像素缓冲区中。

延迟渲染路径光照处理方式:逐像素光照处理方式,但是处理中不支持抗锯齿,不支持半透明物体,并且对显卡有部分要求。

延迟渲染路径内置变量和函数:延迟渲染路径处理光照时,光源和光照信息会填充到对应的变量上,其中常用的变量如下:
这里写图片描述
并且几何缓冲区中,通常存放的对象信息如下:
这里写图片描述

光照衰减:在unity中,平行光的光照强度不变,光照衰减也不变,但是点光源和聚光灯的光照强度和光照衰减是会发生变化的。而在计算光照衰减时,存在两种计算方式:
1.光照衰减纹理:就是使用纹理记录光照衰减信息。使用时可以获取光照空间中的顶点坐标,然后从光照纹理中按照当前光照空间顶点坐标进行采样,从而获取对应的光照衰减,相关代码如下:

// 使用世界空间到光照空间变换矩阵_LightMatrix0,将顶点坐标转换到光照弓箭坐标
float3 light_coord = mul(_LightMatrix0, float4(i.world_pos, 1));

// 从光照衰减纹理_LightTexture0中,按照顶点光照空间坐标进行采样,获取衰减值
fixed atten = tex2D(_LightTexture0, dot(light_coord, light_coord).rr).UNITY_ATTEN_CHANNEL;

2.数学公式计算:就是采用公式计算光照衰减,但是现在的unity并没有提供统一计算公式,也没有提供相关的属性供我们计算,所以只能针对于线性的光照衰减进行计算,相关代码如下:

// 获取世界空间中光照距离
float distece = length(_WorldSpaceLightPos0.xyz - i.world_pos.xyz);

// 获取按照距离线性变化的衰减值
fixed atten = 1.0 / distence;

光照阴影:主要分为接收和投射两个部分的阴影处理,相关区别如下:
1.投射物体的光照阴影:就是通过LightModel为ShadowCaster的Pass来将顶点坐标从模型空间转换到光照空间中,并将此顶点的深度信息输入到阴影映射纹理中,如果使用了屏幕空间阴影映射技术,那么这个Pass还会生成一张摄像机的深度纹理。通过Mesh Render中的Cast Shadows进行设置。
2.接收物体的光照阴影:就是从光照阴影映射纹理以及如果存在的摄像机深度纹理中进行采样,将采样的结果与光照结果进行相乘,从而获取当前接收物体的阴影。通过Mesh Render中的Receive Shadows进行设置。并通过AutoLight.cginc中相关宏来进行操作,如:使用SHADOW_COORDS宏来定义阴影纹理采样坐标,使用TRANSFER_SHAOW来得到阴影纹理采样坐标,使用SHADOW_ATTENNATION宏来用纹理采样坐标对阴影纹理进行采样,最终通过这个采样的数值和光照结果相乘得到最终的阴影。

注意:
1.我们可以使用UNITY_LIGHT_ATTENUATION宏来计算光光照衰减和光照阴影,它的内部主要是分别计算光照衰减和光照阴影,并将结果相乘返回。
2.对于使用透明度测试处理的透明物体,想要得到正确的阴影效果,就必须使ShadowCaster的Pass中也要能够进行透明度测试,否则得到的阴影效果是不正确的。此时可以使用内置的”Transparent/Cutoff/VertexLit”这个着色器来实现。
3.对于使用透明度混合处理的透明物体,由于关闭了深度写入,也会影响阴影的生成。

猜你喜欢

转载自blog.csdn.net/zjz520yy/article/details/78105384