【Siggraph 2017】Crest: Novel ocean rendering techniques in an open source framework

今天分享的是开源水体渲染方案Crest在Siggraph 2017上分享的技术实现细节。

先对相关要点做个总结:

相关细节给出如下。

Page 1

主讲人Huw Bowles之前分享过基于polar mesh的projected grid。

Page 2

水体渲染方案有如下几个挑战点:

  1. 实现水波上的相关细节
  2. 水波的运动要足够真实
  3. 支持与跟水体接触的物件的交互
Page 3

水体的渲染跟之前总结的一样,分为波形模拟(shape)、Mesh组织以及Shading三部分,其中前面两部分是彼此关联的,不应该分开介绍。

下面先来看下mesh数据如何组织。

Page 4

这里是对mesh的目标:

  1. 顶点数(面数)越少越好
  2. 支持LOD
  3. 顶点数据是稳定的,不会因为视角、LOD的切换而跳变(看来是projected grid留下的伤痕太深了)
Page 5

这里采用的策略是Clipmap方案,多级clipmap之间的衔接可以参考Hoppe的实现

Page 6

McGuire给出了一种实现思路可以不用考虑多级Clip之间的衔接(从而可以一个DP绘制完成),这种思路就是在相邻两级clipmap之间保留一定的overlap,比如将更精细的一级往外扩展一部分,同时保证多出来的顶点的高度跟相邻两个顶点插值高度相等即可,具体参考文档

Page 7

这是clipmap的wireframe效果,每一级clip都会被分割成多个tile(从示意图可知,不同clip的tile覆盖范围是不同的,应该有一个倍数关系,看图应该是两倍)

Page 8

这是移动后的debug效果,通过不同颜色标注不同的mesh lod。这里用了McGuire的Mesh Overlap策略,不过可以看到,会存在深度竞争问题(看来魔法并没有那么完美,解决了裂缝问题,会引入深度竞争穿插问题)。

Page 9

因为深度竞争问题,因此这里并不想继续采用McGuire的overlap策略,而是希望采用类似CDLOD的连续形变思路,不过mesh还是Clipmap方案。

Page 10

精度较高的一级,会随着到相机的距离的增加,而不断加大顶点merge的力度,直到跟粗粒度的mesh接壤的时候,达到完全匹配的状态。

Page 11

通过这种方式可以实现顶点精度的连续变化,从而避免移动过程中因为LOD切换导致的顶点跳变问题

Page 12

这里以单个clip来进行解释,通过观察可以发现,其形变策略给出如下:

  1. 只取奇序的顶点进行形变
  2. 奇序顶点队列中,奇数号往左(上),偶数号往右(下)
Page 13

以4x4个顶点为一个section,这里给出了水平方向上各个点的移动说明

Page 14

这个方法看起来似乎可以,但是如果我们把视角拉高就会发现几个问题:

  1. 水体边缘锯齿比较明显
  2. 高视角下可以看到,在低视角下有很多比较密集的区域是被遮挡的,浪费的。
Page 15
Page 16

第一个问题的解决方案是将最外部的顶点往一个更远的地方延伸,从而避免前面说的锯齿问题(这里的前提是距离是足够远的,不能是近岸边缘)

Page 17

针对第二个问题的解法是,根据相机的高度来对mesh的顶点密度进行缩放,随着相机的拉高,逐渐降低mesh的密度,而在相机处于低位的时候,就提高mesh的精度以视角较远时的高精浪费。

如果采用的是平滑的scale方法,会导致顶点的波动,这个会导致水波的跳变,而由于clipmap各级之间的网格密度是2的倍数,因此这里的做法是做成阶跃函数,到达一个阈值时,就将密度按照2的倍率进行缩放,这个能够解决大部分问题,但是对于第一级跟最后一级clipmap,还是会存在问题。

Page 18

第一级LOD采用这种step的切换方法会存在明显的跳变,这里的做法是将第一级的切换从step改成平滑的fade。

最后一级,在视角较低的时候,直接用前面说到的延伸到无穷远的mesh覆盖替代。

Page 19

McGuire的Overlap方案在地形渲染上可能有效,但是在水体渲染上经验证发现是得不偿失的。

Page 20

