跳转至

Assignment 3

文本统计:约 1010 个字 • 140 行代码

本次作业使用到了 OpenGL 的相关内容,做之前需要进行相关的环境配置

OpenGL 相关函数的解析

glPushMatrixglPopMatrix

Use the glPushMatrix() and glPopMatrix() commands to save the current transformation to the stack or restore a transformation matrix from the top of the stack.

要理解这两个函数,首先要理解 OpenGL 的建模方式:OpenGL物体建模实际上是分两步走的。第一步,在世界坐标系的原点位置绘制出该物体;第二步,通过modelview变换矩阵对世界坐标系原点处的物体进行仿射变换,将该物体移动到世界坐标系的目标位置处。

下面三个函数用来平移旋转以及放缩一个物体,操作的数据可以是 double 或者 float,相应地使用对应的函数名。

//向X轴正方向平移x个单位,Y轴正方向平移y个单位,Z轴正方向平移z个单位
glTranslate{fd}(x, y, z);   

//做(0,0,0)到(x,y,z)的向量,用右手握住这条向量,大拇指指向向量的正方向,四指环绕的方向就是旋转的
//方向;函数功能:以点(0,0,0)到点(x,y,z)为轴,旋转angle角度
glRotate{fd}(angle, x, y, z)

//参数x,y,z分别为模型在x,y,z轴方向的缩放比
glScale{fd}(x, y, z);

glVertex 用来定义一个空间中的顶点,参数有多种

glVertex3f(float x, float y, float z);     // List of floats
glVertex3d(double x, double y, double z);  // List of doubles
glVertex3fv(float *arr);                   // Array of floats
glVertex3dv(double *arr);                  // Array of doubles

glMultMatrix{fd} can be used to multiply the current matrix by an arbitrary matrix. We've added a glGet() routine to the Matrix class to construct a matrix with the appropriate structure.

给栈顶乘上任意一个矩阵

GLfloat *glMatrix = matrix.glGet();
glMultMatrixf(glMatrix);
delete[] glMatrix;

如何画图形?使用一系列的 glVertex 被夹在 glBegin()glEnd() 中,然后会有一个参数传给 glBegin() 用来表示夹在中间的带点是怎么被使用的,比如说下面这段代码用来画一个三角形

glBegin(GL_TRIANGLES);
    glVertex3f(x0, y0, z0);
    glVertex3f(x1, y1, z1);
    glVertex3f(x2, y2, z2);
glEnd();

GL_POINTS: Each vertex will be drawn as a single pixel 画一个点

GL_TRIANGLES: Each set of three vertices will be drawn as a triangle 画三角形

GL_QUADS: Each set of four vertices will be drawn as a quadrilateral 画矩形

GL_LINES: Each pair of vertices will be drawn as a line segment 画线段

To change the normal vector, use glNormal3{fd}[v](). This routine can take a variety of different parameters, just like glVertex()

glNormal3f(float x, float y, float z);     // List of floats
glNormal3d(double x, double y, double z);  // List of doubles
glNormal3fv(float *arr);                   // Array of floats
glNormal3dv(double *arr);                  // Array of doubles

Note

Note that some statements, like glVertex() and glNormal() should always appear between glBegin() and glEnd() statements. Other commands, like glPushMatrix() or glTranslate(), should not appear between glBegin() and glEnd().

作业内容

回到本身的作业内容,这一次的内容与前一次作业相比增加了OpenGL的部分,Phong Material 类(用来实现 (Blinn-)Phong 模型)

其中 OpenGL需要完成的部分是

(1) 支持相机的三种操作,分别是平移旋转以及放缩

(2) 利用 OpenGL相关函数绘制相关物体,在每个 Object3d 的子类中增加函数 paint()

Phong Material 类中需要完成的就是增加一个函数 shade,用于得到相应的染色结果

相机操作

将相机拉远和拉近

void OrthographicCamera::dollyCamera(float dist)
{
  center += direction*dist;
}

平移相机

void OrthographicCamera::truckCamera(float dx, float dy)
{
  center += horizontal*dx + up*dy;
}

旋转相机,前面这一部分是为了防止旋转过度导致翻转

void OrthographicCamera::rotateCamera(float rx, float ry)
{
  float tiltAngle = acos(up.Dot3(direction));
  if (tiltAngle-ry > 3.13)
    ry = tiltAngle - 3.13;
  else if (tiltAngle-ry < 0.01)
    ry = tiltAngle - 0.01;

  Matrix rotMat = Matrix::MakeAxisRotation(up, rx);
  rotMat *= Matrix::MakeAxisRotation(horizontal, ry);

  rotMat.Transform(center);
  rotMat.TransformDirection(direction);
  rotMat.TransformDirection(up);
  rotMat.TransformDirection(horizontal);
}

paint() 函数

Group类中的 paint 函数就是一次调用各个 object 的 paint 函数

void Group::paint() {
    for (int i = 0; i < objectNum; i++) {
        objects[i]->paint();
    }
}

Plane 类中的 paint 函数,由于OpenGL中没有平面这一类,所以我们选择画一个巨大的长方形。先根据法向量得到一组单位正交基,然后得到长方形的四个顶点。

