chapter3 Unity 基础光照
1. Unity中的基本光照
光线是如何产生的,光源发射光,一些物体与光交互,吸收一些光,散射一些光,而摄像机吸收一些光,形成了图像。在光学中,使用辐照度(irradiance)来量化光。
那么如何计算辐照度呢?
辐照度与照射到物体表面光线之间的距离d/cosx
成反比。
当光垂直下落cosx=1
,那么距离为d
当光与竖直方向呈x度角,距离为d/cosx
其中x可以由表面法线n和光源方向l的点积得到。
1.1 光与物体相交一般两种结果:散射与吸收
- 散射(scatering)只改变光线方向,不改变光线的密度和颜色。
散射的光线一般有两种方向,一种是向物体内部散射,叫做折射(refraction)或透射(transmission);
另一种是反射(reflection)到外部。
- 吸收(absorption) 则相反,只改变光线密度和颜色。对于不透明物体向物体内部折射时,一部分被吸收,一部分与内部粒子相交并反射出去
为了区分两种散射,用高光反射(specular)和漫反射(diffuse)分别表示物体如何反射以及计算多少光线折射、吸收(absorption)、反射出表面。
根据光线的入射方向和入射量可以得到出射方向和出射量,称之为出射度(exitance)
辐照与出射成线性关系,比值为漫反射和高光。
着色
根据一系列的信息得到计算出射度的等式,也即光照模型。(lighting model )
标准光照模型
自发光(emissive) 光线直接由光源进入摄像机,直接用材质的自发光颜色。
高光反射(specular)
漫反射(diffuse)
漫反射光照符合兰伯特定律 (Lambert's law): 反射光线的强度与表面法线和光源方向之间角的余弦值 成正比。因此, 漫反射部分的计算如下
c light 和 m diffuse 分别表示光源颜色和漫反射颜色
环境光(ambient) 一般是全局变量
1.2 基本光照模型
逐顶点计算漫反射
漫反射光照符合兰伯特定律 (Lambert's law): 反射光线的强度与表面法线和光源方向之间
角的余弦值 。因此, 漫反射部分的计算如下
c_diffuse=( light * m_diffuse) max( n . I )
中, n是表面法线,I 是指向光源的单位矢量 ,m_diffuse 是材质的漫反射颜色 ,light 是光源颜色。
需要注意的是 我们需要防止法线和光源方向点乘的结果为负值,为此 我们使用取最大值的函数来将其截取到 o, 这可以防止物体被从后面来的光源照亮。
以下为具体代码
1 | //顶点着色器代码 |
详情见P128.
矩阵变换见P86
要注意,非统一缩放,经计算得知会导致,法线不再垂直,所以不能用M(A-B)矩阵
需要用变换矩阵的逆转置矩阵
结论
1.进行漫反射计算,需要法线,光照,但是需要两者在同一空间下,这里是世界空间,并且法线变换不同于顶点变换
法线变换mv方法
1 | //右乘mv逆矩阵 |
2.一个矩阵可以左乘达成mv变换,那么它的逆矩阵就可以达成vm变换;逆=换方向,转=换位置
逐像素计算漫反射
类似于顶点
1 | Shader "Unity Shaders Books/Chapter6/Pixel_Level"{ |
但是结果全黑
在片元着色器计算结果更加平滑,但是光照不到的地方全黑;
全黑可以通过添加环境光
但是背光区却和向光区明暗一致,这时可以用半兰伯特模型
2. Unity Complex Light(复杂光照)
2.1 渲染路径(Rendering Path)
额外参考:
分为前向渲染路径,延迟渲染路径(更新),顶点渲染路径(弃用不介绍)
大多数情况下一个项目只使用一种渲染路径。也可以为不同相机设置不同路径。
在shaderlab中Pass中设置对应路径,lightMode告诉引擎程序员所需的光照属性,正确设置,才能适配不同流程获得的光照信息
2.1.1 前向渲染路径
每进行一次前向渲染,需要渲染图元,并计算颜色缓冲和深度缓冲的信息
如果物体受到多个光源照射,就要写多个Pass,然后在帧缓冲中将这些光照结果混合。
前向渲染中,光照类型(平行光parallel light或其他)+光照渲染模式决定了处理光照(照亮物体)的方式。
前向渲染光照处理方式分为逐像素,逐顶点、sh球谐函数计算 三种方法。在Light组件的RenderMode设置。
RenderMode 设置为important为逐像素,后二者则是Not important
2.1.2 延迟渲染路径
前向渲染的问题是 当场景中包含大量实时光源时 ,前向渲染的性能会急速下降。
延迟渲染主要包含了两个 Pass 。在第一 Pass 中,我们不进行任何光照计算,而是仅仅计算
哪些片元是可见的,这主要是通过深度缓冲技术来实现,当发现一个片元是可见的,我们就把它
的相关信息存储到 缓冲区中。然后,在第二个 Pass 中,我们利用 缓冲区的各个片元信息,
例如表面法线 、视角方向、漫反射系数 ,进行真正的光照计算
其它
Unity光源类型
1.平行光、点光源、面光源和聚光灯,面光源只在烘焙时才会产生作用
2.当RenderMode是Auto,Unity自动判断哪些光源逐像素,哪些顶点、sh计算。
3.最亮的平行光是按照逐像素计算,而Auto状态下最多除平行光源外4个逐像素计算,这些在addtional pass中计算
最常用的光源属性有:位置、方向、颜色、强度、衰减
- 光照衰弱
1 | //使用纹理来计算衰弱值 |
Unity阴影
1.如果最重要的平行光开启了阴影,那么Unity会为这个光源生成阴影纹理图,一种由光源出发的深度图。
2.另取一个LightMode为ShadowCaster的Pass,Unity会将摄像头放到光源的地方
3.一个物体想要接收阴影,那么在shader中要对纹理采样;相同地,一个物体想要投射阴影,就要参与纹理计算
见P200
- 如何让正方体接收阴影
SHADOWCOORD,TRANSFER_SHADOW,SHADOW_ATTENUATION分别在v2f,vert,frag中调用
实现接收阴影
1 | SHADOW_COORDS(2) |
光照衰减和阴影的效果是一样的,于是就有了统一光照衰弱和阴影
unity提供一个方法同时管理这两者
UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);
透明物体的阴影
在AlphaTest的基础上使用上述方法