问题五十七:怎么用ray tracing画translational sweeping图形

198 篇文章 12 订阅
195 篇文章 27 订阅

57.1 概述

我们这里考虑的translational sweeping是XOZ平面的封闭曲线沿着y轴平移得到的图形。类似于如下图形:我们把这种图形称为“prism”。我们这里的封闭曲线是由多段三次b-spline曲线组成的。

 

光线和prism相交有如下图中所示的几种情况:

第一种情况:光线和顶面所在的平面相交的交点在底面的投影在封闭曲线内,如红色光线所示。

第二种情况:光线和底面所在的平面相交的交点在封闭曲线内,如绿色光线所示。

第三种情况:光线和顶面所在的平面相交的交点在底面的投影、光线和底面所在平面相交的交点都不在封闭曲线内,如紫色光线所示。



所以,我们“求根”的算法以此分为如下几个步骤:

第一步:求光线R(t)和base plane、cap plane的交点t0、t1;

第二步:将光线R(t)投影到base plane得到R'(t);

第三步:求出投影光线R’(t)与曲线的所有交点t’;

第四步:处理t0、t1有在曲线内的情况;

第五步:处理t0、t1都不在曲线内的情况;


57.2 求根


57.2.1求光线R(t)和base plane、cap plane的交点t0、t1

57.2.2将光线R(t)投影到base plane得到R'(t)




57.2.3 怎么用三次b-spline曲线段形成封闭曲线?

参考PPT截图如下:



 

我们接下来要画的图形,有17个控制点,则有14段(17-3=14)曲线段。示意图如下:




形成封闭曲线,则需要控制点收尾相连。所以,这17个控制点中前三个和后三个是重复的。



57.2.4 求R’(t)和封闭曲线的所有交点

要求R’(t)和封闭曲线的所有交点,我们先求R’(t)和每一曲线段的交点,然后将这14段曲线段对应的交点汇总则得到R’(t)和封闭曲线的所有交点。

 

接下来,我们求R’(t)和每一曲线段的交点。

 



57.2.5 判断t0和t1在base plane的投影是否在封闭曲线内

我们已经求得R’(t)与曲线的所有交点。示意如下图。



t0、t1在base plane的投影必定都在R’(t)上,如上图中三角形示意。

怎么判断点是否在曲线内呢?

若大于t0/t1的交点数为偶数,则t0/t1在曲线外;

若大于t0/t1的交点数为奇数,则t0/t1在曲线内;

 

我们现在不说t0或者t1,改说两者中较小的t0_t1_smaller,两者中较大的t0_t1_bigger。

 

t0_t1_smaller在曲线内:

如“57.1”图示的红色光线

最终要求的t是:t0_t1_smaller

 

t0_t1_bigger在曲线内:

如“57.1”图示的绿色光线

最终要求的t是:所有有效交点中最小的那个对应的t

57.2.6处理t0、t1都不在曲线内的情况

针对t0t1都不在曲线内的情况,如“57.1”图示的蓝色光线紫色光线,我们直到蓝色光线没有撞击到prism,紫色光线撞击到了prism。

 

但是,具体怎么判断光线是否撞击到prism呢?

 

参考前人的成果,判断方式如下:

所有有效交点存在一个t’满足:“式子九”

|t’-t0|*Rd·n<h

其中Rd为光线方向向量,n为base plane的单位法向量,h为prism的高度。

 

将t’、t0都代入光线方程:R(t)=R0+Rd*t

然后,我们可以得到R(t0)-R(t’)= (R0+Rd*t0)-( R0+Rd*t’)=(t0-t’)*Rd

然后,再和n点积(n为单位向量,所以相当于长度乘以cos(theta)),得到高度。

具体如下图所示:




注意到:t1对应的那个高度为h。

上图中存在t’3和t’4满足“式子九”。

 

最终要求的t是:满足“式子九”的最小的那个t’

注意:

