从零开始的Unreal Voxel Shader

发表于 讨论求助 2022-03-22 21:19:46

感谢网友Virgil的投稿!本系列的第一篇介绍了Custom Node与UE4 Shader的写法,第二篇将结合Epic大佬Ryan Brucks的博客介绍三维纹理与Raymarching的算法原理。


Chapter 2

三维纹理与raymarching算法基本原理


这一章我们分别介绍一下三维纹理和raymarching算法。


三维纹理(3D texture),是以三维方式存储的一种贴图形式。不过百闻不如一见,首先我们来看一下三维纹理大概张啥样吧。


相比于传统的2D纹理,3D纹理显示在三维空间中,每一像素(体素)和周围的像素(体素)有着更高维度的联系,(生成mipmap的时候当然也是按照三维的方式生成的)可以用与做全局光照或者一些体积上的效果。不过要使用三维纹理一般都需要一些底层的软硬件支持emmmmmmm。。。


由于unreal本身并不支持三维纹理这一特性,所以这一章里我们要讲的三维纹理并不是真正意义上的三维纹理,而是我们通过对二维纹理中的数据
进行某种解释模拟数据在三维贴图中的状态。


那么我们要如何做这种解释来将二维纹理“变成”三维纹理呢?
简单的说,就是
分块和排序。


就像是学校旁边卖饼的大叔制作千层饼,首先我们要制作一张大饼,然后将这张大饼进行切割,切成很多份,再把他们叠在一起摞起来,就变成了我们要吃的千层饼啦~~


这里Ryan巨佬在他的博客里放了两张非常形象的图来帮助我们理解,这里我把链接和图片一并展现给大家。

http://shaderbits.com/blog/authoring-pseudo-volume-textures


相当形象对吧233333333


那么,Raymarching又是个啥呢?类似于我们做离线渲染时用的Raytracing(光线追踪),Raymarching同样也是逆着光路对场景进行追踪计算。不过Raymarching是一种并不精确的Raytracing,而且它经常被用于体积渲染,比如我们常说的体积雾的渲染。


emmmmm我用我的灵魂画图大概画了一下这两种方法。


上面那个是我们常说的光线追踪。从摄像机原点出发,在屏幕的范围内发射多条射线,使射线前进,遇到障碍物时按照光的反射和折射定律该反射反射该折射折射,直到最终遇到光源,计算这个时候应该呈现在屏幕像素点上的颜色和光强。。。


下面那个就是我们今天要讲的raymarching。同样是从摄像机原点出发发射射线,不过这个时候射线穿过的是一个密度非常不均匀半透明物体。(想象一下,我们看天上的云雾的时候的那种效果,有颜色深浅和明暗的变化)这个时候我们要对这个光线进行准确地追踪就不是那么容易了,因为我们不知道光线什么时候会遇到密度足够,值得我们进行叠加计算的物体,也不知道光线路径上物体密度的准确分布。因此我们就干脆将光线的前进步长固定成某一个定值,按照这个定值沿着光路进行步进和采样。比如我画的这个图,就是从原点出发,在和这个半透明的体积相交之后进行了八次固定步长的步进和采样,以某种形式从1-8点逐个累加采样得到的颜色值和亮度值,直到累加结果达到我们想要的亮度或者光线穿出体积。。。


emmmmmm我发现还是用Ryan巨佬的图吧。。毕竟巨佬的图画的还是更加直观一些。


如果有兴趣而且有能力的小伙伴也同样可以直接翻看他的博客中相关的文章,不过英文确实是一个大坎就是了。。。。

http://shaderbits.com/blog/creating-volumetric-ray-marcher


这是根据ryan巨佬的raymarching算法计算最终所能达到的一个体积云效果。


是不是炫酷的不行!

不过要做出这样炫酷的效果涉及到的数学公式和代码量也相当可观233333333


而且呢,我们接下来要做的并不是这样的一个体积云效果,我们接下来要实现的,是类似于minecraft那样一个一个体素方块效果,就像题头图所看到的一样完全的实心方块体。


那么要做一个这样的效果,说简单也简单,说复杂也复杂。说简单是因为这样一个效果的本质原理就是使用我刚才说的raymarching方法从摄像机发出射线进入体积贴图沿路径追钟,按照一定的步长进行采样,一直向前直到采样值>0,跳出,返回颜色值。说复杂就是因为要做这样的一个效果我们要对这个光线追踪步骤进行不断地优化,以找到最快的采样速度和达到最佳的采样效果。

用伪代码可做如下表述:


对于每一个屏幕像素点:

1. 确定摄像机原点和屏幕像素点在世界空间的位置

2. 计算由摄像机原点射向屏幕像素点的射线方向Dir,并将其转换为局部坐标以方便进行采样

3. 确定射线步进的步长X并定义颜色累加量A

4. 初始化重复次数i = 0,对于i < N 时:

    ① 新的采样点位置P = 旧的采样点位置P + 射线方向Dir * 步长X

    ② 新的颜色累加量A = 旧的颜色累加量A + 采样物体内P点的采样颜 色值

    ③ 如果颜色累加量A大于0,即代表已经得到需要返回的颜色,即跳 出循环

    ④ 已重复次数i = i +1

5. 返回颜色累加量A


算法描述大致就是这样,下一篇我们要做的就是对这个算法进行进一步的优化,优化的第一步就是包围盒的处理。


发表
26906人 签到看排名