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_vector(v)

w=cross(u,v)

所以,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 unknownsfloat 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 tempfor (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 1if(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);//leftnormals[1] = vector_trans_back(vec3(1, 0, 0), vector_u, vector_v, vector_w);//rightnormals[2] = vector_trans_back(vec3(0, 1, 0), vector_u, vector_v, vector_w);;//upnormals[3] = vector_trans_back(vec3(0, -1, 0), vector_u, vector_v, vector_w);;//downnormals[4] = vector_trans_back(vec3(0, 0, 1), vector_u, vector_v, vector_w);;//frontnormals[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 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[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);

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

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

长宽高为1、0.5、2

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

漫射材料

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

前下边方向向量为(1,0,-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的大图:

问题三十四:怎么用ray tracing画任意长方体(generalized box)相关推荐

  1. 问题三十三:怎么用ray tracing画特殊长方体(box)

    33.1 怎么用ray tracing画特殊长方体 在光线追踪中被用到的一种常见形态是长方体盒子.这种基本物体被用于可见物体和包围盒,包围盒被用于加速复杂物体的相交测试. 吐槽:单词都认识,就是不知道 ...

  2. 问题四十二:怎么用ray tracing画任意圆环片段

    42.1 数学推导 引入两个参数theta1.theta2 撞击点P和中心点O连线的向量OP与+X轴的夹角theta在[theta1, theta2]范围内时,才有可能撞击到圆环段. 当然,考虑到th ...

  3. 问题五十:怎么用ray tracing画blobs

    这一节,画这个: 参考文献: Blinn, J.F., A generalization of algebraic surfacedrawing. ACM Trans. Graph. 1(3) , 2 ...

  4. 问题六十:怎么用ray tracing画回旋体(rotational sweeping / revolution)

    60.1 概述 回旋体,大概是长这个样子: 回旋体是指曲线(称为"基本曲线")围绕y轴转一圈得到的图形. (基本曲线是由多段b-spline曲线段连接而成) 这里先强调一下: 上图 ...

  5. 问题四十一:怎么用ray tracing画任意圆柱面(generalized cylinder)

    我们之前在"35.2"章节中画过椭圆柱面: 我们还在"36.4"章节中画过圆柱面的Inverse Mapping图: 但是,这些柱面都是:底面与ZOX平面平行, ...

  6. Python编程基础:第三十四节 文件移动Move a File

    第三十四节 文件移动Move a File 前言 实践 前言 当我们需要将一个文件/文件夹移动到另一个指定路径时,就需要用到shutil.move()函数,该函数需要指定两个参数shutil.move ...

  7. 三十四、深入Java中的泛型(上篇)

    @Author:Runsen @Date:2019年10月22日 19:39:21 作者介绍:Runsen目前大三下学期,专业化学工程与工艺,大学沉迷日语,Python, Java和一系列数据分析软件 ...

  8. [Python人工智能] 三十四.Bert模型 (3)keras-bert库构建Bert模型实现微博情感分析

    从本专栏开始,作者正式研究Python深度学习.神经网络及人工智能相关知识.前一篇文章开启了新的内容--Bert,首先介绍Keras-bert库安装及基础用法及文本分类工作.这篇文章将通过keras- ...

  9. FreeSql (三十四)CodeFirst 迁移说明

    FreeSql 支持 CodeFirst 迁移结构至数据库,这应该是(O/RM)必须标配的一个功能. 与其他(O/RM)不同FreeSql支持更多的数据库特性,而不只是支持基础的数据类型,这既是优点也 ...

最新文章

  1. shell统计ip访问情况并分析访问日志
  2. matlab 中的dir函数使用
  3. 树莓派运行python import os未找到命令_通过pip指令在树莓派上基于Python3安装OpenCV...
  4. tomcat servlet容器请求时序
  5. 产品原型制作_早期制作原型如何帮助您设计出色的数字产品
  6. 我的C#/.NET学习诀窍——LINQPad
  7. Spring Security –在一个应用程序中有两个安全领域
  8. springMVC一些实践总结
  9. 多图技术贴:深入浅出解析大数据平台架构
  10. truffle和remix区别
  11. pip安装mysql驱动_windows环境下Python安装mysql驱动遇到“坎”
  12. SDP中fmtp的使用
  13. iOS app 启动 crash XCode 11 NSPOSIXErrorDomain Code=2 “No such file or directory“
  14. Centos7下载安装教程
  15. rancher及灵雀云TKF中解决中文编码问题
  16. 经典Hbase面试7题(附答案)
  17. 天呐!java兼职接单
  18. 第二周预习——html常用标签,认识浏览器
  19. 揭秘《英雄联盟》的游戏数据服务器
  20. mini2440----keil for AMR之IIC读写EEPROM(AT24C08)

热门文章

  1. PHP+shell实现多线程的方法
  2. 2016-6-3Ajax异步复习
  3. 读懂AIMS 2013中的性能分析报告
  4. ubuntu11.04 --- 没有了上边的导航 和左边的导航 怎么办?
  5. EIGRP分解试验部分-LAB1:EIGRP基本试验
  6. MOQL—筛选器(Selector)(一)
  7. 微程序控制器的组成及原理总结
  8. 大白话说说 朴素贝叶斯
  9. 经验:tensorflow 的 GPU 配置
  10. 从王者荣耀看设计模式(六.状态模式)