问题三十四:怎么用ray tracing画任意长方体(generalized box)_raycasting编程 长方体-程序员宅基地

技术标签: generalized box  C++  computer graphics  计算机图形  ray tracing  ray trace  

34.1 思路分析

这个内容书上没有,但是觉得实际应用中的长方体的位置应该是任意的(表面法向量不一定平行坐标轴)。

怎么画?

1,光线撞击到长方体

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

3,撞击点的法向量

 

怎么确定空间中任意个长方体?

 

对于前下边的方向向量u(Xu, Yu, Zu)不平行于ZOX平面(即Yu不等于零)的情况:

以下六个参数可以确定唯一的空间长方体。


考虑到这种情况,在u确定时,θ只有确定的两个值,而φ可以取任意值,所以将φ作为夹角参数。

前左下顶点坐标A、

前下边的方向向量u、

前左边在ZOX平面投影与+Z轴的夹角φ

长a、

宽b、

高c、

 

对于前下边的方向向量u(Xu, Yu, Zu)平行于ZOX平面(即Yu等于零)的情况:

以下六个参数可以确定唯一的空间长方体。

考虑到这种情况,在u确定时,c只有确定的两个值,而θ可以取任意值,所以将θ作为夹角参数。

前左下顶点坐标A、

前下边的方向向量u、

前左边在与+Y轴的夹角θ

长a、

宽b、

高c、

 

 

思路是:将任意长方体转化为表面法向量平行坐标轴的长方体,以便用到上一节的方法来判定光线是否撞击到长方体和求得光线起点到撞击点的距离和对应法向量。

 

怎么转化?

长方体在当前xyz坐标系,属于任意长方体。如果我们以长方体的前左下顶点作为原点,经过该点的长方体的三条边作为坐标轴建立新的uvw坐标系。

在uvw坐标系中,之前的“任意长方体”就转化成表面法向量平行于坐标轴的“特殊长方体”了。

将光线也转到uvw坐标系。所以,可以在uvw坐标系中完成:判断光线是否撞上长方体,同时可以求得光线起点到撞击点的距离(坐标是相对的,距离是绝对的(在任何坐标系都是一样的))

撞击点的法向量。可以现在uvw坐标系中确定,然后转换到xyz坐标系。在uvw坐标系中是(1,0,0),(-1,0,0),(0,1,0),(0,-1,0),(0,0,1),(0,0,-1)。

 

总结一下:

画任意长方体需要这些参数:

1,光线撞击到长方体(xyz坐标系)

2,撞击点到光线起点的距离(xyz坐标系)

3,撞击点的法向量(xyz坐标系)

其中,1,2在任何坐标系中的结果是一样的,3,最终需要从uvw坐标系转换到xyz坐标系。

 

34.2 数学推导

34.2.1 求uvw坐标系的基     

已知:前左下顶点坐标A(X0, Y0, Z0),前下边方向向量u(Xu, Yu, Zu)

 

设:前左边与+Y轴的夹角为θ,前左边在ZOX平面的投影与+Z轴的夹角为φ。

 

过A点垂直于u的平面P方程为:

Xu*X+Yu*Y+Zu*Z+d=0

将A点代入方程的d=-(Xu*X0+Yu*Y0+Zu*Z0)

所以平面P方程为:“式子一”

Xu*X+Yu*Y+Zu*Z-(Xu*X0+Yu*Y0+Zu*Z0)=0

 

要求的v向量过A点且在平面P内,设v的长度为R0,所以,v向量的另一端在该平面上以A为圆心,R0为半径的圆上。

球心在A点,半径为R0的空间球的参数方程为:“式子二”

X=X0+R0*sinθ*sinφ

Y=Y0+R0*cosθ

Z=Z0+R0*sinθ*cosφ

 

将“式子一”代入“式子二”得到:“式子三”

Xu* sinθ*sinφ+Yu* cosθ+Zu* sinθ*cosφ=0

 

如果Yu等于零,θ可以任意设定,要求的参数为φ

 

所以,“式子三”等价于:“式子四”

Xu* sinθ*sinφ+Zu* sinθ*cosφ=0

若θ=0,则sinθ=0,“式子四”对于任意φ恒成立

若θ!=0,则sinθ!=0,“式子四”等价与“式子五”

Xu *sinφ+Zu *cosφ=0

 

又,根据三角函数万能公式:“式子六”


 

 

如果Yu不等于零,φ可以任意设定,要求的参数为θ

 

三角函数万能公式:“式子八”


