鸿邑网站建设,中铁建设集团有限公司电话号码,seo课程培训机构,重庆做网站个人前言
本文参照自raytracing in one weekend教程#xff0c;地址为#xff1a;https://raytracing.github.io/books/RayTracingInOneWeekend.html
什么是光线追踪#xff1f;
光线追踪模拟现实中的成像原理#xff0c;通过模拟一条条直线在场景内反射折射#xff0c;最终…前言
本文参照自raytracing in one weekend教程地址为https://raytracing.github.io/books/RayTracingInOneWeekend.html
什么是光线追踪
光线追踪模拟现实中的成像原理通过模拟一条条直线在场景内反射折射最终获知物体表面的颜色。现实世界中光线最终射向相机获得成像光线追踪则是从相机出发向场景中反向发射光线从而推出相机“底片”中每个像素的颜色。
现实中的相机很发杂包括多组透镜在成像时不是光线直接射入相机需要经过多次折射。我们这里的光线追踪更类似于在模拟小孔成像只不过小孔成像获得的图像是反置的我们直接得到正向的结果相当于对反置图像做了反置我们在小孔的位置放置相机。 光线追踪和光栅化是两种不同的渲染方式光栅化相当于把物体表面直接反射或发射的颜色返回给相机“底片”场景中的各种阴影、遮蔽等效果都是通过预计算等方法得出的而光线追踪会考虑物体表面光线多次反射或折射的结果直接可以得到场景中的阴影、遮蔽等细节效果。
在具体实现的过程中我们会在相机的正前方设置一个虚拟画布相当于相机的底片正常来说相机底片应当是在相机背面的不过为了直观以及便于确定每条光线的方向直接在相机前方设置虚拟画布上每个位置的颜色代表了最终渲染结果对应像素的颜色。在发射光线时通常以相机为起点虚拟画布上的每个位置为终点构建一条射线每个方向可以根据采样设置发射多条射线。每个方向的射线的平均结果为对应像素的最终颜色。 光栅器的构建过程
这一部分是我在学习raytracing in one weekend教程时我认为重点部分的罗列。
图像格式
raytracing in one weekend教程中采用了ppm格式这种格式很简单可以用ASCII文本表示。详细介绍可以参考https://zhuanlan.zhihu.com/p/609960339
基本的光追过程
简述一下光线追踪的过程 屏幕上的每一个像素都进行光线投射。 光线的每次投射都需要判断交点而且投射到交点后还可能产生反射、折射那么就往相应的方向继续进行新的投射直到投射到天空或者投射次数达到限制。 最后将每个交点的受光照情况以一定权重综合起来得到一束光线获得的颜色根据采样次数每个像素发出的多个颜色的平均值为该像素的颜色。
下图是一个示例。 光线追踪的伪代码
RayTracing(Ray ray, hittable_list world, int depth){if(depth 0)return black color;hit_record rec; // 弹射点的属性记录if(Intersect(ray, world, out rec)){material rec.mat; // 弹射点所在物体的材质normal rec.normal;localColor ShaderCalculate(ray, material, normal);out_ray Get_outputRay(ray, material, normal);localColor shaderCalculate(direction,hitpoint,normal);return localColor * RayTracing(out_ray, world, depth - 1);}else{return the color of background;}
}抗锯齿
看到一个有趣的真相每个小像素块不是正方形参考文献不过为了简单起见我们假设每个小像素块是正方形。
这里为了进行抗锯齿采用了一定程度的随机即从相机向虚拟画布发射光线时以像素为单位为射线的终点做随机扰动。
在代码中的体现如下
ray get_ray(int i, int j) const {// 获取位置 i,j 处像素的随机采样光线。auto pixel_center pixel00_loc (i * pixel_delta_u) (j * pixel_delta_v);auto pixel_sample pixel_center pixel_sample_square();auto ray_origin center;auto ray_direction pixel_sample - ray_origin;return ray(ray_origin, ray_direction);}vec3 pixel_sample_square() const {// 返回一个单位像素正方形周围的随机点。auto px -0.5 random_double();auto py -0.5 random_double();return (px * pixel_delta_u) (py * pixel_delta_v);}漫反射材质
最简单的漫反射材质在光线射入表面后会在法线半球随机射出。
而如何获得一个随机的单位球内的向量文中给出的方法是在单位正方体内随机取点将不在单位球内的点丢弃。而进一步删选是否在表面法线半球则可以通过将获得的向量与法线点积如果点积结果为正则采用为负则丢弃。
为了让漫反射结果更真实我们应该采用Lambertian Reflection。采用这种方法在采样时越靠近法线处概率越高。我们可以在与表面交点相切的球体内采样反射光线示意图如下 gamma矫正
另外需要注意gamma矫正简单来说屏幕是处于gamma空间上的而我们渲染的结果在未经处理时是在linear空间上的为了使色彩不失真我们需要将渲染的结果转换到gamma空间上。
以下是gamma矫正前和gamma矫正后的对比图 金属材质
这里的金属材质与镜子比较类似金属材质有一个fuzz参数代表材质表面反射的模糊度。当反射模糊度为零时这个材质就相当于一个带颜色的镜子。
fuzz参数反射时可以对反射光线加一个随机表示模糊效果。具体随机方式如下图对反射光线的末端加一个半径为fuzz范围的球体随机。 金属材质的效果 玻璃材质
引入了光线的折射下面贴一下教程原文的计算表示过程。 在做折射时需要注意全反射的情况。
另外在现实生活中当我们贴近玻璃表面时玻璃会表现地像镜子这个效果可以用Christophe Schlick给出的公式来模拟。 在代码中当F大于给定的值时我们认为此时为反射。 F ( F 0 , θ i ) F 0 ( 1 − F 0 ) ( 1 − c o s θ i ) 5 F 0 ( η 1 − η 2 η 1 η 2 ) 2 ( η − 1 η 1 ) 2 F(F_0,\theta_i) F_0 (1 - F_0)(1 - cos\theta_i)^5 \\ F_0 (\frac{\eta_1 - \eta_2}{\eta_1 \eta_2})^2 (\frac{\eta - 1}{\eta 1})^2 F(F0,θi)F0(1−F0)(1−cosθi)5F0(η1η2η1−η2)2(η1η−1)2
此时我们得到的是一个通过物体后光线颠倒的效果这显然不真实。 有一个trick我们可以镶嵌两层玻璃球内层的玻璃球采用负半径让表面法线颠倒消除之前的颠倒效果。 散焦模糊defocus blur
这个概念是模仿相机的景深指的是在相机拍摄时焦距附近的图像会很清晰而焦距之外的图像比较模糊。
要模仿真实的相机我们还需要模拟相机内各种透镜的折射这太复杂了。为了简单一点教程中把我们的相机从发射点扩展为发射圆盘即每次发射光线时从一个半径为r的圆盘中发射光线穿过虚拟画布。这种模拟不是严格的相机成像但是效果还不错具体原理我没怎么搞清。渲染出来的结果如下图 最终的渲染结果
最终作者给了一个大场景我在机器上跑了十多个小时才跑出来。 完整代码
不想上传个单独的github项目了就传在网盘上吧 链接https://pan.baidu.com/s/1TQUo7GbRUsR-tyLDOv_vOg?pwdduxm 提取码duxm –来自百度网盘超级会员V6的分享 ps: 我只分享了源代码没有什么依赖库应该可以直接跑出图片。
参考
https://raytracing.github.io/books/RayTracingInOneWeekend.html
https://zhuanlan.zhihu.com/p/168791125
https://zhuanlan.zhihu.com/p/357142662
https://www.cnblogs.com/KillerAery/p/15106773.html