33.1 怎么用ray tracing画特殊长方体

在光线追踪中被用到的一种常见形态是长方体盒子。这种基本物体被用于可见物体和包围盒,包围盒被用于加速复杂物体的相交测试。

吐槽:单词都认识,就是不知道讲的是什么。“包围盒”是什么鬼?不懂!不过感觉好像很厉害的样子。

表面法向量和坐标轴平行的长方体是最简单的形式之一。

我们先就画“表面法向量和坐标轴平行”的长方体。

先回忆一下,已经学过的基本物体的画法:球、多边形。

球:

第一步:一条光线出去,判断是否撞上球?(联立光线和球的方程,判别式大于零)

第二步:是撞上了,求得实根,判断实根是否离光线起点最近?(二次方程求根公式求得实根,然后判断实根是否在有效范围)

第三步:是最近,将该光线在画面中的颜色设置为球的颜色。(根据球的材质和交点处的法向量设置球的颜色)

多边形:

第一步:一条光线出去,判断是否撞上多边形所在的平面?(联立光线和平面的方程,只要光线方向向量和平面法向量不垂直,就会有实根)

第二步:是撞上了,求得实根,根据实根求得光线和平面的交点,交点是否在多边形内?(1,根据平面法向量确定主坐标;2,去掉交点和多边形所有顶点坐标中的主坐标,完成所有点在UV平面的投影;3,将交点移至UV平面原点;4,从原点沿着+U轴引一条射线,射线与多边形的交点个数为奇数,则原交点在多边形内,反之在多边形外)

第三步:交点在多边形内,才算光线撞上了多边形。判断实根是否离光线起点最近?(判断实根是否在有效范围)

第四步:是最近,将该光线在画面中的颜色设置为多边形的颜色。(根据多边形的材质和交点处的法向量设置多边形的颜色)

根据“球”和“多边形”的画法经验得出:要画一个物体,只要判断光线是否和物体撞上和求得撞击点到光线起点的距离,然后根据物体的材质交点处的法向量设置物体的颜色。

现在我们要画是表面法向量与坐标轴平行的长方体,这类长方体的特殊之处在于:

1,六个平面的法向量是已知的:(1,0,0),(-1,0,0),(0,1,0),(0,-1,0),(0,0,1),(0,0,-1)------------------------------交点处的法向量基本搞定;

2,同时,三对平面垂直分别空间平面XOY,YOZ,ZOX,所以长方体在个平面上的投影不会变形。

法向量搞定,材质先不用管,

接下来只要确定:光线撞上了长方体、撞击点到光线起点的距离。

光线和长方体是定义如下:

书上提供了一种很巧妙的方法来判定光线是否撞上长方体。

由于这类长方体的特殊性,光线和长方体的位置关系在坐标平面的投影是这种情况:

然后,书上给出了如下测试项,若其中某一项测试失败,则光线不会撞上长方体;反之,若通过所有测试项,则光线撞上长方体。

测试项如下:

如上,对于每一坐标(X或者Y或者Z),测试项中都有三个可能“FALSE”的地方,所有长方体和光线的坐标关系通过这3*3=9个测试之后,则表示光线撞上了长方体。

理解和分析:

1,t1x, t1y, t1z, t2x, t2y, t2z表示的不是什么鬼投影或者垂直距离,而是光线起点到六个平面的距离。

2,将这些距离分成三组:t1x, t2x;t1y, t2y;t1z, t2z

算法的步骤:

第一步:

t_far = t_x_大

t_near=t_x_小

第二步:

t_y_大 < t_far(即t_x_大),则t_far = t_y_大;反之t_far不变,还是等于t_x_大。

t_y_小 > t_near(即t_x_小),则t_near=t_y_小;反之t_near不变,还是等于t_x_小。

经过这一步之后,t_far等于“t_x_大”和“t_y_大”中较小的,t_near等于“t_x_小”和“t_y_小”中较大的。(也就是,t_far在慢慢变小,t_near在慢慢变大)。

判断t_far和t_near的大小:若t_far<t_near,即是如下情况:

测试顺利完成时(光线撞上长方体了),

t_far={t_x_大,t_y_大,t_z_大}中最小的那个

t_near={t_x_小,t_y_小,t_z_小}中最大的那个

OK,书上只介绍了怎么判断光线是否撞击到长方体,当然“材质”我们先不用考虑。但是,为了画出长方体,我们还必须知道光线起点到撞击点的距离(具体是那六个距离中的哪个,当然,实际t=t_near)和交点处的法向量。