综上,两种情况的sinθ、cosθ、sinφ、cosφ都已求出。

                                     

向量v的起点为A(X0, Y0, Z0),

终点在圆上(X0+R0*sinθ*sinφ, Y0+R0*cosθ, Z0+R0*sinθ*cosφ)

所以,v=(R0*sinθ*sinφ, R0*cosθ, R0*sinθ*cosφ)

标准化之后,v=sinθ*sinφ, cosθ, sinθ*cosφ

 

u=(Xu, Yu, Zu)

标准化u=unit_vectorv

w=crossuv

 

所以,uvw坐标系的基已经求得。

 

34.2.2 将xyz坐标系的坐标转换到uvw坐标系

uvw坐标系的基为向量u、v、w

u=(Xu,Yu,Zu)

v=(Xv,Yv,Zv)

w=(Xw,Yw,Zw)

xyz坐标系的基为向量e1、e2、e3

e1=(1,0,0)

e2=(0,1,0)

e3=(0,0,1)

设xyz坐标系中任意一点A的坐标为(A1,A2,A3),

设A在uvw坐标系中的坐标为(K1,K2,K3)

则:

K1*u+K2*v+w*K3=A1*e1+A2*e2+A3*e3

展开得:



 

吐槽:尼玛,只是一个三元一次方程组的代入消元求解,搞的像是吊炸天的运算。

 

34.2.3 将uvw坐标系中的坐标转换到xyz坐标

还是这三个式子

Xu*K1+Xv*K2+Xw*K3=A1(式子一)

Yu*K1+ Yv*K2+ Yw*K3=A2(式子二)

Zu*K1+Zv*K2+Zw*K3=A3(式子三)

 

等号左边的全是已知了,直接求得等号右边,即为xyz坐标系中的坐标。

 

34.3 看C++代码实现

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

vec3.cpp

 

#include "vec3.h"

vec3 get_vector_v(const vec3& vector_u, float angle) {
/*determin the v axis of u-v-w space*/
/*if y coordinate of vector_u is zero, we regard the angle as theta, because in this case, there is only one certain value;*/
/*if y coordinate of vector_u is not zero, we regard the angle as phi, because in this case, the theta is limited*/
    if (vector_u.y() == 0) {
        if (angle == 0) {
            return vec3(0, 1, 0);
        }
        else {
            float theta = angle*M_PI/180;
            float A = vector_u.z();
            float B = vector_u.x();
            float tan_half_phi, sin_phi, cos_phi;
            tan_half_phi = (B-sqrt(B*B+A*A))/A;;
            sin_phi = 2*tan_half_phi/(1+tan_half_phi*tan_half_phi);
            if (sin_phi < 0) {
                tan_half_phi = (B-sqrt(B*B+A*A))/A;
                sin_phi = 2*tan_half_phi/(1+tan_half_phi*tan_half_phi);
            }

            cos_phi = (1-tan_half_phi*tan_half_phi)/(1+tan_half_phi*tan_half_phi);

            return (vec3(sin(theta)*sin_phi, cos(theta), sin(theta)*cos_phi));
        }

    }
    else {
        float phi = angle*M_PI/180;
        float A = vector_u.y();
        float B = vector_u.x()*sin(phi) + vector_u.z()*cos(phi);
        float tan_half_theta, sin_theta, cos_theta;
        tan_half_theta = (B+sqrt(B*B+A*A))/A;
        sin_theta = 2*tan_half_theta/(1+tan_half_theta*tan_half_theta);
        if (sin_theta < 0) {
            tan_half_theta = (B-sqrt(B*B+A*A))/A;
            sin_theta = 2*tan_half_theta/(1+tan_half_theta*tan_half_theta);
        }
        cos_theta = (1-tan_half_theta*tan_half_theta)/(1+tan_half_theta*tan_half_theta);

        return (vec3(sin_theta*sin(phi), cos_theta, sin_theta*cos(phi)));
    }
}

