Unity实现GPU光追——Part1完全镜面反射
发布网友
发布时间:2024-09-26 06:00
我来回答
共1个回答
热心网友
时间:2024-11-12 01:21
本文主要基于 blog.three-eyed-games.com 的文章进行学习解读,并加入了一些个人内容。
一、光线追踪
光线追踪的原理与光栅化相比,更接近真实物理世界。现实中的光从光源出发,与物体交互,经过一系列反射折射散射进入我们的眼中。光线追踪也是类似的过程,但只有一小部分光会进入视野,所以模拟过程中从光源发出光子来计算是不划算的。但由于光路可逆的原理,我们可以从摄像机发射光子来反相模拟这种情形。本章实现的是Whitted光追,它能实现硬阴影和完美的反射,同时也是其他光追的基础。
二、基础设置
首先我们创建一个脚本RayTracingMaster.cs,用于处理光追的画面。通过渲染时会调用的OnRenderImage函数完成后处理,渲染的内容会存在_target中,并在最终将屏幕输出的图片替换成_target。Dispatch调度ComputeShader同时指定线程组数量,我们的线程组大小为8x8x1个线程,我们要为每个像素都创建一个线程,因此线程组数量为代码中所示。接下来向Shader传递Camera的数据,分别是相机空间到世界空间的矩阵与相机的投影矩阵。
三、ComputeShader部分
我们创建一个计算着色器RayTracingShader.compute,在这里,我们定义需要脚本传入的矩阵,同时定义光线的结构体,对于每个像素的中心,我们计算光线的原点和方向,并将后者作为颜色输出。接下来我们对天空球进行采样。
四、追踪部分
终于到了进行物体渲染的部分了,首先我们创建一个结构体和一个方法来描述光线击中物体的状态。我们先尝试做出与地面相交的效果,先对y=0的地平面做尝试。首先是Intersect函数即碰撞测试,当光线与当前物体符合碰撞条件时更新RayHit数据。为了让Intersect函数更容易使用,我们将它放入Trace这个框架内。此外,我们还需要着色函数,使得当碰撞发生时产生不同的效果。此处我们设定,当击中物体时,返回法线,否则返回天空球采样。
五、抗锯齿
在这里我们采用一种TAA抗锯齿,我们通过在时间上抖动每个射线的出发点,并叠加在原图上,从而达到抗锯齿的效果。为此,我们创建一个新的Shader来实现抗锯齿的效果,其核心代码如下,_Sample为我们传入的一个参数。其混合模式为Blend SrcAlpha OneMinusSrcAlpha。当为第一次采样时不透明度为1,之后为1/2,1/3如此递减。
六、反射
反射的原理很简单,光线击中物体后,损失一定能量,根据击中平面的法线发射。我们让光线在反射过程中损失一定能量,当经过最大的反射次数或者能量归零后停止。给光线加入一个energy属性,并在Create函数中将其设置为float3(1.0f, 1.0f, 1.0f),随光线反射减弱。我们设置最多八次跟踪,将Shade函数的结果乘上光线的能量后相加。我们调整CSMain如下。
七、定向光
现在我们还只有完美的镜面反射,我们还需要阴影与漫反射,因此一步我们需要引入定向光,我们在RayTracingMaster中加入定向光,并传入Shader中,同时也需要在Update中加入对定向光的跟踪,以判断是否要刷新画面。
八、更新结构体
我们不可能一直在Shader中手动设置数据,因此我们需要将球以及之后的三角面数据传递到ComputeShader中。首先我们在Shader中创建一个结构体Sphere,球类相交检测的传入参数也需要改为球体。由于光线发生相交时交点的属性是有物体决定的,因此我们在相交检测中更新击中点的属性,在shade中这两个属性也由相交点传入。之后在C#脚本中我们定义相同的结构体获得球体,并绑定Buffer传入。这里创建了一个新的脚本生成随机的球体。最后由RayTracingShader.SetBuffer(0,"_Spheres",_sphereBuffer)传入。