“立体图示”中,最终t_near=t2x

现在我们要求交点处的法向量。特殊长方体的特殊之处:右、左、上、下、前、后,六个平面的法向量分别是:(1,0,0),(-1,0,0),(0,1,0),(0,-1,0),(0,0,1),(0,0,-1)

但是交点处法向量对应的是哪一个呢?

考虑到:“t_near={t_x_小,t_y_小,t_z_小}中最大的那个”

若t_near为X的值,则所交平面为左平面或者右平面;

若t_near为Y的值,则所交平面为上平面或者下平面;

若t_near为Z的值,则所交平面为前平面或者后平面;

但是,到底具体是左还是右?是上还是下?是前还是后?

怎么判断?????

这个可以根据光线方向向量和法向量点乘的正负来判断,交点是在面向光线的平面上,所以点乘是小于零的那个。

截至当前,画长方体的条件应该齐了:

1,光线撞上长方体

2,光线起点到撞击点的距离

3,撞击点处的法向量

4,(材质)

33.2 看C++代码实现

----------------------------------------------box.h------------------------------------------

box.h

#ifndef BOX_H
#define BOX_H#include <hitable.h>class box : public hitable
{public:box() {}box(vec3 vl, vec3 vh, material *m) : vertex_l(vl), vertex_h(vh), ma(m) {normals[0] = vec3(-1, 0, 0);//leftnormals[1] = vec3(1, 0, 0);//rightnormals[2] = vec3(0, 1, 0);//upnormals[3] = vec3(0, -1, 0);//downnormals[4] = vec3(0, 0, 1);//frontnormals[5] = vec3(0, 0, -1);//back
/*按照这个顺序将六个表面的法向量保存在数组中。注意:保存顺序和后面获取交点处法向量是对应的。*/}virtual bool hit(const ray& r, float tmin, float tmax, hit_record& rec) const;vec3 vertex_l;//前左下顶点坐标vec3 vertex_h;//后右上顶点坐标vec3 normals[6];material *ma;
};#endif // BOX_H

----------------------------------------------box.cpp------------------------------------------

box.cpp

#include <iostream>
#include <limits>
#include "float.h"#include "box.h"
#include "log.h"using namespace std;bool box::hit(const ray& r, float t_min, float t_max, hit_record& rec) const {float t_near = (numeric_limits<float>::min)();float t_far = (numeric_limits<float>::max)();int near_flag, far_flag;vec3 direction = r.direction();vec3 origin = r.origin();vec3 bl = vertex_l;vec3 bh = vertex_h;float array1[6];/*光线方向向量和坐标轴平行。若光线起点不在两个平行平面之间,则光线不可能撞击到长方体,return false;反之,[Xd, Yd, Zd]三个坐标有出现零,零是不能作除数的,所以,可能除以零的交点坐标需要提前算出来*/if(direction.x() == 0) {if((origin.x() < bl.x()) || (origin.x() > bh.x())) {return false;}array1[0] = (numeric_limits<float>::min)();array1[1] = (numeric_limits<float>::max)();}if(direction.y() == 0) {if((origin.y() < bl.y()) || (origin.y() > bh.y())) {return false;}array1[2] = (numeric_limits<float>::min)();array1[3] = (numeric_limits<float>::max)();}if(direction.z() == 0) {if((origin.z() < bl.z()) || (origin.z() > bh.z())) {return false;}array1[4] = (numeric_limits<float>::min)();array1[5] = (numeric_limits<float>::max)();}/*计算六个(三组)交点坐标,一次保存在数组中*/if((direction.x() != 0) && (direction.y() != 0) && (direction.z() != 0)) {array1[0] = (bl.x()-origin.x())/direction.x();array1[1] = (bh.x()-origin.x())/direction.x();array1[2] = (bl.y()-origin.y())/direction.y();array1[3] = (bh.y()-origin.y())/direction.y();array1[4] = (bl.z()-origin.z())/direction.z();array1[5] = (bh.z()-origin.z())/direction.z();}for (int i=0; i<6; i=i+2){
/*i=i+2,说明,只循环3次(和X、Y、Z三组六个交点对应)。顺利完成测试循环后(说明光线撞击到长方体),我们获得t_far、t_near和其对应的位置(对着X或者Y或者Z值)far_flag、near_flag*/if(array1[i] > array1[i+1]) {//将每组中较小的放在i位置,较大的放在i+1位置float t = array1[i];array1[i] = array1[i+1];array1[i+1] = t;}if(array1[i] >= t_near) {t_near = array1[i]; near_flag = i;}
/*较小的值比t_near大时,把较小的值赋给t_near,同时记住当前t_near的位置near_flag。这里的near_flag可能的值0(t_x_小)、2(t_y_小)、4(t_z_小)(交点位置数组(array1[6])中的每组中较小值的位置),后面我们会根据该值去法向量数组(vec3 normals[6])中获取撞击点的法向量,这里可以看出array1[6]和normals[6]是对应的:0、1对应X对应左右,2、3对应Y对应上下,4、5对应Z对应前后*/if(array1[i+1] <= t_far) {t_far = array1[i+1]; far_flag = i+1;}
/*较大的值比t_far小时,把较大的值赋给t_far,同时记住当前t_far的位置far_flag*/if(t_near > t_far) {//t_near>t_far,测试失败,光线不会撞击到长方体return false;}if(t_far < 0) {return false; //t_far<0,测试失败,光线不会撞击到长方体}}if (t_near < t_max && t_near > t_min) {rec.t = t_near;//t_near就是光线起点到撞击点的距离rec.p = r.point_at_parameter(rec.t);rec.mat_ptr = ma;/*接下来求撞击点处的法向量*/vec3 normals_choose[6];for(int j=0; j<6; j++) {normals_choose[j] = vec3(0,0,0);}for(int i=0; i<6; i++) {if(dot(normals[i], r.direction()) < 0) {
/*选出面向光线起点的表面的法向量(最多三个),保存在新的数组normals_choose[]中*/normals_choose[i] = normals[i];}}for(int k=near_flag; k<6; k++) {
/*前面求得near_flag可能的值0(t_x_小)、2(t_y_小)、4(t_z_小)(也就是t_near可能出现的位置)。0、1对应X对应左右,2、3对应Y对应上下,4、5对应Z对应前后。在normals_choose[6]中只在对应位置保存了面向光线起点的长方体表面的法向量。所以,根据t_near值可以在该数组中取的唯一的不等于零的向量,这个向量即为撞击点处的法向量*/if(!vector_equ(normals_choose[k], vec3(0,0,0))) {rec.normal = normals_choose[k];break;}}return true;}return false;
}