void Plane::paint() {
    material->glSetMaterial();
    Vec3f v(1,0,0);
    if (normal.y()==0 && normal.z()==0)
        v.Set(0,1,0);    
    Vec3f b1,b2;
    Vec3f::Cross3(b1,normal,v);
    Vec3f::Cross3(b2,normal,b1);
    b1.Normalize();
    b2.Normalize();
    Vec3f c1 = b1*100000;
    Vec3f c2 = b2*100000;
    Vec3f c3 = d*normal;

    Vec3f d1 = c1+c2+c3;
    Vec3f d2 = c1-c2+c3;
    Vec3f d3 = c3-c1-c2;
    Vec3f d4 = c2+c3-c1;

    glBegin(GL_QUADS);
        glNormal3f(normal.x(), normal.y(), normal.z());
        glVertex3f(d1.x(), d1.y(), d1.z());
        glVertex3f(d2.x(), d2.y(), d2.z());
        glVertex3f(d3.x(), d3.y(), d3.z());
        glVertex3f(d4.x(), d4.y(), d4.z());
    glEnd();
}

Triangle 类中的 paint 函数

void Triangle::paint()
{
    material->glSetMaterial();
    glBegin(GL_TRIANGLES);
    glNormal3f(normal.x(), normal.y(), normal.z());
    glVertex3f(v0.x(), v0.y(), v0.z());
    glVertex3f(v1.x(), v1.y(), v1.z());
    glVertex3f(v2.x(), v2.y(), v2.z());
    glEnd();
}

Sphere 类中的 paint 函数,本来 OpenGL 是有画球的函数的,但是在本次作业中采取的是网格化的球的画法,同时法线的设置分为按面设置以及按顶点设置的 -gouraud 方法。具体思路是画三角形来代替很多的

void Sphere::paint() {
   float deltaPhi = M_PI / phiSteps,
            deltaTheta = 2 * M_PI / thetaSteps;

    material->glSetMaterial();
    glBegin(GL_TRIANGLES);
    // phi from -90 to 90
    for (int iPhi = 1; iPhi <= phiSteps; iPhi++) {
        // theta from 0 to 360
        for (int iTheta = 1; iTheta <= thetaSteps; iTheta++) {
            float phi = -M_PI / 2 + iPhi * deltaPhi,
                    lastPhi = -M_PI / 2 + (iPhi - 1) * deltaPhi;
            float theta = iTheta * deltaTheta,
                    lastTheta = (iTheta - 1) * deltaTheta;

            // compute appropriate coordinates & normals
            Vec3f p1 = getPoint(lastPhi, lastTheta),
                    p2 = getPoint(lastPhi, theta),
                    p3 = getPoint(phi, lastTheta),
                    p4 = getPoint(phi, theta);
            Vec3f n1, n2, n3, n4;
            if (!gouraud) { // facet normal
                Vec3f::Cross3(n1, p2 - p1, p3 - p1);
                n1.Normalize();
                Vec3f::Cross3(n2, p4 - p2, p3 - p2);
                n2.Normalize();
            } else {
                n1 = p1 - center; n1.Normalize();
                n2 = p2 - center; n2.Normalize();
                n3 = p3 - center; n3.Normalize();
                n4 = p4 - center; n4.Normalize();
            }

            // send gl vertex commands
            if (!gouraud) {
                if ((p1 - p2).Length() > EPSILON) {
                    glNormal3f(n1.x(), n1.y(), n1.z());
                    glVertex3f(p1.x(), p1.y(), p1.z());
                    glVertex3f(p2.x(), p2.y(), p2.z());
                    glVertex3f(p3.x(), p3.y(), p3.z());
                }
                if ((p3 - p4).Length() > EPSILON) {
                    glNormal3f(n2.x(), n2.y(), n2.z());
                    glVertex3f(p2.x(), p2.y(), p2.z());
                    glVertex3f(p3.x(), p3.y(), p3.z());
                    glVertex3f(p4.x(), p4.y(), p4.z());
                }
            } else {
                if ((p1 - p2).Length() > EPSILON) {
                    glNormal3f(n1.x(), n1.y(), n1.z());
                    glVertex3f(p1.x(), p1.y(), p1.z());
                    glNormal3f(n2.x(), n2.y(), n2.z());
                    glVertex3f(p2.x(), p2.y(), p2.z());
                    glNormal3f(n3.x(), n3.y(), n3.z());
                    glVertex3f(p3.x(), p3.y(), p3.z());
                }
                if ((p3 - p4).Length() > EPSILON) {
                    glNormal3f(n2.x(), n2.y(), n2.z());
                    glVertex3f(p2.x(), p2.y(), p2.z());
                    glNormal3f(n3.x(), n3.y(), n3.z());
                    glVertex3f(p3.x(), p3.y(), p3.z());
                    glNormal3f(n4.x(), n4.y(), n4.z());
                    glVertex3f(p4.x(), p4.y(), p4.z());
                }
            }
        }
    }
    glEnd();
}

评论区

对你有帮助的话请给我个赞和 star => GitHub stars
欢迎跟我探讨!!!