问题五十三:怎么用ray tracing画参数方程表示的曲面(1)

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

首先,以球面为例学习怎么用ray tracing画参数方程表示的曲面;然后,再画一个牛角面。

 

特别说明:这一章节所画的曲面只是示意性的,所以先不care图片上的瑕疵。

 

53.1 数学推导

球面的参数方程f(φ,θ)如下:



画参数方程表示的球面的步骤如下:

 

第一步:

将φ分成n份,每一份为Δφ;将θ分成m份,每一份为Δθ。

每一对Δφ、Δθ对应球面上一小块曲面(如上图蓝色表示)。

所以,球面一共分成了n*m个这样的蓝色小块。

 

第二步:

根据蓝色小块的四个“顶点”的位置确定包围蓝色小块的最小长方体(如上图紫色长方体表示)。

所以,对应的小长方体个数也为n*m个。

“最小长方体”bl、bh两个顶点坐标的确定:

根据球面参数方程求得蓝色小块的四个“顶点”的坐标,选出其中最小的x、最小的y、最小的z作为bl的坐标,选出其中最大的x、最大的y、最大的z作为bh的坐标。

 

第三步:

光线撞击小长方体。只有撞击到小长方体的光线才有可能撞击到对应的蓝色小块。

所以,判断光线是否撞击球面,需要经过n*m次判断(光线是否撞击到这n*m个小长方体)。

光线撞击到长方体时,可以求得对应的t_near。

所以,每个长方体对应着一组:


 

第四步:

光线撞击到小长方体,不一定能够撞击到对应的蓝色小块。这里我们用三维牛顿迭代法来判断光线是否撞击到蓝色小块。若撞击到,求得其近似坐标值。(二维牛顿迭代对应的是切线,三维牛顿迭代对应的则是切平面)。

在牛顿迭代过程中会涉及到迭代终止条件。

 

下面重点分析其中的“第四步”

 









53.2 球面参数方程的各种微分的计算





53.3 牛角面参数方程的各种微分的计算






53.4 看C++代码实现

----------------------------------------------vec3.h ------------------------------------------

vec3.h

bool get_matrix_3_1(const vec3 a, float (&m)[3][1]);
bool get_matrix_3_3(const vec3 a, const vec3 b, const vec3 c, float (&m)[3][3]);
bool get_matrix_inverse_3_3(const float m[3][3], float (&inverse)[3][3]);
bool matrix_3_3_multiply_3_1(const float m1[3][3], const float m2[3][1], float (&m)[3][1]);
bool matrix_3_1_minus_3_1(const float m1[3][1], const float m2[3][1], float (&m)[3][1]);

----------------------------------------------vec3.cpp ------------------------------------------

vec3.cpp

 

bool get_matrix_3_1(const vec3 a, float (&m)[3][1]) {
/*将向量转换为矩阵*/
        m[0][0] = a.x();
        m[1][0] = a.y();
        m[2][0] = a.z();
        return true;
}

bool get_matrix_3_3(const vec3 a, const vec3 b, const vec3 c, float (&m)[3][3]) {
/*将向量转换为矩阵*/
        m[0][0] = a.x();
        m[1][0] = a.y();
        m[2][0] = a.z();
        m[0][1] = b.x();
        m[1][1] = b.y();
        m[2][1] = b.z();	
        m[0][2] = c.x();
        m[1][2] = c.y();
        m[2][2] = c.z();
        return true;
}

bool get_matrix_inverse_3_3(const float m[3][3], float (&inverse)[3][3]) {
/*求3*3矩阵的逆矩阵*/
        float det_m = m[0][0]*m[1][1]*m[2][2] + m[0][1]*m[1][2]*m[2][0] + 
				    m[0][2]*m[1][0]*m[2][1] - m[0][2]*m[1][1]*m[2][0] - 
m[0][0]*m[1][2]*m[2][1] - m[0][1]*m[1][0]*m[2][2];
/*求对应行列式的值*/
        if (fabs(det_m) < 1e-6) {/*对应行列式的值不为零,才存在逆矩阵*/
            return false;
        }
        else {
/*下面是求各种代数余子式,同时除以行列式的值*/
            vec3 a = (1/det_m)*vec3(m[1][1]*m[2][2] - m[1][2]*m[2][1],
                                    m[1][2]*m[2][0] - m[1][0]*m[2][2],
                                    m[1][0]*m[2][1] - m[1][1]*m[2][0]);
            vec3 b = (1/det_m)*vec3(m[0][2]*m[2][1] - m[0][1]*m[2][2],
                                    m[0][0]*m[2][2] - m[0][2]*m[2][0],
                                    m[0][1]*m[2][0] - m[0][0]*m[2][1]);
            vec3 c = (1/det_m)*vec3(m[0][1]*m[1][2] - m[0][2]*m[1][1],
                                    m[0][2]*m[1][0] - m[0][0]*m[1][2],
                                    m[0][0]*m[1][1] - m[0][1]*m[1][0]);
            get_matrix_3_3(a, b, c, inverse);
            return true;
        }
}

