问题四十三:对ray tracing圆环图形中的细微问题进行修正

198 篇文章 12 订阅
195 篇文章 27 订阅

第一步:将上一章节最后一张图放大,看局部


有两处问题:

其一:蓝色圈内,出现多余的红色像素点

其二:黄色圈内,出现多余的白色像素点(也就是原本应该出现的红色像素点没有出现)

 

第二步:去掉其它圆环和圆柱面,只留下红色圆环


放大(16倍)看局部:


由于图片像素少,所以没有出现白色的缝(小图中白色不足一次一个像素点,所以,白色的效果就是和红色叠加产生最后的颜色较浅的红色像素)。另外,小图中看不到大图蓝色圈内的多余红色像素点(太少了,小图中的光线根本没有撞击到)。

第三步:“多余”或“缺少”像素点对应一元四次方程的根

当前的问题是“问题四十”的残留问题。
截取“问题四十:第七步:2”如下:




 

将对应代码修改如下:

bool roots_quartic_equation2(float a, float b, float c, float d, float e, float (&roots)[5]) {
    //the first element is the number of the real roots, and other elements are the real roots.
    //Descartes's Method.
    if (a == 0) {
        float *roots3;
        roots3 = roots_cubic_equation(b, c, d, e);
        for (int i=0; i<int(roots3[0])+1; i++) {
            roots[i] = roots3[i];
        }
        delete [] roots3;
    }
    else {
        float a1 = b/a;
        float b1 = c/a;
        float c1 = d/a;
        float d1 = e/a;
        float p = (-3*a1*a1)/8 + b1;
        float q = (a1*a1*a1)/8 - (a1*b1)/2 + c1;
        float r = (-3*a1*a1*a1*a1)/256 + (a1*a1*b1)/16 - (a1*c1)/4 + d1;
        float sa = 2*p;
        float sb = p*p - 4*r;
        float sc = -q*q;
        float k, m, t;
        float *roots_s = roots_cubic_equation(1.0, sa, sb, sc);
        float temp;
        for (int i=1; i<int(roots_s[0]); i++) {
            for (int j=i+1; j<int(roots_s[0])+1; j++) {
                if (roots_s[i] > roots_s[j]) {
                    temp = roots_s[i];
                    roots_s[i] = roots_s[j];
                    roots_s[j] = temp;
                }
            }
        }
        if (roots_s[int(roots_s[0])] > 0) {
            k = sqrt(roots_s[int(roots_s[0])]);
            delete [] roots_s;
        }

        else{

           if (fabs(q) < 0.0000001) {

               k = 0.0;

               delete [] roots_s;

           }

           else {

               roots[0] = 0.0;

               delete [] roots_s;

               return true;

           }

        }

        if (fabs(k) == 0.0) {

           if (fabs(q) < 0.0000001) {

                float *roots2;

                roots2 =roots_quadratic_equation(1, -p, r);

                if (roots2[0]== 0.0) {

                    roots[0] =0.0;

                    delete []roots2;

                    returntrue;

                }

                else {

                    m =roots2[1];

                    t =roots2[2];

                }

                delete []roots2;

           }

           else {

               roots[0] = 0.0;

               return true;

           }

        }
        else {
            m = (k*k*k + k*p + q) / (2*k);
            t = (k*k*k + k*p - q) / (2*k);
        }

        float *roots_y1;
        float *roots_y2;
        roots_y1 = roots_quadratic_equation(1.0, k, t);
        roots_y2 = roots_quadratic_equation(1.0, -k, m);
        if (roots_y1[0] != 0.0) {
            for (int i=1; i<roots_y1[0]+1; i++) {
                roots[i] = roots_y1[i] - a1/4;
            }
        }
        if (roots_y2[0] != 0.0) {
            int roots_y1_number = int(roots_y1[0]);
            for (int j=1; j<roots_y2[0]+1; j++) {
                roots[roots_y1_number+j] =roots_y2[j] - a1/4;
            }
        }
        roots[0] = roots_y1[0] + roots_y2[0];
        delete [] roots_y1;
        delete [] roots_y2;
    }
    return true;
}


 

“多根”的问题搞定了,现在再查查看哪个地方导致了“少根”

 

第四步:分析一元三次方程




拦到这样一组数据:


但是,用网页上的一元四次方程计算器求解是有实根的:


这样看来,是这个一元四次方程求错了。

