休闲着色玩具路径跟踪1:基本摄影机,漫反射,发射

2020-05-27 04:46:02

下面是这篇文章附带的阴影玩具的截图。单击以查看全尺寸。该阴影玩具可在以下网址找到:https://www.shadertoy.com/view/tsBBWW。

当你看到一张照片真实感的图像,有人说它是“光线跟踪”的,他们的意思可能是它是“路径跟踪”的。

路径跟踪可能需要相当多的数学运算,并且需要非常注意细节,但它可以生成一些非常棒的图像。

不过,有趣的是。你可以扔掉很多形式主义,但仍然可以得到极好的结果。

这是这一系列博客文章的前提。目标是让你进行路径跟踪,让你玩耍,享受乐趣,创作艺术,而不必担心积分,PDF和图形的存在问题:“圆周率还是不圆周率?”不幸的是,这意味着我们的图像可能需要更长的时间来渲染,并且可能并不完全“正确”,但是您可以在以后感兴趣的时候学习更复杂的技术。

渲染是一项艺术和创造性的工作,所以现在让我们忽略一些细节。我们不是在制作地面真实感渲染器,我们只是在享受制作酷渲染的乐趣。

ShaderTool是一个网站,可以让你用一种类似c的语言编写叫做GLSL的小程序,这些小程序在你的GPU(而不是CPU)上为图像中的每个像素运行。ShaderToy使用WebGL,但会为您管理除像素着色器之外的所有内容。

这些程序的输入非常有限,包括像素坐标、帧号、时间和鼠标位置,并且只给出一种颜色作为输出。

尽管很简单,人们还是做了令人惊叹的事情,比如制作了一个可玩的末日级别1。

ShaderTool是由演示人员创建的,演示人员是那些举办聚会和竞赛的人,他们制作能产生令人印象深刻的音频和视觉效果的非常小的可执行文件。

除了演示人员,shadertoy.com还经常有专业的和业余的游戏开发人员、图形研究人员、电影工作者以及其他各种各样的人光顾。

这是一个很好的玩耍和学习的地方,因为它可以让你不用大惊小怪地潜入水中。我在那里学到的东西太多了,有时会让我难以置信。…。各种各样的数学技巧,图形技术,自动微分的双数,光线行进立体几何,我甚至在2013年做过实时光线跟踪。实时光线跟踪对世界来说似乎是新事物,但演示编剧正在对Comodore 64这样的设备进行实时光线跟踪,并且已经这样做了几年或几十年。我们其余的人只是在迎头赶上!

在这些文章中,我们将在shadertoy中进行路径跟踪,因为这样做非常容易。你应该去那里,创建一个账户,四处看看一些阴影玩具。ShaderTool在Chrome或Firefox中效果最好。

要开始,请在登录后,单击右上角的“新建”按钮以创建新的着色器。迎接您的应该是类似于以下内容的内容:

继续为着色器命名-名称必须是全局唯一的!-以及至少一个标记和说明。所有这些都是必需的,以便您可以单击“提交”并对着色器执行初始保存。

提交着色器后,提交按钮将变为保存按钮,您可以继续下一步。

路径跟踪是光线跟踪的一种,这意味着我们将从每个像素向世界发射一条射线。对于每条单独的光线,我们将查看它击中了什么,并使用该信息为我们的像素赋予颜色。对每个像素独立地这样做,我们就可以得到最终的图像。

因此,进行路径跟踪的第一步是计算每个像素的光线。光线有起点和方向,所以我们需要计算光线的起点和方向。

光线将从相机位置开始(您可以将其视为眼睛位置),它将拍摄相机前面一个假想矩形上的像素,穿过这些像素,然后进入场景,看看它们击中了什么。

我们将使这个像素矩形在x和y轴上从-1到+1,现在我们将使它在z轴上有1个单位的距离。我们将假定摄影机位于原点(0,0,0)。我们的目标是计算每个像素在此矩形上的3D点,然后计算光线从原点到该3D点的方向。

我们将首先计算每个像素在x和y轴上从0到1的百分比。我们将在红色和绿色通道中显示它,以确保我们做得正确。

