查看原文
其他

Demo 开源丨Cocos Creator 快速实现 2D 动态光照

好巧啊c COCOS 2022-06-10

脑海中有个炫酷的效果,却不知道怎么实现?茫茫互联网竟然找不到自己需要的 Cocos Creator 教程?


其实,许多热心开发者、Cocos 引擎官方技术团队,一直在论坛、GitHub、公众号等地方持续输出着自己对 Cocos Creator 的探索成果。Cocos 布道师 好巧啊c & 哲锋 收集了部分优质开源 Demo 工程,将陆续同大家分享一些 Cocos Creator 3.x 游戏常用效果技术实现方案


今天先从 2D 动态光照说起,下一章再来讲讲「阴影」


「八方旅人」光照效果,图源网络


在游戏里面,良好的光照是烘托气氛的绝佳方式。以前的 2D 游戏大部分是不带任何动态光照的,而随着图形学和硬件的进步,现在,越来越多的 2D 游戏也开始采用动态光照来丰富游戏的表现,比如大名鼎鼎的《八方旅人》、《泰拉瑞亚》等等。


恰好最近 Cocos Creator 的技术支持团队发布了基于 Coocos Creator 3.3.2 的 2D 动态光照 Demo,今天我们就基于这个 Demo,一起看看如何为自己 2D 游戏添加动态光照。


2D 动态光照的 Demo 及资源贴见文末,请往下拉↓


法线贴图


首先看看 Demo 的预览效果。可以看到,当我们在 Demo 中移动光源的位置,小人和场景也会随之表现出不同光照的情景(案例素材源于CODEWEB)



那么它和普通的 Sprite 有什么区别呢?


通过对比不难看出它们的差别主要是集中在材质上面。小人使用了自定义的 mat_normal 材质。



在 Cocos Creator 里,我们可以通过不同的材质来定义物体渲染的样式。


而对于带光照的 Sprite,他多出了一个叫做 USE_2D_NORMALUSE_2D_LIGHT 的选项,同时多了一张叫做 normal 的法线贴图



仅仅只增加这么一张小小的法线就能实现光照效果,这就是图形学的神奇之处了。


那么该如何制作法线贴图呢?


  • 如果你的项目是 3D 转 2D,法线贴图的制作也不是太困难的事情,只要让项目组的美术大佬在导出图片的同时导出对应的法线贴图就可以;

  • 但如果你的项目是手绘的,想要光照效果就比较麻烦了,毕竟你需要手绘法线贴图;

  • 当然现在也有可以通过图片生成法线的算法[2],只不过效果毕竟没有手调的好。要好的效果的话,还是需要手调法线。


准备好了自己的法线贴图,就可以去启用 2D 光照了。


添加动态光照


第一步,为 Sprite 创建一个新的材质。



第二步,在材质中选择 light/eff 作为材质的 Effect。



第三步,勾选 USE_2D_NORMALUSE_2D_LIGHT 并将准备好的法线贴图赋予给 Light Normal 这个属性。



最后我们需要创建一个光源。3D 的光源是不行的,我们需要创建的是一个 Demo 中自带的光源组件 light/Light.ts



到这里我们已经成功添加了动态光照,接下来简单了解一下光照的实现原理,这能帮助我们更好理解光照效果的实现。


实现原理


在物理世界中,我们看到的物体的颜色,其实是物体本身反射光线的颜色,因为物体的材质不同,会吸收部分不同的颜色分量而导致我们看到的物体颜色不同。


模拟光照的过程实际上就是模拟整个光的传播过程。



I 为入射光线  

R 为反射光线  

n 为法线

v 为物体表面某个位置到视点,也就是摄像机的向量


一般来说我们可以将光照简单的分为以下三类:


  1. 漫反射(Diffuse)。漫反射描述了粗糙表面、无光泽表面对光的反射。常见的模型为 Lamber模型,它描述了入射光在射入粗糙物体表面以后所产生的反射物理现象。其反射光的方向由入射光和法线的夹角决定,当夹角越大,损失的能量就越大,看到的物体就越暗。同时离光源越远,能量损失也越大。

  2. 镜面反射(Specular)。镜面反射描述了光线在光滑表面对光的反射。

  3. 环境光(Ambient)。环境光表示光线在场景内进行复杂的传播以后,形成的弥漫整个空间的光线。


最终的光线颜色计算满足下面的公式:



I 为最终的光颜色

Id 为漫反射光

Is 为镜面反射光

Ia 为环境光


在图形学的光照模型中,几乎大部分的光照算法都来源于对上述公式的扩展。


Shader 解析


让我们回到 Demo 中看看光照是怎么实现的。


针对漫反射,我们需要定义光源的位置,因此在 light/Light.ts 脚本将光源的位置传递给着色器:

    spr.getMaterial(0).setProperty('light_worldpos', lightPos);


其中 lightPos 是计算了光源节点 light 在世界坐标位置。


在着色器内,通过计算位置和光源的距离,对光线的能量衰减进行计算。



之后使用光源到位置点的矢量和法线的夹角计算反射光的强度。

 // 计算光照反射系数,向量点积
 float normalDot = max(0.0, dot(normal, -normalize(vec3(object_direction.x, object_direction.y, -60))));

 // 反射光 * 法向量衰减 + 环境光      
 return col * (diffuse * light_brightness * normalDot + vec3(light_ambientColor));
 


通常环境光不会有大的变化,因此使用了常量来代替,代码中使用 light_ambientColor 来描述。



最后将物体本身的颜色 col 和光线的颜色进行相乘得到该片元的颜色。



在 Demo 中我们可以看到通过计算入射光和物体法线的夹角来计算光照明亮变化的过程。


而这里的法线则来自从法线贴图内采样的值,这也就是为什么在实现 2D 光照时,我们需要提供法线贴图。毕竟对于普通的 Sprite 来说,它本身是不含有任何法线信息的。

 // 获取法向量
    vec3 normal = texture(light_normal, uv0).rgb;
 normal = normal * 2.0 - 1.0;


资源链接


点击文末【阅读原文】下载 Demo

https://github.com/cocos-creator/CococsCreator-public-technology-solutions.git


论坛地址

https://forum.cocos.org/t/topic/124637


参考文章

2D 中的法线贴图

https://zhuanlan.zhihu.com/p/111211259

基础法线贴图生成算法

https://zhuanlan.zhihu.com/p/111419293


往期精彩

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存