根据之前的分析,应该是对应一元三次方程的求解除了问题。

 

在main()函数单独测试一个一元四次方程的求解:

 

----------------------------------------------main.cpp ------------------------------------------

main.cpp

    int main(){

        float roots[5];
        if (roots_quartic_equation2(1.4656595, -1.43020415, 4.95183134, -2.24647474, 0.0000393861628, roots)) {
            for (int i=0; i<(roots[0]+1); i++) {
                std::cout << "roots[" << i << "]=" << roots[i] << endl;
            }
        }

 

另外,在一元四次方程求解函数中添加log:

 

对应的一元三次方程系数上面已经标出,现在在一元三次方程求解函数中设置断点,另截图如下:

 

对应的一元三次方程系数上面已经标出,现在在一元三次方程求解函数中设置断点,另截图如下:

对应的一元三次方程有一个实根,是负的,而且是10-8数量级,尼玛,和计算过程的0是一个数量级哈。

一个很小很小很小非常非常接近0的负数,但也是个负数啊,所以导致原一元四次方程没有实根。

 

网页计算器求解该一元三次方程结果如下:

尼玛,尼玛,什么鬼,什么鬼???

 

发现,网页上的正确答案是一个10-8数量级的正实根,我们求解出来的一个10-8数量级的负实根,两个实根相差很小很小(10-8数量级)。

所以,我们有理由猜测是计算精度的问题。

所以,所以,我们将一元三次方程求解过程的数据全部换成“double”类型,函数修改如下:

float* roots_cubic_equation2(float a, float b, float c, float d) {
    //the first element is the number of the real roots, and other elements are the real roots.
    //Shengjin's formula
    float *roots = new float[4];
    if (a == 0) {
        float *roots2;
        roots2 = roots_quadratic_equation(b, c, d);
        for (int i=0; i<int(roots2[0])+1; i++) {
            roots[i] = roots2[i];
        }
        delete [] roots2;
    }
    else {
        double A = double(b*b - 3*a*c);
        double B = double(b*c - 9*a*d);
        double C = double(c*c - 3*b*d);
        double deita = double(B*B - 4*A*C);

        if ((A == B) && (A == 0)) {
            //the three roots are the same
            if (a != 0) {
                roots[1] = -b/(3*a);
            }
            else {
                if (b != 0) {
                    roots[1] = -c/b;
                }
                else {
                    if (c != 0) {
                        roots[1] = -3*d/c;
                    }
                }
            }
            roots[2] = roots[1];
            roots[3] = roots[1];
            roots[0] = 3;
        }
        else if (deita > 0) {
            //only one real root
            double y1 = double(A*b + (3*a)*(-B + sqrt(deita))/2);
            double y2 = double(A*b + (3*a)*(-B - sqrt(deita))/2);
            double pow_y1, pow_y2;
            if (y1 < 0) {
            //for pow(a,b), when b is not int, a should not be negative.
                pow_y1 = - pow(double(-y1), double(1.0/3.0));
            }
            else {
                pow_y1 = pow(double(y1), double(1.0/3.0));
            }
            if (y2 < 0) {
                pow_y2 = - pow(double(-y2), double(1.0/3.0));
            }
            else {
                pow_y2 = pow(double(y2), double(1.0/3.0));
            }
            roots[1] = float((-b - pow_y1 - pow_y2) / (3*a));
            roots[0] = 1;
        }
        else if (deita == 0) {
            //three real roots and two of them are the same
            double K = B/A;
            roots[1] = float(-b/a + K);
            roots[2] = float(-K/2);
            roots[3] = float(-K/2);
            roots[0] = 3;
        }
        else if (deita < 0) {
            //three different real roots
            double theta = acos((2*A*b-3*a*B) / (2*pow(A, 1.5)));
            roots[1] = float((-b - 2*sqrt(A)*cos(theta/3)) / (3*a));
            roots[2] = float((-b + sqrt(A) * (cos(theta/3) + sqrt(3)*sin(theta/3))) / (3*a));
            roots[3] = float((-b + sqrt(A) * (cos(theta/3) - sqrt(3)*sin(theta/3))) / (3*a));
            roots[0] = 3;
        }
    }
    return roots;
}


修改后,运行结果如下:



第五步:验证修改一元三次方程求解函数后图片效果

输出图片如下:


对比之前的图片:


 


放大截图:


对比之前的截图:


 

组合图:


大图~大图~看大图:



哦也~搞定~


计算机图形学课程设计任务书[1] ———————————————————————————————— 作者: ———————————————————————————————— 日期: 南京工程学院 课程设计任务书                课  程 名  称  计算机图形学  院(系、部、心) 计算机工程学院 专       业  计算机科学与技术 班       级 多媒体091  起  止   日 期 2011.12.19—12.25 指   导 教 师 丁宇辰                     " 1.课程设计应达到的目的 " "《计算机图形学》是一门理论与实践并重的课程,既要注重理解基本知识,也要注重培" "养基本技能。相应的计算机图形学课程设计对帮助学生全面牢固地掌握课堂教学内" "容、培养学生编程能力、提高学生全面素质具有很重要的意义。通过本课程的实践" "环节,使学生具有使用计算机图形学的基本算法开发简单应用程序的能力,并理解 " "软件系统开发的基本流程。 " "2.课程设计题目及要求 " "选择实现以下任务的1~2个。 " "1、《多边形填充》(直线、圆、椭圆利用算法自编函数进行调用) " "具体要求: " "(1)能利用交互手段实现多边形、圆或椭圆的绘制,并能实现填充; " "(2)自己编写划线函数; " "(3)自己编写填充函数; " "2、《二维图形裁剪》 " "具体要求: " "设计程序实现二维图形的裁剪; " "设计坐标系,画出二维图形; " "用点法或区域代码的方法实现裁剪。 " "3、《图形变换》 " "具体要求: " "绘制出图形; " "实现图形的平移、旋转、缩放、错切等变换。 " "4、《绘制曲线》 " "具体要求: " "编写一个交互绘制Bezier或B样条曲线的程序; " "给出控制点就能绘制曲线; " "自己编写函数实现。 " "5、《可见面判断》 " "具体要求: " "绘制多面体图形; " "输出各面的可见性; " "做任意角度旋转变换,再判断各面的可见性 " "6、《颜色渐变三角形》 " "具体要求: " "绘制三角形; " "实现颜色的各种变换; " "实现填充色的渐变(从一个方向开始颜色由浅入深) " "7、《绘制三维五角星》 " "具体要求: " "编写一个绘制三维五角星的程序; " "实现填充,心点最红,渐变到外围颜色变浅; " "8. 《观察和投影变换》 " "具体要求: " "设计一立体图形; " "输出其三视图; " "定义观察窗口,输出其三维线架图。 " "9.《设计一个动画程序》 " "具体要求: " "设计一个图形,完成该图形的动画演示; " "给出使用说明。 " "10.《用OpenGL绘制一三维图形》 " "具体要求: " "绘制三维图形; " "添加纹理。 " "11.《设计一个分形树》 " "具体要求: " "写出算法分析步骤; " "实现该算法; " "改变输入,观察分形图形。 " "12.《设计B样条曲面》 " "具体要求: " "对B样条曲面的数学表达式进行分析,写出算法步骤; " "实现该算法; " "改变输入,观察曲面图形。 " "13.《自造字库,设计一个特大型汉字》 " "具体要求: " "构造汉字字库; " "实现汉字的输出; " "对汉字进行填充及各种变换。 " "14.在屏幕上使用鼠标左键绘制数量大于4的任意顶点形成控制多边形,单机鼠标 " "右键绘制三次B样条曲线,同时在控制多边形的每一个特征三角形内用虚线显示三 " "次B样条曲线的几何生成原理。 " "具体要求: " "分析原理,写出算法; " "实现该算法; " "15.给定"树叶"轮廓的控制点,要求使用B样条曲线的特殊构造技巧,使用B样条绘制" "所构造的树叶。 " "具体要求: " "分析原理,写出算法; " "实现该算法; " "16.样条曲线和正弦曲线同时绘制,进行效果对比,绘制的长度不少于两个周期。" "具体要求: " "分析原理,写出算法; " "实现该算法; " "17.自定义一个三维图形,可参考下图,绘出其三视图。 " "具体要求: " "分析原理,写出算法; " "实现该算法; " "18.模仿Windows画图的界面,编写一个交互画图的小系统程序。 " "具体要求: " "写出实现方法; " "实现该方法; " "19.设计一个多边形,利用活化边表算法进行填充。 " "具体要求: " "写出实现方法; " "实现该方法; " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "3.课程设计任务及工作
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值