首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >LayaAir技术分享: Shader 光照模型详解

LayaAir技术分享: Shader 光照模型详解

作者头像
Layabox Charley
发布2021-07-08 16:22:09
1.5K1
发布2021-07-08 16:22:09
举报
文章被收录于专栏:LayaboxLayabox

现实世界中,我们看到的任何物体都受光照的影响。没有光,我们也就看不到任何东西,因为光,我们才能感知到这个丰富的世界。而在3D渲染中为了能获得更加真实的渲染效果,光照计算就不可或缺。

在Shader的光照计算中,主要有 环境光,漫反射光,镜面高光。光源类型分为:平行光,点光源,聚光灯。本文我们将通过对LayaAir引擎 BlinnPhongMaterial 材质中光照计算进行讲解,以方便我们的学习。

漫反射光

漫反射代表了从一个表面相等地向所有方向反射出去的方向光,光的放射量与光到达表面的入射角度成正比。无论视点在哪里,表面上的一个点的漫反射都是一样的。

漫反射光的计算模型主要有 兰伯特光照模型和 半兰伯特光照模型。在 BlinnPhongMaterial 中使用的是 兰伯特光照模型。

兰博特光照模型

规范化的向量 N 和 L 的点积是两个向量之间夹角的一个度量。两个向量之间的夹角越小,点积的值越大,而表面会受到更多的入射光照。背向光源的表面将产生负的点积值,因此在公式中的 max(N · L , 0) 项确保了这样的表面不会显示漫反射光照。

在 BlinnPhongMaterial 中计算漫反射使用了 Lighting.glsl 库中的函数LayaAirBlinnPhongLight ,该函数接收的输入光向量(L)为光源 到3D模型点的方向,所以在计算 N和L的点积时对输入的光向量lightVec取反。

兰伯特漫发射渲染效果:

半兰博特光照模型

实际上,我们在现实世界中经常会发现,即使我们让一个物体不被光直接照射,我们也可能会看到物体,虽然亮度不是很高。半兰伯特模型可以使计算出来的光照结果大于0,又整体提升了亮度,使非直接受光面不是单纯的置为黑色。

兰伯特计算N和L向量的点积值的值域为[-1 ,1]:

dot(N, L) 值域: -1.0 –> 1.0

半兰伯特就是将N和L的点积值域转换到 [0 , 1]:

dot(N, L)* 0.5 + 0.5 值域: 0.0 –> 1.0

半兰伯特渲染效果:

环境光

环境光看起来并不是来自某个方向的,相反它看起来像是来自所有方向,所以环境光并不依赖于光源的位置。环境光是一个全局的光照颜色。

BlinnPhongMaterial材质中获取环境光的函数调用:

GlobalIllumination.glsl库中的函数:layaGIBase

获得环境光后,在最后的片段颜色输出时,将 环境光 和 漫反射光 进行计算:

将环境光(globalDiffuse)和 漫反射光 (diffuse)相加,再乘以 纹理采样颜色,即可得到纹理颜色+环境光颜色+漫反射光颜色的 最终输出值。

镜面反射高光

镜面反射高光代表了从一个表面主要的反射方向附近被反射的光,镜面反射高光在非常光滑和光泽的表面上是最显著的。不像漫反射,镜面反射的作用依赖于观察者的位置(即摄像机的位置),如果观察者不在一个能够接收反射光线的位置上,观察者将不可能在表面上看到一个镜面反射高光。镜面反射高光不仅受光源和材质的镜面反射颜色的影响,而且受表面的光泽度的影响。越光泽的材质的高光区域越小,而不那么光泽的材质的高光区域则分散的很开。

BlinnPhong光照模型:

当视向量(V)和半角向量(H)之间的夹角很小时,材质的镜面反射外表将变得很明显。N和H的点积的幂确保了镜面反射外表当H和V分开的时候能够迅速的减弱。如何N和H的点积为负,将强制限定为0,这能确保镜面反射高光不会出现在背向光源的几何表面。

BlinnPhongMaterial材质中镜面高光的计算:

