SDF&RayMarching

这个号荒废了好久,因为疫情在家这些日子继续学了点跟Shader有关的知识,就拿工作号做个记录吧,我会慢慢从最基础的Shader知识开始分享同时也会有一部分数学原理的,如果有错误也希望大家及时更正,这个东西还是非常有意思的。

这几天打交道的东西比较多的就是RayMarching同时也避免不了用SDF建模,可以理解为纯数学硬核建模。以前因为家里电脑配置太垃圾,有时玩的64KB的枪战小游戏,这种程序体积特别小但是制作出来的效果特别惊人,直到最近我才知道这可能就是运用了图形学的知识实时生成,才能达到这么小。国外有个艺术家’reptile’,经常做一些体积非常小的可执行程序绘出非常漂亮的画面,下面的画面就是4KB生成的,现在为了直观地了解4KB的大小,现在所产生的1080p视频为40MB,这比产生它的可执行文件大10000倍。而且可执行文件还包含生成音乐的代码。
reptile
视频传送门
https://youtu.be/roZ-Cgxe9bU?list=PLVbS70ERPhCCGKc-MdKsH03R7o6TNbGoZ

许多demo中使用的技术叫RayMarching-光线步进。这个算法与SDF-有向距离场结合使用,可以实时创建一些非常有意思的东西。下面是这个文章的目录,搬好小板凳慢慢看。

SDF

SDF(Signed Distance Field),译为有向距离场。'GPU Gems 3’中是这么描述SDF:

“SDF是由到(多边形模型)物体表面最近距离的采样网格。一般使用负值来表示物体内部,使用正值表示物体外部。SDF理念对于图形图像及相关领域具有很大的诱惑力。它经常被用于布料动画碰撞检测、多物体动力学、变形物体、mesh网格生成、运动规划和雕刻。”

举个例子,一个以原点为中心的球体。球体内的点到原点的距离小于半径,球上的点的距离等于半径,球外的点的距离大于半径。

f ( x , y , z ) = x 2 + y 2 + z 2 − 1 f(x,y,z)=\sqrt{x^2+y^2+z^2}-1 f(x,y,z)=x2+y2+z2 1
用向量表示一下
f ( p ⃗ ) = ∥ p ⃗ ∥ − 1 f(\vec{p})=\|\vec{p}\|-1 f(p )=p 1
数学公式非常简洁明了,再用GLSL表示一下

float sphereSDF(vec3 p) {
   
    return length(p) - 1.0;
}

在未来我会整理一部分SDF模型和原理。

RayMarching

首先从知乎大佬‘叛逆者’的一个回答COPY一下几个概念

  • Ray Tracing:这其实是个框架,而不是个方法。符合这个框架的都叫ray tracing。这个框架就是从视点发射ray,与物体相交就根据规则反射、折射或吸收。遇到光源或者走太远就停住。一般来说运算量不小。
  • Ray Casting:其实这个和volumetric可以脱钩。它就是ray tracing的第一步,发射光线,与物体相交。这个可以做的很快,在Doom 1里用它来做遮挡。
  • Path Tracing:ray tracing + 蒙特卡洛法。在相交后会选一个随机方向继续跟踪,并根据BRDF计算颜色。运算量也不小。还有一些小分类,比如Bidirectional Path Tracing。
  • Ray Marching:顾名思义,是一根ray一步一步向前走(marching),知道与物体相交。基本只用于volumetric,或可以当作volumetric处理的情况。

返回来继续,再我们将物体建模为SDF,该怎么去渲染它呢?接下来该光线步进登场了。

就像在RayTracing光线追踪中一样,首先相机确定位置,在其前面放置一个网格,通过网格中的每个点发送来自相机的光线,并且每个网格点对应于输出图像中的一个像素。

Ray Marching 1

区别在于场景的定义方式,而且这也改变了用于查找视线与物体之间的交点的方法。

在光线行进中,整个场景是根据SDF定义的。为了找到视线和物体之间的交点,我们首先从Camera开始,沿着视线一点一点地移动。在每个Step中,要执行判断此点是否在场景表面内,或者用另一种语言来来表达,SDF在这一点上的值是正还是负。如果是负,那这条射线的判断就结束了!即光线与物体相交。如果不是,则我们继续沿着射线不断增加距离。

每次以沿很小的Step增加光线,同时使用sphere tracing可以做得更好,无论是速度还是准确性。我们采取最安全的最大步骤,而不是一步步走。

Ray Marching 2
在这张图里, p 0 p_0 p0是相机。沿着从Camera通过视平面投射的光线的方向行进。第一步迈的是最远的,以最短的距离到达表面。由于表面上最接近 p 0 p_0 p0的点并不在视线上,所以我们需要不断向前步进(写到这我想起三体,维德的口号:向前!向前!不择手段的向前!),直到最后落到物体表面,即 p 4 p_4 p4

float RayMarch(vec3 start, vec3 viewRayDirection) {
   
    float depth=0.;
    
    for(int i=0; i<MAX_STEPS; i++) {
   
        vec3 p = start + viewRayDirection*depth;
        //倒视线所及之处
        float dist = senceSDFGetDist(p);
        //看看是不是达到物体表面?
        depth += dist;
        //开始前进!
        if(depth>MAX_DIST || dist<SURF_DIST) break;
        //大于视距或进入物体
    }
    return depth;
}

当设置得到的结果放在R通道会得到以下的效果

Result 1

接下来就是计算法向量和光照了。

Normal&Lights

计算机图形学中的大多数光照模型都使用表面法线的一些概念来计算物体在表面上的颜色。当表面由诸如多边形之类的几何定义时,通常为每个顶点指定法线,并且可以通过对周围的顶点法线进行插值来找到面上任意给定点的法线。

那么,怎么

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值