路径追踪中的纹理过滤

rrldj9b26oh4lryp3a.png

问题引出

最近在做基于WebGL的路径追踪时,遇到了一个法线(凹凸)贴图的问题,如下图,凹凸效果走样特别严重。通过问题分析,目前渲染器还缺少对不同纹理过滤类型的实现,今天刚好完成了相关内容,趁热将其记录下来。

20230807-215811.png

小球凹凸效果的问题

20230807-215832.jpeg

小球细节

render_3987.png

其他渲染器的效果

原因分析

本文示例里小球的凹凸贴图如下图。通过凹凸贴图计算法线的过程如下:

  1. 通过对贴图采样得到点PP的高度hph_p;

  2. 分别右移和上移一个微小的距离得到hp+Δxh_{p+\Delta{x}}hp+Δyh_{p+\Delta{y}};

  3. 最终的法线可以表示为:

    N=N+α(hp+Δxhp)T+α(hp+Δyhp)(N×T)\mathbf{N}^{‘} = \mathbf{N} + \alpha(h_{p+\Delta{x}}-h_p)\mathbf{T} + \alpha(h_{p+\Delta{y}}-h_p)(\mathbf{N}\times\mathbf{T})

    别忘了归一化:N=NN\mathbf{N}^{”} = \tfrac{\mathbf{N}^{‘}}{\begin{Vmatrix} \mathbf{N}^{‘}\end{Vmatrix}}

    上式中α\alpha为凹凸强度,T\mathbf{T}为切向向量。

以上过程,最重要的便是Δx\Delta{x}Δy\Delta{y}的选取。已知的是在屏幕空间水平和竖直方向的偏移分别为1/resolutionx1/resolution_x1/resolutiony1/resolution_yresolutionresolution为渲染屏幕的分辨率,我们需要通过屏幕空间中的偏移去求每个物体在其所在的UV空间的偏移Δx\Delta{x}Δy\Delta{y},最后采样求得最终的法线。显然Δx\Delta{x}Δy\Delta{y}还与视角有关,即当物体离摄像机较近时,偏移很小,而距离增加时,偏移增大。

ca730c993ef5964aa383ec28d6d2b3ab.jpeg