bool matrix_3_3_multiply_3_1(const float m1[3][3], const float m2[3][1], float (&m)[3][1]) {
/*3*3矩阵与3*1矩阵相乘*/
        m[0][0] = m1[0][0]*m2[0][0] + m1[0][1]*m2[1][0] + m1[0][2]*m2[2][0];
        m[1][0] = m1[1][0]*m2[0][0] + m1[1][1]*m2[1][0] + m1[1][2]*m2[2][0];
        m[2][0] = m1[2][0]*m2[0][0] + m1[2][1]*m2[1][0] + m1[2][2]*m2[2][0];
        return true;
}

bool matrix_3_1_minus_3_1(const float m1[3][1], const float m2[3][1], float (&m)[3][1]) {
/*矩阵相减*/
        m[0][0] = m1[0][0] - m2[0][0];
        m[1][0] = m1[1][0] - m2[1][0];
        m[2][0] = m1[2][0] - m2[2][0];
        return true;
}

----------------------------------------------parametric_surface.h ------------------------------------------

parametric_surface.h

 

#ifndef PARAMETRIC_SURFACE_H
#define PARAMETRIC_SURFACE_H

#include <hitable.h>
#include "material.h"
#include "log.h"


class parametric_surface : public hitable
{
    public:
        parametric_surface() {}
        parametric_surface(vec3 cen, float a, float b, float c, float hy, material *m, float r, float t, float n) : center(cen), intercept_x(a), intercept_y(b), intercept_z(c), height_half_y(hy), ma(m), rho(r), theta(t), num(n) {}
/*rho即为ρ,一般为0.05;theta为相机的张角;num为长宽像素数目值中较大的那个值*/
        virtual bool hit(const ray& r, float tmin, float tmax, hit_record& rec) const;
        vec3 center;
        float intercept_x;
        float intercept_y;
        float intercept_z;
        float height_half_y;
        material *ma;
        float rho, theta, num;
};

#endif // PARAMETRIC_SURFACE_H

----------------------------------------------parametric_surface.cpp ------------------------------------------

parametric_surface.cpp

#include "parametric_surface.h"

#include <iostream>
#include <limits>
#include "float.h"
#include "log.h"

using namespace std;

#define TYPE 2
/*
TYPE=1: sphere;
TYPE=2: horn;
*/

bool ray_hit_box_para(const ray& r, const vec3& vertex_l, const vec3& vertex_h, float& t_near, float& t_far) {
/*重写了光线撞击长方体的函数*/
        t_near = (numeric_limits<float>::min)();
        t_far = (numeric_limits<float>::max)();
        vec3 direction = r.direction();
        vec3 origin = r.origin();
        vec3 bl = vertex_l;
        vec3 bh = vertex_h;
        float array1[6];

        if(direction.x() == 0) {
            if((origin.x() < bl.x()) || (origin.x() > bh.x())) {
#if PARAMETRIC_SURFACE_LOG == 1
                std::cout << "the ray is parallel to the planes and the origin X0 is not between the slabs. return false" <<endl;
#endif // PARAMETRIC_SURFACE_LOG
                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())) {
#if PARAMETRIC_SURFACE_LOG == 1
                std::cout << "the ray is parallel to the planes and the origin Y0 is not between the slabs. return false" <<endl;
#endif // PARAMETRIC_SURFACE_LOG
                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())) {
#if PARAMETRIC_SURFACE_LOG == 1
                std::cout << "the ray is parallel to the planes and the origin Z0 is not between the slabs. return false" <<endl;
#endif // PARAMETRIC_SURFACE_LOG
                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=i+2){
            if(array1[i] > array1[i+1]) {
                float t = array1[i];
                array1[i] = array1[i+1];
                array1[i+1] = t;
            }
#if PARAMETRIC_SURFACE_LOG == 1
            std::cout << "array1[" << i << "]:" << array1[i] <<endl;
            std::cout << "array1[" << i+1 << "]:" << array1[i+1] <<endl;
#endif // PARAMETRIC_SURFACE_LOG
            if(array1[i] >= t_near) {t_near = array1[i];}
            if(array1[i+1] <= t_far) {t_far = array1[i+1];}
            if(t_near > t_far) {
#if PARAMETRIC_SURFACE_LOG == 1
            std::cout << "No.(0=X;2=Y;4=Z):" << i << "  :t_near > t_far. return false" <<endl;
#endif // PARAMETRIC_SURFACE_LOG
                return false;
            }
            if(t_far < 0) {
#if PARAMETRIC_SURFACE_LOG == 1
            std::cout << "No.(0=X;2=Y;4=Z):" << i << "  :t_far < 0. return false" <<endl;
#endif // VEC3_LOG
                return false;
            }
        }
        if (t_near != t_near) {
            t_near = t_near * 1;
        }
        return true;
}

 

