unity 显示太阳_Unity光线追踪实践

3293fca161921d9f40c25b97537f8678.png

NVIDIA前段时间推出了NVIDIA RTX,使得光线追踪这一古老而又年轻的技术再次进入人们的视野,相比传统的光栅化算法,光线追踪更加符合数学家对物理世界的描述,得益于这个技术我们能在游戏或者电影里面看到更加炫酷,更加真实,更激动人心的画面。本文立足于Unity渲染API,以及Real Time Rending等参考资料,意在说明实现一个简单的光线追踪渲染器需要注意的点和主要的模块,主要用于技术探索和交流。(文章所用原理图片来自网络)

1.光线追踪原理

1.1渲染方程

渲染方程可以简单理解为:在一个光照环境中,一个物体w在不同观察方向上的光照强度分布情况。本文后面的光照模型,phone模型等,都是对这个方程的简单模拟。

07a5a09d160bab114ffd8cd40031e0d6.png

5b5509e2a1c88c67557c38902bb4c26d.png

d3b7837a307ef3ffdaea86aa805f74db.png

渲染方程中,x为当前的物体,L(x,w) 表示,在x处,沿着w方向射出的光照强度,Le为自发光,后半部分是反射光。方程里面的坐标表示都是球坐标。,球坐标的解释如下,具体的计算和推导细节请读者参考其他资料。

16c1603540ea38ade661e48dbec9b4c9.png

物理学中通常使用的球坐标(r, θ, φ)(ISO 约定):径向距离 r,极角θ (theta) 与方位角φ (phi)。在数学里,球坐标系(spherical coordinate system)是一种利用球坐标表示一个点P在三维空间的位置的三维正交坐标系。右图显示了球坐标的几何意义:原点与点P之间的径向距离(radial distance),原点到点P的连线与正z-轴之间的极角(polar angle),以及原点到点P的连线在xy-平面的投影线,与正x-轴之间的方位角(azimuth angle)。它可以被视为极坐标系的三维版本。

1.2光线追踪过程

严格来讲,光线追踪应该叫做视线追踪,其本质是逆物理过程,这里的物理过程指的是:光线从光源发出,照射到物体上面通过屏幕进入相机,从而能够成像。光线追踪就是假设从摄像机发射一束视线穿过屏幕到物体上,观察这个地方的光照结果,并且把这些结果叠加从而作为屏幕上当前点的颜色信息。这里光照结果大体分为两个部分,第一个部分指的是光照直接投射到物体上产生的结果,第二个部分指的是其他物体发出的光线对该物体的影响,这时候就需要对其他物体递归使用视线追踪,最终把结果叠加。在本文中,光线的产生和相交运算采用Unity Api,主要是Physica.Raycast模块,为了更好的性能这一部分操作理应在GPU中进行,这也是笔者后面的优化方向。

80bb73e391bd43b54315dc4581621c89.png

2.Unity常见API和概念

  • onRenderImage

当图像已经渲染完成,相当于后期处理,这里能够拿到原始的图片,然后通过3d数据进行图像的修正。这个函数可以访问当前正在渲染的图像。这个函数只出现在Camera上的脚本

  • Graphics.Blit

后期处理的过程:在onRenderImage里面拿到当前的渲染图像,然后Blit传递给shader进行渲染,渲染完成之后会再次通过onRenderImage进行回调,这时候可以通过Blit函数渲染到屏幕上。sourceTexture会成为material的mainTexture。Blit(sourceTarget,des,material),渲染的时候会使用material里面的shader。

  • Mesh

Mesh,网格 。 MeshFilter 用于获取网格数据的组件。MeshRender,用于渲染网格的组件。因为mesh可能被多个模型使用,mesh是每个模型单独的,sharedMesh则指的是共享的mesh,修改之后所有使用的mesh都会进行改变。Mesh实际上是三角形的点和边的集合。

  • ComputeShader

需要判断ComputeShader是否支持,ComputeShader不能挂在mesh上面,只能在脚本里面调用。SetTexture 传递数据,Dispatch 函数名字,线程组个数,每个线程组的线程数目,每次处理的像素个数,StructBuffer可以双向传递数据albedo

  • Awake

创建之后立即调用

  • Start

update之前调用

  • 部分Shader内置函数

UnityObjectToClipPos

本地坐标转换成相机空间的坐标,光线追踪里面都是以相机空间作为基准。在顶点着色器拿到的都是本地坐标,计算的时候需要转换。

unity_CameraInvProjection,摄像机投影矩阵的逆矩阵

  • 冯氏光照模型材质

l Albedo Color 反照颜色

l Metalness 金属性

l Fresnel Color 菲涅尔颜色,反射颜色

l Roughness 粗糙程度 光滑程度

冯氏光照模型决定了光照的颜色

Albedo定义了物体的整体颜色

3.Unity光照系统参数

Unity的光照模型至关重要。Unity里面有四种光源类型

  • Directional Light 方向光,类似太阳光的效果,消耗的系统资源最少
  • Point Light 点光源,类似蜡烛
  • Spotlight 聚光灯,蕾丝手电筒
  • Area Light 区域光,一般用于烘焙贴图,在光线追踪里面可以用来产生软阴影,这里会涉及对光源进行采样,但是不规则的光源计算起来太复杂,所以后面都会把光源模拟成球体来简化计算。