----------------------------------------------main.cpp------------------------------------------

main.cpp

//triangle2, the green lambertian onevec3 vertexes3_2[3];vertexes3_2[0] = vec3(1.5,0.5,1.0);vertexes3_2[1] = vec3(2.5,0.5,1.0);vertexes3_2[2] = vec3(2.0,2.0,1.0);hitable *list[7];list[0] = new sphere(vec3(0.0,-100.5,-1), 100, new lambertian(vec3(0.8, 0.8, 0.0)));
        list[1] = new box(vec3(-2.0,-0.5,4.0), vec3(-1.0,1.0,2.0), new lambertian(vec3(0.0, 1.0, 0.5)));list[2] = new box(vec3(-0.25,-0.5,0.0), vec3(0.75,0.5,-1.0), new metal(vec3(0.8, 0.2, 0.2), 0.0));list[3] = new box(vec3(-5.0,-0.5,-5.0), vec3(5.0,3.0,-6.0), new metal(vec3(0.8, 0.6, 0.4), 0.0));
        list[4] = new sphere(vec3(2.0,0.0,1.0), 0.5, new lambertian(vec3(0.5, 0.7, 0.6)));list[5] = new sphere(vec3(0.75,-0.25,5.0), 0.25, new lambertian(vec3(0.8, 0.7, 0.6)));list[6] = new polygon(vertexes3_2, 3, new lambertian(vec3(0.3, 0.8, 0.0)));hitable *world = new hitable_list(list,7);vec3 lookfrom(0,0,12);vec3 lookat(0,1,-1);float dist_to_focus = (lookfrom - lookat).length();float aperture = 0.0;camera cam(lookfrom, lookat, vec3(0,1,0), 20, float(nx)/float(ny), aperture, 0.7*dist_to_focus);

三个长方体,一个漫射材料,两个镜面材料。

输出图片如下:

心血来潮,输出了对应的2048*1024分辨率的图片:

问题三十三:怎么用ray tracing画特殊长方体(box)相关推荐

  1. 问题三十四:怎么用ray tracing画任意长方体(generalized box)

    34.1 思路分析 这个内容书上没有,但是觉得实际应用中的长方体的位置应该是任意的(表面法向量不一定平行坐标轴). 怎么画? 1,光线撞击到长方体 2,撞击点到光线起点的距离 3,撞击点的法向量 怎么 ...

  2. 问题五十三:怎么用ray tracing画参数方程表示的曲面(1)

    首先,以球面为例学习怎么用ray tracing画参数方程表示的曲面:然后,再画一个牛角面. 特别说明:这一章节所画的曲面只是示意性的,所以先不care图片上的瑕疵. 53.1 数学推导 球面的参数方 ...

  3. 问题五十四:怎么用ray tracing画参数方程表示的曲面(2)—— bezier surface

    首先,需要说明的是: 这一章节可以看作"问题五十三"的另一个例子--bicubic bezier surface: 之前已经用"球面"和"牛角面&qu ...

  4. Q77:怎么用Ray Tracing画仿射变换之后的图形

    77.1 理论说明 如上图所示,椭球面是球面通过仿射变换T得到的. 现在我们的问题是:怎么ray trace变换后的椭球面? (先重申一点:经过仿射变换之后,直线还是直线,光线还是光线) 我们应该这么 ...

  5. 问题六十六:怎么用ray tracing画CSG(Constructive Solid Geometry 构造实体几何)图形

    66.1 概述 什么是CSG图形? 若干简单图形通过集合运算后得到的复杂图形,被称为"CSG图形". 其中"简单图形",包括:sphere, box, cyli ...

  6. 问题三十:《Ray Tracing In One Weekend》封面图形生成

    30.1 封面图片 先完成封面图片. Code如下: ----------------------------------------------main.cpp------------------- ...

  7. 问题六十三:怎么用ray tracing画sphere sweeping图形

    63.1 概述 Translational sweeping.conic sweeping.rotational sweeping都是任意曲线以某种固定方式移动后形成的图形. 接下来,我们要画的sph ...

  8. 问题三十二:怎么用ray tracing画多边形(polygon, triangle)

    画多边形主要分为两步: 1,光线和多边形所在的平面相交,求得交点: 2,判断交点是否在多边形内: 32.1 光线和多边形所在的平面相交 分别定义光线.多边形和多边形所在平面.光线和平面的方程如下:(注 ...

  9. 问题三十五: 怎么用ray tracing画二次曲面(quadratic surfaces)(1)——椭球面

    二次曲面包括:球面.椭圆球面.单页双曲面.双页双曲面.椭圆锥面.椭圆柱面.椭圆抛物面.双曲抛物面等等. 注意到:只有球面和椭球面是封闭面,其他的都是开放面. 二次曲面是有方程的(我们已经学过的多边形. ...

最新文章

  1. java反序列化漏洞的一些gadget
  2. 9月数据库排行:Microsoft SQL Server分数罕见下滑
  3. CSS3技巧 —— 渐变
  4. zabbix自定义监控Key值
  5. 书单丨被强化学习一次次伤害?本书单带你一步步入门!
  6. Xcode 真机 iPhone is not available 及 is busy 解决
  7. C# 静态变量及静态函数
  8. ei会议和ei源刊的区别_ei会议论文和ei期刊论文的区别是什么
  9. 2020年3月全国程序员工资统计,平均工资13820元
  10. STM32产生固定频率和占空比可变的PWM
  11. zemax场曲畸变图_zemax初学入门像差分析各种图像(上)
  12. 十进制转二进制函数实现(C语言)
  13. 10.5国庆作业(IIC实验)
  14. lg g5 h868 android 7,LG G5 (H868)全网通智能手机
  15. Vue详解及综合案例
  16. 1985-2016年全球自杀数据分析(Kaggle)
  17. 使用手机控制电脑:百变遥控
  18. 关于部分Vista驱动丢失的解决办法
  19. access如何保存小数点后_条码标签打印软件如何批量制作订单标签
  20. hadoop搭建成功的标志

热门文章

  1. Rsync 参数详解
  2. SSAS实践问题记录--后端数据库访问模块中存在错误。 为绑定指定的大小太小,导致一个或多个列值被截断。
  3. 【DFS + Backtracking】LeetCode 79. Word Search
  4. 剑指offer——面试题57:删除链表中重复的结点
  5. Cache缺失率的计算原理
  6. python plt 批量修改全局样式
  7. [转]STL(容器)与DEBUGNEW运算符冲突的解决
  8. http之content-type
  9. 对称矩阵、Hermite矩阵、正交矩阵、酉矩阵、奇异矩阵、正规矩阵、幂等矩阵、合同矩阵、正定矩阵...
  10. 字符串转化为整型,为0,而不是1