vec3 vector_trans(const vec3& v1, const vec3& u, const vec3& v, const vec3& w) {
/*translate vector v1 from normal space to u-v-w space*/
    int i,j;
    int h1=0;
    int h2=2;
    float k1,k2,k3;//the three unknowns
    float temp[3][4] = {0};
    float mn[3][4] = {
        {u.x(), v.x(), w.x(), v1.x()},
        {u.y(), v.y(), w.y(), v1.y()},
        {u.z(), v.z(), w.z(), v1.z()}};

    /*eliminate k1*/
    for (i=0; i<3; i++) {
        if(mn[i][0] != 0) {//choose the first row for temp
            for (j=0; j<4; j++) {
                temp[h1][j] = mn[i][j]/mn[i][0];
//set the coefficient of k1 in the h1-th row of temp to 1
                if(h1 != 0) {
                    temp[h1][j] = temp[h1][j] - temp[0][j];
//temp: the h1 row minus the first row
                }
            }
            h1++;
        }
        else {
            for (j=0; j<4; j++) {
                temp[h2][j] = mn[i][j];
//copy the row of mn whose coefficient of k1 is 0 to the last row of temp
            }
            h2--;
        }
    }
    for (i=0; i<3; i++) {
        for (j=0; j<4; j++) {
            mn[i][j] = temp[i][j];
        }
    }
    h1 = 1;
    h2 = 2;

    /*eliminate k2*/
    for (i=1; i<3; i++) {
        if(temp[i][1] != 0) {
            for (j=1; j<4; j++) {
                mn[h1][j] = temp[i][j]/temp[i][1];
                if(h1 != 1) {
                    mn[h1][j] = mn[h1][j] - mn[1][j];
                }
            }
            h1++;
        }
        else {
            for (j=1; j<4; j++) {
                mn[h2][j] = temp[i][j];
            }
            h2--;
        }
    }

    k3 = mn[2][3] / mn[2][2];
    k2 = mn[1][3] - mn[1][2]*k3;
    k1 = mn[0][3] - mn[0][2]*k3 - mn[0][1]*k2;

    return vec3(k1, k2, k3);
}

vec3 vector_trans_back(const vec3& v1, const vec3& u, const vec3& v, const vec3& w) {
/*translate vector v1 from u-v-w space to normal space*/
    return vec3((v1.x()*u.x()+v1.y()*v.x()+v1.z()*w.x()),
                (v1.x()*u.y()+v1.y()*v.y()+v1.z()*w.y()),
                (v1.x()*u.z()+v1.y()*v.z()+v1.z()*w.z()));
}

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

vec3.h

 

vec3 get_vector_v(const vec3& vector_u, float angle);
vec3 vector_trans(const vec3& v1, const vec3& u, const vec3& v, const vec3& w);
vec3 vector_trans_back(const vec3& v1, const vec3& u, const vec3& v, const vec3& w);

----------------------------------------------box2.h------------------------------------------

box2.h

 

#ifndef BOX2_H
#define BOX2_H

#include <hitable.h>

class box2 : public hitable
{
    public:
        box2() {}
        box2(vec3 u, float an, float a, float b, float c, vec3 p, material *m) {
/*u为前下边的方向向量,an夹角(yu等于0时,为θ;yu不等于0时,为φ),a、b、c为长方体的长、宽、高(和u、w、v对应),p为前左下顶点坐标*/
            vector_u = unit_vector(u);
            vector_v = unit_vector(get_vector_v(vector_u, an));
            vector_w = unit_vector(cross(vector_u, vector_v));

            vertex_l = vector_trans(p, vector_u, vector_v, vector_w);
/*将前左下顶点转换到uvw坐标系*/
            vertex_h = vector_trans((p + a*vector_u + c*vector_v - b*vector_w), vector_u, vector_v, vector_w);
/*这里求后右上顶点坐标是为了使用上一章节画长方体的方法。也需要转换到uvw坐标系*/

/*uvw坐标系中对应的特殊法向量最后用的时候是需要转换到xyz坐标系的*/
            normals[0] = vector_trans_back(vec3(-1, 0, 0), vector_u, vector_v, vector_w);//left
            normals[1] = vector_trans_back(vec3(1, 0, 0), vector_u, vector_v, vector_w);//right
            normals[2] = vector_trans_back(vec3(0, 1, 0), vector_u, vector_v, vector_w);;//up
            normals[3] = vector_trans_back(vec3(0, -1, 0), vector_u, vector_v, vector_w);;//down
            normals[4] = vector_trans_back(vec3(0, 0, 1), vector_u, vector_v, vector_w);;//front
            normals[5] = vector_trans_back(vec3(0, 0, -1), vector_u, vector_v, vector_w);;//back

            ma = m;
        }
        virtual bool hit(const ray& r, float tmin, float tmax, hit_record& rec) const;
        vec3 vector_u;
        vec3 vector_v;
        vec3 vector_w;
        vec3 vertex_l;
        vec3 vertex_h;
        vec3 normals[6];
        material *ma;
};

#endif // BOX2_H

----------------------------------------------box2.cpp------------------------------------------

box2.cpp

 

