问题二十五:为什么有时候XnView无法显示PPM图片?

来源:互联网 发布:mac 安装apache 编辑:程序博客网 时间:2024/06/06 07:18

25.1 当前问题分析

之前在“问题二”中说明过,有截图如下:


文件显示不出来,原因肯定是文件格式不对。

所有的文件都应包含如下文件头,该文件头的格式如下:

P3

200 100

255

没见显示不出来,可能的原因:

1,RGB值个数未到200*100个

2,RGB值个数刚好200*100个,但某个R值、G值或者B值缺失(也就是RGB值出现残疾)

3,RGB值不在[0,255]之间,可能是因为值溢出而出现负数。

 

25.2 在学习“问题四十六(superellipsoid)”时,添加本章节如下内容:

 

当像素点的值溢出时,其值输出为:

-2147483648 -2147483648 -2147483648

 

若某个变量x的值出现溢出,则该值在程序中表示为“nan(Not a Number)”,怎么判断x的值是否溢出呢?

If (x != x) {

//表示x值溢出,即为nan

}

或者:

If(isnan(x)) {

//表示x值溢出,即为nan

}

这条语句经常用于设置断点,在x值溢出时拦截程序,从而可以查看断点处程序的状态。

 

接下来我们在程序中反推是什么原因导致像素点的值出现溢出的:

main():

                    col += color(r, world, 0);

color():

           return (1.0-t)*vec3(1.0,1.0, 1.0) + t*vec3(0.5, 0.7, 1.0);//white, light blue

           float t = 0.5*(unit_direction.y()+ 1.0);

           vec3 unit_direction = unit_vector(r.direction());

传入color()函数的光线有两种情况:其一,原始光线;其二,反射/折射光线

 

对于第一种情况:一般不会出错。对应main()函数中的如下代码:

                    float u = float(i + random)/ float(nx);

                    float v = float(j + random)/ float(ny);

                    ray r = cam.get_ray(u, v);

 

对于第二种情况:

color():

                return attenuation*color(scattered, world,depth+1);

           if (depth < 50 && rec.mat_ptr->scatter(r, rec, attenuation, scattered)) {

反射/折射光线是在对应的材料类的scatter()成员方法返回来的。下面我们分别看看

lambertian::scatter():

scattered =ray(rec.p, target-rec.p);

vec3 target =rec.p + rec.normal +random_in_unit_sphere();

 

metal::scatter():

   scattered = ray(rec.p, reflected + fuzz*random_in_unit_sphere());

vec3 reflected= reflect(unit_vector(r_in.direction()), rec.normal);

 

dielectric::scatter():既有反射,也有折射:

反射:

       scattered = ray(rec.p, reflected);

vec3 reflected= reflect(r_in.direction(), rec.normal);

折射:

       scattered = ray(rec.p, refracted);

if (refract(r_in.direction(),outward_normal, ni_over_nt, refracted)) {

 

bool refract(const vec3& v, constvec3& n, floatni_over_nt, vec3& refracted) {

   float dt = dot(uv, n);

       refracted = ni_over_nt*(uv - n*dt) - n*sqrt(discriminat);

 

if(refract(r_in.direction(), outward_normal,ni_over_nt, refracted)) {

       outward_normal = rec.normal;

 

如上,不管是什么材料,最终导致像素点的值溢出的是撞击点处的法向量normal的值。

这里以superellipsoid类为例,到其对应的hit()函数中看看normal的值的来源:

superellipsoid::hit():

                        rec.normal =unit_vector(vec3(nx,ny,nz));

 

                        nx = (p_e2/p_e1) * (pow((pow(fabs(pc.x()/intercept_x),(2/p_e2)) +

pow(fabs(pc.z()/intercept_z), (2/p_e2))), (p_e2/p_e1-1))) *

(2/p_e2)*(pow(fabs(pc.x()/intercept_x),(2/p_e2-1)))/intercept_x;

                        ny = (2/p_e1)*(pow(fabs(pc.y()/intercept_y),

                                                                 (2/p_e1-1)))/intercept_y;

                        nz = (p_e2/p_e1) * (pow((pow(fabs(pc.x()/intercept_x),(2/p_e2)) +

pow(fabs(pc.z()/intercept_z), (2/p_e2))), (p_e2/p_e1-1))) *

(2/p_e2)*(pow(fabs(pc.z()/intercept_z),(2/p_e2-1)))/intercept_z;

从这里可以看到,可能导致nx,ny,nz值溢出的是pow()函数计算的值超出数据类型的范围(1,当指数为小数时,底数为负;2,底数为0;3,底数绝对值大于1,指数很大,然后求幂后超出数据类型能够表示的最大值)

25.3 单独分析pow()函数

从C++ Reference官网获知:

If the base is finite negative and the exponent is finite but not an integer value, it causes a domain error潜在错误一:底数为负时,指数为小数
If both base and exponent are zero, it may also cause a domain error on certain implementations.     
If 
base is zero and exponent is negative, it may cause a domain error or a poleerror (or none, depending onthe library implementation).      潜在错误二:底数为0
The function may also cause a range error if the result is too great or toosmall to be represented by a value of the return type.     潜在错误三:计算结果超出数据类型范围

应对这三个潜在错误的措施:

1,单独考虑底数为负的情况,比如:

                        if (pc.x() < 0) {d_a1 = -intercept_x;}                        if (pc.y() < 0) {d_a2 = -intercept_y;}                        if (pc.z() < 0) {d_a3 = -intercept_z;}

2,单独考虑底数为0的情况,比如:

                        if (pc.x() == 0) {                            nx = 0;                        }                        else {                            nx = double(p_r)*pow(double(fabs(pc.x()/d_a1)), double(p_r-1))/double(d_a1);                        }                        if (pc.y() == 0) {                            ny = 0;                        }                        else {                            ny = double(p_s)*pow(double(fabs(pc.y()/d_a2)), double(p_s-1))/double(d_a2);                        }                        if (pc.z() == 0) {                        }                        else {                            nz = double(p_t)*pow(double(fabs(pc.z()/d_a3)), double(p_t-1))/double(d_a3);                        }

3,将数据类型换成double类型,比如(同上)(double类型都溢出了,直接输出报错吧!数值太大了,亲)




4 0
原创粉丝点击