Cocos Creator 3.6 渲染新特性详解:我们如何获得更高「颜值」?

上一篇文章,麒麟子和各位一起体验了 Cocos Creator 3.6 版本中那些令人激动的编辑器新特性。不管从编辑器外观,还是从工作效率上都带来了不错的提升。

相信有不少朋友和麒麟子一样,每次的版本更新,都会分外关注引擎渲染相关的特性,有没有新增渲染技术,有没有画质方面的提升。今天我们细数一下 3.6 版本中那些与渲染特性相关的内容。

150d345749511f1dbf531b20e0de232c.jpeg

3.6 中较为硬核的新渲染特性:

  • 环境反射卷积(Reflection Convolution)

  • 级联阴影(CSM, Cascaded Shadow Maps)

  • 粒子噪声模块(Noise Module)

  • 动态模型(Dynamic Mesh)

  • 各向异性光照(Anisotropic Lighting)

  • 表面着色器(Surface Shader)

如果朋友们在之前的版本中遇上过:

  • 粗糙表面油油的?

  • 阴影效果糊糊的?

  • 粒子轨迹傻傻的?

不用担心,这些问题在 3.6 中都将不复存在。

如果朋友们在之前的版本中问过:

  • 如何使用代码控制模型顶点运动?

  • 如何制作出如丝般的材质效果?

  • 如何更简单的自定义 Shader?

不用着急,这些问题在 3.6 中都有有解决方案。

铺垫了这么多,就是想说:还不赶紧到碗里来!

GGX 环境反射卷积图

这是麒麟子一直想要的特性,它可以极大的提升非光滑表面的真实感,请看下图:

55d6799fc779897e309a98488947c94f.png

从上面的对比图中,我们可以明显感知到有卷积(GGX Convolution)和无卷积(Mipmaps)的效果差异。

那什么是 GGX 环境反射卷积图呢?

它是指采用 GGX 算法,使用卷积方式生成的用于环境反射计算的 Cubemap 贴图。

这东西性能如何?是我这800块的手机能跑的吗?

由于卷积图是预生成的,朋友们完全不用担心性能问题,在同样需要环境反射的情况下,使用卷积图和使用 Mipmap 图性能是一致的,只需在编辑器中烘焙好即可。

既然它这么棒,我们如何在 Cocos Creator 中生成和使用 GGX 环境反射卷积图呢?

大家请看下图:

c400cab0f3d61112c606b159a7e2ce45.png

这是 Cocos Creator 3.6 中最新的 Skybox 面板,我们重点关注:

  • 环境光照类型(Env Lighting Type)

  • 环境反射卷积图(Reflection Convolution)烘焙(Bake)

麒麟小贴士:PBR 材质在处理环境光照时,会将环境贴图视作一个巨大的环绕光源,贴图上的每一个像素都各自代表了一个方向上的光亮度。这个处理方式被称为:IBL(Image Based Lighting),而 IBL 中主要由漫反射和环境反射两个部分的计算构成。

环境光照类型

引擎环境光照提供了三种组合选择:

  • HEMISPHERE_DIFFUSE(半球光照)

  • AUTOGEN_HEMISPHERE_DIFFUSE_WITH_REFLECTION(自动计算半球光照漫反射+环境反射)

  • DIFFUSEMAP_WITH_REFLECTION(环境辐照度卷积+环境反射)

选择 XXX_WITH_REFLECTION 表示我们希望环境反射,反之则没有环境反射。如果把同样的测试场景切换到 HEMISPHERE_DIFFUSE,我们只能得到下面这样的效果:

a6a1df5bf3edfd7b54cfb1e78c6f99de.jpeg

HEMISPHERE 与 AUTOGEN_HEMISPHERE 的区别是,AUTOGEN_HEMIPSHERE 会根据环境贴图自动计算天光和地光。

环境反射卷积图烘焙

环境反射卷积图后面的烘焙(Bake)按钮,用于生成环境反射卷积图,当烘焙成功,IBL 运算会采用生成好的环境反射卷积图。

如果不想使用环境反射卷积图,可点击移除按钮,IBL 运算会采用环境反射贴图的 Mipmaps 参与运算。

633ec8ab20af98271feeffc0c3787f9f.png

那么,什么是环境反射卷积呢?

对于想要了解它背后原理的朋友,我们先来看看两个名词解释:

  • GGX:一种微表面分布函数,用 GGX 算法渲染的材质,过渡会更加自然,分布曲线更加拟合真实物体,最主要的是可以还原一部分真实 BRDF 的高光拖尾,对比效果如下图所示:

125975615a09536fd737254e5d491676.png

  • 环境反射卷积图:通过适合的算法,预先生成对应粗糙度的反射图并存到 Cubemap 的各个 Mipmap 等级中。可以明显看到,Cubemap 中的 Mipmaps 每一级都比上一级更模糊,这是普通的 Mipmap 生成算法无法达到的效果。如下图所示:

2afbe976edf192d4f7c015e0a21019db.png

d6080f585b3f966c9c4c4aaa3954c6bd.png

由于本文重点关注它能带来什么效果,因此不会解释是什么 GGX,什么是卷积。有兴趣了解这方面的朋友们可以自行搜索,或者私信交流。

高质量动态阴影(CSM)

Cocos Creator 从 3.0 版本开始就支持了 ShadowMap,这是一个在实时图形引擎中被广泛采用的阴影技术。

但开发者们在使用的时候,经常遇到下面两个问题困惑:

  • 如果 Shadow Distance 设小了,离摄像机远一点的地方就没了阴影,如下图所示:

436aa4b9ad27f7d787ce6be0567ce6a1.png

  • 如果 Shadow Distance 设大了,阴影又会特别模糊,如下图所示:

03a99176f5039faa2baf75b1bc2a9523.png

以上问题,开启 CSM 就好啦,不管从远处看,还是从近处看,效果都妥妥的,如下图所示:

7246dac5feea941a638f06e2596fed9b.png

那什么是 CSM 呢?

CSM 是 Cascaded Shadow Maps 的缩写,中文一般叫此术语翻译为:级联阴影。

为什么叫级联呢,请看下图:

1d92ff1d5e4120713dfef11d8fc9120b.gif

素材来源于网络

CSM 中,将视锥体内的场景从近到远分为了四个层级(在 Cocos Creator 3.6中,默认的 CSM 层级为 4 层),每一个层级对应一张 ShadowMap,在阴影采样阶段再根据物体位置计算出使用哪一张 ShadowMap 最适合。

这样的操作,使得近处的 ShadowMap 覆盖较少的内容,从而确保近处的阴影清晰,而越远的 ShadowMap 则需要覆盖较多的内容,从而确保较远处的阴影可见。

CSM 的 DrawCall 开销如何呢?

虽然 4 层 CSM 需要绘制四次场景,但由于引擎的裁剪机制和阴影 Pass 绘制时的优化,额外的 DrawCall 开销不超过普通 ShadowMap 的 40%。

CSM 的内存开销如何呢? 

在 Cocos Creator 的场景面板中,ShadowMap Size有四个选项:

  • Low_256x256

  • Medium_512x512

  • High_1024x1024

  • Ultra_2048x2048

CSM 4 级共享一张 ShadowMap,且每一级的开销占ShadowMap Size的1/4,比如选择了 Ultra_2048x2048,则每一级的 ShadowMap 占 1024x1024 大小。

简单说来就是,开启 CSM 和不开 CSM 的 ShadowMap 内存开销是一样的。

除此之外,在上一篇关于编辑器的介绍中,我们提到了渲染调试功能,通过此功能,可以查看 CSM 各级所占的比例,如下图所示:

5d82eb0886c7cf31fc963abe3ec4ce58.png

这个功能可以使我们在调节 Shadow Distance 和 Shadow Invisble Occlusion Range 的时候,直观地看到各级变化,根据需求精确定位各级位置。

CSM 四级太多了,可以少些吗?

目前只开放了4级,后续规划中,可能会开放层级设置和层级的 lambda 分布设置,从而实现更精准的层级分布和性能节省。

粒子噪声模块

(Noise Module)

Cocos Creator 3.6 为粒子系统新增了噪声模块(Noise Module),可以很方便地制作出一些粒子随机飘荡的效果,如下所示:

bfd032a107d28307ac81cfaa17f44a3d.gif

目前粒子噪声模块有以下参数:

  • Noise Preview:预览目前采样所使用的噪点图

  • Stength X:粒子在X方向的noise强度

  • Stength Y:粒子在Y方向的noise强度

  • Stength Z:粒子Z方向的noise强度

  • Noise Speed X:噪点图在X方向的滚动变化速度

  • Noise Speed Y:噪点图在Y方向的滚动变化速度

  • Noise Speed Z:噪点图在Z方向的滚动变化速度

  • Noise Frequency:生成噪点图所使用的噪点频率,数值越大,噪点越密集

  • Octaves:生成噪点图所使用的算法层数,数值越大变化越多,计算量也越大

  • Octave Multiplier:对新层数的强度乘数

  • Octave Scale:对新层数的频率乘数

那个噪声预览图(Noise Preview)是拿来干什么用的呢?

麒麟子猜到大家会有这个疑问,所以提前问了引擎组相关人员这个问题。由于他不愿意透露自己的身份,所以在这里我们就用安迪来替代。