但是这里发现,如果对tile做精细设计的话,是可以通过9种不同的tile来实现相邻clip之间overlap的平滑的(具体细节这里没有介绍)。

Page 21

为了能够将高精的LOD0覆盖尽可能多的屏幕像素,这里还做了一个trick,即给相机添加一个跟随的sphere,sphere的半径跟相机的高度有关,相机越高,半径越大,从而使得相机拉高了之后,LOD0的覆盖范围(应该是整个mesh的覆盖范围)也随之扩大,从而避免高精mesh的浪费。

Page 22

效果展示。

Page 23

CDLOD是神海4所用的方案,基于距离进行切换,DP数等于LOD数

Page 24

两者用于LOD切换的公式是不同的,CDClipmap用的taxicab距离,因此iosline是方形的,而CDLOD则是圆形的(更好)。

CDLOD需要在每一帧决定每个四叉树node的LOD层级,而CDClipmap则是在初始化的时候设计好了,后面只需要GPU根据参数进行fade即可。

Page 25

CDClipmap还有一个优点,就是mesh的LOD等级覆盖的区域的形状跟FFT的wave texture的形状是一致的(可能也没那么重要)。

Page 26

接下来看看波形。

Page 27

有了mesh之后,接下来需要介绍mesh上的顶点该如何在运行时驱动。

简单来说,就是有一张displacement贴图,如上图所示,有三个通道,分别代表顶点在XYZ三个方向上的偏移。

那么这张displacement贴图是怎么得到的呢?

Page 28

这张贴图是在运行时生成的,会将需要模拟的区域,分割成一个个的tile,每个tile执行一遍shape shader,计算结果写入到shape对应的贴图的tile中。《深海4》用的也是类似思路(将粒子的形变数据写入到offset贴图中)

Page 29

每一级LOD都会对应于一张displacement map,绘制的时候,相机采用的是正交投影,正好覆盖前面clipmap的范围,对于两级之间的缝合区域,会在shader中对两张贴图进行采样并混合。

这里没有采用mipmap,因为要生成覆盖各个区域的mip0成本比较高。

Page 30

这里提供了一个设置:每个wave绘制的最少顶点数(左上角的滑动条)。通过这个配置可以让美术同学很直观的知道缩小面数带来的效果差异,可以更好的在兼顾表现的前提下优化性能。

Page 31

Shape Shader是啥,其实所有能够生成displacement贴图的shader都可以,比如左边列举的这些波形计算算法。

实际使用中可能是其中的一种或者多种的组合,接下来会对其中的实现细节做一下展开。

Page 32

FFT直接看这个链接即可。

Page 33

Gerstner Wave是一种用多种正弦波叠加来模拟水波的方法,这种方法是可以通过过滤高频信号来解耦视觉效果跟物理模拟(gameplay数据)的。

Page 34

这里介绍了Wave Particle的应用细节,采用了一个叫做cheapstep的简化函数来代替smoothstep(指令数从三变成二)的方法

Page 35

波形模拟走离线,还是运行时?

Page 36

运行时模拟的一种常用简化手段,是将3D的水体模拟精简为2D的。

其中:

  1. 2D Wave Equation可以用低成本提供一种波形的感觉,但是距离物理正确差的有点远。
  2. 浅水方程(SWE)的模拟效果更为物理,当然,消耗也更高

模拟的一个问题是,我们是应该在LOD0上模拟,还是应该在所有的LOD上做模拟?

Page 37

这里给出了一个波形实现的效果,方案为给水体表面添加一个力,使之产生一个能够匹配移动效果的波形,之后将这个波形叠加到水体的原始波形上,形成一个受移动物体影响的波形效果。

Page 38

另外一个演示视频,方案同上

Page 39

如果是一个带有动画的角色或者物体,其产生的水波影响则可以通过在maya等建模软件中提前设计而得到

Page 40

这里介绍了另一个用类似思路来实现breaking wave的视频(方案),不过breaking wave不仅仅需要驱动的数据,还需要保证wave处的顶点密度要足够高(对应于这里的方案,在需要播剧情的时候,可以考虑将波形的中心放到角色身上)。

Page 41

任何可以通过数学公式表达的形状,都可以通过波形方案模拟出来。