这个时候还需要判断对应的y值是否在[base_y, cap_y]之间。若在,该t即为所求;若不在,则光线没有撞击到prism

具体做法:将t代入光线方程“式子一”求出y值,然后判断一下即可。

上图中为t’3.

 

57.3 求法向量

t0_t1_smaller在曲线内:

法向量即为cap plane的法向量:(0, 1, 0)

 

 

t0_t1_bigger在曲线内

或者

t0t1都不在曲线内

 

这两种情况的根是通过投影光线和曲线相交求得,对应的法向量即为曲线在该处的切向量的垂直向量。

我们可以通过对“式子六”求对u的微分来求得切向量。式子十



57.4 看C++代码实现


----------------------------------------------vec3.h ------------------------------------------

vec3.h

 

bool matrix_4_4_multiply_4_1(const float matrix1[4][4], const float matrix2[4][1], float (&result)[4][1]);

----------------------------------------------vec3.cpp ------------------------------------------

vec3.cpp


bool matrix_4_4_multiply_4_1(const float matrix1[4][4], const float matrix2[4][1], float (&result)[4][1]) {
        for (int k=0; k<1; k++) {
            for (int i=0; i<4; i++) {
                result[i][k] = 0.0;
                for (int j=0; j<4; j++) {
                    result[i][k] = result[i][k] + matrix1[i][j]*matrix2[j][k];
                }
            }
        }
        return true;
}

----------------------------------------------sweeping_translational.h ------------------------------------------

sweeping_translational.h

 

#ifndef SWEEPING_TRANSLATIONAL_H
#define SWEEPING_TRANSLATIONAL_H

#include <hitable.h>
#include <log.h>

class sweeping_translational : public hitable
{
    public:
        sweeping_translational() {}
        sweeping_translational(vec3 *cp, float by, float cy, material *m){
            base_y = by;
            cap_y = cy;
            ma = m;
            float matrix_t_6[4][4] = {{ 1,  4,  1, 0},
                                      {-3,  0,  3, 0},
                                      { 3, -6,  3, 0},
                                      {-1,  3, -3, 1}};
            float matrix_t[4][4];
            for (int i=0; i<4; i++) {
                for (int j=0; j<4; j++) {
                    matrix_t[i][j] = matrix_t_6[i][j] / 6.0;
                }
            }
            float points_x[17], points_z[17], b_points_x[4][1], b_points_z[4][1], b_result_x[4][1], b_result_z[4][1];
            int b_num = 0;
            float min_x, max_x, min_z, max_z;
            for (int i=0; i<17; i++) {
                points_x[i] = cp[i].x();
                points_z[i] = cp[i].z();
            }
            min_x = points_x[0];
            max_x = points_x[0];
            min_z = points_z[0];
            max_z = points_z[0];
            for (int i=0; i<17; i++) {
                if (min_x > points_x[i]) {
                    min_x = points_x[i];
                }
                if (max_x < points_x[i]) {
                    max_x = points_x[i];
                }
                if (min_z > points_z[i]) {
                    min_z = points_z[i];
                }
                if (max_z < points_z[i]) {
                    max_z = points_z[i];
                }
            }
            sweeping_bl = vec3(min_x, base_y, min_z);
            sweeping_bh = vec3(max_x, cap_y, max_z);
/*获取包围prism的最小的长方体的参数*/

            for (int i=0; i<14; i=i+1) {
                b_points_x[0][0] = points_x[i];
                b_points_x[1][0] = points_x[i+1];
                b_points_x[2][0] = points_x[i+2];
                b_points_x[3][0] = points_x[i+3];
                b_points_z[0][0] = points_z[i];
                b_points_z[1][0] = points_z[i+1];
                b_points_z[2][0] = points_z[i+2];
                b_points_z[3][0] = points_z[i+3];
                matrix_4_4_multiply_4_1(matrix_t, b_points_x, b_result_x);
                matrix_4_4_multiply_4_1(matrix_t, b_points_z, b_result_z);
                for (int j=0; j<4; j++) {
                    matrix_c_x[j][b_num] = ((fabs(b_result_x[j][0])<1e-6)? (0.0):(b_result_x[j][0]));
                    matrix_c_z[j][b_num] = ((fabs(b_result_z[j][0])<1e-6)? (0.0):(b_result_z[j][0]));
/*这里是求“式子六”中提到的[Cx][Cz]*/
                }
                b_num ++;
            }

        }
        virtual bool hit(const ray& r, float tmin, float tmax, hit_record& rec) const;
        float matrix_c_x[4][14], matrix_c_z[4][14];
        vec3 sweeping_bl, sweeping_bh;
        float base_y, cap_y;
        material *ma;
};

