水体渲染

【GDC 2011】Approximating Transluc

2024-04-21  本文已影响0人  离原春草

今天给大家来自于对寒霜引擎在GDC 2011上分享的如何用一种低成本的方式来实现通透的次表面散射(Subsurface Scattering)效果的技术方案,原文链接照例放在文末。

总体内容可以分为几个部分:效果 -> 透明度渲染的知识回顾->技术细节介绍->落地细节介绍

Demo这里贴不了视频,就贴一个链接地址吧:这里说到,在场景中如果能够实现物件的通透感效果,将对于视觉表现甚至玩法带来非常大的帮助。

接下来先来看看图形学中的通透感方案的现状。

首先来给通透感下一个定义,指的是光线能够部分通过物体,且在物件的内部会发生一些内部的diffuse反射的效果。这个效果不但可以应用在如下图所示的雅典娜石雕等无机物上,也可以应用于人体肌肤等有机物上。

正常情况下,场景的渲染主要是依赖于BRDF,这里描述的是光线打在物件表面随即直接反射出去的效果。

但是有些情况下,只看BRDF是不够的,部分物件是通透的,允许光线穿过的,更好的着色模型是BSSRDF(bidirectional subsurface scattering reflection distribution function,双向子面散射反射分布函数),但是这个模型的问题在于计算过于复杂,消耗太高不适合实时。

这里给出的是采用BSDF与BSSRDF的部分效果相结合的实现方案。这里也对这些概念做一个回顾:

  1. BSDF指的是Bidirectional Scattering Distribution Function,即双向散射分布函数,指的是入射光照射到物体表面之后散射出来的部分,包含穿透后的散射部分BTDF与直接反射散射的部分BRDF两项
  2. BTDF指的是Bidirectional Transmittance Distribution Function,即双向透射分布函数,是入射光穿透物体表面后从另一个面散射出来的部分
  3. BRDF则是Bidirectional Reflection Distribution Function,即双向反射分布函数,是入射光照射到物体表面后直接反射出来的部分

关于如何在实时实现SSS效果,我们其实可以找到几个Siggraph上的复杂实现,不过这些实现的时间消耗都还不能真正用在游戏产品中。

能够成功应用到游戏上的方案,这里列举了几个,其中:

  1. [Chang08] & [Hable09] 的方案都是作用在贴图空间上的,基于一个给定的surface(贴图?)来描述光线在皮肤内部的散射过程,其中前者针对的是大理石或其他的半透材质,后者关注的则主要是角色皮肤
  2. [Ki09]以及类似的方案则大多是发源于Carsten Dachsbacher的工作,通过shadowmap的方式,利用depth来获取物件的厚度的方式来实现的

Crysis为植被(树叶)的次表面散射设计了一种简单的实现:基于gradient
mask texture来为不同透明度的树叶设置不同的lighting效果[Sousa08]。

这个方法只能用在树叶这种相对较薄的物件表面,对于厚一点的物体就无能为力了,而本文则尝试在同样的消耗下实现前述的各类物件下的复杂次表面反射效果。

这个是采用本文所述方案得到的效果

作用在人体跟大理石(右图)上的表现

接下来看看技术细节

其实,要想得到令人信服的次表面散射,并不一定需要完全精确或物理,只需要做到两点即可:

  1. 在物件内部传播的光照能够受到物体厚度的影响
  2. diffusion/attenuation能够根据视角、光源的变化而变化

也就是说,我们可以用一个fake的效果来替代高昂的计算,同时,这种方式也有利于将这个方案推广到场景的各个方面。

为了做到前面的两点,这里尝试将如下几点结合起来:

  1. 基于距离衰减的diffuse light
  2. 基于距离衰减的N Dot L

这种计算对于简单物件如cube、sphere可能还行,但是用在复杂物体上,效果就不太够用。要想覆盖更为复杂的场景,还得考虑光线在物件中的传播距离。

虽然通过depth map可以拿到深度数据,但是前面说了,希望采用不用depth map的方案。

这里的做法是仿造Crysis的植被叶片方案,通过一张离线计算的thickness map来给出物件上各个像素点的厚度数据(估算数据,目测是以当前点作为出射点,到各个方向的入射点的距离的平均值)

为了降低人工编辑的成本,这里的做法是将物件身上的法线做翻转,朝外指向朝里,之后计算每个点的AO,这时候AO值就可以表示为这个点接收到的环境光的数值,之后将数值翻转,得到的结果也就某种程度上可以代表此点的厚度(不是太理解这里的AO计算算法,不过可以更直观的采用从某个顶点沿着翻转的法线为上表面,向各个方向发射射线,查找交点,之后求平均来获取厚度)