#if TYPE == 2

bool horn_parametric(const vec3 cen, const float a, const float b, const float c, const float u, const float v, vec3& parametric) {
/*求牛角曲面的参数方程*/
        float sin_u = sin(2*M_PI*u);
        float sin_v = sin(v);
        float cos_u = cos(2*M_PI*u);
        float cos_v = cos(v);
        sin_u = (fabs(sin_u)<1e-6)? 0.0:sin_u;
        sin_v = (fabs(sin_v)<1e-6)? 0.0:sin_v;
        cos_u = (fabs(cos_u)<1e-6)? 0.0:cos_u;
        cos_v = (fabs(cos_v)<1e-6)? 0.0:cos_v;
        parametric = vec3(a*(2.0+u*cos_v)*sin_u+cen.x(),
                          c*((2+u*cos_v)*cos_u+2*u)+cen.y(),
                          b*u*sin_v+cen.z());
        return true;
}

bool horn_parametric_f1(const ray& r, const float a, const float b, const float c, const float u, const float v, vec3& fu, vec3& fv, vec3& am) {
/*求牛角曲面的微分,为求Jacobi矩阵*/
        float sin_u = sin(2*M_PI*u);
        float sin_v = sin(v);
        float cos_u = cos(2*M_PI*u);
        float cos_v = cos(v);
        sin_u = (fabs(sin_u)<1e-6)? 0.0:sin_u;
        sin_v = (fabs(sin_v)<1e-6)? 0.0:sin_v;
        cos_u = (fabs(cos_u)<1e-6)? 0.0:cos_u;
        cos_v = (fabs(cos_v)<1e-6)? 0.0:cos_v;
        fu = vec3(a*(2.0*M_PI*(2.0+u*cos_v)*cos_u+cos_v*sin_u),
                  c*(-2.0*M_PI*(2.0+u*cos_v)*sin_u+cos_v*cos_u+2.0),
                  b*(sin_v));
        fv = vec3(a*(-u*sin_u*sin_v),
                  c*(-u*cos_u*sin_v),
                  b*(u*cos_v));
        am = vec3(-r.direction().x(), -r.direction().y(), -r.direction().z());
        return true;
}

bool horn_parametric_f1_f2(const float a, const float b, const float c, const float u, const float v, vec3& fu, vec3& fv, vec3& fuu, vec3& fuv, vec3& fvv) {
/*求牛角曲面的各种微分*/
        float sin_u = sin(2*M_PI*u);
        float sin_v = sin(v);
        float cos_u = cos(2*M_PI*u);
        float cos_v = cos(v);
        sin_u = (fabs(sin_u)<1e-6)? 0.0:sin_u;
        sin_v = (fabs(sin_v)<1e-6)? 0.0:sin_v;
        cos_u = (fabs(cos_u)<1e-6)? 0.0:cos_u;
        cos_v = (fabs(cos_v)<1e-6)? 0.0:cos_v;
        fu = vec3(a*(2.0*M_PI*(2.0+u*cos_v)*cos_u+cos_v*sin_u),
                  c*(-2.0*M_PI*(2.0+u*cos_v)*sin_u+cos_v*cos_u+2.0),
                  b*(sin_v));
        fv = vec3(a*(-u*sin_u*sin_v),
                  c*(-u*cos_u*sin_v),
                  b*(u*cos_v));
        fuu = vec3(a*(-4.0*M_PI*M_PI*(2.0+u*cos_v)*sin_u+4.0*M_PI*cos_v*cos_u),
                   c*(-4.0*M_PI*(cos_v*sin_u+M_PI*(2.0+u*cos_v)*cos_u)),
                   0.0);
        fuv = vec3(-a*sin_v*(sin_u+2*M_PI*u*cos_u),
                   -c*sin_v*(cos_u-2*M_PI*u*sin_u),
                   b*cos_v);
        fvv = vec3(-a*u*sin_u*cos_v,
                   -c*u*cos_u*cos_v,
                   -b*u*sin_v);
        return true;
}

