ShaderToy入门教程(3) - CSG

回顾上一篇 ShaderToy入门教程(2) - 光照和相机

这篇涵盖以下黑体所示内容

  • 符号距离函数
  • Ray-marching算法
  • 曲面法线和光照
  • 相机变换
  • 构造实体形状(CSG)
  • 模型变换
    • 平移和旋转
    • 比例缩放
    • 非均匀缩放
  • 结论
  • 参考

构造实体形状(CSG)

构造实体形状(简称CSG)是一种通过布尔运算从简单几何形状创建复杂几何形状的方法。 WikiPedia的这张图表显示了该技术的可能性:
在这里插入图片描述
CSG建立在3个原始操作上:交集,合并和差异

事实证明,当组合表示为SDF的两个表面时,这些操作都是简单的。

float intersectSDF(float distA, float distB) {
    return max(distA, distB);
}

float unionSDF(float distA, float distB) {
    return min(distA, distB);
}

float differenceSDF(float distA, float distB) {
    return max(distA, -distB);
}

如果您设置这样的场景:

float sceneSDF(vec3 samplePoint) {
    float sphereDist = sphereSDF(samplePoint / 1.2) * 1.2;
    float cubeDist = cubeSDF(samplePoint) * 1.2;
    return intersectSDF(cubeDist, sphereDist);
}

然后你得到这样的东西(参见下面关于缩放的部分,看看1.2的除法和乘法的缘由)。
在这里插入图片描述
代码在这里

在同一个Shadertoy中,如果编辑代码,也可以使用联合和差异操作。

考虑由这些二进制操作产生的SDF来尝试建立它们工作原理的直觉是很有趣的。
在这里插入图片描述记住SDF为负的区域代表在面的内部,上面图中相交,sceneSDF只有在cube§和sphere§都是负的时候才是负的。就是说一个点必须都在立方体和球体内部,才会在场景面的内部,这符合CSG对交集的定义。

同样的逻辑也适合于并集,如果一点只要在两者之一的SDF为负,场景SDF则为负,那么就在面的内部
在这里插入图片描述差异操作对我来说是最棘手的
在这里插入图片描述
SDF为负值意味着什么?

如果你再想一想SDF的负面和正面区域是什么意思,你可以看到SDF的负值是表面内外的反转。 表面内的部分被认为现在都被视为外部,反之亦然。
这意味着您可以将差异视为第一个SDF和第二个SDF的反转的交集。 因此,当第一个SDF为负且第二个SDF为正时,得到的场景SDF仅为负。
切换回几何术语,这意味着当且仅当我们在第一个表面内和第二个表面之外时,我们才在场景表面内 - 正好是CSG差异的定义!

模型变换

能够移动相机给我们一些灵活性,但能够独立地移动场景的各个部分肯定会提供更多灵活性。 让我们来探索一下如何做到这一点。

SDF的旋转和平移

建模为SDF的曲面的变换或旋转,可以在评估SDF之前对点进行逆变换实现。

正如您可以对不同的网格对象应用不同的变换一样,您可以将不同的变换应用于SDF的不同部分 - 只需将变换后的视线发送到您感兴趣的SDF部分。例如,使立方体浮出在水面上下浮动,将球体留在原地,但仍然取交集,你可以这样做:

float sceneSDF(vec3 samplePoint) {
    float sphereDist = sphereSDF(samplePoint / 1.2) * 1.2;
    float cubeDist = cubeSDF(samplePoint + vec3(0.0, sin(iGlobalTime), 0.0));
    return intersectSDF(cubeDist, sphereDist);
}

在这里插入图片描述代码在这里
如果你进行这样的变换,结果函数仍然是一个带符号的距离场吗? 对于旋转和平移,它是,因为它们是“刚体变换”,意味着它们保持点之间的距离。

更一般地说,您可以通过将采样点乘以变换矩阵的倒数来进行任何刚体变换。

例如,你可以使用旋转矩阵进行变换,可以这样做:

mat4 rotateY(float theta) {
    float c = cos(theta);
    float s = sin(theta);

    return mat4(
        vec4(c, 0, s, 0),
        vec4(0, 1, 0, 0),
        vec4(-s, 0, c, 0),
        vec4(0, 0, 0, 1)
    );
}

float sceneSDF(vec3 samplePoint) {
    float sphereDist = sphereSDF(samplePoint / 1.2) * 1.2;

    vec3 cubePoint = (invert(rotateY(iGlobalTime)) * vec4(samplePoint, 1.0)).xyz;

    float cubeDist = cubeSDF(cubePoint);
    return intersectSDF(cubeDist, sphereDist);
}

但是如果你在这里使用WebGL,那么现在GLSL中没有内置的矩阵反转例程,但你可以做相反的转换。 所以上面的场景功能改为等效:

float sceneSDF(vec3 samplePoint) {
    float sphereDist = sphereSDF(samplePoint / 1.2) * 1.2;

    vec3 cubePoint = (rotateY(-iGlobalTime) * vec4(samplePoint, 1.0)).xyz;

    float cubeDist = cubeSDF(cubePoint);
    return intersectSDF(cubeDist, sphereDist);
}

有关更多转换矩阵,请参阅图形教科书的任何介绍,或查看这些幻灯片:3D仿射变换

比例缩放

好吧,让我们回到之前我们掩盖的这个奇怪的缩放技巧:

float sphereDist = sphereSDF(samplePoint / 1.2) * 1.2;

1.2的除法是将球体缩放1.2倍(请记住,在将其发送到SDF之前,我们将逆变换应用于该点)。 但是为什么我们之后乘以该比例因子呢? 为简单起见,让我们检查一下2倍的情况。

float sphereDist = sphereSDF(samplePoint / 2) * 2;

缩放不是刚体变换 - 它不保持点之间的距离。 如果我们通过将它们除以2来转换(0,0,1)和(0,0,2)(这导致模型的均匀放大),那么 点之间的距离从1切换到0.5。
在这里插入图片描述

因此,当我们在sphereSDF中对缩放点进行采样时,我们最终会从该变换球体的表面返回该点距离的一半。 最后的乘法是为了补偿这种失真。

有趣的是,如果我们在着色器中尝试这一点,并且不使用比例校正,或者使用较小的比例校正值,则会呈现完全相同的事物。 为什么?

// All of the following result in an equivalent image
float sphereDist = sphereSDF(samplePoint / 2) * 2;
float sphereDist = sphereSDF(samplePoint / 2);
float sphereDist = sphereSDF(samplePoint / 2) * 0.5;

请注意,无论我们如何缩放SDF,返回距离的符号都保持不变。 “签名距离场”的标志部分仍在工作,但距离部分现在正在撒谎

要了解为什么这是一个问题,我们需要重新检查光线行进算法的工作原理。
在这里插入图片描述
回想一下,在光线行进算法的每一步,我们都想沿着视线射线移动一个距离等于到地面的最短距离。 我们使用SDF预测最短距离。 为了使算法更快,我们希望这些步骤尽可能大,但是如果我们下冲,算法仍然有效,它只需要更多的迭代。

但如果我们高估距离,我们就会遇到一个真正的问题。 如果我们尝试缩小模型而不进行更正,如下所示:

float sphereDist = sphereSDF(samplePoint / 0.5);

然后球体完全消失。 如果我们高估距离,我们的光线追踪算法可能会超越表面,从未找到它。

对于任何SDF,我们可以安全地统一它,如下所示:

float dist = someSDF(samplePoint / scalingFactor) * scalingFactor;

非均匀缩放

如果我们想要非均匀地缩放模型,我们如何安全地避免上面缩放部分中描述的距离过高估计问题? 与均匀缩放不同,我们无法准确地补偿由变换引起的距离失真。 由于所有尺寸均匀缩放,因此可以在均匀缩放中进行缩放,因此无论表面上与采样点的最近点在何处,缩放补偿都是相同的。