安迪说,设计这个主要有三个原因:

  1. 目前所有的噪声控制参数会生成到一张 Noise Map中,并传递给粒子系统,所以希望把这个图的样子呈现给开发者。

  2. 有一些参数在拖动的时候,很难察觉,但会在 Noise Map 中表现出来,能够有效感知参数变化。

  3. 有助于粒子系统开发过程中的迭代调试,避免工程师嘴硬。

看这毫无规律、翩翩起舞的蝴蝶,只需要一个粒子发射器就搞定,是不是很棒?

283d52aaa6f57e3a0207406ad867c9ca.gif

除此之外,我们还可以利用噪声模块为粒子效果添加细节,如下所示:

ce9c47f0843e244af14b2cd6591a6ea9.png

3c429886b30c20a8d5d85a1a39d0b81d.gif

动态模型

(Dynamic Mesh)

如果你想实现一条弯弯扭扭,由你掌控的 3D 小蛇;如果你想在 3D 空间中生成根据指示方向动态生成的提示弧线;如果你想发挥你才华横溢的优势,自己写一套 3D 特效、云雾、天气系统……

你最需要的特性,就是动态更新模型数据

代码创建静态模型

在 3.6 版本之前,Cocos Creator 引擎只提供了一个用于创建静态模型的函数:utils.MeshUtils.createMesh 函数。但它每次都会构建模型相关对象,只适合在更新频率不高的情况下使用。

代码创建动态模型

3.6 提供了专门用于处理动态模型的 utils.MeshUtils.createDynamicMesh 函数,如下图所示:

35152f9ca41a66eea5f46e628e5fb2f1.png

三步搞定动态模型

1、options 定义容量

276b88104be6d33f918853006a2b3469.png

动态模型创建时,需要通过 options 参数指定 3 个数据:

  • maxSubMeshes:子网格数量

  • maxSubMeshVertiecs:每一个子网格最大顶点数

  • maxSubMeshIndices:每一个子网格最大索引数

2、调用 createDynamicMesh 创建动态模型

53809e0df5dd4cce4735d5bebf924e26.png

大部分情况下,我们都只有一个 SubMesh,那在创建的时候顺便填充数据就是最方便的。这个函数的原型像这样就够了:createDynamicMesh(geometry,options):Mesh

但设计者觉得这样不够灵活,所以添加了 primitiveIndex。这个参数用于指定 geometry 属于哪个 SubMesh。

注意:记得调用 meshRender.onGeometryChanged 函数。

3、调用 updateSubMesh 更新动态模型

函数原型为:updateSubMesh(primitiveIndex, geometry): void

用途是:用 geometry 的数据更新 primitiveIndex 指定的 SubMesh。

49198104e3cc7e7537260571b195fc5c.png

注意:更新完数据后,别忘了调用 meshRender.onGeometryChanged 函数。

动态模型顶点数量

回顾一下 options 中的两个属性:

  • maxSubMeshVertiecs:每一个子网格最大顶点数。

  • maxSubMeshIndices:每一个子网格最大索引数。

这里之所以叫「最大」,是因为这两个值主要决定了分配的模型数据大小,而决定真正使用多少空间的是 geometry 数据, 只要确保 geometry 数据的顶点和索引数据不超过 options 指定的值,均可正常工作。

对于顶点和索引固定的情况,建议 maxSubMeshVertiecsmaxSubMeshIndices 的值与 geometry 数据保持一致,以避免不必要的内存浪费。

但对于顶点和索引会更改的情况,则需要确保这两个最大值总是能满足 geometry 的需要。

举个简单的例子:假如我们要实现一个由 10x10x10 个方块组成的物体,当用户点中某个方块的时候,方块会消失。

我们创建一个只有一个 SubMesh 的动态模型,maxSubMeshVertiecs 为 8000,maxSubMeshIndices 为 36000。当点中的方块消失时,我们只需要移除 geometry 中对应的顶点数据,并调用 updateSubMesh 刷新模型即可。

类似的情况还有自制粒子、植被系统、自定义特效等场合。

官方示例

引擎组提供了一个龙的示例,点击 Update 按钮,龙会从无到有显示出来,如下所示。有需要研究和学习的朋友,可以查看引擎官方 Github 仓库。

9898acad8d97472d78a07ee100f9bce6.gif

Github 地址:

https://github.com/cocos/cocos-test-projects/tree/v3.6

场景:

assets/cases/dynamic-mesh

各向异性光照

(Anisotropic Lighting)

在自然界中,有一些物体的表面,有大量细小齐整的沟槽。普通的图形学光照模型无法很好的重现这类物体表面的光照效果,于是产生了专门模拟此类光照效果的技术:各向异性光照。

7cfc5884e521b571f7b5d654f92fe608.png