#endif // SWEEPING_TRANSLATIONAL_H

----------------------------------------------sweeping_translational.cpp ------------------------------------------

sweeping_translational.cpp

#include "sweeping_translational.h"
 
#include <iostream>
#include <limits>
#include "float.h"
 
bool ray_hit_box_st(const ray& r, const vec3& vertex_l,const vec3& vertex_h, float& t_near,
float& t_far) {
/*这里重写光线撞击长方体的函数*/
        t_near =(numeric_limits<float>::min)();
        t_far =(numeric_limits<float>::max)();
        vec3 direction =r.direction();
        vec3 origin =r.origin();
        vec3 bl = vertex_l;
        vec3 bh = vertex_h;
        float array1[6];
 
        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){
            if(array1[i] >array1[i+1]) {
                float t =array1[i];
                array1[i] =array1[i+1];
                array1[i+1] =t;
            }
            if(array1[i] >= t_near) {t_near =array1[i];}
            if(array1[i+1]<= t_far) {t_far = array1[i+1];}
            if(t_near >t_far) {
                return false;
            }
            if(t_far < 0) {
                return false;
            }
        }
        if (t_near != t_near){
            t_near = t_near *1;
        }
        return true;
}
 
bool sweeping_translational::hit(const ray& r, float t_min, float t_max,hit_record& rec) const {
#if SWEEPING_TRANSLATIONAL_LOG == 1
        std::cout <<"-------------sweeping_translational::hit---1-------------" <<endl;
#endif // SWEEPING_TRANSLATIONAL_LOG
        float t_near, t_far;
        if (ray_hit_box_st(r,sweeping_bl, sweeping_bh, t_near, t_far)) {
            float x0 = r.origin().x();
            float y0 =r.origin().y();
            float z0 =r.origin().z();
            float xd =r.direction().x();
            float yd =r.direction().y();
            float zd =r.direction().z();
           if (fabs(xd) < 1e-6) {
                xd = 1e-6;
           }
           if (fabs(yd) < 1e-6) {
               yd = 1e-6;
           }
           if (fabs(zd) < 1e-6) {
               zd = 1e-6;
           }
/*非常接近0的值都用1e-6替代*/
            float cos_theta =dot(r.direction(), vec3(0, -1, 0))/(r.direction().length());
            float t0, t1,t0_t1_smaller, t0_t1_bigger, a3, a2, a1, a0, *roots3_base, roots_base[21][3],temp0, temp1, temp2, root[5], u, check_y;
/*
root[0]:    0表示没有根;1表示1个根
root[1]:   存放根t
root[2]:    1表示t0_t1_smaller在曲线内;2表示t0_t1_bigger在曲线内;3表示t0、t1都不在曲线内。(不同情况,法向量的求解方式不一样)
root[3]:    存放曲线段编号(法向量求解时需要)
root[4]:    存放根t对应的u的值(法向量求解时需要)
*/
            roots_base[0][0] =0.0;
            root[0] = 0.0;
            root[2] = 0.0;
            root[3] = -1.0;
            root[4] = -1.0;
            int num_roots_base= 0;
            intnum_t0_t1_smaller = 0;
            intnum_t0_t1_bigger = 0;
            intnum_roots_base_valid = 0;
           t0 = (base_y - y0) / yd;
           t1 = (cap_y - y0) / yd;
/*对应“式子三”*/
            t0_t1_smaller = (t0>t1)? t1:t0;
            t0_t1_bigger =(t0>t1)? t0:t1;
            for (int i=0;i<14; i++) {
/*14段曲线段。求解14个一元三次方程。对应“式子七”*/
                a3 =zd*matrix_c_x[3][i] - xd*matrix_c_z[3][i];
                a2 =zd*matrix_c_x[2][i] - xd*matrix_c_z[2][i];
                a1 =zd*matrix_c_x[1][i] - xd*matrix_c_z[1][i];
                a0 =zd*matrix_c_x[0][i] - xd*matrix_c_z[0][i] + xd*z0 - zd*x0;
                roots3_base =roots_cubic_equation(a3, a2, a1, a0);
                if(roots3_base[0] != 0.0) {
                    for (intj=1; j<(int(roots3_base[0])+1); j++) {
                        u =roots3_base[j];
                        if((u>=0.0) && (u<=1.0)) {
                           roots_base[num_roots_base+1][0] =(matrix_c_x[0][i]+matrix_c_x[1][i]*u+matrix_c_x[2][i]*u*u+matrix_c_x[3][i]*u*u*u-x0)/xd;
/*对应“式子八”,将u转化为t*/
                           roots_base[num_roots_base+1][1] = i;
                           roots_base[num_roots_base+1][2] = u;
/*同时保存曲线段编号和u,为了后面求法向量*/
                           num_roots_base ++;
                        }
                    }
                   roots_base[0][0] = float(num_roots_base);
                }
                delete []roots3_base;
            }
            if (roots_base[0][0] != 0.0) {
/*对所有根进行从小到大排序*/
                for (int i=1;i<int(roots_base[0][0]); i++) {
                    for (intj=i+1; j<int(roots_base[0][0])+1; j++) {
                        if(roots_base[i][0] > roots_base[j][0]) {
                            temp0 =roots_base[i][0];
                           roots_base[i][0] = roots_base[j][0];
                           roots_base[j][0] = temp0;
                           temp1 = roots_base[i][1];
                           roots_base[i][1] = roots_base[j][1];
                           roots_base[j][1] = temp1;
                           temp2 = roots_base[i][2];
                           roots_base[i][2] = roots_base[j][2];
                            roots_base[j][2] = temp2;
                        }
                    }
                }
                for (int i=1;i<(int(roots_base[0][0])+1); i++) {
/*判断大于t0_t1_smaller的根的个数,为了判断其是否在曲线内,对应57.2.5*/
                    if(roots_base[i][0] > (t0_t1_smaller)) {
                       num_t0_t1_smaller = int(roots_base[0][0])+1-i;
                        break;
                    }
                }
                if((num_t0_t1_smaller%2) != 0) {
                    root[1] =t0_t1_smaller;
                    root[0] =1.0;
                    root[2] =1.0;
                }
                else {
                    for (inti=1; i<(int(roots_base[0][0])+1); i++) {
/*判断大于t0_t1_bigger的根的个数,为了判断其是否在曲线内,对应57.2.5*/
                        if(roots_base[i][0] > (t0_t1_bigger)) {
                           num_t0_t1_bigger = int(roots_base[0][0])+1-i;
                           break;
                        }
                    }
                    if((num_t0_t1_bigger%2) != 0) {
                       root[1] = roots_base[1][0];//fabs(cos_theta);
                       root[0] = 1.0;
                       root[2] = 2.0;
                       root[3] = roots_base[1][1];
                       root[4] = roots_base[1][2];
                    }
                    else {
/*如下处理t0,t1都不在曲线内的情况,对应“57.2.6”*/
                        for(int i=1; i<(int(roots_base[0][0])+1); i++) {
                            if(fabs(roots_base[i][0]-t0_t1_bigger)*fabs(dot(r.direction(), vec3(0, -1,0)))<fabs(cap_y-base_y)) {
/*对应“式子九”*/
//                           if (roots_base[i][0] > t0_t1_smaller) {
                               num_roots_base_valid = i;
/*保存大于t0_t1_smaller的最小的那个根的编号*/
                               break;
                            }
                        }
                        if(num_roots_base_valid != 0) {
                           root[1] = roots_base[num_roots_base_valid][0];//fabs(cos_theta);
                           check_y = y0 + yd * root[1];

                            if ((check_y > base_y) &&(check_y < cap_y)) {

/*这里就是“57.2.6中提到的需要注意的地方,必须加这个限制条件。后续我们对比有无这个限制条件时的输出图形*/

                                root[0] = 1.0;
                                root[2] = 3.0;
                                root[3] = roots_base[num_roots_base_valid][1];
                                root[4] = roots_base[num_roots_base_valid][2];
                            }
                        }
                    }
                }
                if (root[0] != 0.0) {
                    if ((root[1] < t_max) && (root[1] > t_min)) {
                        rec.t = root[1];
                        rec.p = r.point_at_parameter(rec.t);
                        if (root[2] == 1.0) {
/*若t0_t1_smaller在曲线内:法向量即为cap plane的法向量:(0, 1, 0)*/
                            rec.normal = vec3(0, 1, 0);
                        }
                        else {
                            float nx, nz, nu;
                            int num = int(root[3]);
                            nu = root[4];
                            nx = matrix_c_x[1][num]+2.0*matrix_c_x[2][num]*nu+3.0*matrix_c_x[3][num]*nu*nu;
                            nz = matrix_c_z[1][num]+2.0*matrix_c_z[2][num]*nu+3.0*matrix_c_z[3][num]*nu*nu;
/*对应“式子十”*/
                            rec.normal = unit_vector(vec3(nx, 0, nz));
                        }
                        if(dot(r.direction(), rec.normal) > 0) {
                            rec.normal = - rec.normal;
                        }
                        rec.mat_ptr = ma;
                        rec.u = -1.0;
                        rec.v = -1.0;
                        return true;
                    }
                    else {
                        return false;
                    }
                }
                else {
                    return false;
                }
            }
            else {
                return false;
            }
        }
        else {
            return false;
        }
}

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