bool horn_parametric_bl_bh(const vec3 cen, const float a, const float b, const float c, const float u1, const float u2, const float v1, const float v2, vec3& bl, vec3& bh) {
/*求牛角曲面的小长方体的bl、bh*/
        float x[4], z[4], y[2];
        float sin_u1 = sin(2*M_PI*u1);
        float sin_u2 = sin(2*M_PI*u2);
        float sin_v1 = sin(v1);
        float sin_v2 = sin(v2);
        float cos_u1 = cos(2*M_PI*u1);
        float cos_u2 = cos(2*M_PI*u2);
        float cos_v1 = cos(v1);
        float cos_v2 = cos(v2);
        sin_u1 = (fabs(sin_u1)<1e-6)? 0.0:sin_u1;
        sin_u2 = (fabs(sin_u2)<1e-6)? 0.0:sin_u2;
        sin_v1 = (fabs(sin_v1)<1e-6)? 0.0:sin_v1;
        sin_v2 = (fabs(sin_v2)<1e-6)? 0.0:sin_v2;
        cos_u1 = (fabs(cos_u1)<1e-6)? 0.0:cos_u1;
        cos_u2 = (fabs(cos_u2)<1e-6)? 0.0:cos_u2;
        cos_v1 = (fabs(cos_v1)<1e-6)? 0.0:cos_v1;
        cos_v2 = (fabs(cos_v2)<1e-6)? 0.0:cos_v2;

        float temp;
        x[0] = a*((2+u1*cos_v1)*sin_u1)+cen.x();
        x[1] = a*((2+u1*cos_v2)*sin_u1)+cen.x();
        x[2] = a*((2+u2*cos_v1)*sin_u2)+cen.x();
        x[3] = a*((2+u2*cos_v2)*sin_u2)+cen.x();
        for (int i=0; i<3; i++) {
            for (int j=i+1; j<4; j++) {
                if (x[i] > x[j]) {
                    temp = x[i];
                    x[i] = x[j];
                    x[j] = temp;
                }
            }
        }
        y[0] = c*((2+u1*cos_v1)*cos_u1+2*u1)+cen.y();
        y[1] = c*((2+u1*cos_v2)*cos_u1+2*u1)+cen.y();
        y[2] = c*((2+u2*cos_v1)*cos_u2+2*u2)+cen.y();
        y[3] = c*((2+u2*cos_v2)*cos_u2+2*u2)+cen.y();
        for (int i=0; i<3; i++) {
            for (int j=i+1; j<4; j++) {
                if (y[i] > y[j]) {
                    temp = y[i];
                    y[i] = y[j];
                    y[j] = temp;
                }
            }
        }
        z[0] = b*u1*sin_v1+cen.z();
        z[1] = b*u1*sin_v2+cen.z();
        z[2] = b*u2*sin_v1+cen.z();
        z[3] = b*u2*sin_v2+cen.z();
        for (int i=0; i<3; i++) {
            for (int j=i+1; j<4; j++) {
                if (z[i] > z[j]) {
                    temp = z[i];
                    z[i] = z[j];
                    z[j] = temp;
                }
            }
        }

        bl = vec3(x[0], y[0], z[0]);
        bh = vec3(x[3], y[3], z[3]);

        return true;
}

#endif // TYPE

 

#if TYPE == 1

bool sphere_parametric(const vec3 cen, const float a, const float b, const float c, const float u, const float v, vec3& parametric) {
/*求球面的参数方程*/
        float sin_u = sin(u);
        float sin_v = sin(v);
        float cos_u = cos(u);
        float cos_v = cos(v);
        sin_u = (fabs(sin_u)<1e-6)? 0.0:sin_u;
        sin_v = (fabs(sin_v)<1e-6)? 0.0:sin_v;
        cos_u = (fabs(cos_u)<1e-6)? 0.0:cos_u;
        cos_v = (fabs(cos_v)<1e-6)? 0.0:cos_v;
        parametric = vec3(a*sin_u*sin_v+cen.x(), c*cos_u+cen.y(), b*sin_u*cos_v+cen.z());
        return true;
}