specColorIntensity:高光强度值(0 至 1),控制了高光区域的大小。 gloss:控制高光的强弱。

通过函数LayaAirBlinnPhongLight计算出镜面反射高光后,只需要将高光颜色叠加最后的颜色输入值中:

镜面高光渲染效果:

获取光源

在shader代码中,我们主要通过引擎提供的几个uniform参数来获取场景中的光源数据,如平行光,点光源,聚光灯。

u_DirationLightCount:平行光的数量

u_LightBuffer: 灯光数据。LayaAir引擎将场景中的灯光数据存入一张贴图中,在shader代码中计算正确的UV坐标,就可以获取到灯光数据。

u_LightClusterBuffer:灯光聚类纹理,存储 点光源 和聚光灯 相对于当前 3D模型的信息。

平行光

平行光的数据结构:

BlinnPhongMaterial材质中获取平行光:

getDirectionLight函数需要入 灯光数据贴图和平行光编号。

点光源

是从空间中的一个点,向四周均匀的发光,被照点离光源的位置越远,光照越弱。超出点光源照射范围的物体将不受此光源的影响。

点光源的数据结构:

BlinnPhongMaterial材质中获取点光源:因为点光源和聚光灯都是有照射范围,只有在它的范围内的物体才受光照的影响,所以对一个渲染片段,我们只需要计算在光源有效范围内的光照。通过函数getClusterInfo ,可以找出对此片段有效的点光源和聚光灯数据。

函数 getClusterInfo 的实现:函数需要的参数 (聚类纹理(u_LightClusterBuffer) , 视图矩阵 (u_View) , 摄影机视口 (u_Viewport) , 投影参数 (u_ProjectionParams))都是引擎为我们提供的uniform变量;片段在世界空间中的位置(position)需要我们在顶点着色器中计算;相对坐标信息 (gl_FragCoord)是glsl内置变量。

获得点光源后,就可以计算 漫反射光和 镜面反射高光:

在点光源的计算中,漫反射和镜面反射高光的计算和 平行光是一样的,唯一的差别是需要对计算的光照结果进行一个距离的衰减。在 Lighting.glsl 库中,有一个衰减函数:

聚光灯

是从空间中的一个点,向一定方位发光,被照点离光源的位置越远,光照越弱;被照点离中心方向越远,光照越弱。

聚光灯的数据结构:

在聚光灯的计算中,我们需要继续距离衰减 和 角度衰减

角度衰减:将圆锥体分成两部分:一个内部圆锥和一个外部圆锥,内部圆锥发出固定强度的光,在内部圆锥以外强度平滑地逐渐减少。

通过以上内容的介绍,我们可以了解到:如何在LayaAir引擎中获取3中灯光数据(平行光,点光源,聚光灯),如何去计算经典光照模型(环境光,漫反射光,镜面反射高光)。在我们的自定义shader开发中,只需要去获取对应的光源,就可以调用 Lighting.glsl 库中的函数去计算对应光源的光照结果。

LayaAirBlinnPhongDiectionLight:平行光 漫反射和镜面高光计算函数 LayaAirBlinnPhongPointLight:点光源 漫反射和镜面高光计算函数 LayaAirBlinnPhongSpo:聚光灯 漫反射和镜面高光计算函数

平行光的代码:

点光源的代码:

聚光灯的代码:

END

作者简介

本篇文章的作者唐涛,LayaAir引擎社区的一名游戏开发者,从事了12年游戏前端开发的游戏爱好者。他常说的一句话是:代码世界是我的乐园,shader编程是乐园中最美的花朵!

唐涛不仅投稿了本篇技术分享的内容,还投稿了基于LayaAir引擎的视频课程《webGL Shader从入门到精通》。该课通过99个视频课程,结合LayaAir 2.x引擎,从基础原理入门开始到大量的自定义shader效果实战。帮助开发者更好的学习和使用shader,开发出更丰富炫酷的3D效果!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-06-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Layabox 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
图像处理
图像处理基于腾讯云深度学习等人工智能技术,提供综合性的图像优化处理服务,包括图像质量评估、图像清晰度增强、图像智能裁剪等。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档