一般来说,添加一些噪声效果会更好。

Page 42

这里就实现了一个角色避水的效果。

Page 43

这里介绍了水珠脱离水面的一些效果实现方案,通过特效来实现,会继承水面的速度并稍作减损(能量守恒)

Page 44

一些高频的信号,通过波形不一定能表达(顶点密度不足),这时候就要借助于表面法线来实现。

法线的采样如果不做缩放, 那就可能出现上图的的tiling与远处的高频闪烁噪声。说到底就是缺了normal mipmap的逻辑,也就是随着距离的拉远,法线应该采样一些低频的信号来避免重复与噪声。

Page 45

最后来看下着色部分,包括foam跟depth peeling。

Page 46

foam效果

Page 47

foam的计算逻辑还是一如既往的,基于displacement贴图的雅克比矩阵的行列式值来定,当小于1的时候,表明水面是压缩的,需要生成foam。

Page 48

为了保持foam的稳定,需要存储上一帧的foam效果并与当前帧做叠加,当然,叠加之前需要对上一帧的数据做衰减,这里可以通过ping-pong buffer的方式来完成数据的存储与复用。

Page 49

foam的效果会通过前面计算的数值从贴图采样得到,采样时需要对UV做求余计算。

需要注意的是,foam有水上(白色)跟水下(淡蓝色)两种。

(会跟随foam的强度来动态调整black-point的fade数值?)

Page 50

没有foam

Page 51

水上foam

Page 52

水下foam

Page 53

两种foam(效果在开源的unity项目中可以看到)

Page 54

Depth Peeling是一种OIT(order independent transparency)方案,基于这种方案可以实现复杂的光照路径:光线在水体-空气中经历多次穿梭。

Page 55

关闭时的效果是不正确的,水体背后的体积光散射效果都是错的

Page 56

这是因为水体渲染的时候,并不能感知到背后的数据。正确的效果如上图所示。

Page 57

这里大概介绍了depth peeling的实现思路,即按照从后往前的方式进行多次渲染,保留背后的光照数据,之后绘制最上层,将前面layer的数据叠加并应用起来,得到相对正确的效果。

Depth Peeling是一种经典的OIT方案,可以有效解决半透物体穿插导致的效果异常,不过因为需要多个pass,所以性能较差

Page 58

相机跟水面的相对位置如上图所示

Page 59

其中红色部分是最前面的表面。

Page 60

也就是说,这部分表面对应的水体并不会覆盖相机

Page 61

接着将depth pass设置为greater,意味着背后的surface会被绘制(橙色)

不会将第一段橙色背后的蓝色数据保留下来吗?Depth Peeling算法会通过一个额外的Depth RT存储上一帧的depth数据,之后通过shader采样这个depth比对,当小于(更远)即存储,否则就丢弃,同时借助硬件自带的depth test,保证得到的用于是后续多层中最接近相机的一层。

这里会通过stencil标记出哪些是需要peel depth的区域

Page 62

基于前后两次的绘制,计算中间的光照scatterring

Page 63
Page 64
Page 65

接着继续peeling,直到完成

Page 66

完成后,得到多层的数据。

Page 67

接着绘制最前面一层,叠加此前的数据,完成折射、光照散射以及焦散等效果。

Page 68

左边只有front layer,右边则是depth peeling效果

Page 69

性能跟层数有关,上面给出了具体的数据,虽然在运行时不能多用,不过或许可以考虑用在一些特殊场景(比如剧情?)

Page 70

使用到了上述RT

Page 71

多层效果叠加的视频

Page 72

最后对方案做个总结

Page 73

这里给出了最终的性能数据(没有应用场景验证,没有美术同学调校,不敢说质量一流),还有一些优化点没有做

Page 74

这里做个总结,下面是附录。

Page 76
Page 77

首先是对Projected Grid的介绍。

Page 78
Page 79

河流等局部水体可以通过对海洋水体的剔除来实现,常用的mesh表达方案是CDLOD(神海4)

Page 80
Page 81
Page 82
Page 83
Page 84
Page 85

Depth Peeling细节,前面已经介绍过了

Page 94
Page 95
Page 96
Page 97
Page 98
Page 99
Page 100
Page 101
Page 102
Page 103
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容