bool sphere_parametric_f1(const ray& r, const float a, const float b, const float c, const float u, const float v, vec3& fu, vec3& fv, vec3& am) {
/*求球面的微分,为求Jacobi矩阵*/
        float sin_u = sin(u);
        float sin_v = sin(v);
        float cos_u = cos(u);
        float cos_v = cos(v);
        sin_u = (fabs(sin_u)<1e-6)? 0.0:sin_u;
        sin_v = (fabs(sin_v)<1e-6)? 0.0:sin_v;
        cos_u = (fabs(cos_u)<1e-6)? 0.0:cos_u;
        cos_v = (fabs(cos_v)<1e-6)? 0.0:cos_v;
        fu = vec3(a*cos_u*sin_v, -c*sin_u, b*cos_u*cos_v);
        fv = vec3(a*sin_u*cos_v, 0, -b*sin_u*sin_v);
        am = vec3(-r.direction().x(), -r.direction().y(), -r.direction().z());
        return true;
}

bool sphere_parametric_f1_f2(const float a, const float b, const float c, const float u, const float v, vec3& fu, vec3& fv, vec3& fuu, vec3& fuv, vec3& fvv) {
/*求球面的各种微分*/
        float sin_u = sin(u);
        float sin_v = sin(v);
        float cos_u = cos(u);
        float cos_v = cos(v);
        sin_u = (fabs(sin_u)<1e-6)? 0.0:sin_u;
        sin_v = (fabs(sin_v)<1e-6)? 0.0:sin_v;
        cos_u = (fabs(cos_u)<1e-6)? 0.0:cos_u;
        cos_v = (fabs(cos_v)<1e-6)? 0.0:cos_v;
        fu = vec3(a*cos_u*sin_v, -c*sin_u, b*cos_u*cos_v);
        fv = vec3(a*sin_u*cos_v, 0, -b*sin_u*sin_v);
        fuu = vec3(-a*sin_u*sin_v, -c*cos_u, -b*sin_u*cos_v);
        fuv = vec3(a*cos_u*cos_v, 0, -b*cos_u*sin_v);
        fvv = vec3(-a*sin_u*sin_v, 0, -b*sin_u*cos_v);
        return true;
}

bool sphere_parametric_bl_bh(const vec3 cen, const float a, const float b, const float c, const float u1, const float u2, const float v1, const float v2, vec3& bl, vec3& bh) {
/*求球面的小长方体的bl、bh*/
        float x[4], z[4], y[2];
        float sin_u1 = sin(u1);
        float sin_u2 = sin(u2);
        float sin_v1 = sin(v1);
        float sin_v2 = sin(v2);
        float cos_u1 = cos(u1);
        float cos_u2 = cos(u2);
        float cos_v1 = cos(v1);
        float cos_v2 = cos(v2);
        sin_u1 = (fabs(sin_u1)<1e-6)? 0.0:sin_u1;
        sin_u2 = (fabs(sin_u2)<1e-6)? 0.0:sin_u2;
        sin_v1 = (fabs(sin_v1)<1e-6)? 0.0:sin_v1;
        sin_v2 = (fabs(sin_v2)<1e-6)? 0.0:sin_v2;
        cos_u1 = (fabs(cos_u1)<1e-6)? 0.0:cos_u1;
        cos_u2 = (fabs(cos_u2)<1e-6)? 0.0:cos_u2;
        cos_v1 = (fabs(cos_v1)<1e-6)? 0.0:cos_v1;
        cos_v2 = (fabs(cos_v2)<1e-6)? 0.0:cos_v2;

        float temp;
        x[0] = a*sin_u1*sin_v1+cen.x();
        x[1] = a*sin_u1*sin_v2+cen.x();
        x[2] = a*sin_u2*sin_v1+cen.x();
        x[3] = a*sin_u2*sin_v2+cen.x();
        for (int i=0; i<3; i++) {
            for (int j=i+1; j<4; j++) {
                if (x[i] > x[j]) {
                    temp = x[i];
                    x[i] = x[j];
                    x[j] = temp;
                }
            }
        }
        z[0] = b*sin_u1*cos_v1+cen.z();
        z[1] = b*sin_u1*cos_v2+cen.z();
        z[2] = b*sin_u2*cos_v1+cen.z();
        z[3] = b*sin_u2*cos_v2+cen.z();
        for (int i=0; i<3; i++) {
            for (int j=i+1; j<4; j++) {
                if (z[i] > z[j]) {
                    temp = z[i];
                    z[i] = z[j];
                    z[j] = temp;
                }
            }
        }
        y[0] = ((c*cos_u1+cen.y())<(c*cos_u2+cen.y()))? (c*cos_u1+cen.y()):(c*cos_u2+cen.y());
        y[1] = ((c*cos_u1+cen.y())<(c*cos_u2+cen.y()))? (c*cos_u2+cen.y()):(c*cos_u1+cen.y());

        bl = vec3(x[0], y[0], z[0]);
        bh = vec3(x[3], y[1], z[3]);

        return true;
}

