光线跟踪之简介
来源:互联网 发布:签证cp同人文乐乎 编辑:程序博客网 时间:2024/05/29 12:29
2016-04-25
我以前学习OpenGL 的时候,总是在一些基本的概念(display list,sampler,vao,vbo等等)上浪费了不少时间,然而,在前面的blog 里也提到过,这些知识都不是图形学真正重点的东西,只是OpenGL 这种实现中的概念而已。我希望,打算学学渲染的同学,应该都首先尝试着写出ray tracer,而不是是去使用DX11或者OpenGL、Vulkan。OpenGL只是一种为了加速而采用很多投机取巧的方式,刚开始的时候学习它反而会给自己带来困扰。所以,我尝试着以ray tracing来介绍图形学中较为基础的知识点。这一系列的blog都有对应的一个完整的程序。后续也会有多篇此系列的文章。
既然要写最简单的实例程序,我们就尽量减少代码量、文件个数,用纯C来写。为什么光线追踪程序大多是C++ 写的呢? 是否采用C++来写对于简单程序来讲并不是很重要。 当程序的规模增大时,C++的继承多态机制能够更好的组织代码。这里的实例程序就不用考虑这个因素了。
读者应该对图形学、光线追踪、OpenGL等有一些了解为好。下面参考文档的前几个都是很好的学习材料。在这里,我尝试这以自己的经验讲解一下图形学中常见的问题,而不是按照教科书的那种方式来讲(我也没有那种能力),我只是想从一个学习者的角度来看,哪些步骤对初学者比较有难度的,需要多投放精力的。
一、 图形学的基础知识
坐标系
我们需要构建一个三维的世界,在这个世界中放置物体,我采用较为常用的右手坐标系,多数建模工具采用,这也是OpenGL 中采用的,DX采用的是左手坐标系。不过,坐标系的选择是没有大的关系的,系统内保持一致即可。
“上帝说要有光,于是就有了光”
光源实在太重要了,没有光,我们什么也看不见。光源也是有很多种类的,如最常见的点光源(灯泡),面光源(街上的广告灯), 平行光(太阳光),环境光()。渲染程序中,一般是直接叠加多种光源来模拟光照效果。而且,关于光照效果的模拟,有多种模型,什么意思呢?就相当于一道题,可以选择几何法来解,也可以选择代数法解题。最常见的Phone模型的原理如下图所示:
本文示例用点光源。我们假设点光源极小,不像灯泡有比较大的发光钨丝,所以我们看不到这个点光源。
再有物体
如果只有光而没有物体,我们看到的效果就如同观察太空的效果,几乎什么都看不到:全黑的。光照射到物体上,才能被我们看见。所以,我们需要把物体的形状、位置,用代码表示出来。有了物体,才能选用光照模型进行计算。
构建世界
把三维的物体,放到二维的view plane 上观察,就相当于把三维的世界投影(projection)到二维世界。渲染程序中最常用的两种投影模式:正交投影(平行投影),透视投影。
我们假设xoz 平面代表地面,是绿色的。物体盒子是蓝色,
在orthogonal 投影下,就看不到地面了,只能看到一个正方形。正交投影在CAD工具软件中有使用。
在透视投影下,我们能够看到地面,和”天空“,类似与人眼的观察效果。越远的物体,投影到view plane 上的面积越小。透视投影是我们关注的重点。
二、ray traing 过程 构建最简场景并用程序表示
光线跟踪的工作流程
光源发出的光到达物体表面之后,会发生发射和折射,反射分为镜面反射、漫反射。现实世界的光线会发生多次反射折射,我们称之为全局光照。ray tracing 是global illumination的一种实现, 模拟我们真是世界的光照方式,只不过采用与真实过程相反的过程,用来计算镜面反射、折射、阴影。全局光照的另外一种实现是 Radiosity,用来实现漫反射。ray tracer中我们一般需要表现出三种ray:
- 眼睛”发出的“,经过view plane 栅格的ray(e_ray)
- e_ray 与物体交点到光源连线(用来检测是否是阴影区域,还是直接光照区域)
- 折射光线
假设物体是一个地面,我们在后续的学习过程中再让物体逐渐复杂化。那么平面应该如何表示呢?
typedef struct {
Point p;
Vector normal;
} Plane; 这样就可以表示一个平面了, 而且是无限大的。不妨指定 xoy 平面,Plane basePlane = {0,0,0, 0,1,0};
本来,光线照射到物体上,反射出无数的光线,其中很小一部分会进入人的眼睛。如果模拟这个正向的过程,计算量上来看那是不可行的。因为光源发出的光是无限密致的,物体表面可以无限分割,有无限细节。所以,只能 采用逆向过程。当然,这也会引入一些问题:需要判断判断整个过程中模拟的光线是否真正存在。
三,程序计算例子
我使用最简单的渲染场景:一个平面 + 一个点光源。最简单版本的算法伪代码:
for (ray in rays)
for(obj in objects)
intersectPoint = intersect(ray, obj)
if ( NULL != intersectPoint ) {
ray2Light = light - intersectPoint // 点相减,获得向量
distance = vectorLenght( ray2Light)
double scale = 1 / distance^2 // 根据距离获取光源光线的衰减率
color = materialColor * scale // color 是最终呈现在view plane 上的像素
}
else
color = 天空的颜色
在这里,我们偷懒一下,把模型简单化处理。我们假设eye 位置是固定的,view plane 也是固定的,以世界坐标的形式给出。我们再次假设光源发出的光没有衰减,从无穷远出发过来的光也没有衰减(好清爽的世界啊)。设置地面平面为红色色, 光源为纯白色,视点、光源处在平面上方,我们能够看到的应该是一望无际的蓝色和黑色的天空。 就如:
实际上,光源的光线会随着距离衰减的。 常见的有两种光线衰减公式:
- i1 / i2 = d2^2 / d1^2 => i1 单位距离的衰减因子, d1 单位距离,故 i2 = 1 / d2^2
对于二次曲线来讲,一次部分可以的影响不大。也就是说第一公式就是第二个公式的简化版本。
假设视线与物体交点P,P到光源L 的向量 pl, P 点最终颜色就是rgb三个分量 各乘以 1/ lenght(pl) ^ 2。 所以,我们看到的是地面上一个逐渐变暗的光圈,而且,变化的速度还很快。有如路灯的效果。也有项目采用线性速度变化的公式,计算量和渲染效果平衡。视线没有和平面相交时,就照射到了天空,在这里,我强制把天空设置为白色,以便把无穷远处区分出来。
最简单的光线最终程序就完成了。我们走出了第一步。接下来,还有几篇blog会讲解ray tracer基础的其他部分。
四、结果的保存
我们计算完了整个过程,要怎么样才能把计算结果展现出来呢。 这是非常简单的。最终绘制在view plane上的像素,我们直接保存为bmp 格式的图片。bmp格式采用位映射存储格式,不采用压缩,扫描方式:按从左到右、从下到上的顺序,和view plane上像素的排放格式是一致的。有开源lib 能帮助写bmp 文件。
本文代码在https://github.com/cloudqiu1110/tinyrt.git single_plane 分支
五、相关讨论
对比Ray Tracer 和 OpenGL
我相信,很多同学都是对游戏里面那种渲染是更感兴趣一些的,实时的、有交互的,就如同我们刚开始学习编程的时候,对有界面的程序更加感兴趣一些。
OpenGL实时性比较高,而Ray Tracer 一般比较慢,为什么呢?
for each (obj)got projected pixel areafor each pixel in depth buffergot nearest pixel by depth comparation
OpenGL 遍历object, 渲染为二维图片到buffer(带有depth), 比较pixel depth, 决定哪些像素被舍弃。这是硬件实现的,而不需要程序负责。 OpenGL采用局部光照。 只考虑光源到模型表面的照射效果,物体之间的光照效果是不考虑的,遮挡效果用像素的深度来判断。 在游戏中,也会要求有全局光照的效果。那是怎么实现的呢?其实,是采用了一种取巧的方法,就是提前把模型建好,在3ds max,maya里面预烘培为贴图,游戏中直接应用贴图来模拟阴影效果。
for each (ray)for each (obj)pixel = intersect( ray, obj)
实际上相当于把一层三维空间的计算 降维到 二维空间的计算了,所以计算量就少了。也就是把物体相互之间的光照效果忽略掉了,这也是“全局光照”和“局部光照”的区别。
“栅格化”(rasterization) : 并不是只有OpenGL 才有这个术语的,也不是3D 图形渲染最早使用的,别忘了,之前还有过2D 图形加速器的。 请参考wiki。
最后总结一下,知乎上有个话题“现阶段应该怎么学习计算机图形学呢?”,我觉得对看到这篇博客的同学都应该围观一下“文刀秋二“的回答。我以前就是以为学会了OpenGL,就算是学会了图形学,至少是入门了。但是,这只是我的一厢情愿而已。
ref :
- http://web.cse.ohio-state.edu/~hwshen/681/Site/Slides_files/basic_algo.pdf 这篇文档是必看的
- http://www.jianshu.com/p/0375389e6a3e
- http://www.scratchapixel.com/lessons/3d-basic-rendering/introduction-to-ray-tracing 必须看
- https://en.wikipedia.org/wiki/Ray_tracing_%28graphics%29
- Ray Tracing from the Ground up
- 《3D数学基础:图形与游戏开发》
- https://en.wikipedia.org/wiki/Rasterisation
- https://en.wikipedia.org/wiki/Rendering_(computer_graphics)
- https://en.wikipedia.org/wiki/Raster_graphics
- http://www.scratchapixel.com/lessons/3d-basic-rendering/introduction-to-ray-tracing 非常不错的教程
- https://www.ics.uci.edu/~gopi/CS211B/RayTracing%20tutorial.pdf
- https://raw.githubusercontent.com/quietshu/-paper-cg-RayTracing/master/RayTracing.pdf 类似于catelog, Good
- https://www.ics.uci.edu/~gopi/CS211B/RayTracing%20tutorial.pdf
[ 主页]
1 0
- 光线跟踪之简介
- CG_Assignment_光线跟踪之光线散射
- 第一章: Optix光线跟踪引擎简介
- 光线跟踪
- 光线跟踪之 物体表示方式
- 光线跟踪之 视窗与投影变换
- 【译】光线跟踪:理论与实现(一) 简介
- 光线跟踪之 :Phong模型,镜面反射及阴影
- 光线跟踪作业
- 光线跟踪的算法
- CUDA 交互式光线跟踪
- 光线跟踪程序
- 光线跟踪的基本原理
- 光线跟踪进阶版
- 光线跟踪的算法
- 光线跟踪算法
- 光线投射算法与光线跟踪算法
- 光线投射与光线跟踪算法归纳
- Java实例说明 嵌套类包括内部类(即非静态嵌套类)和静态嵌套类 两者的区别
- 如何解决PC端和移动端自适应问题?
- 【初等概率论】 03
- mfc中父对话框变量获取子对话框控件的变量
- Android 小问题汇总解决
- 光线跟踪之简介
- Linux磁盘管理:LVM逻辑卷的创建及使用
- POJ-2828-Buy Tickets
- Oracle中INT、FLOAT、NUMBER区别
- VMWare中设置静态IP CentOS
- 光线跟踪之 物体表示方式
- ubuntu网页开发问题汇总[1]
- intelliJ IDEA 版本更新后,如何关闭参数提示。
- Android APK打包(多渠道)