如果顶点密度够高,可以考虑存顶点色。基于这张贴图,就可以提供一种光线在物体内部传播的通透的感觉。

这张贴图模拟的是从表面逃逸出来的光照强度,也就是说,如果我们能够知道光线打在物体表面之后投射出的颜色,那我们就可以离线计算这张贴图,并在运行时的时候来读取。

上述模型的thickness map如下图所示:

这里给出的解决方案虽然不是物理正确的,但依然能给出非常接近真实的效果:

  1. 通过local thickness来模拟光线在几何体中的传播衰减
    1.1 使用的贴图不是灰度的,而是有颜色的,
    1.2 这里会为direct light跟indirect light指定不同的颜色来给出更真实的表现
  2. 为了得到更为真实的随着视角变化的散射效果,在实际使用的时候还需要基于视角来进行调整(distortion & attenuation)

下面看下实现细节。

这里给出了具体的实现代码,再次强调,并没有非常严谨的数学依据,只是一种面向效果的逼近。

所有的计算代码加起来大概13~15个ALU,可以在SM 2.0上的硬件上跑起来。

整个计算过程中的参数可以分为两类:

  1. 跟光照相关的,在deferred shading的lighting pass中传入
  2. 跟材质相关的,存储在G-Buffer中(当然,也可以根据需要,放在其他地方,看具体的项目)

先来看下Ambient这个参数,这个参数代表的是物体表面上散射出来的环境光照数据,这个数据不随视角、时间、物件跟光源的位置(朝向)而变化。

从截图来看,这个数据是跟随材质而变化的,也是跟随物体表面像素而变化的。

Power是一个与视角有关的参数,用于表述该点的back translucency(光源从物件背后照射,物体被透射后的效果)

有了这个参数,就可以实现效果随着视角而变化了,如下图右下角的效果,其中左边是没有使用该参数的效果,右边则是使用了该参数的效果,可以看到雕像身上的散射效果,随着法线的差异,散射效果的差异就变得明显,从而使得效果更逼近生物身上的SSS效果。

这里顺便一提,因为power计算消耗比较高,如果对这个比较介意的话,可以通过预计算一系列的数值,在运行时的时候直接查表。

distortion参数用于将光照散射的主方向从光照的(反)方向朝着法线方向偏折。

增加了这个参数,可以在散射上增加法线的参与感,使得效果更真实,甚至还能添加一定的fresnel反射效果。

通过提前预计算的厚度来对光照效果进行调制,调制的不只是背面透射的光线,还包括环境散射光照。

最后,这里用一个Scale来控制透射的光强,这个参数是跟光照绑定的,一般可以将这个参数跟光照强度关联起来。

将所有参数合并在一起之后,就得到了如下图所示的效果:

在G-Buffer中,只需要一个8bit的translucency来代表材质的参数,这个参数只需要对启用SSS的材质使用 ,对于其他材质,这个slot还可以用来存储其他数据。

这张图中的所有物件都只需要存储translucency就能得到如图所示的散射效果。

如果对单通道表示的translucency数据的散射效果还是不太满意的话,可以考虑降至扩展到3通道,这三个通道得到是一个颜色值,代表的是该物体内部的材质的颜色。

这里抠出来的三个通道的代价有两点:

  1. 放弃了env map,这是基于如下的一种假设做出的决策:因为带有SSS的物体更关心的是物体内部的光照散射,而非外部的反射,所以可以将该通道换出来供SSS使用。
  2. 压缩了Material ID的长度,这是因为SSS的材质种类相对较少,而蓝色通道的变化不那么明显,因此可以将这个通道一分为二存储两个数据。

这是带颜色的G-Buffer的效果,左边box中的是绿色光源,box的内部translucency(thickness)贴图是红色的;右边的box的translucency则是灰度(白色)

下面给出不同平台上的渲染消耗。

PS3采用了tile rendering,只对有SSS效果的tile进行绘制;PC则采用了CS来计算;对于性能比较差的Wii,则建议不要使用translucency贴图,而是将颜色直接存储在顶点色中。

这个方法在部分模型上会存在问题(原文都给了一些解决思路,不过应该都有副作用):

  1. 非凸多面体
  2. 带有形变、动画的物体

参考

[1]. Approximating Translucency for a Fast, Cheap and Convincing Subsurface Scattering Look

上一篇下一篇

猜你喜欢

热点阅读