#endif // TYPE

 

int get_root_by_newton_iteration(const ray& r, vec3 cen, float a, float b, float c, vec3 x0, float tao, float uid, float vid, vec3& root) {
/*三维牛顿迭代*/
        vec3 fun0, dis0, fu, fv, am, fun1, dis1, fuu, fuv, fvv, normal;
        float m_d0[3][1], m_j[3][3], m_j_i[3][3], m_j_i_d0[3][1], m_x_k0[3][1], m_x_k1[3][1];
        float d0, d1, d2, h;
        get_matrix_3_1(x0, m_x_k0);
#if TYPE == 1
        sphere_parametric(cen, a, b, c, m_x_k0[0][0], m_x_k0[1][0], fun0);
#endif // TYPE
 #if TYPE == 2
        horn_parametric(cen, a, b, c, m_x_k0[0][0], m_x_k0[1][0], fun0);
#endif // TYPE
        dis0 = fun0 - r.point_at_parameter(m_x_k0[2][0]);

        for (int i=0; i<20; i++) {
#if TYPE == 1
            sphere_parametric_f1(r, a, b, c, m_x_k0[0][0], m_x_k0[1][0], fu, fv, am);
#endif // TYPE
#if TYPE == 2
            horn_parametric_f1(r, a, b, c, m_x_k0[0][0], m_x_k0[1][0], fu, fv, am);
#endif // TYPE
            get_matrix_3_1(dis0, m_d0);
            get_matrix_3_3(fu, fv, am, m_j);
            if (get_matrix_inverse_3_3(m_j, m_j_i)) {
/*迭代格式。对应上面推导过程中的“式子七”*/
                matrix_3_3_multiply_3_1(m_j_i, m_d0, m_j_i_d0);
                matrix_3_1_minus_3_1(m_x_k0, m_j_i_d0, m_x_k1);
//                if (m_x_k1[2][0] > 1e-16)
                {
#if TYPE == 1
                    sphere_parametric(cen, a, b, c, m_x_k1[0][0], m_x_k1[1][0], fun1);
#endif // TYPE
#if TYPE == 2
                    horn_parametric(cen, a, b, c, m_x_k1[0][0], m_x_k1[1][0], fun1);
#endif // TYPE
                    dis1 = fun1 - r.point_at_parameter(m_x_k1[2][0]);
                    if (dis1.length() < tao) {

#if TYPE == 1
                        sphere_parametric_f1_f2(a, b, c, m_x_k1[0][0], m_x_k1[1][0], fu, fv, fuu, fuv, fvv);
#endif // TYPE
#if TYPE == 2
                        horn_parametric_f1_f2(a, b, c, m_x_k1[0][0], m_x_k1[1][0], fu, fv, fuu, fuv, fvv);
#endif // TYPE
                        normal = unit_vector(cross(fu, fv));
                        d0 = dot(normal, fuu);
                        d1 = dot(normal, fuv);
                        d2 = dot(normal, fvv);
                        h = 0.5*(d0*uid*uid+2*d1*uid*vid+d2*vid*vid);
/*对应上面推导过程中的“式子八”*/
                        if (fabs(h) < tao) {
                            if (root.z() == 0.0) {
                                root = vec3(m_x_k1[0][0], m_x_k1[1][0], m_x_k1[2][0]);
                            }
                            else {
                                if (m_x_k1[2][0] < root.z()) {
                                    root = vec3(m_x_k1[0][0], m_x_k1[1][0], m_x_k1[2][0]);
                                }
                            }
                            return 1;
                        }
                        else {
                            root = vec3(m_x_k1[0][0], m_x_k1[1][0], m_x_k1[2][0]);
                            return 2;//0;
                        }
                    }
                    else {
                        m_x_k0[0][0] = m_x_k1[0][0];
                        m_x_k0[1][0] = m_x_k1[1][0];
                        m_x_k0[2][0] = m_x_k1[2][0];
                        dis0 = dis1;
                    }
                }
            }
            else {
                return 0;
            }
        }
        return 0;
}