main.cpp

 

        vec3 ctrl_points[17] = {vec3(-4.0,  0.0, -1.0), vec3(-3.0,  0.0, -4.0), vec3(-2.0,  0.0, -4.0), vec3(-1.0,  0.0,  -1.0),
                                vec3( 1.0,  0.0, -1.0), vec3( 1.0,  0.0, -5.0), vec3( 3.0,  0.0, -5.0), vec3( 4.0,  0.0,   1.0),
                                vec3( 2.0,  0.0,  2.0), vec3( 0.5,  0.0,  2.0), vec3(-2.0,  0.0,  1.0), vec3(-2.0,  0.0,  -2.0),
                                vec3(-2.5,  0.0, -3.0), vec3(-3.0,  0.0, -1.0), vec3(-4.0,  0.0, -1.0), vec3(-3.0,  0.0,  -4.0), vec3(-2.0,  0.0, -4.0)};
        hitable *list[1];
        list[0] = new sweeping_translational(ctrl_points, 0.0, 3.0, new lambertian(vec3(1.0, 0.0, 0.0)));
        hitable *world = new hitable_list(list,1);

        vec3 lookfrom(0.0001, 20, 20);
        vec3 lookat(0, 1.5, 0);
        float dist_to_focus = (lookfrom - lookat).length();
        float aperture = 0.0;
        camera cam(lookfrom, lookat, vec3(0,1,0), 30, float(nx)/float(ny), aperture, 0.7*dist_to_focus);

 

输出图片如下:(前边是没有加“57.2.6”中提到的限制条件时;后边是加了该限制条件时)

 

(0.0001,20, 20)

 

(0.0001,20, 10)

 

(0.0001,20, 5)

 

(-5, 20, 10)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值