休闲阴影玩具路径跟踪2:图像改进和光泽反射

2020-06-07 17:01:18

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

在这篇博客文章中,我们将改进上次渲染图像的质量,并添加镜面反射/光泽反射,以得到上面的图像。将其与下图进行比较,下图是我们在第1部分之后开始的地方。

抗锯齿-这会使像素化的边缘变得平滑,从而获得更好的渲染效果。

sRGB-我们进行照明计算的空间与显示所需的空间不同。这个可以解决这个问题。

曝光-这可以让我们在色调映射之前使图像变亮或变暗,就像相机的曝光设置所做的那样。

向路径跟踪器添加抗锯齿非常容易。我们所需要做的就是在x和y轴上的像素位置上添加一个介于-0.5和+0.5之间的随机数。

因为像素颜色是很多帧上的平均值,所以这会给我们提供“像素的平均颜色”。这意味着如果一个形状的边缘穿过一个像素,该像素将平均该形状的颜色,以及背景中任何东西的颜色。这给了我们平滑的抗锯齿边缘。

这样做,我们就会看到下面这张图片。您可能想要单击以查看其全尺寸,并将其与上一幅图像进行比较,以查看球体的边缘现在是如何平滑的。查看较小的图像会隐藏像素化。

我们发送到显示器的颜色与我们看到的颜色不同。这是因为人类的视觉不是线性的。

在8位/色通道的图像中只有256种色调,如果我们平均分割色调,我们会发现没有足够的深色,因为我们的眼睛可以在更深的色调中看到更多的细节。

为了弥补这一点,制作了sRGB,这使得0到255之间的值不是线性间隔的-深色的值更多,浅色的值更少。

但是,如果我们在sRGB中进行照明计算,数学就不会正确工作,因为我们的颜色通道应用了非线性变换。

要解决此问题,无论何时从纹理读取颜色,都应将sRGB转换为线性。此外,每当我们写出像素时,我们都希望将其从线性转换为sRGB,以便它在显示器上正确显示。

我们需要将这些函数添加到我们的着色玩具中。创建一个新的“公共”标签,并将其放入其中。公共选项卡是所有其他选项卡包含的标题,因此我们放在那里的内容可供所有其他选项卡使用。在本文附带的着色器中,我在公共选项卡中放置了许多实用函数、常量和用户可控制的参数。

我们需要在两个地方使用此功能。第一个位置是当光线未命中场景几何体时读取立方体贴图的位置。

我们想要更改的另一个地方是写出最终像素颜色的地方。

你可能会注意到球的颜色发生了很大的变化。这是因为它们以前是sRGB颜色,但现在是线性颜色。不过,我们可以调整它们的颜色,让它们看起来更好看。如果我们把0.75s的反照率改成0.5s,就会得到这样的图像:

下一件要解决的事情是,当我们计算照明时,光的范围从0(完美黑色)到无限的亮度,但是我们的显示器只能显示介于0和1之间的值。

色调映射是将值重新映射到0到1范围的过程。色调映射保留了暗色中的细节,同时使太亮的像素也具有可识别的细节。我们将使用仅适合ACES色调映射器的亮度。这个色调映射代码是由Krzysztof Narkowicz制作的,您可以在他的网站上阅读更多信息:https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/。

该函数接受线性颜色,并给出线性颜色,因此我们需要在获得原始颜色之后,但在将该颜色转换为sRGB之前,在图像选项卡中调用它。

后墙的灯光变得柔和了一些,看起来自然多了,这很不错。不过,它看起来还是有点亮。

为了解决这个问题,我们要做两件事。首先,我们要将从天空框读取的值乘以0.5,以使天空框变暗一点。(我不会为此粘贴代码,只需将纹理值转换为线性后乘以0.5即可)。

其次,我们将对场景应用曝光,在我们将其放入色调映射器之前,将颜色乘以0.5的值。因此,“Image”选项卡中的全部代码如下所示:

现在是大家期待的部分:光泽反光,做出闪亮的东西!

如果您以前在着色器中进行过简单照明,则可能已经使用Reflect()来计算灯光反射光线,以便在对象上进行一些镜面反射高光(光斑)。

我们将把反照率和发射率放入SRayHitInfo内部的SMaterialInfo结构中,我们还将添加以下字段:

镜面反射百分比-介于0和1之间的浮点数。投射到此对象的灯光将镜面反射而不是漫反射的百分比。这是照射到该表面的光线选择镜面反射而不是漫反射的百分比。

粗糙度-介于0和1之间的浮点。曲面的粗糙程度控制反射的模糊程度。值为0是非常清晰的镜面,类似于反射,而值为1则非常模糊,看起来就像漫反射。

specularColor-一个向量3,它是镜面反射的颜色,就像反照率是漫反射的颜色一样。这使您可以使用不同的颜色进行漫反射和镜面反射,从而获得有色的金属和类似的颜色。