将下面的代码放在右侧的代码框中,然后单击播放按钮(屏幕截图中的红色圆圈)查看结果。

从这张图中我们可以看到(0,0)在左下角,颜色是黑色。当像素向右移动时,它们会变得更红,这表明x轴向右移动。随着像素的增加,它们变得更绿,这表明y轴向上。

像这样将值可视化是调试着色器玩具着色器的一种很好的方法。通常的图形调试器和调试技术在执行WebGL或使用shadertoy时不可用,因此如果您遇到问题,这种“printf”样式的调试是查看正在发生的事情的最佳方式。

现在我们的屏幕百分比从0到1,我们希望将其从-1更改为1,这样它实际上只是虚构像素矩形上的x和y坐标。

我们现在可以看到屏幕中间是黑色的,向右转会使像素变得更红,而向上则会使像素变得更绿。但是,向左和向下并不会改变颜色,只会使其保持黑色。这是因为颜色在显示之前被钳制到0到1之间,使得所有负数都只是零。

如果你想要确保负值在那里,而且它真的不只是零,你可以把绝对值想象成这样。

好的,现在我们有了射线目标在虚构像素矩形上的x,y位置。我们可以将z位置设为1,因为我们希望它离我们有1个单位的距离,这就为我们的光线提供了一个目标。现在,我们希望获得从相机位置(原点)到此光线目标的规格化向量。这就给了我们光线方向,我们可以可视化该光线,以验证它看起来是否合理。

这有点微妙,但当你从屏幕中心向右移动时,像素变得更红,变成了紫色。当你从中心往上走,像素变得更绿,变得更蓝。这表明我们的射线方向似乎是合理的。当我们在x轴上向右移动时,归一化的射线方向也开始偏离直线,并且在x轴上指向更右、更正的方向。y轴和绿色也是如此。

从中心向左向下移动,蓝色变暗,但由于x和y轴再次为负值,所以没有显示红色和绿色。

如果您玩弄光线目标的z坐标,您可以通过移动虚构的像素矩形来放大或缩小相机,从而使光线方向或多或少发生明显的变化,但我们稍后会更多地讨论这一点。

屏幕上几乎恒定的蓝色是由于光线方向的正z值。所有的光线都指向前方,这是我们所期待的。

我们已经成功计算出射线的起始位置和方向,准备开始向世界发射射线了!

接下来,我们想要使用这些光线来实际渲染一些几何体。要做到这一点,我们需要一些函数来告诉我们光线是否与形状相交,以及相交发生在光线的多远处。

我们将使用函数“TestSphereTrace”和“TestQuadTrace”来处理光线相交和球体相交,以及光线相交和矩形相交(四边形)。很抱歉没有在这里以复制/粘贴的格式将它们放在帖子中。当我试图将它们放入代码块时,WordPress会出现故障,但它们在最终的着色器玩具中。