bool parametric_surface::hit(const ray& r, float t_min, float t_max, hit_record& rec) const {
#if PARAMETRIC_SURFACE_LOG == 1
        std::cout << "-------------parametric_surface::hit----------------" << endl;
#endif // PARAMETRIC_SURFACE_LOG
#if TYPE == 1
/*球面只需要分成4*8份,即可画出可接受的图片*/
        #define UN 4
        #define VN 8
#endif // TYPE
#if TYPE == 2
/*在当前程序中,牛角面分成80*80份,输出的图片还是有较多瑕疵*/
        #define UN 80
        #define VN 80
#endif // TYPE
        float ui1, ui2, vi1, vi2, t_near, t_far, tao, u1, v1, uid, vid;
        vec3 bl, bh, fu, fv, am, x0;
        vec3 root = vec3(0.0, 0.0, 0.0);
        int rn = 2;
        int root_num = 0;
        bool hitted1 = 0;
        float t_near_a, t_far_a;
        t_near_a = -1.0;
        t_far_a = t_near_a;
        bool hitted2 = 0;
#if TYPE == 1
#endif // TYPE
#if TYPE == 3
        float delta_t[4] = = {-1.0, -1.0, -1.0, -1.0};
        int min_num = 0;
#endif // TYPE
        float ui1_a[4], ui2_a[4], vi1_a[4], vi2_a[4];
#if TYPE == 1
/*包围整个球面的长方体*/
        bl = vec3(center.x()-intercept_x, center.y()-intercept_y, center.z()-intercept_z);
        bh = vec3(center.x()+intercept_x, center.y()+intercept_y, center.z()+intercept_z);
#endif // TYPE
#if TYPE == 2
/*包围整个牛角面的长方体*/
        bl = vec3(center.x()-intercept_x*2, center.y()-intercept_y*4, center.z()-intercept_z*2);
        bh = vec3(center.x()+intercept_x*2, center.y()+intercept_y*4, center.z()+intercept_z*2);
#endif // TYPE
        if(ray_hit_box_para(r, bl, bh, t_near, t_far)) {
            for (int i=0; i<UN; i++) {
/*这两个for循环即是将曲面分成n*m个蓝色小块*/
#if TYPE == 1
                ui1 = float(i)*M_PI/float(UN);
                ui2 = float(i+1)*M_PI/float(UN);
#endif // TYPE
#if TYPE == 2
                ui1 = float(i)/float(UN);
                ui2 = float(i+1)/float(UN);
#endif // TYPE
                for (int j=0; j<VN; j++) {
                    vi1 = float(j)*2*M_PI/float(VN);
                    vi2 = float(j+1)*2*M_PI/float(VN);
                    u1 = (ui1+ui2)/2;
                    v1 = (vi1+vi2)/2;
#if TYPE == 1
                    sphere_parametric_bl_bh(center, intercept_x, intercept_y, intercept_z, ui1, ui2, vi1, vi2, bl, bh);
#endif // TYPE
#if TYPE == 2
                    horn_parametric_bl_bh(center, intercept_x, intercept_y, intercept_z, ui1, ui2, vi1, vi2, bl, bh);
#endif // TYPE
                    hitted1 = ray_hit_box_para(r, bl, bh, t_near, t_far);
                    if(hitted1 && (t_near > 1e-7)) {
                        while (rn == 2) {

                            tao = fabs(2*rho*t_near*sin(theta*(M_PI/180)/2)/num);
                            x0 = vec3(u1, v1, t_near);
                            uid = ui2-ui1;
                            vid = vi2-vi1;
                            rn = get_root_by_newton_iteration(r, center, intercept_x, intercept_y, intercept_z, x0, tao, uid, vid, root);
                            if (rn == 1) {
                                root_num ++ ;
                            }
                            else if (rn == 2) {
                                ui1_a[0] = ui1;
                                ui2_a[0] = ui1+uid/2;
                                vi1_a[0] = vi1;
                                vi2_a[0] = vi1+vid/2;

                                ui1_a[1] = ui1;
                                ui2_a[1] = ui1+uid/2;
                                vi1_a[1] = vi1+vid/2;
                                vi2_a[1] = vi2;

                                ui1_a[2] = ui1+uid/2;
                                ui2_a[2] = ui2;
                                vi1_a[2] = vi1;
                                vi2_a[2] = vi1+vid/2;

                                ui1_a[3] = ui1+uid/2;
                                ui2_a[3] = ui2;
                                vi1_a[3] = vi1+vid/2;
                                vi2_a[3] = vi2;

                                for (int j=0; j<4; j++) {
                                    ui1 = ui1_a[j];
                                    ui2 = ui2_a[j];
                                    vi1 = vi1_a[j];
                                    vi2 = vi2_a[j];
#if TYPE == 1
                                    sphere_parametric_bl_bh(center, intercept_x, intercept_y, intercept_z, ui1, ui2, vi1, vi2, bl, bh);
#endif // TYPE
#if TYPE == 2
                                    horn_parametric_bl_bh(center, intercept_x, intercept_y, intercept_z, ui1, ui2, vi1, vi2, bl, bh);
#endif // TYPE
                                    hitted2 = ray_hit_box_para(r, bl, bh, t_near_a, t_far_a);
                                    if(hitted2 && (t_near_a > 1e-7)) {
    //                                    if (t_near_a == t_near) {
                                            break;
    //                                    }
                                    }
                                }
                                if (hitted2) {
                                    t_near = root.z(); //t_near_a;
                                    u1 = root.x();
                                    v1 = root.y();
                                }
                                else {
                                    break;
                                }
                            }
                            else if (rn == 0) {
                                return false;
                            }
                        }
                        if (root_num == 2) {
                            break;
                        }
                   }
                }
                if (root_num == 2) {
                    break;
                }
            }
        }
        else {
            return false;
        }


        if (root.z() < t_max && root.z() > t_min) {
            rec.t = root.z();
            rec.p = r.point_at_parameter(rec.t);
            vec3 pc = rec.p - center;
#if TYPE == 1
            sphere_parametric_f1(r, intercept_x, intercept_y, intercept_z, root.x(), root.y(), fu, fv, am);
#endif // TYPE
#if TYPE == 2
            horn_parametric_f1(r, intercept_x, intercept_y, intercept_z, root.x(), root.y(), fu, fv, am);
#endif // TYPE
/*在上面数学推导过程中的“式子八”求h时,有提到法向量的求法*/
            rec.normal = unit_vector(cross(fu, fv));
            if(dot(r.direction(), rec.normal) > 0) {
                rec.normal = - rec.normal;
            }
            rec.mat_ptr = ma;
            rec.u = -1.0;
            rec.v = -1.0;
            return true;
        }
        return false;
}


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