#include "box2.h"
#include <iostream>
#include <limits>
#include "float.h"

#include "box.h"
#include "log.h"

using namespace std;

bool box2::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 = vector_trans(r.direction(), vector_u, vector_v, vector_w);
        vec3 origin = vector_trans(r.origin(), vector_u, vector_v, vector_w);
/*这个文件直接从box.cpp中copy过来就可以,只需要改动这么两行:将光线起点和方向向量从xyz坐标系转换到uvw坐标系*/
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++){
            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]; near_flag = i;}
            if(array1[i+1] <= t_far) {t_far = array1[i+1]; far_flag = i+1;}
            if(t_near > t_far) {
                return false;
            }
            if(t_far < 0) {
                return false;
            }
            i++;
        }

        if (t_near < t_max && t_near > t_min) {
            rec.t = 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[i] = normals[i];
                }
            }
            for(int k=near_flag; k<6; k++) {
                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 one
        vec3 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[1] = new box2(vec3(1, 0.5, -0.5), 0, 1, 2, 1.5, vec3(-2.0,-0.5,4.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);

如上只是将上一章节输出图片中的绿色长方体改动了一下:只是将前下边的方向向量有原来的(1,0,0)改成(1,0.5,-0.5),对比看看前后效果。

 

改动前:


改动后:



测一组yu=0且夹角φ=0的情况(只改变前下边方向向量)

 

        hitable *list[3];

        list[0] = newsphere(vec3(0.0,-100.5,-1), 100, new lambertian(vec3(0.8, 0.8, 0.0)));

        list[1] = newbox(vec3(-1.5,-0.5,4.0), vec3(-1.0,1.5,3.5), new lambertian(vec3(0.0, 1.0,0.5)));

       list[2] = new box2(vec3(1, 0, 0), 0, 1.0, 0.5, 2, vec3(0.0,-0.5,4.0), newlambertian(vec3(0.0, 0.1, 0.5)));

        hitable *world = newhitable_list(list,3);

 

        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);

 

前下边方向向量为100

夹角(yu=0,为θ)为0度

长宽高为1、0.5、2

前左下顶点坐标为(0,-0.5,4)

漫射材料

 

前下边方向向量为100



前下边方向向量为100.5


前下边方向向量为101


前下边方向向量为0.501


前下边方向向量为001


前下边方向向量为-0.501


前下边方向向量为-101


前下边方向向量为-100.5


前下边方向向量为-100



前下边方向向量为-10-0.5


前下边方向向量为-10-1


前下边方向向量为-0.50-1


前下边方向向量为00-1


前下边方向向量为0.50-1


前下边方向向量为10-1


前下边方向向量为10-0.5

 

测一组yu=0情况(只改变夹角θ的大小)

 

        hitable *list[3];

        list[0] = newsphere(vec3(0.0,-100.5,-1), 100, new lambertian(vec3(0.8, 0.8, 0.0)));

        list[1] = newbox(vec3(-1.5,-0.5,4.0), vec3(-1.0,1.5,3.5), new lambertian(vec3(0.0, 1.0,0.5)));

       list[2] = new box2(vec3(1, 0, 0), -90, 1.0, 0.5, 2, vec3(0.0,-0.5,4.0), newlambertian(vec3(0.0, 0.1, 0.5)));

        hitable *world = newhitable_list(list,3);

 

        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);

 

前下边方向向量为(1,0,-0.5)

夹角(yu=0,为θ)为0度

长宽高为1、0.5、2

前左下顶点坐标为(0,-0.5,4)

漫射材料

 

改变夹角θ的大小(注意:这里的夹角是前左边与+Y轴的夹角),看看效果。

夹角(yu=0,为θ)为-90



夹角(yu=0,为θ)为-60


夹角(yu=0,为θ)为-30


夹角(yu=0,为θ)为0



夹角(yu=0,为θ)为30



夹角(yu=0,为θ)为60


夹角(yu=0,为θ)为90



接下来测一组yu不等于0的情况。

list[2] = new box2(vec3(1, 0.5, -0.5), 60, 1.0, 0.5, 2,vec3(0.0,-0.5,4.0), new lambertian(vec3(0.0, 0.1, 0.5)));

 

前下边方向向量为(1,0.5,-0.5)