您还可以找到其他光线与对象相交函数。我改编自Christer Ericson的书“实时碰撞检测”(https://www.amazon.com/Real-Time-Collision-Detection-Interactive-Technology/dp/1558607323),),如果你用谷歌搜索的话,网上有一堆。光线行进还可以用作光线与对象相交的数值方法,从而使您拥有更丰富的形状、无限重复的对象等。

无论如何,我们将针对由多个对象组成的场景测试光线,并为每个对象赋予不同的颜色,而不是为每个像素的颜色使用光线方向。我们在GetColorForRay函数中执行此操作。

我们在屏幕上看到了一些不错的东西,但在进行路径跟踪之前,我们还有很长的路要走。

如果查看最终着色器玩具中的代码,则仅当光线与形状相交且相交距离比hitInfo.dist更近时,TestSphereTrace和TestQuadTrace才返回true。在第一次执行任何类型的光线跟踪(路径跟踪或其他类型)时,经常会犯的一个错误是接受光线击中的第一个对象,但实际上您希望测试所有对象并保留最接近的击中。

如果只找到第一个命中,而不是最近的命中,那么蓝色球就会消失,因为灰色四边形的光线测试在蓝色球之前进行,即使它更远。

您可能注意到,在最后一张图像中球体被拉伸。这是因为我们虚构的像素矩形是正方形(它在x和y轴上从-1到+1),而我们呈现的图像不是正方形。这使得图像被水平拉伸。

解决此问题的方法是在x轴上增大虚像素矩形,或者在y轴上缩小它,以便虚像素矩形上的宽度与高度的比率与我们渲染的图像中的宽度与高度之比相同。(=。我要缩小y轴。下面是要使用的新mainImage函数。添加了第10行到第12行来解决该问题。

我之前说过,虚构的像素矩形将在1个单位之外,但如果我们将距离变小,就等同于缩小相机或增大视场。如果我们把距离调大,就相当于放大或缩小视场。

结果是,像素矩形距离为1个单位时,您可以看到90度的视野。

用于计算特定视场距离的公式位于下面新的mainImage函数的第7行,并在第11行使用。

我们往前走时c_FOV Degrees是90度,但这里是120度。

如果你把视野调得太宽,你会开始在边缘得到扭曲。这也是摄像机经历的一个深刻的话题,我们不会深入探讨,但我想确保你们知道这一点。通过将FOV设置为非常大的值(如360度或720度),您可以获得一些真正有趣的渲染效果!

现在我们可以向世界发射射线了,我们终于准备好进行一些路径跟踪了!

要做着色,我们需要光源,而我们的光源将只是有发光的物体,也就是发光的物体。

对于这篇文章,我们只做漫反射灯光着色,这意味着没有闪亮的反射。我们可能会在本系列的下一篇文章中做闪亮的反射,但现在我们只做漫反射。

路径跟踪在此设置中的工作方式非常简单。我们将从黑色开始一个像素的颜色,从白色开始使用“吞吐量”颜色,然后我们将按照以下规则向世界发射一条射线:

当光线击中对象时,会将发射*吞吐量添加到像素的颜色上。

当光线击中物体时,吞吐量乘以物体的反照率,这会影响未来发射光的颜色。

当光线击中对象时,光线将以随机方向反射,光线将继续(更多信息将在一秒钟内提供)。

当光线未命中所有对象或达到N射线反弹时,我们将终止。(在该着色器玩具中n=8,但可以针对速度与正确性调整该值)。

“吞吐量”的概念乍看起来可能很奇怪,但随着复杂性的增加,请考虑一下这些场景:

一束光线打到一个白色的球上,反弹出来,然后打到一束白光上。像素应该是白色的,因为白色的球是由白光照亮的。

一束光线打到一个红色的球上,反弹回来,然后打到同样的白光上。像素应该是红色的,因为红色的球是由白光照亮的。

光线打到白色球上,反弹到红色球上,反弹到白光上。像素应该是红色的,因为白色的球是由白光点亮的红色球点亮的。

当光线从曲面反弹时,该光线的所有未来照明都将乘以该曲面的颜色。

这些简单的规则就是获得柔和阴影、反弹照明、环境光遮挡以及在路径跟踪器中“自动”显示您看到的所有其他很酷的东西所需的全部。

我说光线随机反弹,但有两种不同的处理方法:

在正常的正半球中随机反弹,然后用点乘以吞吐量(newRayDir,faceNormal)。这是漫反射照明的余弦θ术语。

或者,在曲面法线的余弦加权半球方向上随机反弹。这对余弦θ项使用重要性抽样,是更好的方法。

要做#2,我们所需要做的就是获得一个“球面上的随机点”(也称为随机单位向量),将其添加到法线上,然后将结果规格化。这么简单真是太好了。

您可能想知道如何在着色器中生成随机数。有很多方法可以处理它,但我们将如何做到这一点。

首先,我们将根据像素位置和帧编号初始化当前像素的随机种子,以便每个像素获得不同的随机数,并使每个像素在每帧获得不同的随机数。

然后,我们可以将这些函数放在mainImage函数之上,该函数接受rngState并对其进行修改,然后生成一个随机数。

要实现我们前面描述的路径跟踪规则,我们将更改一些内容…。

将光线与对象测试从GetColorForRay移动到名为TestSceneTrace的新函数中。这使我们可以将场景测试逻辑与我们将要执行的迭代反弹光线跟踪分开。

添加反照率和发射矢量3到命中信息,并在我们与物体相交时设置这两个字段。这是对象材质数据。

当我们运行着色器玩具时,我们看到了类似上面的东西,但是我们看到的特定点在每一帧周围移动,因为随着时间的推移是随机的。

这可能看起来不太好,但我们现在真的很接近了。我们只需要让像素显示它们的平均值,而不是只显示单个值。

为了平均像素值,我们需要使我们的着色器变成一个多过程着色器。

第一步是再通过一次。单击“image”选项卡旁边的+号,然后选择“Buffer A”

将所有内容从图像选项卡移动到BUF A选项卡。在图像选项卡中,为纹理“iChannel0”选择“Buffer A”,并将此代码放入以读取和显示缓冲区A中的像素。

在“buf A”选项卡中,也为纹理“iChannel0”选择“Buffer A”,这样我们就可以从最后一帧读取缓冲区A的像素,以平均到当前帧的输出。现在我们只需在下面添加第27、28、29行,将所有像素平均在一起!

经过5分钟的渲染,结果看起来相当嘈杂,像素太亮太裁剪了,这不是一个很好的场景,但实际上我们现在正在跟踪路径!你甚至可以看到一些整齐的灯光效果,比如你如何看到红色球旁边的墙上有一些红色,因为它反射了光线。球体也投射了一些好看的阴影。

我们可以稍微清理一下场景,制作一些类似康奈尔盒子的东西,并在5分钟后完成渲染。

现在,当光线错过场景并飞入空洞时,我们会停止光线跟踪,它隐含地表示场景外的空洞是黑色的。与其让我们的背景是黑色,不如让它成为一个环境。

在“buf A”选项卡中,选择纹理iChannel1的森林立方体贴图,然后将第19行添加到下面的GetColorForRay()。

在30秒内,它看起来和之前的5分钟渲染一样嘈杂。5分钟的渲染看起来非常糟糕,因为场景中既有非常暗的地方,也有非常亮的地方。场景中不同位置的照明条件越不相同,产生的噪波就越多。此外,较小的较亮的灯光会比较大的较暗的灯光产生更多的噪音。

学习更复杂的路径跟踪技术(如直接光采样,也称为下一事件估计)会使这不是真的,但我们的目标是在此路径跟踪器中易于理解,而不是收敛速度。

但是,您可能会注意到,在渲染场景时,shadertoy报告为60fps,这意味着场景渲染受到vsync的限制。如果可以,渲染速度将超过60fps,因此收敛速度会更快,但它不能。

有一个用于Chrome的着色玩具插件,你可以安装它来帮助它,你可以告诉它每帧最多绘制64张图,这真的有助于它更快地收敛。我用的是一台使用了5年的游戏笔记本电脑,里面装有NVIDIA 980M GPU,我马上就得到了这个全屏渲染,它融合得相当好!单击它可以全屏查看。

获得铬的着色玩具插件的另一种选择是每帧只为像素发射N条光线,并对其进行平均,然后将其放入平均缓冲区。这比使用多个着色器在一帧中传递N次效率更高,因为当在着色器内迭代时,对象位于内存、缓存等位置。这意味着您的图像将在相同的时间内收敛更多。

制作这张图片的阴影玩具包含了本文讨论的所有内容,位于:https://www.shadertoy.com/view/tsBBWW。

尝试使用路径跟踪器,创建不同的场景,并修改代码以获得其他视觉效果。

如果你能做点什么,我很想看看!你可以在推特上找到我@Atrix256。

在这个系列中,很快还会有3到4篇帖子,内容涉及各种主题,包括景深和波克、闪亮的反射、抗锯齿和雾/烟。我们甚至还不是sRGB正确的!这些东西会显着改善形象,所以敬请关注。

彼得·雪莉的“一个周末的光线追踪”(现在是免费的pdf)https://www.realtimerendering.com/raytracing/Ray%20Tracing%20in%20a%20Weekend.pdf。

不过,路径跟踪不一定只用于逼真的渲染。下面是我制作的蒸汽波风格的风格化路径跟踪着色器玩具。