Shader 3d RayMarching6 3D SDF造型

IQ的这篇文章 iquilezles.org/articles/di... 有这个世界上关于3D SDF已知的一切。 本篇将尝试走一遍并理解其所有的operator,理解无法

拉长Elongation

拉长的基本逻辑是将某个空间直接吃掉,例如在平面坐标中如果将y axis 往左到-1,往右到1,假设左边界和右边界都是0 开始。 等于中间[-1, 1]的空间被创造出来了

glsl 复制代码
float opElongate( in sdf3d primitive, in vec3 p, in vec3 h )
{
    vec3 q = p - clamp( p, -h, h );
    return primitive( q );
}

上面这段函数在对于一维拉伸,第一个函数完美运行,并给出精确的外部和内部距离。然而,对于二维和三维拉伸,第一个实现会在体积内部产生一个小的零距离核心。于是IQ为了解决这个问题创造了第二个函数

glsl 复制代码
float opElongate( in sdf3d primitive, in vec3 p, in vec3 h )
{
    vec3 q = abs(p) - h;
    return primitive( max(q, 0.0) ) + min(max(q.x, max(q.y, q.z)), 0.0);
}

这里通过min(max(q.x, max(q.y, q.z)), 0.0) 获取到了SDF内部正确的负值

圆角Rounding

Rounding的基本逻辑是将体积扩充到外部世界,由于外部世界与边界的距离是一个圆,自然就形成了rounded的效果。当然在2D SDF中可以先扩充在回收

glsl 复制代码
float rounding( in float d, in float h )
{
    return d - h;
}

镂空Onion

Onion是让SDF变得像洋葱一样,其实就是将物体内部挖空,在数学上就是abs。 同时可以引入一个粗细参数 thinkness

arduino 复制代码
float opOnion( in float sdf, in float thickness )
{
    return abs(sdf)-thickness;
}

距离公式变换

一般来说计算的距离都是欧几里得距离,但是如果将距离函数变化下有时候会得到出其不意的效果。 但是距离函数变化会带来非常多的影响,阴影和环境光灯效果都依赖于正确的欧式距离。

css 复制代码
float length2( vec3 p ) { p=p*p; return sqrt( p.x+p.y+p.z); }

float length6( vec3 p ) { p=p*p*p; p=p*p; return pow(p.x+p.y+p.z,1.0/6.0); }

float length8( vec3 p ) { p=p*p; p=p*p; p=p*p; return pow(p.x+p.y+p.z,1.0/8.0); }

并集 Union

j这个操作返回两个几何体中任一点到它们的最近点的距离。在两个几何体的合并中,任意一点到合并体的距离等于该点到两个几何体的距离中的较小者。因此,这个函数通过计算min(d1,d2)返回两个几何体的合并。

glsl 复制代码
float opUnion( float d1, float d2 )
{
    return min(d1,d2);
}

交集 Intersection

集操作返回两个几何体共有部分中任意一点到边界的距离。在交集中,点到结果几何体的距离等于该点到两个几何体距离中的较大者。原因在于,只有当这个点同时在两个几何体的内部时,它才属于交集部分,这通过max(d1,d2)来确保。

glsl 复制代码
float opIntersection( float d1, float d2 )
{
    return max(d1,d2);
}

差集 Subtraction

当从一个几何体中减去另一个时,所有在第二个几何体内的点都变成了在结果几何体外的点,这通过取d1的负值来实现(在SDF中,负值表示在内部)。这个操作返回所有点中对于结果几何体来说,"最外面"点的距离,max(-d1,d2)确保了所有在减数几何体内部的点都被正确处理。

glsl 复制代码
float opSubtraction( float d1, float d2 )
{
    return max(-d1,d2);

}

异或 XOR

异或操作产生一个只包含两个几何体非共享部分的结果。换句话说,它包含属于第一个但不属于第二个几何体的部分,以及属于第二个但不属于第一个几何体的部分。这个函数通过首先查找两个几何体中较近的一个(min(d1,d2)),然后将其与两者中较远者的负值(-max(d1,d2))进行比较。

glsl 复制代码
return max(min(d1,d2),-max(d1,d2));

丝滑逻辑运算 SmoothLogical

有以下两个函数图像, <math xmlns="http://www.w3.org/1998/Math/MathML"> a = x a=\sqrt{x} </math>a=x 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> b = ( 2 − x ) 2 b = (2-x)^2 </math>b=(2−x)2

用 <math xmlns="http://www.w3.org/1998/Math/MathML"> m i n ( a , b ) min(a, b) </math>min(a,b)的图像会产生两个尖角,这两个点是不连续的

IQ的设计函数 smooth_min(a, b, k), 输入两个函数a,b,通过k值可以控制两个函数smooth min到下图