夹角(yu!=0,为φ)为60度(依次改变。注意:这里的夹角是前左边在ZOX平面的投影与+Z轴的夹角

长宽高为1、0.5、2

前左下顶点坐标为(0,-0.5,4)

漫射材料

 

夹角(yu!=0,为φ)为0

接下来测一组yu不等于0的情况。

list[2] = new box2(vec3(1, 0.5, -0.5), 60, 1.0, 0.5, 2,vec3(0.0,-0.5,4.0), new lambertian(vec3(0.0, 0.1, 0.5)));

 

前下边方向向量为(1,0.5,-0.5)

夹角(yu!=0,为φ)为60度(依次改变。注意:这里的夹角是前左边在ZOX平面的投影与+Z轴的夹角

长宽高为1、0.5、2

前左下顶点坐标为(0,-0.5,4)

漫射材料

 

夹角(yu!=0,为φ)为0


夹角(yu!=0,为φ)为45



夹角(yu!=0,为φ)为60



夹角(yu!=0,为φ)为-45



夹角(yu!=0,为φ)为-90


夹角(yu!=0,为φ)为-135


夹角(yu!=0,为φ)为-180


夹角(yu!=0,为φ)为-225


夹角(yu!=0,为φ)为-270



夹角(yu!=0,为φ)为-315



下面看一张综合各种长方体的图吧:

        hitable *list[3];
        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(-1.5,-0.5,4.0), vec3(-1.0,1.5,3.5), new lambertian(vec3(0.0, 1.0, 0.5)));
//表面法向量与坐标轴平行的漫射材质长方体

//五个只是夹角φ不同的漫射材质长方体
        list[2] = new box2(vec3(1, 0.5, -0.5), 0, 0.5, 0.5, 2, vec3(0.0,-0.5,4.0), new lambertian(vec3(0.0, 1.0, 0.5)));
        list[3] = new box2(vec3(1, 0.5, -0.5), 30, 0.5, 0.5, 2, vec3(0.0,-0.5,4.0), new lambertian(vec3(0.8, 0.1, 0.5)));
        list[4] = new box2(vec3(1, 0.5, -0.5), 45, 0.5, 0.5, 2, vec3(0.0,-0.5,4.0), new lambertian(vec3(0.6, 0.3, 0.5)));
        list[5] = new box2(vec3(1, 0.5, -0.5), 60, 0.5, 0.5, 2, vec3(0.0,-0.5,4.0), new lambertian(vec3(0.4, 0.5, 0.5)));
        list[6] = new box2(vec3(1, 0.5, -0.5), 90, 0.5, 0.5, 2, vec3(0.0,-0.5,4.0), new lambertian(vec3(0.2, 0.7, 0.5)));

//两个两个大的镜面材料夹角θ不同的长方体
        list[7] = new box2(vec3(1, 0, -0.5), 90, 8.0, 0.5, 3.5, vec3(-4.0,-0.5,0.0), new metal(vec3(0.8, 0.8, 0.8), 0.0));
        list[8] = new box2(vec3(1, 0, 0.5), -90, 8.0, 0.5, 3.5, vec3(-1.0,-0.5,-4.0), new metal(vec3(0.8, 0.8, 0.8), 0.0));

        hitable *world = new hitable_list(list,3);

        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的大图:


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/libing_zeng/article/details/54561605

智能推荐

使用nginx解决浏览器跨域问题_nginx不停的xhr-程序员宅基地

文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr

在 Oracle 中配置 extproc 以访问 ST_Geometry-程序员宅基地

文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc

Linux C++ gbk转为utf-8_linux c++ gbk->utf8-程序员宅基地

文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8

IMP-00009: 导出文件异常结束-程序员宅基地

文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束

python程序员需要深入掌握的技能_Python用数据说明程序员需要掌握的技能-程序员宅基地

文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求

Spring @Service生成bean名称的规则(当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致)_@service beanname-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname

随便推点

二叉树的各种创建方法_二叉树的建立-程序员宅基地

文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include&lt;stdio.h&gt;#include&lt;string.h&gt;#include&lt;stdlib.h&gt;#include&lt;malloc.h&gt;#include&lt;iostream&gt;#include&lt;stack&gt;#include&lt;queue&gt;using namespace std;typed_二叉树的建立

解决asp.net导出excel时中文文件名乱码_asp.net utf8 导出中文字符乱码-程序员宅基地

文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码

笔记-编译原理-实验一-词法分析器设计_对pl/0作以下修改扩充。增加单词-程序员宅基地

文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词

android adb shell 权限,android adb shell权限被拒绝-程序员宅基地

文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限

投影仪-相机标定_相机-投影仪标定-程序员宅基地

文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定

Wayland架构、渲染、硬件支持-程序员宅基地

文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland

推荐文章

热门文章

相关标签