问题三十四:怎么用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

智能推荐

分布式光纤传感器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告_预计2026年中国分布式传感器市场规模有多大-程序员宅基地

文章浏览阅读3.2k次。本文研究全球与中国市场分布式光纤传感器的发展现状及未来发展趋势,分别从生产和消费的角度分析分布式光纤传感器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。主要生产商包括:FISO TechnologiesBrugg KabelSensor HighwayOmnisensAFL GlobalQinetiQ GroupLockheed MartinOSENSA Innovati_预计2026年中国分布式传感器市场规模有多大

07_08 常用组合逻辑电路结构——为IC设计的延时估计铺垫_基4布斯算法代码-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏12次。常用组合逻辑电路结构——为IC设计的延时估计铺垫学习目的:估计模块间的delay,确保写的代码的timing 综合能给到多少HZ,以满足需求!_基4布斯算法代码

OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版-程序员宅基地

文章浏览阅读3.3k次,点赞3次,收藏5次。OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版

关于美国计算机奥赛USACO,你想知道的都在这_usaco可以多次提交吗-程序员宅基地

文章浏览阅读2.2k次。USACO自1992年举办,到目前为止已经举办了27届,目的是为了帮助美国信息学国家队选拔IOI的队员,目前逐渐发展为全球热门的线上赛事,成为美国大学申请条件下,含金量相当高的官方竞赛。USACO的比赛成绩可以助力计算机专业留学,越来越多的学生进入了康奈尔,麻省理工,普林斯顿,哈佛和耶鲁等大学,这些同学的共同点是他们都参加了美国计算机科学竞赛(USACO),并且取得过非常好的成绩。适合参赛人群USACO适合国内在读学生有意向申请美国大学的或者想锻炼自己编程能力的同学,高三学生也可以参加12月的第_usaco可以多次提交吗

MySQL存储过程和自定义函数_mysql自定义函数和存储过程-程序员宅基地

文章浏览阅读394次。1.1 存储程序1.2 创建存储过程1.3 创建自定义函数1.3.1 示例1.4 自定义函数和存储过程的区别1.5 变量的使用1.6 定义条件和处理程序1.6.1 定义条件1.6.1.1 示例1.6.2 定义处理程序1.6.2.1 示例1.7 光标的使用1.7.1 声明光标1.7.2 打开光标1.7.3 使用光标1.7.4 关闭光标1.8 流程控制的使用1.8.1 IF语句1.8.2 CASE语句1.8.3 LOOP语句1.8.4 LEAVE语句1.8.5 ITERATE语句1.8.6 REPEAT语句。_mysql自定义函数和存储过程

半导体基础知识与PN结_本征半导体电流为0-程序员宅基地

文章浏览阅读188次。半导体二极管——集成电路最小组成单元。_本征半导体电流为0

随便推点

【Unity3d Shader】水面和岩浆效果_unity 岩浆shader-程序员宅基地

文章浏览阅读2.8k次,点赞3次,收藏18次。游戏水面特效实现方式太多。咱们这边介绍的是一最简单的UV动画(无顶点位移),整个mesh由4个顶点构成。实现了水面效果(左图),不动代码稍微修改下参数和贴图可以实现岩浆效果(右图)。有要思路是1,uv按时间去做正弦波移动2,在1的基础上加个凹凸图混合uv3,在1、2的基础上加个水流方向4,加上对雾效的支持,如没必要请自行删除雾效代码(把包含fog的几行代码删除)S..._unity 岩浆shader

广义线性模型——Logistic回归模型(1)_广义线性回归模型-程序员宅基地

文章浏览阅读5k次。广义线性模型是线性模型的扩展,它通过连接函数建立响应变量的数学期望值与线性组合的预测变量之间的关系。广义线性模型拟合的形式为:其中g(μY)是条件均值的函数(称为连接函数)。另外,你可放松Y为正态分布的假设,改为Y 服从指数分布族中的一种分布即可。设定好连接函数和概率分布后,便可以通过最大似然估计的多次迭代推导出各参数值。在大部分情况下,线性模型就可以通过一系列连续型或类别型预测变量来预测正态分布的响应变量的工作。但是,有时候我们要进行非正态因变量的分析,例如:(1)类别型.._广义线性回归模型

HTML+CSS大作业 环境网页设计与实现(垃圾分类) web前端开发技术 web课程设计 网页规划与设计_垃圾分类网页设计目标怎么写-程序员宅基地

文章浏览阅读69次。环境保护、 保护地球、 校园环保、垃圾分类、绿色家园、等网站的设计与制作。 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点,网页的风格主题也很全面:如爱好、风景、校园、美食、动漫、游戏、咖啡、音乐、家乡、电影、名人、商城以及个人主页等主题,学生、新手可参考下方页面的布局和设计和HTML源码(有用点赞△) 一套A+的网_垃圾分类网页设计目标怎么写

C# .Net 发布后,把dll全部放在一个文件夹中,让软件目录更整洁_.net dll 全局目录-程序员宅基地

文章浏览阅读614次,点赞7次,收藏11次。之前找到一个修改 exe 中 DLL地址 的方法, 不太好使,虽然能正确启动, 但无法改变 exe 的工作目录,这就影响了.Net 中很多获取 exe 执行目录来拼接的地址 ( 相对路径 ),比如 wwwroot 和 代码中相对目录还有一些复制到目录的普通文件 等等,它们的地址都会指向原来 exe 的目录, 而不是自定义的 “lib” 目录,根本原因就是没有修改 exe 的工作目录这次来搞一个启动程序,把 .net 的所有东西都放在一个文件夹,在文件夹同级的目录制作一个 exe._.net dll 全局目录

BRIEF特征点描述算法_breif description calculation 特征点-程序员宅基地

文章浏览阅读1.5k次。本文为转载,原博客地址:http://blog.csdn.net/hujingshuang/article/details/46910259简介 BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,BRIEF是对已检测到的特征点进行描述,它是一种二进制编码的描述子,摈弃了利用区域灰度..._breif description calculation 特征点

房屋租赁管理系统的设计和实现,SpringBoot计算机毕业设计论文_基于spring boot的房屋租赁系统论文-程序员宅基地

文章浏览阅读4.1k次,点赞21次,收藏79次。本文是《基于SpringBoot的房屋租赁管理系统》的配套原创说明文档,可以给应届毕业生提供格式撰写参考,也可以给开发类似系统的朋友们提供功能业务设计思路。_基于spring boot的房屋租赁系统论文

推荐文章

热门文章

相关标签