【发布时间】:2021-04-01 13:51:09
【问题描述】:
我编写了一个基本的光线追踪器来跟踪屏幕空间。每个片段都有一个相关的像素半径。当射线dir 被一些distance 从eye 挤压时撞击几何体,我计算该撞击的法线向量N,并将其与另外四个射线组合。在伪代码中:
def distance := shortestDistanceToSurface(sdf, eye, dir, pixelRadius)
def p := eye + dir * distance
def N := estimateNormal(sdf, p)
def glance := distance * glsl.dot(dir, N)
def dx := (dirPX / glsl.dot(dirPX, N) - dirNX / glsl.dot(dirNX, N)) * glance
def dy := (dirPY / glsl.dot(dirPY, N) - dirNY / glsl.dot(dirNY, N)) * glance
这里,dirPX、dirNX、dirPY 和 dirNY 是在屏幕空间的四个方向中的每一个方向上偏移了dir 像素半径的光线,但仍然瞄准相同参照点。这给出了dx 和dy,它们是像素上的偏导数,表示当光线穿过屏幕空间时命中沿几何体表面移动的速率。
因为我跟踪屏幕空间,我可以使用pre-filtered samplers, as discussed by Inigo Quilez。他们看起来很棒。但是,现在我想添加反射(和折射),这意味着我需要递归,我不确定如何计算这些光线并跟踪屏幕空间。
根本的问题是,为了弄清楚几何体上某个位置反射了什么颜色的光,我不仅需要采集点样本,还需要检查反射的整个屏幕空间。我可以使用偏导数在几何上给我四个新点,它们近似于一个椭圆,这是原始像素从屏幕上的投影:
def px := dx * pixelRadius
def py := dy * pixelRadius
def pPX := p + px
def pNX := p - px
def pPY := p + py
def pNY := p - py
我可以通过将椭圆弄成圆形来计算近似的像素半径。我知道这会破坏某些理想的各向异性模糊,但是没有作弊的光线追踪器是什么?
def nextRadius := (glsl.length(dx) * glsl.length(dy)).squareRoot() * pixelRadius
但是,我不知道在哪里将这些点反映到几何中;我不知道将它们的光线聚焦在哪里。如果我必须选择焦点,那么它将是任意的,并且取决于几何体反映其自身图像的位置,这可能会任意模糊或摩尔反射图像。
我需要取二阶偏导数吗?我可以像一阶导数一样逼近它们,然后我可以用它们来调整正常的N,只需稍加改变,就像点击p。然后法线引导椭圆的焦点,并将其映射到近似圆锥截面。我担心三件事:
- 我担心做一些额外的向量加法和乘法的成本,这可能可以忽略不计;
- 还有关于精度损失(在进行这些廉价衍生品时已经非常糟糕)是否会在多次反射中损失过大;
- 最后,我应该如何处理屏幕空间爆炸的情况;当我有一个镜像球体时,我应该如何在反射空间的大楔形上进行采样,例如将棋盘格图案平均成灰色?
虽然不用担心,但我根本不知道如何获取四个向量并快速为它们拟合一个令人信服的锥体,但这可能只是花一些时间在白板上做代数的问题。
编辑:在 John Amanatides 1984 年的论文 Ray Tracing with Cones 中,曲率信息确实被计算出来,并用于将估计的锥体拟合到反射光线上。在 Homan Igehy 1999 年的论文Tracing Ray Differentials 中,仅使用一阶导数,而明确忽略了二阶导数。
可能还有其他选择吗?我已经尝试在一次反射后丢弃像素半径并仅采集点样本,它们看起来很糟糕,有很多锯齿和噪音。也许存在可以基于每种材料计算的视场或景深近似值。像往常一样,多重采样会有所帮助,但我想要一个分析解决方案,这样我就不会不必要地浪费这么多 CPU。
(sdf 是 signed distance function,我正在做球体追踪;同样的程序既计算距离又计算法线。glsl 是 GLSL 标准库。)
【问题讨论】:
标签: trigonometry raytracing antialiasing