美文网首页
作业一Readme

作业一Readme

作者: 正经龙 | 来源:发表于2018-09-24 14:04 被阅读0次

    1. 实现大致框架,实现下图继承关系的几个类并给对应类写好空过程

    继承关系

    各类继承关系如下所示

    Spline

    class Spline
    {
    public:
        Spline();
        ~Spline();
        // 用于画图的FOR VISUALIZATION
        virtual void Paint(ArgParser *args);
        virtual void addControlPoint(int pt, float x, float y);
        virtual void set(int i, Vec3f v);
        // 用于实现样条类型转换的FOR CONVERTING BETWEEN SPLINE TYPES
        virtual void OutputBezier(FILE *file) {};
        virtual void OutputBSpline(FILE *file) {};
        // 用于得到控制点的FOR CONTROL POINT PICKING
        virtual int getNumVertices();
        virtual Vec3f getVertex(int i);
    
        // 用于编辑操作的FOR EDITING OPERATIONS
        virtual void moveControlPoint(int selectedPoint, float x, float y);
        virtual void ddControlPoint(int selectedPoint, float x, float y);
        virtual void deleteControlPoint(int selectedPoint);
    
        // 用于产生三角形的FOR GENERATING TRIANGLES
        virtual TriangleMesh* OutputTriangles(ArgParser *args);
        /////////
    
    };
    

    BezierCurve

    class BezierCurve:public Curve
    {
    
    public:
        BezierCurve();
        BezierCurve(int array);
        void Paint(ArgParser *args);//绘制函数
        void GetCnk(GLint n, GLint* c);//获取参数1,3,3,1
        void GetPointPr(GLint *c, GLfloat t, Point3D *Pt, int ControlN, Point3D *ControlP);//获取当前点的坐标
        void OutputBSpline(FILE *file);
        void OutputBezier(FILE *file);
        void addControlPoint(int pt, float x, float y);
        Point3D* caluate(Point3D * pt);//传入四个点,传出四个点
        TriangleMesh* OutputTriangles(ArgParser *args);
        ~BezierCurve();
    };
    

    BSplineCurve

    {
    public:
        BSplineCurve();
        BSplineCurve(int array);
        void Paint(ArgParser *args);
        void GetPointPr(GLfloat t,Point3D& Pt, int ControlN, Point3D* ControlP);
        void addControlPoint(int pt, float x, float y);
        void OutputBSpline(FILE *file);
        void OutputBezier(FILE *file);
        TriangleMesh* OutputTriangles(ArgParser *args);
        Point3D * caluate(Point3D * pt);
        ~BSplineCurve();
    };
    

    SurfaceOfRevolution

    class SurfaceOfRevolution : public Surface
    {
    private:
        Curve * myCurve;
    public:
        SurfaceOfRevolution();
        SurfaceOfRevolution(Curve* curve);
        ~SurfaceOfRevolution();
        virtual void OutputBezier(FILE *file);
        virtual void OutputBSpline(FILE *file);
        void moveControlPoint(int selectedPoint, float x, float y);
        void addControlPoint(int pt, float x, float y);
        void deleteControlPoint(int selectedPoint);
        void Paint(ArgParser *args);//绘制函数
        int getNumVertices();
        Vec3f getVertex(int i);
        TriangleMesh* OutputTriangles(ArgParser *args);
    };
    

    BzeierPatch

    class BezierPatch :
        public Surface
    {
        Point3D Point[16];
    public:
        BezierPatch();
        virtual void set(int c, Vec3f v);
        ~BezierPatch();
        void Paint(ArgParser *args);//绘制函数
        void GetCnk(GLint n, GLint* c);//获取参数1,3,3,1
        void GetPointPr(GLint *c, GLfloat t, Point3D *Pt, int ControlN, Point3D *ControlP);//获取当前点的坐标
        void OutputBSpline(FILE *file);
        void OutputBezier(FILE *file);
        GLfloat** mxn(GLfloat ** m1, GLfloat ** m2, int m, int n, int p);
        Point3D* caluate(Point3D * pt);//传入四个点,传出四个点
        TriangleMesh* OutputTriangles(ArgParser *args);
    };
    

    1. 遇到的问题

    vs2017版本不兼容问题

    右键工程->属性->常规->附加包含目录
    修改成刚刚的工程目录

    添加链接目录

    然后将头文件与源文件拷贝到工程中,就完成了第一步

    2. 实现Bezier 与 BSpline曲线的绘制

    实现思路:由于首先让我们绘制的是四个控制点的Bezier与BSpline的曲线,但是这里看了一下接下来的题目,对于接下来的多Bezier与多BSpline控制点进行了兼容。

    对于Bezier曲线来说曲线的生成首先指定其控制点,然后根据其控制点的坐标与Bezier矩阵进行矩阵相乘,得出一个曲线对应公式,然后根据传进来的参数值进行曲线分割。

    Bezier 绘制

    首先在set函数里面保存控制点信息

    void Curve::set(int i, Vec3f v)
    {
        GLfloat x = 0.0, y = 0.0, z = 0.0;
        v.Get(x,y,z);
        Point3D PointADD;
        PointADD.x = x;
        PointADD.y = y;
        PointADD.z = z;
        Point.push_back(PointADD);
        num++;
    }
    

    由于Curve是Bezier与BSpline的父类,所以对于两者实现相同的函数都写在Curve中。这里的Point3D是自定义的数据类型,与文件中的Vec3f保留数据相同,都是三个GLfloat数据点

    实现Paint函数
    void BezierCurve::Paint(ArgParser *args) {
        Vec3f myVec[4];
        for (int n = 0; n < (num-1)/3; n++) {
            for (int four = 0; four < 4; four++) {
                myVec[four].Set( Point[n * 3 + four].x, Point[n * 3 + four].y, Point[n * 3 + four].z);
            }
        glPointSize(5);
        glLineWidth(5);
        glBegin(GL_LINES);
        glColor3f(0, 0, 1);
        GLfloat x[4] = { 0,0,0,0 };
        GLfloat y[4] = { 0,0,0,0 };
        GLfloat z[4] = { 0,0,0,0 };
        for (int i = 0; i < 4; i++) {
            myVec[i].Get(x[i], y[i], z[i]);
        }
        for (int i = 0; i < 3; i++) {
            glVertex3f(x[i], y[i], z[i]);
            glVertex3f(x[i + 1], y[i + 1], z[i + 1]);
        }
        glEnd();
        glColor3f(0.0, 1, 0.0);
        GLint *c;
        Point3D Pt(0, 0, 0);
        GLint ControlN = 4;
        c = new GLint[ControlN];
        GetCnk(ControlN - 1, c);
        Point3D* ControlP = new Point3D[4];
        for (int i = 0; i < 4; i++) {
            ControlP[i].x = x[i];
            ControlP[i].y = y[i];
            ControlP[i].z = z[i];
        }
        glBegin(GL_LINES);
        for (int i = 0; i <args->curve_tessellation; i++) {
            GetPointPr(c, (GLfloat)i / (GLfloat)args->curve_tessellation, &Pt, 4, ControlP);
            glVertex3f(Pt.x, Pt.y, Pt.z);
            GetPointPr(c, (GLfloat)(i + 1) / (GLfloat)args->curve_tessellation, &Pt, 4, ControlP);
            glVertex3f(Pt.x, Pt.y, Pt.z);
        }
        glEnd();
        glColor3f(1, 0, 0);
        glBegin(GL_POINTS);
        for (int i = 0; i < 4; i++) {
            glVertex3f(x[i], y[i], z[i]);
        }
        glEnd();
        delete[] ControlP;
        }
        
    }
    

    核心就是绘制曲线部分,通过获取四个控制点,与Bezier控制点相乘,得到一个曲线公式,利用杨辉三角求出(1-t)与(t)的参数关系,这里由于是四个控制点所以是1331,最后通过指定由t段直线描绘曲线来绘制t次直线,从而得到曲线。

    BSpline Paint

    BSpline曲线与Bzier曲线生成几乎相同,要注意的只有一点,就是Bezier的多点绘制方式是1234\2345\3456这种一次向后推移一位的绘制方式,除此之外就没有什么区别了,利用BSpline矩阵与四个控制点生成对应的曲线,然后根据传进来的参数确定曲线上近似点的位置,最后绘制出曲线。

    效果图

    Bezier


    Bezier

    BSpline


    BSpline

    3.Bezier与BSpline的转换

    根据公式G*Bezier T = G BSpline * T
    假设T相等
    要求G(BSpline),则公式变成
    G x Bezeir x BSpline﹣¹
    由于G已知,Bezier 已知,只要求出BSpline的逆矩阵就可得到对应的控制点。
    这里求逆用的是伴随矩阵与行列式求逆
    给出求伴随矩阵的代码。通过两层循环得到每一行的每一个先行数,剩下两层循环用于求出剩下3乘3矩阵对应的行列式的值

        //求伴随矩阵
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                for (int x = 0; x < 4; x++) {
                    for (int y = 0; y < 4; y++) {
                        if (x == i || y == j) {
                            continue;
                        }
                        else {
                            small[x > i ? x - 1 : x][y > j ? y - 1 : y] = BSplineNumber[x][y];
                        }
                    }
                }
                GLfloat addNum = small[0][0] * small[1][1] * small[2][2] + small[0][1] * small[1][2] * small[2][0] +
                    small[0][2] * small[1][0] * small[2][1];
                GLfloat jianNum = small[0][0] * small[1][2] * small[2][1] + small[0][1] * small[1][0] * small[2][2] + small[0][2] * small[1][1] * small[2][0];
                GLfloat help = (addNum - jianNum);
                A_[j][i] = pow(-1, j + i)*help;
            }
        }
    
    最后通过矩阵相乘得到最后的点矩阵,给出矩阵相乘代码
    GLfloat** Curve::mxn(GLfloat ** m1, GLfloat ** m2, int m, int n, int p)//矩阵相乘
    {
        GLfloat **result = new GLfloat*[m];
        for (int i = 0; i < m; i++) {
            result[i] = new GLfloat[p];
        }
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < p; j++) {
                result[i][j] = 0;
                for (int z = 0; z < n; z++) {
                    result[i][j] += m1[i][z] * m2[z][j];
                }
            }
        }
        return result;
    }
    

    最后实现写入文件就完成了第三部分任务
    写入文件就是根据老师的文件规格写入对应的控制点的数据就行。
    这里给出Bezier转BSpline的写入

    这里的caluatef返回对应的BSpline的四个控制点
    void BezierCurve::OutputBSpline(FILE * file)
    {
    
        fprintf(file, "%s %s ", "bspline", "num_vertices");
        fprintf(file, "%d ", 4);
        Point3D* pt=caluate(&Point[0]);
        for (int i = 0; i < 4; i++) {
            fprintf(file, "%f %f %f ", (float)pt[i].x, (float)pt[i].y, (float)pt[i].z);
        }
    }
    

    效果图

    Bezier转BSpline BSpline转Bezier

    4. 实现多点曲线绘制

    由于在之前实现了对多点的兼容,所以就不副贴代码,对于之前的Paint函数的相关部分做一展示

    for (int n = 0; n < (num-1)/3; n++) {
            for (int four = 0; four < 4; four++) {
                myVec[four].Set( Point[n * 3 + four].x, Point[n * 3 + four].y, Point[n * 3 + four].z);
            }
    

    Bezier是1234、4567、789 10 进行控制点复制,所以初始点坐标n = (num-1)/3
    每次根据这个控制点获取四个控制点,然后与初始只有四个点的绘制过程相同,最后绘制出对应的多段曲线

    BSpline对应的控制点公式为n = num - 3;

    效果图

    image.png
    image.png
    image.png

    5. 实现控制点编辑功能

    实现思路:通过点击选定对应控制点,利用向量存储控制点
    添加点:在选定线上与点击位置求垂足,确定垂足坐标,插入到向量中形成这条线的两点中间,然后调用重绘函数,重新绘制
    void BSplineCurve::addControlPoint(int pt, float x, float y)
    {
        Point3D newPoint(x, y, 0);
        Point.insert(Point.begin() + pt, newPoint);
        num++;
    }
    
    删除点:删除对应选重点即在控制点向量中删除对应点坐标,也就是调用erase删除指定下标的向量。
    void Curve::deleteControlPoint(int selectedPoint)
    {
        if (num > 4) {
            Point.erase(Point.begin() + selectedPoint);
            num--;
        }
    }
    
    移动点:鼠标点击选定,记录坐标覆盖对应控制点坐标位置,并在拖动中保持重绘
    void Curve::moveControlPoint(int selectedPoint, float x, float y)
    {
        Point[selectedPoint].x = x;
        Point[selectedPoint].y = y;
    }
    

    效果图(由于Bezier不可添加控制点,这里采用BSpline演示)

    移动点
    添加点

    6.实现旋转曲线的绘制

    实现思路:在SurfaceOfRevolution中实现绘制函数,在Paint函数中调用对应的Curve子类绘制函数,同时实现各种包括添加点,移动点,删除点的函数,仅仅通过初始化的时候传进来的Curve指针就可以调用Curve对应的函数,SurfaceOfRevolution仅仅只是调用一下。
    关键:实现OutputTriangles,初始化TriangleNet,第一个参数是所画的笔数,第二个参数是画几笔,框架会自动连接好每一笔形成一个几何体
    利用Bezier或者BSpline对应的计算曲线函数求出当前曲线在空间中的对应坐标,然后通过指定的描述曲面个数来确定每次绕y轴旋转的角度,最后将每次求出来的点的坐标绕轴旋转对应次数,并且使用SetVertex存入即可
    TriangleMesh * BezierPatch::OutputTriangles(ArgParser * args)
    {
        //获取点的个数与分割个数
        int patch_tessellation = args->patch_tessellation;
        int num_vertices = 16;
        //获取面的分割个数
        int _v_tess = patch_tessellation;
        Vec3f** myVec3f = new Vec3f*[patch_tessellation];
        for (int i = 0; i < patch_tessellation; i++) {
            myVec3f[i] = new Vec3f[patch_tessellation];
        }
        Vec3f *helpVec = new Vec3f[4 * patch_tessellation];
        TriangleNet *myMesh = new TriangleNet(patch_tessellation, _v_tess);
        GLint *c;
        Point3D* Pt = new  Point3D;
        GLint ControlN = 4;
        c = new GLint[ControlN];
        GetCnk(ControlN - 1, c);
        Point3D* ControlP = new Point3D[4];
        for (int i = 0; i < 4; i++) {
            for (int n = 0; n < 4; n++) {
                ControlP[n].x = Point[i*4 + n].x;
                ControlP[n].y = Point[i*4 + n].y;
                ControlP[n].z = Point[i*4 + n].z;
            }
            for (int j = 0; j < patch_tessellation; j++) {
                GetPointPr(c, (GLfloat)j / (GLfloat)patch_tessellation, Pt, 4, ControlP);
                helpVec[i*patch_tessellation+j].Set(Pt->x, Pt->y, Pt->z);
            }
        }
        for (int i = 0; i < patch_tessellation; i++) {
            
            for (int n = 0; n < 4; n++) {
                helpVec[i + patch_tessellation * n].Get(ControlP[n].x, ControlP[n].y, ControlP[n].z);
            }
            for (int j = 0; j < patch_tessellation; j++) {
                GetPointPr(c, (GLfloat)j / (GLfloat)patch_tessellation, Pt, 4, ControlP);
                myVec3f[i][j].Set(Pt->x, Pt->y, Pt->z);
            }
        }
        for (int i = 0; i < patch_tessellation*patch_tessellation; i++) {
            myMesh->SetVertex(i / patch_tessellation, i%patch_tessellation, myVec3f[i / patch_tessellation][i%patch_tessellation]);
        }
        for (int i = 0; i < patch_tessellation; i++) {
            delete[] myVec3f[i];
        }
        delete[] myVec3f;
        return myMesh;
    }
    

    效果图

    image.png image.png

    7. 实现自定义绘制

    实现思路:由于在surfaceOfRevolution中实现了对控制点的各种点击事件,所以就可以对传进来的曲线进行自定义的移动,然后保存到obj文件中,最后可以绘制出自定义的图形
    由于各种点击触发的响应事件已经在Curve中实现过了,所以SurfaceOfRevolution中仅仅只需要调用一下就可以了
    void SurfaceOfRevolution::OutputBezier(FILE * file)
    {
        myCurve->OutputBezier(file);
    }
    
    void SurfaceOfRevolution::OutputBSpline(FILE * file)
    {
        myCurve->OutputBSpline(file);
    }
    
    void SurfaceOfRevolution::moveControlPoint(int selectedPoint, float x, float y)
    {
        myCurve->moveControlPoint(selectedPoint, x, y);
    }
    
    void SurfaceOfRevolution::addControlPoint(int pt, float x, float y)
    {
        myCurve->addControlPoint(pt, x, y);
    }
    
    void SurfaceOfRevolution::deleteControlPoint(int selectedPoint)
    {
        myCurve->deleteControlPoint(selectedPoint);
    }
    
    void SurfaceOfRevolution::Paint(ArgParser * args)
    {
        myCurve->Paint(args);
    }
    
    int SurfaceOfRevolution::getNumVertices()
    {
        return myCurve->getNumVertices();
    }
    
    Vec3f SurfaceOfRevolution::getVertex(int i)
    {
        return myCurve->getVertex(i);
    }
    
    TriangleMesh * SurfaceOfRevolution::OutputTriangles(ArgParser * args)
    {
        TriangleMesh *myTriangleMesh;
        myTriangleMesh=myCurve->OutputTriangles(args);
        return myTriangleMesh;
    }
    

    效果图

    罐子
    罐子

    8. 实现4乘4格网绘制曲面

    实现思路:
    1. 声明16个大小的控制点数组,装入控制点
    2. 读入要求绘制的曲线层数,初始化对应的TriangleNet,第一个参数为一条曲线所需要的点的个数,第二个参数为曲线的条数
    3. 利用Bezier曲线公式求出对应点的坐标
    4. 通过TriangleNet的SetVertex函数将求出点的坐标保存
    5. 返回TriangleNet
    具体实现函数
    //获取点的个数与分割个数
        int patch_tessellation = args->patch_tessellation;
        int num_vertices = 16;
        //获取面的分割个数
        int _v_tess = patch_tessellation;
        Vec3f** myVec3f = new Vec3f*[patch_tessellation];
        for (int i = 0; i < patch_tessellation; i++) {
            myVec3f[i] = new Vec3f[patch_tessellation];
        }
        Vec3f *helpVec = new Vec3f[4 * patch_tessellation];
        TriangleNet *myMesh = new TriangleNet(patch_tessellation, _v_tess);
        GLint *c;
        Point3D* Pt = new  Point3D;
        GLint ControlN = 4;
        c = new GLint[ControlN];
        GetCnk(ControlN - 1, c);
        Point3D* ControlP = new Point3D[4];
        for (int i = 0; i < 4; i++) {
            for (int n = 0; n < 4; n++) {
                ControlP[n].x = Point[i*4 + n].x;
                ControlP[n].y = Point[i*4 + n].y;
                ControlP[n].z = Point[i*4 + n].z;
            }
            for (int j = 0; j < patch_tessellation; j++) {
                GetPointPr(c, (GLfloat)j / (GLfloat)patch_tessellation, Pt, 4, ControlP);
                helpVec[i*patch_tessellation+j].Set(Pt->x, Pt->y, Pt->z);
            }
        }
        for (int i = 0; i < patch_tessellation; i++) {
            
            for (int n = 0; n < 4; n++) {
                helpVec[i + patch_tessellation * n].Get(ControlP[n].x, ControlP[n].y, ControlP[n].z);
            }
            for (int j = 0; j < patch_tessellation; j++) {
                GetPointPr(c, (GLfloat)j / (GLfloat)patch_tessellation, Pt, 4, ControlP);
                myVec3f[i][j].Set(Pt->x, Pt->y, Pt->z);
            }
        }
        for (int i = 0; i < patch_tessellation*patch_tessellation; i++) {
            myMesh->SetVertex(i / patch_tessellation, i%patch_tessellation, myVec3f[i / patch_tessellation][i%patch_tessellation]);
        }
        for (int i = 0; i < patch_tessellation; i++) {
            delete[] myVec3f[i];
        }
        delete[] myVec3f;
        return myMesh;
    

    效果图

    image.png image.png

    9. 绘制茶壶

    茶壶函数已经写好,调用一下绘制保存就行了


    image.png
    image.png

    至此,完成作业要求。

    相关文章

      网友评论

          本文标题:作业一Readme

          本文链接:https://www.haomeiwen.com/subject/ocmcoftx.html