我们将滚动一个从0到1的随机数。如果它小于镜面反射的百分比,我们就是在做镜面反射光线,否则我们就是在做漫反射光线。

零粗糙度镜面反射光线将使用从Reflect()计算的光线方向。

粗糙度在0到1之间的镜面反射光线将平方粗糙度(个人偏好,您不必对其平方),并使用该值在镜面反射光线方向和漫反射光线方向之间进行线性插值,从而规格化结果。

如果是镜面反射光线,我们不会总是将吞吐量乘以反照率,而是将其乘以specularColor。

如果我们稍微更新场景几何体和材质,现在就可以获得最终图像:

背面的绿色球的镜面反射百分比为1.0,因此它们都是镜面反射。它们的粗糙度从左到右分别为:0,0.25,0.5,0.75,1.0。它们的镜面反射颜色为(0.3,1.0,0.3),这使它们具有绿色。用PBR的话说,这将是一种绿色金属(我们没有使用PBR镜面反射计算!)。

左前方的黄色球的镜面反射百分比为0.1,粗糙度为0.2,镜面反射颜色为(0.9F、0.9F、0.9F)。用PBR的话说,这是一种电介质。

前部中心的粉红色球除了镜面反射率为0.3%外,其余部分相同,因此更有光泽。(仍为电介质)。

右前方的蓝色和洋红相间的球表明,如果你选择了不好的材料价值,你可以得到看起来不太好的东西。该球的反照率为纯蓝色,镜面反射率为0.5%,粗糙度为0.5,镜面颜色为纯红。这不是一个你在现实世界中可以看到的非常逼真的东西-一个带有红色反射的蓝色物体-所以它在渲染中看起来不是很好。垃圾进来,垃圾出来。用pbr的话说,你可以通过双层材料…得到这一点。一种蓝色的非常粗糙的或弥漫的电介质,有一层清晰的涂层,就像金属一样?这在PBR中没有太多意义,这进一步表明它不是一种非常有动力的材料,这就是为什么它看起来不是很好。

如果您想做得更深入一些,我们的渲染可以使用菲涅尔效果更好,使掠过的光线角度更具反射性。我不打算在本系列中这样做,但我在这里有一篇关于它的博客文章:https://blog.demofox.org/2017/01/09/raytracing-reflection-refraction-fresnel-total-internal-reflection-and-beers-law/。

每条光线最多会反弹8次,但有些时候它真的不需要8次反弹就能给出正确的外观。

如果我们能以某种方式检测到这一点,我们就可以提前结束一条射线,特别是如果我们知道如何正确处理概率,而不会因为提前结束而增加偏差的话。

我们将在吞吐量R、G或B中找到最大颜色通道值,该值将是我们在每次反弹后继续使用光线的百分比。当我们继续前进时,我们将把吞吐量除以保留射线的机会,这弥补了我们有时会杀死那里的一些射线的事实,这是通过使未来的反弹更明亮来弥补的。

这意味着吞吐量越低,意味着未来的光线反弹对最终图像的影响可能越小,我们就更有可能提前停止光线反弹。

这主要是性能优化,但这很好,因为它可以更快地获取更多样本,从而使您可以更快地收敛。

只需将此代码放在我们通过将其乘以漫反射或镜面反射来更新吞吐量的位置即可。

为了了解这是如何影响性能的,在我的机器上,以(我忘记)分辨率和(我忘记)每帧渲染,我得到了30fps,即每帧33.3毫秒。在我输入这个俄罗斯轮盘赌代码后,它上升到了40fps,也就是每帧25毫秒。视觉效果完全相同,但每帧渲染时间减少了8ms,或速度提高了25%。

在shadertoy中可能很难获得全屏截图,因为你必须单击按钮才能使其全屏(见下图),然后屏幕上已经有一些小的渲染效果。

当您按空格键时,我们可以将其设置为重置渲染。

您可以这样做,而不是使用iframe来计算将此帧混合到下一帧中的程度,而是将混合(1/framesRended)存储在每个像素的Alpha通道中。然后,可以将键盘作为纹理指定给iChannel2(位于Misc选项卡下),然后读取该“键盘纹理”以查看是否按下了空格键。

并将其替换为(在将键盘指定为iChannel2插槽中的纹理之后!):

现在,您可以翻转到全屏并按空格键重置渲染,以获得更好的更大渲染效果。

别忘了…。如果您正在尝试进行渲染,并且您看到的是60fps,这意味着您可能会收敛得更快,而您等待的原因并不充分。尝试将c_numRendersPerFrame调高,直到不再是60fps。任何低于60表示它是工作在接近容量,降低到10或5或更低的fps是不会帮助你的,所以不要担心让你的阴影玩具标签爬行,它没有任何好处。

请留意本系列的下一篇文章。我们有更多的主题可以随意实现,包括:景深和波克、相机移动、参与的媒体(雾)、光晕、折射和放大镜等等。

感谢您在Twitter@Atrix256上阅读并与我分享您制作的任何很酷的东西