小球凹凸贴图(150015001500*1500

对于上述的根据视角自适应采样的方法如何实现?这篇文章详细的介绍了纹理采样,其中的基于MipMap的三线性过滤可以满足我们的要求。在我们使用光栅化渲染,当设置纹理的TEXTURE_MIN_FILTER或TEXTURE_MAG_FILTER为LINEAR_MIPMAP_LINEAR时,OpenGL/WebGL会自动的根据当前像素在UV上的变化率选取合适的MipMap,这是已经集成在硬件上的功能。

解决方案一

基于上述的分析,我们知道光栅化的时候,可以直接利用纹理过滤选项,让硬件帮我们完成最佳的采样。对于凹凸贴图,我们可以直接使用内置的微分函数dFdxdFdxdFdydFdy

float hp = texture2D(bump, uv).r;
float hpdx = texture2D(bump, uv + dFdx(uv)).r;
float hpdy = texture2D(bump, uv + dFdy(uv)),r;

我们怎么将上面的光栅化应用到光线追踪呢?我们可以把法线的结果通过光栅化预计算到FrameBuffer,将计算结果传入光追的Shader,通过坐标变换求得屏幕坐标,采样即可得到法线结果,下图分别为光栅化得到的法线以及最终渲染结果:

20230807-233450.jpeg

20230807-234141.jpeg
但,这种方案有哪些问题呢?

从这种方案的原理出发,很显然,可以预见它有如下的一些问题:

  1. 仅对摄像机视角内的像素点有效,且无法得到被遮挡的物体的法线结果;
  2. 折射/反射后失真;
  3. 视角转动需重新渲染法线结果的FrameBuffer;
  4. 光线追踪每个像素都会使用低差异序列的抗锯齿采样,而光栅化并无此特性,造成两者实际的渲染点不一致,容易引起物体边缘的不连续。

基于上述问题,引出本文的重点:光线微分法。

光线微分法

感兴趣的朋友可以搜索原论文:《Tracing ray differentials.》Igehy, H.本文结合这篇文章以及实际工程中的一些问题来介绍这个算法。
对于任一射线R\overrightarrow {R}可以表示为:

R=P,D\overrightarrow {R} = \lang \mathbf{P}, \mathbf{D}\rang

P\mathbf{P}为射线的起点,D\mathbf{D}为射线的方向向量。Ray Tracing的第一次求交时起点为相机的位置,求方向时将屏幕坐标考虑进来,令:

d(x,y)=View+xRight+yUp\mathbf{d}(x,y) = \mathbf{View} + x\mathbf{Right} + y\mathbf{Up}

View\mathbf{View}为相机的朝向,Right\mathbf{Right}为相机的xx轴方向向量,Up\mathbf{Up}为相机的yy轴方向向量,因此:

D=dd=d(dd)1/2\mathbf{D} = \tfrac{\mathbf{d}}{\begin{Vmatrix} \mathbf{d}\end{Vmatrix}} = \tfrac{\mathbf{d}}{(\mathbf{d}\cdot\mathbf{d})^{1/2}}

初始化时:

Px=0\tfrac{\partial\mathbf{P}}{\partial{x}}=0

Dx=(d(dd)1/2)x=(dd)Right(dRight)d(dd)3/2\tfrac{\partial\mathbf{D}}{\partial{x}}=\tfrac{\partial({\tfrac{\mathbf{d}}{(\mathbf{d}\cdot\mathbf{d})^{1/2}})}}{\partial{x}}= \tfrac{(\mathbf{d}\cdot\mathbf{d})\mathbf{Right}-(\mathbf{d}\cdot\mathbf{Right})\mathbf{d}}{(\mathbf{d}\cdot\mathbf{d})^{3/2}}

Dy\tfrac{\partial\mathbf{D}}{\partial{y}}的求解方法与Dx\tfrac{\partial\mathbf{D}}{\partial{x}}类似,本文不再列出。

当光线沿着方向D\mathbf{D}传播时,直到与某点相交时,得到交点P\mathbf{P}^{‘}:
P=P+tD\mathbf{P}^{‘}=\mathbf{P} + t\mathbf{D},求微分,得:

Px=Px+tDx+txD\tfrac{\partial\mathbf{P}^{‘}}{\partial{x}}=\tfrac{\partial\mathbf{P}}{\partial{x}}+t\tfrac{\partial\mathbf{D}}{\partial{x}}+\tfrac{\partial{t}}{\partial{x}}\mathbf{D}

上式中的Dx\tfrac{\partial\mathbf{D}}{\partial{x}}和前一步的Dx\tfrac{\partial\mathbf{D}}{\partial{x}}一致,因为射线直线传播时方向不变,那么如何求tx\tfrac{\partial{t}}{\partial{x}}?

image.png

如上图,射线从点PP出发,沿方向D\mathbf{D}传播,与ABC\triangle{ABC}相交于点PP{‘}。设ABC\triangle{ABC}的平面方程为Ax+By+Cz=dAx+By+Cz=d,则其法线N=[A,B,C]\mathbf{N}=[A,B,C],法线方向由三角形确定,与xxyy不相关。由几何关系得到:

(PP)N=tND\mathbf{(P-P’)\cdot\mathbf{N}} = -t\mathbf{N}\cdot\mathbf{D}

t=PNND+dND\Rightarrow t = -\tfrac{\mathbf{P\cdot\mathbf{N}}}{\mathbf{N}\cdot\mathbf{D}}+\tfrac{d}{\mathbf{N}\cdot\mathbf{D}}

tt求微分,可得:

tx=(Px+Dx)NNDd(NDx)(ND)2\tfrac{\partial{t}}{\partial{x}}=-\tfrac{(\tfrac{\partial{\mathbf{P}}}{\partial{x}}+\tfrac{\partial{\mathbf{D}}}{\partial{x}})\cdot\mathbf{N}}{\mathbf{N}\cdot\mathbf{D}}-\tfrac{d\cdot(\mathbf{N}\cdot\tfrac{\partial{\mathbf{D}}}{\partial{x}})}{(\mathbf{N\cdot{D}})^2}

接下来我们来分析PP{‘}处的UV坐标。已知PP{‘}的UV、法线、顶点坐标均为点ABCA、B、C三个顶点内差所得,令点ABCA、B、C处的占比分别为αβγ\alpha、\beta、\gamma,则满足以下条件:

α+β+γ=1\alpha+\beta+\gamma=1

αA+βB+γC=P\alpha\mathbf{A}+\beta\mathbf{B}+\gamma\mathbf{C}=\mathbf{P{}’}

[AxAyAzBxByBzCxCyCz111][αβγ]=[PxPyPz1]\Rightarrow \left[ \begin{array}{ccc} \mathbf{A}_x & \mathbf{A}_y & \mathbf{A}_z \\ \mathbf{B}_x & \mathbf{B}_y & \mathbf{B}_z \\ \mathbf{C}_x & \mathbf{C}_y & \mathbf{C}_z \\ 1 & 1 & 1 \end{array} \right]\left[ \begin{array}{ccc} \alpha \\ \beta \\ \gamma \end{array} \right]=\left[ \begin{array}{ccc} \mathbf{P{‘}}_x \\ \mathbf{P{‘}}_y \\ \mathbf{P{‘}}_z \\ 1 \end{array} \right]

假定当前的交点是满足上述内差条件的,则可得:

[AxAyAzBxByBzCxCyCz][αβγ]=[PxPyPz]\left[ \begin{array}{ccc} \mathbf{A}_x & \mathbf{A}_y & \mathbf{A}_z \\ \mathbf{B}_x & \mathbf{B}_y & \mathbf{B}_z \\ \mathbf{C}_x & \mathbf{C}_y & \mathbf{C}_z \end{array} \right]\left[ \begin{array}{ccc} \alpha \\ \beta \\ \gamma \end{array} \right]=\left[ \begin{array}{ccc} \mathbf{P{‘}}_x \\ \mathbf{P{‘}}_y \\ \mathbf{P{‘}}_z \end{array} \right]

α+β+γ=1\alpha+\beta+\gamma=1

M=[AxAyAzBxByBzCxCyCz]\mathbf{M}=\left[ \begin{array}{ccc} \mathbf{A}_x & \mathbf{A}_y & \mathbf{A}_z \\ \mathbf{B}_x & \mathbf{B}_y & \mathbf{B}_z \\ \mathbf{C}_x & \mathbf{C}_y & \mathbf{C}_z \end{array} \right],若M\mathbf{M}可逆,可得:

[αβγ]=M1[PxPyPz]\left[ \begin{array}{ccc} \alpha \\ \beta \\ \gamma \end{array} \right]=\mathbf{M}^{-1}\left[ \begin{array}{ccc} \mathbf{P{‘}}_x \\ \mathbf{P{‘}}_y \\ \mathbf{P{‘}}_z \end{array} \right]

由此我们得到了内差系数αβγ\alpha、\beta、\gamma和内差结果的变换关系。然而上述等式成立的条件是变换矩阵可逆,这个条件有的时候可能不满足,比如所有顶点都在xyxy平面上,此时所有点的zz轴分量为0,矩阵不可逆。

为了解决这个问题,笔者构造了一个新的空间,使得该空间的xx轴为三角形其中一边,zz轴为与三角形所在平面的法线和新的xx轴都成45度的向量,yy轴即为两者的正交向量,令新空间的变换矩阵为M1\mathbf{M_1},则变换后的顶点ABC\mathbf{A’}、\mathbf{B’}、\mathbf{C’}分别为:

A=M1A\mathbf{A’}=\mathbf{M_1}\mathbf{A}

B=M1B\mathbf{B’}=\mathbf{M_1}\mathbf{B}

C=M1C\mathbf{C’}=\mathbf{M_1}\mathbf{C}

ABC\mathbf{A’}、\mathbf{B’}、\mathbf{C’}依旧满足:

[AxAyAzBxByBzCxCyCz][αβγ]=M1[PxPyPz]\left[ \begin{array}{ccc} \mathbf{A’}_x & \mathbf{A’}_y & \mathbf{A’}_z \\ \mathbf{B’}_x & \mathbf{B’}_y & \mathbf{B’}_z \\ \mathbf{C’}_x & \mathbf{C’}_y & \mathbf{C’}_z \end{array} \right]\left[ \begin{array}{ccc} \alpha \\ \beta \\ \gamma \end{array} \right]=\mathbf{M_1}\left[ \begin{array}{ccc} \mathbf{P{‘}}_x \\ \mathbf{P{‘}}_y \\ \mathbf{P{‘}}_z \end{array} \right]

M2=[AxAyAzBxByBzCxCyCz]\mathbf{M_2}=\left[ \begin{array}{ccc} \mathbf{A’}_x & \mathbf{A’}_y & \mathbf{A’}_z \\ \mathbf{B’}_x & \mathbf{B’}_y & \mathbf{B’}_z \\ \mathbf{C’}_x & \mathbf{C’}_y & \mathbf{C’}_z \end{array} \right],则:

[αβγ]=M21M1[PxPyPz]\left[ \begin{array}{ccc} \alpha \\ \beta \\ \gamma \end{array} \right]=\mathbf{M_2^{-1}}\mathbf{M_1}\left[ \begin{array}{ccc} \mathbf{P{‘}}_x \\ \mathbf{P{‘}}_y \\ \mathbf{P{‘}}_z \end{array} \right]

对于PP{‘}的UV坐标S=[uv1]\mathbf{S^{‘}}=\left[ \begin{array}{ccc} u^{‘} \\ v^{‘} \\ 1 \end{array} \right],依然满足内插规则:

αSa+βSb+γSc=S\alpha\mathbf{S_a}+\beta\mathbf{S_b}+\gamma\mathbf{S_c}=\mathbf{S’}

M=M21M1M=\mathbf{M_2^{-1}}\mathbf{M_1},对xx求微分:

Sx=αxSa+βxSb+γxSc\tfrac{\partial{\mathbf{S’}}}{\partial{x}}=\tfrac{\partial{\alpha}}{\partial{x}}\mathbf{S}_a+\tfrac{\partial{\beta}}{\partial{x}}\mathbf{S}_b+\tfrac{\partial{\gamma}}{\partial{x}}\mathbf{S}_c

Sx=M[0]PxSa+M[1]PxSb+M[2]PxSc\tfrac{\partial{\mathbf{S’}}}{\partial{x}}=\mathbf{M}_{[0]}\tfrac{\partial{\mathbf{P’}}}{\partial{x}}\mathbf{S}_a+\mathbf{M}_{[1]}\tfrac{\partial{\mathbf{P’}}}{\partial{x}}\mathbf{S}_b+\mathbf{M}_{[2]}\tfrac{\partial{\mathbf{P’}}}{\partial{x}}\mathbf{S}_c

M[i]\mathbf{M}_{[i]}M\mathbf{M}的第ii行向量。

下两张图分别为使用光线微分和光栅化计算得到的Sx\tfrac{\partial{\mathbf{S’}}}{\partial{x}},为了显示更明显,将其值放大了10倍。

image.png

image.png

现在我们得到了Sx\tfrac{\partial{\mathbf{S’}}}{\partial{x}},对纹理进行采样时需要利用相关数据计算MipMap的等级。

渲染点PP{‘}与其向右和向上一个像素点对应的UV差值为:

ΔTxΔxSx\Delta\mathbf{T}_x\approx\Delta{x}\tfrac{\partial{\mathbf{S’}}}{\partial{x}}

ΔTyΔySy\Delta\mathbf{T}_y\approx\Delta{y}\tfrac{\partial{\mathbf{S’}}}{\partial{y}}

MipMap的等级lodlod可以表示为:

lod=0.5log2[max(ΔxΔx,ΔyΔy)]lod=0.5log_2[max(\Delta{x}\cdot\Delta{x},\Delta{y}\cdot\Delta{y})]

计算出了lodlod的值,我们需要对纹理进行三线性插值计算:

插值计算的两级MipMap分别为:

lodsub=floor(lod)lod_{sub} = floor(lod)

lodup=floor(lod)+1lod_{up} = floor(lod)+1

Fup=lodlodsubF_{up} = lod – lod_{sub}

分别采样lodsublod_{sub}loduplod_{up}两个等级的结果,再将两者进行线性插值,其中loduplod_{up}的占比为FupF_{up}

使用光线微分后,渲染的结果如下:

image.png

image.png

总结

要做出高质量的光线追踪渲染,在做纹理采样时需要应用纹理过滤,光线追踪时由于无法使用诸如dFdxdFdx的函数,需要根据射线的表达式手动计算微分,而本文所用的光线微分便为其中一种方法。需要注意的是,本文仅对光线直线传播时进行了分析,当光线发生折射和反射时,光线的方向发生了变化,还需要将Nx\tfrac{\partial{\mathbf{N}}}{\partial{x}}Ny\tfrac{\partial{\mathbf{N}}}{\partial{y}}考虑进来,感兴趣的读者可以阅读上面的Paper,这部分的内容我也将在近期分享。

参考

《Tracing ray differentials.》Igehy, H. (1999). SIGGRAPH ’99 Proceedings

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYYKuuh9' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片