虽然实现原理很复杂,但在 Cocos Creator 3.6 中使用各向异性光照只需下面三步:

  • 在 Cocos Creator 中新建一个材质。

  • 将材质的 Effect 切换为 surfaces/standard

  • 开启 IS ANISOTROPY 宏。

fae4ad1e605fe26934d10a95ef74171c.png

相关的参数解释:

  • USE ANISOTROPY ROTATION MAP:是否启用各向异性旋转图。

  • Anisotropy Shape:各向异性形状, 0为完全同性,1为完全异性。这个参数也可以理解为强度(Intensity),但由于它会决定此效果的最终形状,所以用了 Shape 这个词。。

  • Anisotropy Rotation:控制各向异性的条纹方向。

  • Anisotropy Rotation Map:各向异性旋转图,用于精确控制条纹方向,仅在USE ANISOTROPY ROTATION MAP 宏开启时有效。

Cocos Creator 3.6 中渲染的效果如下:

4f7a1a7bc5e546a717a0636b99e309d2.png

表面着色器

(Surface Shader)

什么是 Surface Shader?

Surface Shader 并不是一个新的 Shader 品种,它是引擎为了方便 Shader 编写提供的流程和框架。它对底层的 Vertex/Fragment Shader 做了封装,省去了一些重复代码编写的工作量,从而降低了 Shader 编写复杂度,提升 Shader 编写效率和兼容性。

为什么需要 Surface Shader?

随着 Cocos Creator 的 3D 特性越来越好,工作流越来越完善。越来越多的 3D 开发者,美术,TA,图形学爱好者加入到了 Cocos 生态行列。

社区 KOL 们,以及 Cocos 官方布道师们也输出了不少关于 Cocos 3D 编程,Cocos Shader 学习的教程。

关于这方面的问题,麒麟子收到的反馈集中在以下几个方面:

  • Cocos Effect 内容太多了,新手看着就打退堂鼓。

  • 有一些 PBR 运算相关参数和过程暴露得不够多,需要去改 trunks 里的内置 Shader 文件才能实现想要的效果。

  • surf 函数里暴露了太多不需要改动的内容,比如法线、roughness、metallic 等基础运算。

  • 没有模块化,实现功能更像是在默认的Shader上打补丁,导致新版本兼容和跨项目复用困难。

Surface Shader 流程的出现,极大的改善了以上情况,使自定义 Shader 更便捷。由于写法上更加简洁,使得Shader编写更容易上手,兼容性更高。

到底有多简洁?

如下图所示,一个完整的 standard-fs 满打满算 10 行。

02f162acd2b05e4201b5fb394dd5f03d.png

一个完整的 Vertex Shader 或者 Fragment Shader 需要包含宏定义、公共文件、UBO定义、光照模型、Shader Stage、输出目标六个部分。开发者可以根据需要,分别对这六个部分做自定义,不需要自定义的部分保持默认即可。

如何自定义 Surface Shader?

官方文档中,有完整的如何编写 Surface Shader 的步骤,其中最重要的一个就是宏定义机制。

Internal/chunks/surfaces/default-functions 目录下有 common-vsstandard-fstoon-fs 三个文件,其中定义了所有可以用宏替换的内置函数。如下图所示:

5d32c0f316ee8712a406761bb050850a.png

#ifndef CC_SURFACES_XXXX 的意思是指,假如未定义 CC_SURFACES_XXXX,则使用下面的函数。

以较为简单的边缘光(RimLight)为例,只需要在自己的 Surface Shader 的 CCProgram surface-fragment %{ ... }% 片段中,定义 #define CC_SURFACES_FRAGMENT_MODIFY_BASECOLOR_AND_TRANSPARENCY 宏,并实现 vec4 SurfacesFragmentModifyBaseColorAndTransparency 函数即可。

以上就是关于 Surface Shader 编写中,麒麟子认为最核心的机制部分。

由于篇幅有限,其余内容就不展开细说,大家可结合官方文档和 surfaces/standard.effect 研究。

小结一下

此次的渲染相关的更新主要集中在环境反射卷积、级联阴影、粒子噪声模块、动态模型几个部分,当然也有许多渲染相关的提升由于太过细微,就没有在本文中出现,但并不意味着它们不重要。

这些细节部分的价值挖掘就交给朋友们自行解决咯。点击文末【阅读原文】前往官网下载体验 Cocos Creator 3.6,还等什么,行动起来!

下一篇文章,麒麟子将为大家呈现 3.6 版本中,关于性能部分的提升,敬请期待!更多 Cocos 技术分享,欢迎关注我的个人公众号。

往期精彩

5d41de30a29cca5676126846e5a8e17e.png

ad02d13f41f6d6a9f93a4efa2b119333.png

6d7deec6dd89c0aa8c73816fa5e114de.png245603e6e73fe99dc06144e13c0ea356.gif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值