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