main.cpp

/*先画个球面*/

 

        int nx = 200;
        int ny = 100;
        int ns = 1;

//        ofstream outfile( ".\\results\\superhyperboloid-two.txt", ios_base::out);
        ofstream outfile( ".\\results\\parametric_surface.txt", ios_base::out);
        outfile << "P3\n" << nx << " " << ny << "\n255\n";

        std::cout << "P3\n" << nx << " " << ny << "\n255\n";

        hitable *list[1];        list[0] = new parametric_surface(vec3(0.0, 3.0, 0.0), 3.0, 3.0, 3.0, 8.0, new lambertian(vec3(1.0, 0.0, 0.0)), 0.05, 20, 200);

        hitable *world = new hitable_list(list,1);

        vec3 lookfrom(0, 3, 20);
        vec3 lookat(0, 3, 0);
        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);

 

输出图片如下:


将采样次数ns设为100后的图片:


有明显的瑕疵,先不管了,重要的是方法,细节方面先不care。

 


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

main.cpp

/*再画个牛角面*/

        int nx = 200;
        int ny = 100;
        int ns = 1;

//        ofstream outfile( ".\\results\\superhyperboloid-two.txt", ios_base::out);
        ofstream outfile( ".\\results\\parametric_surface.txt", ios_base::out);
        outfile << "P3\n" << nx << " " << ny << "\n255\n";

        std::cout << "P3\n" << nx << " " << ny << "\n255\n";

        hitable *list[1];
        list[0] = new parametric_surface(vec3(0.0, 3.0, 0.0), 3.0, 3.0, 3.0, 8.0, new lambertian(vec3(1.0, 0.0, 0.0)), 0.05, 70, 200);

        hitable *world = new hitable_list(list,1);

        vec3 lookfrom(0, 6, 20);
        vec3 lookat(0, 6, 0);
        float dist_to_focus = (lookfrom - lookat).length();
        float aperture = 0.0;
        camera cam(lookfrom, lookat, vec3(0,1,0), 70, float(nx)/float(ny), aperture, 0.7*dist_to_focus);


输出图片如下:

分成40*40


分成80*40


分成80*80



分成100*100

 

分成200*100

 

分成300*100



每张图片都是有的地方多了像素点,有的地方少了像素点。Anyway,“方法比细节重要”at this moment.

 

最后贴一张球面和牛角面的大小对比图(是分两次输出的两张图片,然后用画图工具合到了一块)




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值