152d4087b837464ada28854857fd2318.png

4.参考资料

  • https://github.com/SIZMW/unity-raytracer/blob/master

CPU计算,只计算了反射,但是对从模型获取三角形,光源的处理有很强的借鉴意义。

  • http://blog.three-eyed-games.com/2018/05/03/gpu-ray-tracing-in-unity-part-1/

实现了GPU求交,采样等等,优点是能够借鉴GPU处理光线追踪的框架。缺点是求交运算太简单,材质处理也太简单

  • 《Ray Trace In One Week》

对光线追踪和图像渲染有一个高屋建瓴的认识,能够快速建立起一个大概的印象,但是只能渲染球体,其他进阶的处理还需要额外看资料

  • 《Real Time Rending》

既有基本框架,也有高深的数据描述,但是没有多少实践部分

5.光线追踪模型框架

参照前文所述的光线追踪原理,我们可以得出一个光线追踪模型的基本框架。

//参考 real time rending 

6.法线插值计算

当渲染一个三角形的时候整个面的法线方向都是一致的,因此会出现棱角分明的效果,需要对法线进行插值。Mesh里面包含了每个顶点的法线信息,顶点法线其实也是通过对面的计算然后加权平均计算的,raycast会返回重心坐标,这个坐标反应了重心分割的时候三块面积的大小。以此对三角形面的法线进行插值。需要注意的是如何获取顶点index,根据三角形的index,去mesh.triangles里面查找。mesh里面的信息都是本地坐标系统需要用transform进行转换。

public 

7.局部光照模型简单实现

using 

8.软阴影

在光线追踪里面,阴影的产生和判断都是通过发射shadow ray判断是否有障碍物来计算的,这样的话其实阴影会有一个很明显的边界,在现实世界,光源不可能是一个点,方向光也不可能绝对平行,因此阴影会有一个平滑的过渡过程,这样的阴影就是软阴影。在光线追踪里面,软阴影的实现采用发射shadow ray的时候在光源表面随机采样来实现,按理说应该用光源上的每个点进行计算,但是这样计算量太大了。这里光源就不是一个点(其他部分为了方便计算都抽象成一个点)。

Vector3 

2d29163bcb4eaca8dc671fc65ca128c8.png

9.体积光渲染

093427d2a8ea3b371aa642075d7b0be2.png

32a84f5a0fe63f2816f31d0eb794b570.png

体积光使用RayMarch的方式渲染,测试每一条光和光源之间的距离,采用合适的衰减和采样函数来确定当前方向上的光线表现。实现如下:

目前光线的衰减函数是和距离平方成反比,这样的函数衰减特别厉害,会导致光线到不了物体,后面需要优化。下图是函数衰减曲线。

当物体在光线的阴影里面的时候需要进行相交判断,具体做法是从当前位置向光源发射一束光线,如果没有碰撞说明没有遮挡,这里又个需要注意的地方,有可能会导致光源前后都形成遮挡区域这时候要根据碰撞距离来保留合适的那一个。

cdbf8daa1c0a736588991b8fee4c43a0.png
using 

11.初步结果

  • 采用图像二次采样,即关闭所有光照,环境光调整为1,然后在需要图像颜色的时候对原始图片进行采样,这样其实不正确,但是也能得到一个近似结果

1670bc778c7767b25e1bf2e1ec093919.png
  • 上面那种方法,对阴影采样进行收敛之后的结果

5f76d7e907b5ddafe0012626f4a6aeea.png
  • 上面的方法,材质全光滑的结果

b676b23e18c4d89972cfd89edcdd025a.png
  • 下面都是正常处理,采用Unity的材质系统处理颜色。单一方向光

051f9cc43bdbed7cc42f2a1a555fc62b.png
  • alpha = 3.2f,specular = 1.1f,可以看到出现了光斑

c6eb03225fbc73b5fee97778ff2074b8.png
  • 修改成B-Phong模型

50326c0303c0aed540a06115d48d3533.png
  • 根据Mesh读取颜色

de0b8fa4f2399fec9d13df08e6319b30.png
  • 法线插值效果

008886b7c8b6a3cc73294fbb41ca2e70.png

74fee27b286495fdafa1ce05fef98af2.png
  • 一些模型

13ae0eeb2ea69aa88750a65117c8a91a.png
  • 软阴影,50条光线

b04ca51f3817a456a45861e1501caf1e.png
  • 软阴影 400条光线

dde2adef0c0315dc0f957c42f3d31cba.png
  • 无限制体积光

afe175471dc5e3ee69bd01f63fce4e6c.png

3eca384ee44b2ec95c3f7b30f3afa283.png

12.后期的构想

一方面需要提升性能,因此我打算在GPU里面实现相交,采样等运算,Unity仍然负责提供材质的解析,一些初始化的工作。另一方面需要完善自发光物体的效果,因此引入了体积光渲染,但是要渲染一个不规则的自发光物体,还需要对体积光渲染进行修正,比如可以对发光物体先进行一次发光物体的求交运算。细节问题欢迎私信交流讨论。

代码已经开源,欢迎学习交流:https://github.com/zfymoon/RayTrace

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值