但是对于非均匀缩放,我们需要知道表面上最近的点在哪里知道校正距离的程度。

要了解为什么会出现这种情况,请考虑单位球体的SDF,沿x轴缩放到其大小的一半,并保留其他尺寸。

s p h e r e S D F ( x , y , z ) = ( 2 x ) 2 + y 2 + z 2 − 1 sphereSDF(x,y,z)=\sqrt {(2x)^2+y^2+z^2}-1 sphereSDF(x,y,z)=(2x)2+y2+z2 1

如果我们在(0,2,0)处计算SDF,我们会回到1个单位的距离。 这是正确的:球体表面上的最近点是(0,1,0)。但如果在(2,0,0)进行计算,我们会回到3个单位的距离,这是不对的。 表面上的最近点是(0.5,0,0),产生1.5个单位的世界坐标距离。

因此,正如在均匀缩放中一样,我们需要校正SDF返回的距离,以避免过高估计距离,但需要多少? 高估因子取决于点的位置和表面的位置。

由于通常可以低估距离,我们可以乘以最小的比例因子,如下所示:

float dist = someSDF(samplePoint / vec3(s_x, s_y, s_z)) * min(s_x, min(s_y, s_z));

其他非刚性变换的原理是相同的:只要符号通过变换保留,您只需要找出一些补偿因子,以确保您永远不会过高估计到曲面的距离。

结论

通过学习这篇文章中的内容,您现在可以创建一些非常有趣,复杂的场景。 将这些与使用法线向量作为材质的环境/漫反射组件的简单技巧相结合,您可以创建在帖子的开头着色器的内容。完整代码

参考

有关渲染有符号距离函数的方法还有很多。 Inigo Quilez是这个主题最富有成效的作家之一。 我通过阅读他的网站和着色器代码了解了这篇文章的大部分内容。 他也是Shadertoy的共同创作者之一。

一些有趣的SDF相关材料来自他的网站,我根本没有介绍,包括smooth blending between surfaces and soft shadows

其他参考:

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
xc7a35t-2csg325i是Xilinx公司推出的一款FPGA(现场可编程门阵列)芯片。该芯片采用了Xilinx公司的最新FPGA架构,具备了出色的性能和灵活性,为各种应用提供了可靠的解决方案。xc7a35t-2csg325i具有35,200个逻辑单元和1,800个数字信号处理(DSP)切片,以及35,200个存储单元。它支持多种接口和通信协议,包括PCI Express、Gigabit Ethernet、USB和串行通信接口等,使得它可以很方便地与其他设备进行连接和通信。 该芯片还集成了丰富的IP核和开发工具,包括Vivado开发套件和Xilinx IP库。这些工具和IP核可以帮助用户快速设计和开发各种应用,加快产品上市时间。xc7a35t-2csg325i在通信、图像处理、工业控制和嵌入式系统等领域都有着广泛的应用。例如,在通信领域,该芯片可以用于实现高速数据传输和数字信号处理,为无线通信和网络设备提供强大的处理能力。在图像处理领域,它可以用于实现实时图像处理和视频编码解码等功能,帮助实现高质量的图像和视频应用。在工业控制领域,它可以作为控制器使用,实现实时控制和数据采集等任务。在嵌入式系统领域,它可以作为处理器使用,实现复杂的算法和应用。 总之,xc7a35t-2csg325i是一款功能强大、性能出色的FPGA芯片,具备广泛的应用潜力。它的引入为各种领域的设计师提供了一种可靠、灵活的解决方案,帮助他们实现更多样化和创新的产品。Xilinx公司将继续努力,为客户提供更多优秀的电子器件和解决方案。<span class="em">1</span> #### 引用[.reference_title] - *1* [AD9164BBCZ 原厂原装(DAC)芯片](https://blog.csdn.net/SZLHDDZ/article/details/131963647)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

科技与文明

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值