体探针漏光解决方案

2022-4-9 20:30蓝琦儿 996 0

问题描述

在全局照明领域,体探针插值漏光是存在多年的顽疾.比如 games202 里LPV(Light Propagation Volumes),unity的LPPV(light probe proxy volume),GDC上各种GI分享(某版cod) 还有之前我给项目实现的类似ue VLM的功能.都存在这个问题.和很多技术人员一样,一看到行业困难的技术点,就会自己动脑想一套更好的方案来解决他,因为这样可以名正言顺的造轮子.俺也一样,唯一不同的是我会想出3套^_^.

基础思路

根本的原因当然是探针的密度不肯能无限密,而切分空间的表面可以没有厚度.而一般的探针信息里 并没有描述空间划分的逻辑,所以上图中,只有红色探针往左和绿色探针往右2个区域是正确的,中间的插值就出错了.所以 所有的思路都是去描述空间的划分.或表面上一个像素与体素的归属关系修复上.就是让A点只采样红色探针,B点只采样绿色探针.因为他们的空间归属如此.

方案1:bent normal偏移

我没有把简单normal偏移作为一种方案,因为这种太基础了,unity杭州大会上 分享过.而我在写初版vlm的时候就顺手写了出来基本是非常容易想到的,而且效果也不够好.看下图,AB有个简单的区分方式,就是他们法线不同,沿着各自法线方向去取探针,那么会得到正确的结果.也让他们跳出了中间的错误插值地段. 但是,墙面不是总是简单一个方向的,有些横梁等结构会像黄色一样,法线偏移后依然是错误的探针,因为 AB2个面 这种结构法线完全一样无法区分彼此,还有更严重的问题是,当角色从A面靠近墙面的时候,靠墙的这部分角色法线是朝右边的,所以导致 A处的角色显示绿色的GI.

所以,我想到了用更大尺度的法线来描述总体法线朝向(或可见度方向).首先想到是屏幕空间normal的mipmap,但是 屏幕空间有相机外丢失信息的天然弊端,所以后面选择了,离线制作场景的bentnormal ,正确做法应该是对应lightmap 用uv2 解析.但为了演示方便我写到了顶点上.

测试下效果,用2个非垂直(垂直问题小很多)交叉,隔开的4个空间 分布打3个不同眼色的灯,不打灯的空间为黑色换角色,然后每0.5米一个探针 均匀放置探针.并创建出3dTexture .可以看到 normal 偏移 在非交叉处是对的 偏移方向就刚好是法线方向.

bentnormal算法

bentnormal的离线计算是比较常见的技术,比较简单就自己想了实现.原理就是 对某个点,法线所在的半球方向随机发出很多射线,没有发生碰撞的那些方向全部加起来,然后归一化.很像初中物理里求一堆力的合力,简单相加符合的力就可以.但是这种简单的计算依然会出现小的漏光,加强版里解决他.


这个图里 黑色的mesh与绿色mesh相交,那么 露出的顶点 显示的红色bentnormal方向是对的,但是最右边的顶点,因为在物体内部 所以他没有计算出bentnormal,这样导致,渲染的黄色部分,bentnormal是 左右2个顶点插值的结果,这样就导致黄色的bentnormal 偏移角度不够,依然会插值到 绿色框内部或右面的探针,产生漏光.

具体的改善是 不要原点发射方向,而是沿着射线方向偏移一点再发射.如下图,再考虑射线的双向碰撞检测,就可以抛弃蓝色碰撞部分,获得绿色部分的合成,这样得到顶点bentnormal(黄色),会更符合环境描述,导致红黄2个插值的结构也更正确.

该方法最终效果,关于动态对象,不能取这份数据,需要逐对象分帧每帧发射一条射线,累积附近的偏移方向判断(实时bentnormal计算)

bentnormal 方向偏移方案效果

方案2:DistanceProbe

同样是空间划分的描述,我们可以记录下碰撞体距离,根据是否大于距离判断是否取对面,总之在这种过渡位置不插值,要么取x处 要么取x+1处探针.取哪个根据shadingpoint 与probe的距离 是否大于提前记录的probe到障碍物距离来定.

下图所示,绿色圈表示 当前shadingpoint位置. 2个红圈表示 用来插值的2个probe颜色.为了直观只演示 x方向上的判断.

1.左边红圈处 没有记录往右存在1米内的碰撞 所以直接取x与x+1 ,2个红圈位置的probe颜色来插值.

2.右边存在碰撞,但与shadingpoint 距离小于 与碰撞体距离,那么就直接取x处probe颜色为 shadingpoint颜色.

3.X处 右边存在碰撞, 与shadingpoint 距离大于 与碰撞体距离,那么就直接取X+1 处probe颜色为 shadingpoint颜色.

所以只要把 偏移数据 再创建一个texture3D 来存储,设置为point filter.即可实现功能.

看下效果(每米增加到8个probe的效果).

记录距离到probe的 偏移采样

这种方式有2个问题.1 需要较高的摆放密度,这是因为 同一个空间内比如 方案1里的 (0.5x0.5x0.5)内,只记录一个 到x+方向 的float距离,(y+ z+同理都是一个float),但是一个空间内不同位置 这个距离应该是不同的,所以需要尽量让这个单位空间去小的范围.2 这种距离固定是只出现在 碰撞面刚好与xyz坐标轴垂直的情况下.如果 表面不是轴对齐 那么 一个小空间内也不能用一个float描述距离,这时候需要记录 平面方程来 判断空间划分 而不是一个 float的distance,存储容量x4,而实际工程中 一定是需要支持非轴对齐的情况的,除非无限细分空间.

关于动态对象,这个算这个方案优势之处,因为存储的是空间信息所以 任何shadingpoint都能根据worldposition 直接获取偏移量 得到正确数据

方案3:Decal Offset

前面2种方案 都需要受到 体素/空间 划分密度影响,这是因为他们都是均匀摆放,导致一些位置受限.所以还有一种更直观的想法,就是我可以把某个不规则空间内 应该往哪个方向偏移 记录起来,等采样的时候访问.这种记录可以在任意位置(不需要均匀间隔),任意形状大小(为了方便用cube形状 但支持任何大小). 至于如何区分哪些是框住的部分,那么只要类似延迟贴花,算对象空间内坐标是否<0.5即可(cube中心 对象空间内 xyz任何一方>0.5都不在cube 包含范围内).

从顶视图看 我们为 其中一个空间 摆放 3个偏移cube,预计算每个cube中心的 非遮挡范围的中心方向(可见度cone的轴线方向)为箭头所示,并把这个方向 当作颜色 为3个cube 渲染到单独RendererTexture

该方案最终效果

Decal Offset 效果

可以看到,漏光基本修复了,但是 cube之间有接缝,这个需要多摆放一份cube让 偏移方向可以过渡 ,或者 对相邻cube做fade类似 reflectionprobe的 2个fade.

这种方案,因为cube数量多,而材质统一 顶点数少,正式工程,肯定用gpu culling来做,hzb等做剔除后 绘制instance性能很高.对于动态物体因为也是描述空间 而非表面所以 可以直接支持.


3个demo的 源文件工程

jackie2009/Probe_Light_Leak (github.com)


鲜花

握手

雷人

路过

鸡蛋

最新评论

QQ|手机版|小黑屋|九艺游戏动画论坛 ( 津ICP备2022000452号-1 )

GMT+8, 2024-4-20 15:38 , Processed in 0.067015 second(s), 18 queries .

Powered by Discuz! X3.4  © 2001-2017 Discuz Team.