scss 复制代码
float opSmoothUnion( float d1, float d2, float k )
{
    float h = clamp( 0.5 + 0.5*(d2-d1)/k, 0.0, 1.0 );
    return mix( d2, d1, h ) - k*h*(1.0-h);
}

float opSmoothSubtraction( float d1, float d2, float k )
{
    float h = clamp( 0.5 - 0.5*(d2+d1)/k, 0.0, 1.0 );
    return mix( d2, -d1, h ) + k*h*(1.0-h);
}

float opSmoothIntersection( float d1, float d2, float k )
{
    float h = clamp( 0.5 - 0.5*(d2-d1)/k, 0.0, 1.0 );
    return mix( d2, d1, h ) + k*h*(1.0-h);
}

旋转移动 Rotation/Translation

坐标系中形状的变换可以由坐标系的逆变换完成

glsl 复制代码
vec3 opTx( in vec3 p, in transform t, in sdf3d primitive )
{
    return primitive( invert(t)*p );
}

缩放 Scale

缩放是通过压缩/扩张空间来做的,所以在最后的distance需要反向扩张/压缩回来。均匀缩放的计算非常简单,通用的非均匀缩放且获得正确的SDF是不可能的。因为维度的信息丢失了。这里直接贴IQ的代码

glsl 复制代码
float opScale( in vec3 p, in float s, in sdf3d primitive )
{
    return primitive(p/s)*s;
}

镜像Symmetry

镜像也是通过操作空间来达到的,对于单个axis镜像,只需要abs那个axis就行,如果对于某个平面做镜像,那就abs那个平面就可以了

glsl 复制代码
float opSymX( in vec3 p, in sdf3d primitive )
{
    p.x = abs(p.x);
    return primitive(p);
}

float opSymXZ( in vec3 p, in sdf3d primitive )
{
    p.xz = abs(p.xz);
    return primitive(p);
}

表面位移 Displacement

改变物体表面点的位置,有时候通过三角函数,有时候沿着法线random。 displacement可以随意实验

glsl 复制代码
float opDisplace( in sdf3d primitive, in vec3 p )
{
    float d1 = primitive(p);
    float d2 = displacement(p);
    return d1+d2;
}

扭曲 Twist

扭曲是一种将几何体绕着某个轴线旋转的变形操作。通常来说,扭曲操作会将几何体沿着指定轴线分成多个层次,各层次依次旋转一定角度,以产生类似螺旋的效果. 例如下面的方法IQ就是绕着Y axis做了一些扭曲动作

glsl 复制代码
float opTwist( in sdf3d primitive, in vec3 p )
{
    const float k = 10.0; // or some other amount
    float c = cos(k*p.y);
    float s = sin(k*p.y);
    mat2  m = mat2(c,-s,s,c);
    vec3  q = vec3(m*p.xz,p.y);
    return primitive(q);
}

弯曲 Bend

弯曲是一种将几何体沿着某个方向或轴线弯曲的变形操作。通过将物体的一部分固定,然后将另一部分沿某个指定方向弯曲,可以产生柔和的曲线效果. 例如下面的方法IQ就绕着 x axis做了弯曲

glsl 复制代码
float opCheapBend( in sdf3d primitive, in vec3 p )
{
    const float k = 10.0; // or some other amount
    float c = cos(k*p.x);
    float s = sin(k*p.x);
    mat2  m = mat2(c,-s,s,c);
    vec3  q = vec3(m*p.xy,p.z);
    return primitive(q);
}

到了这里除了空间复制之外,基本所有的操作都走了一遍,下一期将试着用现在所学做一个3D的卡通形象

相关推荐
光影少年7 小时前
WebGIS 和GIS学习路线图
学习·前端框架·webgl
DBBH10 小时前
Cesium源码分析之渲染3DTile的一点思考
图形渲染·webgl·cesium.js
唯道行1 天前
计算机图形学·19 Shadings in OpenGL
人工智能·算法·计算机视觉·几何学·计算机图形学·opengl
Robet1 天前
TS2d渲染引擎
webgl
Robet1 天前
WebGL2D渲染引擎
webgl
goodName2 天前
如何实现精准操控?Cesium模型移动旋转控件实现
webgl·cesium
丫丫7237345 天前
Three.js 模型树结构与节点查询学习笔记
javascript·webgl
allenjiao7 天前
WebGPU vs WebGL:WebGPU什么时候能完全替代WebGL?Web 图形渲染的迭代与未来
前端·图形渲染·webgl·threejs·cesium·webgpu·babylonjs
mapvthree8 天前
mapvthree Engine 设计分析——二三维一体化的架构设计
webgl·数字孪生·mapvthree·jsapi2d·jsapigl·引擎对比
GISer_Jing9 天前
3D Cesium渲染架剖析
javascript·3d·webgl