[XJTUSE]计算机图形学实验

首先是Windows10 + Visual Studio 2019 搭建OpenGL环境可以查看如下链接:

萌新向!!!Windows10 + Visual Studio 2019 搭建OpenGL环境(图文教程) - 哔哩哔哩 (bilibili.com)

//使用PC端浏览器打开,Android端似乎被删除了

实验要求

实验一   2D图形变换

一、实验目的

1、了解和掌握2D图形变换-学会使用OPENGL平移、旋转和比例缩放函数。

2、掌握基本图形变换和复合图形变换实现的方法。  

二、实验内容与要求

     要求使用OpenGL几何变换函数改变当前2D图形:

    (1) 使用 glTranslatef()函数,实现2D图形平移,如图4-1所示。

 

图4-1:a)平移前

b)平移后

         (2) 使用 glRotatef()函数,实现2D图形绕平面固定点旋转,如图4-2所示。

图4-2:a)旋转前

b)旋转后

 

      (3) 使用 glScalef()函数,实现缩放2D图形绕固定点缩放,在前面程序基础上设计修改,如图4-3所示。

图4-3:a)缩放前

b)缩放后

 

        (4)修改代码,使得一面带杆小三角红旗沿着杆低不断旋转。

 

实验二 3D编程基础

一、实验目的

1、熟悉3D基本编程。

2、熟悉视图变换和投影变换。  

3、熟悉基本的3D图元的绘制。

二、实验内容与要求

  1、绘制给定半径、经线和纬线数目的圆球。

  2、设置光照和材质效果。

  3、可以考虑加入纹理和阴影。

 实验三 Bezier曲线曲面绘制 

一、实验目的

1、了解OpenGL绘制Bezier曲线的方法。

2、了解OpenGL绘制Bezier曲面的方法

二、实验内容与要求

  1、给定控制顶点,绘制出相应的曲线,通过改变控制顶点,观察曲线的变化。

  2、给定控制顶点,绘制出相应的曲面,通过改变控制顶点,观察曲面的变化。

实验四 纹理映射 

一、实验目的

1、掌握纹理映射。

二、实验内容与要求

绘制正方形,并贴上任意的纹理,通过设置,让正方形按照一定的速率自动旋转。

图3-1 实现效果图 

《计算机图形学》实验课程大作业

-地球仪的绘制

基本要求

  1. 绘制圆球,采用纹理映射的方式将给定的世界地图贴到圆球上(纹理贴图无缝),也可以使用其它世界地图;
  2. 需要具有简单的光照和材质效果;
  3. 双点触控缩放球体,拖动旋转球体;

附加要求

  1. 增加支架,生成真实的地球仪;
  2. 加入光照,阴影,增加逼真度。

 

实验一

1.1(1)glTranslatef(x, y, z)函数得使用

1.1.1核心代码

        glTranslatef(x, y, z) 沿着 X, Y 和 Z 轴移动。

        注意在glTranslatef(x, y, z)中,当你移动的时候,你并不是相对屏幕中心移动,而是相对与当前所在的屏幕位置。

        其作用就是将你绘点坐标的原点在当前原点的基础上平移一个(x,y,z)向量。

void myDisplay(void)
{
    glClearColor(1.0f,1.0f,0.0f,1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glTranslatef(0.00001, 0.0f, 0.0f );
    glColor3f(1.0f,0.0f,0.0f);
    glRectf(-0.3f, 0.2f, -0.1f, 0.4f);
    glFlush();
    glutSwapBuffers();
}

1.1.2完整代码

//使用 glTranslatef()函数,实现2D图形平移
#include <Windows.h>
#include <GL/glut.h>
#include <cmath>
#include <cstdio>
#include <cstdlib>

static int day = 200; // day的变化:从0到359
void myDisplay(void)
{
    glClearColor(1.0f,1.0f,0.0f,1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glTranslatef(0.00001, 0.0f, 0.0f );
    glColor3f(1.0f,0.0f,0.0f);
    glRectf(-0.3f, 0.2f, -0.1f, 0.4f);
    glFlush();
    glutSwapBuffers();
}

void myIdle(void)
{
    /* 新的函数,在空闲时调用,作用是把day加1并重新绘制,达到动画效果 */
    ++day;
    if (day >= 360)
        day = 0;
    myDisplay();
}

int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); // 修改了参数为GLUT_DOUBLE
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(400, 400);
    glutCreateWindow("使用 glTranslatef()函数,实现2D图形平移");   // 改了窗口标题
    glutDisplayFunc(&myDisplay);
    glutIdleFunc(&myIdle);               // 新加入了这句
    glutMainLoop();
    return 0;
}

1.1.3结果展示 

1.2(2)glrotatef()函数得使用

1.2.1核心代码 

        所有的opengl操作都以点(0,0,0)作为基点。

        glrotatef(旋转角度(0~360),x,y,z),z,y,z是旋转轴。由于旋转操作全都围绕点(0,0,0),所以如果你的物体并非在(0,0,0)点绘制,那么其旋转的时候就会有偏移。

        比如,我们假设有一个正方体,近角点的坐标是(0,0,0)远角点的坐标是(1,1,1)。为了使这个正方体绕其中心旋转,我们必须把它的中心点移动到点(0,0,0)

void myDisplay(void)
{
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glRotatef(0.005,0,0,-1);
    glColor3f(1.0f, 0.0f, 0.0f);
    glBegin(GL_POLYGON);
    glVertex2f(-0.4, -0.7);
    glVertex2f(-0.8, 0.0);
    glVertex2f(-0.4, 0.7);
    glVertex2f(0.4, 0.7);
    glVertex2f(0.8, 0.0);
    glVertex2f(0.4, -0.7);
    glEnd();
    glFlush();
    glutSwapBuffers();
}

1.2.2完整代码 

#include <Windows.h>
#include <GL/glut.h>
#include <cmath>
#include <cstdio>
#include <cstdlib>

static int day = 200; // day的变化:从0到359
void myDisplay(void)
{
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glRotatef(0.005,0,0,-1);
    glColor3f(1.0f, 0.0f, 0.0f);
    glBegin(GL_POLYGON);
    glVertex2f(-0.4, -0.7);
    glVertex2f(-0.8, 0.0);
    glVertex2f(-0.4, 0.7);
    glVertex2f(0.4, 0.7);
    glVertex2f(0.8, 0.0);
    glVertex2f(0.4, -0.7);
    glEnd();
    glFlush();
    glutSwapBuffers();
}

void myIdle(void)
{
    /* 新的函数,在空闲时调用,作用是把day加1并重新绘制,达到动画效果 */
    ++day;
    if (day >= 360)
        day = 0;
    myDisplay();
}

int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); // 修改了参数为GLUT_DOUBLE
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(400, 400);
    glutCreateWindow("使用 glRotatef()函数,实现2D图形绕平面固定点旋转");   // 改了窗口标题
    glutDisplayFunc(&myDisplay);
    glutIdleFunc(&myIdle);               // 新加入了这句
    glutMainLoop();
    return 0;
}

1.2.3结果展示

 

1.3(3)glScalef()函数的使用

1.3.1核心代码 

        glScalef是openGL中的模型缩放函数,就是把当前矩阵与一个表示延各个轴对物体进行拉伸、压缩、反射的矩阵相乘。这个物体的每个点的x,y,z坐标与对应的xyz参数相乘。

        例如:

        如glScalef(2.0f,3.0f,4.0f);

        将模型按x,y,z方向分别拉伸了2,3,4倍。

         参数也可取负数,也可以理解为先关于某轴翻转180°,再缩放;

         如glScalef(1.0f,1.0f,-1.0f);

        将模型关于z轴翻转了180°(即关于xy轴所在平面对称);

         如glScalef(1.0f,1.0f,-2.0f);将模型关于-z轴拉伸2倍。 

void myDisplay(void)
{
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glScalef(0.99999f,0.99999f,0.99999f);
    glColor3f(1.0f, 0.0f, 0.0f);
    glBegin(GL_POLYGON);
    glVertex2f(-0.4, -0.7);
    glVertex2f(-0.8, 0.0);
    glVertex2f(-0.4, 0.7);
    glVertex2f(0.4, 0.7);
    glVertex2f(0.8, 0.0);
    glVertex2f(0.4, -0.7);
    glEnd();
    glFlush();
    glutSwapBuffers();
}

1.3.2完整代码

#include <Windows.h>
#include <GL/glut.h>
#include <cmath>
#include <cstdio>
#include <cstdlib>

static int day = 200; // day的变化:从0到359
void myDisplay(void)
{
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glScalef(0.99999f,0.99999f,0.99999f);
    glColor3f(1.0f, 0.0f, 0.0f);
    glBegin(GL_POLYGON);
    glVertex2f(-0.4, -0.7);
    glVertex2f(-0.8, 0.0);
    glVertex2f(-0.4, 0.7);
    glVertex2f(0.4, 0.7);
    glVertex2f(0.8, 0.0);
    glVertex2f(0.4, -0.7);
    glEnd();
    glFlush();
    glutSwapBuffers();
}

void myIdle(void)
{
    /* 新的函数,在空闲时调用,作用是把day加1并重新绘制,达到动画效果 */
    ++day;
    if (day >= 360)
        day = 0;
    myDisplay();
}

int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); // 修改了参数为GLUT_DOUBLE
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(400, 400);
    glutCreateWindow("使用 glRotatef()函数,实现2D图形绕平面固定点旋转");   // 改了窗口标题
    glutDisplayFunc(&myDisplay);
    glutIdleFunc(&myIdle);               // 新加入了这句
    glutMainLoop();
    return 0;
}

 1.3.3结果展示

1.4(4)综合应用 

1.4.1核心代码

void myDisplay(void)
{
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glPointSize(3);//一个点占据三个像素
    glColor3f(0.0f, 0.0f, 0.0f);
    glBegin(GL_LINES);//开始画图
    glVertex2f(0.0f, height1 += -step);
    glVertex2f(0.0f, height2 += -step);
    glEnd();
    
    glTranslatef(0.0f, step, 0.0f);
    glRotatef(0.005, 0, -1, 0);
    glColor3f(1.0f, 0.0f, 0.0f);
    glBegin(GL_POLYGON);
    glVertex2f(-0.35f, 0.2f);//三角旗与矩形旗帜
    glVertex2f(-0.35f, 0.4f);
    glVertex2f(0.0f, 0.4f);
    glVertex2f(0.0f, 0.2f);
    glEnd();

    glFlush();
    glutSwapBuffers();
}

1.4.2完整代码

#include <Windows.h>
#include <GL/glut.h>
#include <cmath>
#include <cstdio>
#include <cstdlib>

static int day = 0; // day的变化:从0到359
static GLfloat height1 = 0.4f;
static GLfloat height2 = -0.4f;
static GLfloat step = -0.00001f;
void myDisplay(void)
{
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glPointSize(3);//一个点占据三个像素
    glColor3f(0.0f, 0.0f, 0.0f);
    glBegin(GL_LINES);//开始画图
    glVertex2f(0.0f, height1 += -step);
    glVertex2f(0.0f, height2 += -step);
    glEnd();
    
    glTranslatef(0.0f, step, 0.0f);
    glRotatef(0.005, 0, -1, 0);
    glColor3f(1.0f, 0.0f, 0.0f);
    glBegin(GL_POLYGON);
    glVertex2f(-0.35f, 0.2f);//三角旗与矩形旗帜
    glVertex2f(-0.35f, 0.4f);
    glVertex2f(0.0f, 0.4f);
    glVertex2f(0.0f, 0.2f);
    glEnd();

    glFlush();
    glutSwapBuffers();
}

void myIdle(void)
{
    /* 新的函数,在空闲时调用,作用是把day加1并重新绘制,达到动画效果 */
    ++day;
    if (day >= 60000)
        day = 0,step=-step;
    myDisplay();
}

int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); // 修改了参数为GLUT_DOUBLE
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(400, 400);
    glutCreateWindow("带杆小三角红旗沿着杆低不断旋转");   // 改了窗口标题
    glutDisplayFunc(&myDisplay);
    glutIdleFunc(&myIdle);               // 新加入了这句
    glutMainLoop();
    return 0;
}

1.4.3结果展示

实验二

2.1核心代码

更新小球尺寸的函数

static void resize(int width, int height)
{
    const float ar = (float)width / (float)height;

    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(-ar, ar, -1.0, 1.0, 2.0, 100.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

绘画小球的函数

static void display(void)
{
    const double t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
    const double a = t * 90.0;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3d(1, 0, 0);

    glPushMatrix();
    glTranslated(-2.4, 1.2, -6);
    glRotated(60, 1, 0, 0);
    glRotated(a, 0, 0, 1);
    glutSolidSphere(radius, slices, stacks);
    glPopMatrix();

    glutSwapBuffers();
}

按键控制小球经纬数的函数

static void key(unsigned char key, int x, int y)
{
    switch (key)
    {
    case 27:
    case 'q':
        exit(0);
        break;

    case '+':
        slices++;
        stacks++;
        break;

    case '-':
        if (slices > 3 && stacks > 3)
        {
            slices--;
            stacks--;
        }
        break;
    }

    glutPostRedisplay();
}

 重绘画小球的函数

static void idle(void)
{
    glutPostRedisplay();
}

 光照与材质

const GLfloat light_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
const GLfloat light_diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat light_position[] = { 2.0f, 5.0f, 5.0f, 0.0f };

const GLfloat mat_ambient[] = { 0.7f, 0.7f, 0.7f, 1.0f };
const GLfloat mat_diffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f };
const GLfloat mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat high_shininess[] = { 100.0f };

2.2完整代码

//1、绘制给定半径、经线和纬线数目的圆球。
//2、设置光照和材质效果。
#include<Windows.h>
#include <GL/glut.h>
#include <stdlib.h>

static int slices = 16;
static int stacks = 16;
static int radius = 1;

static void resize(int width, int height)
{
    const float ar = (float)width / (float)height;

    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(-ar, ar, -1.0, 1.0, 2.0, 100.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

static void display(void)
{
    const double t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
    const double a = t * 90.0;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3d(1, 0, 0);

    glPushMatrix();
    glTranslated(-2.4, 1.2, -6);
    glRotated(60, 1, 0, 0);
    glRotated(a, 0, 0, 1);
    glutSolidSphere(radius, slices, stacks);
    glPopMatrix();

    glutSwapBuffers();
}


static void key(unsigned char key, int x, int y)
{
    switch (key)
    {
    case 27:
    case 'q':
        exit(0);
        break;

    case '+':
        slices++;
        stacks++;
        break;

    case '-':
        if (slices > 3 && stacks > 3)
        {
            slices--;
            stacks--;
        }
        break;
    }

    glutPostRedisplay();
}

static void idle(void)
{
    glutPostRedisplay();
}

const GLfloat light_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
const GLfloat light_diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat light_position[] = { 2.0f, 5.0f, 5.0f, 0.0f };

const GLfloat mat_ambient[] = { 0.7f, 0.7f, 0.7f, 1.0f };
const GLfloat mat_diffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f };
const GLfloat mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat high_shininess[] = { 100.0f };



int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    glutInitWindowSize(640, 480);
    glutInitWindowPosition(10, 10);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);

    glutCreateWindow("3D编程基础");

    glutReshapeFunc(resize);
    glutDisplayFunc(display);
    glutKeyboardFunc(key);
    glutIdleFunc(idle);

    glClearColor(1, 1, 1, 1);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    glEnable(GL_LIGHT0);
    glEnable(GL_NORMALIZE);
    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_LIGHTING);

    glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);

    glutMainLoop();

    return EXIT_SUCCESS;
}

2.3结果展示 

实验三 

3.1Bezier曲线的绘制

3.1.1核心代码

点类以及初始化点的位置

//这是一个点的类,用于存储其中点的坐标
class Point
{
public:
    int x, y;
    void setxy(int _x, int _y) {
        x = _x;
        y = _y;
    }
};

//点的数量
static int POINTSNUM = -1;

//用于存储点的集合,因为绘制的都是4个点的贝塞尔曲线,所以数组大小为4
static Point points[4] = { {40,38},{190,170}, {490,289},{560,420} };

绘画函数

//display函数
void display()
{
    glClear(GL_COLOR_BUFFER_BIT);
    glFlush();
}

 绘画点、直线和曲线的函数

//绘制点
void setPoint(Point p) {
    glBegin(GL_POINTS);
    glVertex2f(p.x, p.y);
    glEnd();
    glFlush();
}

// 绘制直线
void setline(Point p1, Point p2) {
    glBegin(GL_LINES);
    glVertex2f(p1.x, p1.y);
    glVertex2f(p2.x, p2.y);
    glEnd();
    glFlush();
}

// 绘制贝塞尔曲线
Point setBezier(Point p1, Point p2, Point p3, Point p4, double t) {
    Point p;
    double a1 = pow((1 - t), 3);
    double a2 = pow((1 - t), 2) * 3 * t;
    double a3 = 3 * t * t * (1 - t);
    double a4 = t * t * t;
    p.x = a1 * p1.x + a2 * p2.x + a3 * p3.x + a4 * p4.x;
    p.y = a1 * p1.y + a2 * p2.y + a3 * p3.y + a4 * p4.y;
    return p;
}

鼠标点击事件处理

// 鼠标事件
void mymouseFunction(int button, int state, int x, int y) {
    if (state == GLUT_DOWN) // 如果鼠标按下,不区分左右键的
    {
        if (POINTSNUM == -1) {
            glColor3f(0.0, 0.0, 1.0); // 设定贝塞尔曲线的颜色

            Point p_current = points[0]; //设为起点
            for (double t = 0.0; t <= 1.0; t += 0.05)
            {
                Point P = setBezier(points[0], points[1], points[2], points[3], t);
                setline(p_current, P);
                p_current = P;
            }

            POINTSNUM = -1;
        }
        else {
            points[POINTSNUM].setxy(x, 480 - y); // 这里求鼠标点的坐标的时候
            // 设置点的颜色,绘制点
            glColor3f(1.0, 0.0, 0.0);
            setPoint(points[POINTSNUM]);
            // 设置线的颜色,绘制线
            glColor3f(1.0, 0.0, 0.0);
            if (POINTSNUM > 0) setline(points[POINTSNUM - 1], points[POINTSNUM]);

            //如果达到了4个绘制贝塞尔曲线,并在之后给计数器清零
            if (POINTSNUM >=0 && POINTSNUM<=3) {
                //绘制贝塞尔曲线  
                glColor3f(0.0, 0.0, 1.0); // 设定贝塞尔曲线的颜色

                Point p_current = points[0]; //设为起点
                for (double t = 0.0; t <= 1.0; t += 0.05)
                {
                    Point P = setBezier(points[0], points[1], points[2], points[3], t);
                    setline(p_current, P);
                    p_current = P;
                }

                POINTSNUM = -1;
            }
            else {
                POINTSNUM = -1;
            }
        }
    }
}

键盘点击事件处理

static void key(unsigned char key, int x, int y)
{
    switch (key)
    {
    case '0':
        POINTSNUM = 0;
        break;
    case '1':
        POINTSNUM = 1;
        break;
    case '2':
        POINTSNUM = 2;
        break;
    case '3':
        POINTSNUM = 3;
        break;
    }

    glutPostRedisplay();
}

 3.1.2完整代码

//贝塞尔曲线绘制
#include <Windows.h>
#include <GL/glut.h>
#include <cmath>
#include <cstdio>
#include <cstdlib>

//这是一个点的类,用于存储其中点的坐标
class Point
{
public:
    int x, y;
    void setxy(int _x, int _y) {
        x = _x;
        y = _y;
    }
};

//点的数量
static int POINTSNUM = -1;

//用于存储点的集合,因为绘制的都是4个点的贝塞尔曲线,所以数组大小为4
static Point points[4] = { {40,38},{190,170}, {490,289},{560,420} };

//初始化函数
void init(void)
{
    glClearColor(1.0, 1.0, 1.0, 0); //设定背景为黑色
    glColor3f(0.0, 0.0, 0.0); //绘图颜色为白色
    glPointSize(2.0); //设定点的大小为2*2像素的
    glMatrixMode(GL_PROJECTION); // 设定合适的矩阵
    glLoadIdentity(); // 是一个无参的无值函数,其功能是用一个4×4的单位矩阵来替换当前矩阵,实际上就是对当前矩阵进行初始化。也就是说,无论以前进行了多少次矩阵变换,在该命令执行后,当前矩阵均恢复成一个单位矩阵,即相当于没有进行任何矩阵变换状态
    gluOrtho2D(0.0, 600.0, 0.0, 480.0); //平行投影,四个参数分别是x,y范围
}

//绘制点
void setPoint(Point p) {
    glBegin(GL_POINTS);
    glVertex2f(p.x, p.y);
    glEnd();
    glFlush();
}

// 绘制直线
void setline(Point p1, Point p2) {
    glBegin(GL_LINES);
    glVertex2f(p1.x, p1.y);
    glVertex2f(p2.x, p2.y);
    glEnd();
    glFlush();
}

// 绘制贝塞尔曲线
Point setBezier(Point p1, Point p2, Point p3, Point p4, double t) {
    Point p;
    double a1 = pow((1 - t), 3);
    double a2 = pow((1 - t), 2) * 3 * t;
    double a3 = 3 * t * t * (1 - t);
    double a4 = t * t * t;
    p.x = a1 * p1.x + a2 * p2.x + a3 * p3.x + a4 * p4.x;
    p.y = a1 * p1.y + a2 * p2.y + a3 * p3.y + a4 * p4.y;
    return p;
}


//display函数
void display()
{
    glClear(GL_COLOR_BUFFER_BIT);
    glFlush();
}



// 鼠标事件
void mymouseFunction(int button, int state, int x, int y) {
    if (state == GLUT_DOWN) // 如果鼠标按下,不区分左右键的
    {
        if (POINTSNUM == -1) {
            glColor3f(0.0, 0.0, 1.0); // 设定贝塞尔曲线的颜色

            Point p_current = points[0]; //设为起点
            for (double t = 0.0; t <= 1.0; t += 0.05)
            {
                Point P = setBezier(points[0], points[1], points[2], points[3], t);
                setline(p_current, P);
                p_current = P;
            }

            POINTSNUM = -1;
        }
        else {
            points[POINTSNUM].setxy(x, 480 - y); // 这里求鼠标点的坐标的时候
            // 设置点的颜色,绘制点
            glColor3f(1.0, 0.0, 0.0);
            setPoint(points[POINTSNUM]);
            // 设置线的颜色,绘制线
            glColor3f(1.0, 0.0, 0.0);
            if (POINTSNUM > 0) setline(points[POINTSNUM - 1], points[POINTSNUM]);

            //如果达到了4个绘制贝塞尔曲线,并在之后给计数器清零
            if (POINTSNUM >=0 && POINTSNUM<=3) {
                //绘制贝塞尔曲线  
                glColor3f(0.0, 0.0, 1.0); // 设定贝塞尔曲线的颜色

                Point p_current = points[0]; //设为起点
                for (double t = 0.0; t <= 1.0; t += 0.05)
                {
                    Point P = setBezier(points[0], points[1], points[2], points[3], t);
                    setline(p_current, P);
                    p_current = P;
                }

                POINTSNUM = -1;
            }
            else {
                POINTSNUM = -1;
            }
        }
    }
}

static void key(unsigned char key, int x, int y)
{
    switch (key)
    {
    case '0':
        POINTSNUM = 0;
        break;
    case '1':
        POINTSNUM = 1;
        break;
    case '2':
        POINTSNUM = 2;
        break;
    case '3':
        POINTSNUM = 3;
        break;
    }

    glutPostRedisplay();
}

int main(int argc, char* argv[])
{
    glutInit(&argc, argv); //固定格式
    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);  //缓存模式
    glutInitWindowSize(600, 480);  //显示框的大小
    glutInitWindowPosition(100, 100); //确定显示框左上角的位置
    glutCreateWindow("Bezier曲线绘制");
    init(); // 初始化
    glutMouseFunc(mymouseFunction); // 添加鼠标事件
    glutKeyboardFunc(key);
    glutDisplayFunc(display); // 执行显示  
    glutMainLoop(); //进人GLUT事件处理循环
    return 0;
}

3.1.3结果展示 

3.2Bezier曲面的绘制 

3.2.1核心代码

初始控制顶点和初始化函数

static unsigned char ctrlpoint = 'a';
static unsigned char coordinate = 'x';
static GLfloat ctrlpoints[4][4][3] = {
{{ -3, -3, 4.0 },{ -1, -3, 4.0 },{ 1, -3, -2.0 },{ 3, -3, 4.0 }},
{{ -3, -1, 2.0 },{ -1, 3, 4.0 },{ 1, 1, 2},{3,-1,-2}},
{{ -3, 1, 4.0 },{ -1, 1, 2.0 },{ 1,1, 6},{3,-3,3} },
{{ -3,3, -4},{ -1,3,-4.0},{1,1,2},{3,3,-2}}
};

void init(void)
{
    glClearColor(1.0, 1.0, 1.0, 0.0);
    //下行的代码用控制点定义Bezier曲面函数
    glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &ctrlpoints[0][0][0]);
    glEnable(GL_MAP2_VERTEX_3); //激活该曲面函数
    glOrtho(-5.0, 5.0, -5.0, 5.0, -5.0, 5.0); //构造平行投影矩阵 设置正投影空间
}

绘制曲面的函数

void display(void)
{
    int i, j;
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3f(0.0, 0.0, 1.0);
    glPushMatrix();//矩阵入栈
    glRotatef(30.0, 1.0, 1.0, 1.0);//旋转一下便于查看
    for (j = 0; j <= 20; j++)
    {
        glBegin(GL_LINE_STRIP);
        for (i = 0; i <= 20; i++)
            glEvalCoord2f((GLfloat)i / 20.0, (GLfloat)j / 20.0); //调用求值器
        glEnd();
        glBegin(GL_LINE_STRIP);
        for (i = 0; i <= 20; i++)
            glEvalCoord2f((GLfloat)j / 20.0, (GLfloat)i / 20.0); //调用求值器
        glEnd();
    }
    glPopMatrix();//矩阵出栈
    glFlush();
    glutSwapBuffers();
}

键盘操作控制顶点的坐标改变 

static void key(unsigned char key, int x, int y)
{
    if (key>='a'&&key<='p')
    {
        ctrlpoint = key;
        printf("当前可改变的是%c点的%c坐标\n请输入a-p以确定要改变点\n输入x,y,z以确定要改变那个坐标\n请输入+或-以增加或减少坐标的值\n\n", ctrlpoint, coordinate);
    }
    else if (key>='x'&&key<='z')
    {
        coordinate = key;
        printf("当前可改变的是%c点的%c坐标\n请输入a-p以确定要改变点\n输入x,y,z以确定要改变那个坐标\n请输入+或-以增加或减少坐标的值\n\n", ctrlpoint, coordinate);
    }
    else if (key=='-'||key=='+') {
        int pointNum = ctrlpoint - 'a';
        int cooNum = coordinate - 'x';
        float add = (key == '-') ? (-0.5) : (0.5);
        if (ctrlpoints[pointNum / 4][pointNum % 4][cooNum] + add > 5.0 || ctrlpoints[pointNum / 4][pointNum % 4][cooNum] + add < -5.0) {
            printf("越界\n\n");
        }
        else{
            ctrlpoints[pointNum / 4][pointNum % 4][cooNum] += add;
            printf("当前可改变的是%c点的%c坐标\n请输入a-p以确定要改变点\n输入x,y,z以确定要改变那个坐标\n请输入+或-以增加或减少坐标的值\n\n", ctrlpoint, coordinate);
            glClearColor(1.0, 1.0, 1.0, 0.0);
            //下行的代码用控制点定义Bezier曲面函数
            glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &ctrlpoints[0][0][0]);
            glEnable(GL_MAP2_VERTEX_3); //激活该曲面函数
            glutPostRedisplay();
        }
    }
    else { printf("错误输入\n\n"); }
}

3.2.2完整代码

//贝塞尔曲面绘制
#include <Windows.h>
#include <GL/glut.h>
#include <cmath>
#include <cstdio>
#include <cstdlib>

static unsigned char ctrlpoint = 'a';
static unsigned char coordinate = 'x';
static GLfloat ctrlpoints[4][4][3] = {
{{ -3, -3, 4.0 },{ -1, -3, 4.0 },{ 1, -3, -2.0 },{ 3, -3, 4.0 }},
{{ -3, -1, 2.0 },{ -1, 3, 4.0 },{ 1, 1, 2},{3,-1,-2}},
{{ -3, 1, 4.0 },{ -1, 1, 2.0 },{ 1,1, 6},{3,-3,3} },
{{ -3,3, -4},{ -1,3,-4.0},{1,1,2},{3,3,-2}}
};

void init(void)
{
    glClearColor(1.0, 1.0, 1.0, 0.0);
    //下行的代码用控制点定义Bezier曲面函数
    glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &ctrlpoints[0][0][0]);
    glEnable(GL_MAP2_VERTEX_3); //激活该曲面函数
    glOrtho(-5.0, 5.0, -5.0, 5.0, -5.0, 5.0); //构造平行投影矩阵 设置正投影空间
}

void display(void)
{
    int i, j;
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3f(0.0, 0.0, 1.0);
    glPushMatrix();//矩阵入栈
    glRotatef(30.0, 1.0, 1.0, 1.0);//旋转一下便于查看
    for (j = 0; j <= 20; j++)
    {
        glBegin(GL_LINE_STRIP);
        for (i = 0; i <= 20; i++)
            glEvalCoord2f((GLfloat)i / 20.0, (GLfloat)j / 20.0); //调用求值器
        glEnd();
        glBegin(GL_LINE_STRIP);
        for (i = 0; i <= 20; i++)
            glEvalCoord2f((GLfloat)j / 20.0, (GLfloat)i / 20.0); //调用求值器
        glEnd();
    }
    glPopMatrix();//矩阵出栈
    glFlush();
    glutSwapBuffers();
}

static void key(unsigned char key, int x, int y)
{
    if (key>='a'&&key<='p')
    {
        ctrlpoint = key;
        printf("当前可改变的是%c点的%c坐标\n请输入a-p以确定要改变点\n输入x,y,z以确定要改变那个坐标\n请输入+或-以增加或减少坐标的值\n\n", ctrlpoint, coordinate);
    }
    else if (key>='x'&&key<='z')
    {
        coordinate = key;
        printf("当前可改变的是%c点的%c坐标\n请输入a-p以确定要改变点\n输入x,y,z以确定要改变那个坐标\n请输入+或-以增加或减少坐标的值\n\n", ctrlpoint, coordinate);
    }
    else if (key=='-'||key=='+') {
        int pointNum = ctrlpoint - 'a';
        int cooNum = coordinate - 'x';
        float add = (key == '-') ? (-0.5) : (0.5);
        if (ctrlpoints[pointNum / 4][pointNum % 4][cooNum] + add > 5.0 || ctrlpoints[pointNum / 4][pointNum % 4][cooNum] + add < -5.0) {
            printf("越界\n\n");
        }
        else{
            ctrlpoints[pointNum / 4][pointNum % 4][cooNum] += add;
            printf("当前可改变的是%c点的%c坐标\n请输入a-p以确定要改变点\n输入x,y,z以确定要改变那个坐标\n请输入+或-以增加或减少坐标的值\n\n", ctrlpoint, coordinate);
            glClearColor(1.0, 1.0, 1.0, 0.0);
            //下行的代码用控制点定义Bezier曲面函数
            glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &ctrlpoints[0][0][0]);
            glEnable(GL_MAP2_VERTEX_3); //激活该曲面函数
            glutPostRedisplay();
        }
    }
    else { printf("错误输入\n\n"); }
}

int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("贝塞尔曲面的绘制");
    init();
    glutKeyboardFunc(key);
    glutDisplayFunc(display);
    glutMainLoop();
    return 0;
}

3.2.3结果展示 

实验四 

4.1核心代码

正方体的初始化

以及绘制顺序

纹理的绘制顺序

//定义纹理对象编号
GLuint texWood;

#define BMP_Header_Length 54  //图像数据在内存块中的偏移量
int angle = 0;   //旋转角度

// 将立方体的八个顶点保存到一个数组里面
static const GLfloat vertex_list[][3] = {
    -0.5f, -0.5f, -0.5f,
     0.5f, -0.5f, -0.5f,
    -0.5f,  0.5f, -0.5f,
     0.5f,  0.5f, -0.5f,
    -0.5f, -0.5f,  0.5f,
     0.5f, -0.5f,  0.5f,
    -0.5f,  0.5f,  0.5f,
     0.5f,  0.5f,  0.5f,
};
//绘制次序
static const GLint index_list[][4] = {
    0, 2, 3, 1,
    0, 4, 6, 2,
    0, 1, 5, 4,
    4, 5, 7, 6,
    1, 3, 7, 5,
    2, 6, 7, 3,
};
//纹理绘制次序
static const GLfloat coord_list[][2] = {
    0,0,
    0,1,
    1,1,
    1,0,
};

函数power_of_two用于判断一个整数是不是2的整数次幂

// 函数power_of_two用于判断一个整数是不是2的整数次幂
int power_of_two(int n)
{
    if (n <= 0)
        return 0;
    return (n & (n - 1)) == 0;
}

纹理映射函数

/* 函数load_texture
* 读取一个BMP文件作为纹理
* 如果失败,返回0,如果成功,返回纹理编号
*/
GLuint load_texture(const char* file_name)
{
    GLint width, height, total_bytes;
    GLubyte* pixels = 0;
    GLuint last_texture_ID = 0, texture_ID = 0;

    // 打开文件,如果失败,返回
    FILE* pFile;
    int err = fopen_s(&pFile, file_name, "rb");
    if (err != 0) { return 0; }

    // 读取文件中图象的宽度和高度
    fseek(pFile, 0x0012, SEEK_SET);
    fread(&width, 4, 1, pFile);
    fread(&height, 4, 1, pFile);
    fseek(pFile, BMP_Header_Length, SEEK_SET);

    // 计算每行像素所占字节数,并根据此数据计算总像素字节数
    {
        GLint line_bytes = width * 3;
        while (line_bytes % 4 != 0)
            ++line_bytes;
        total_bytes = line_bytes * height;
    }

    // 根据总像素字节数分配内存
    pixels = (GLubyte*)malloc(total_bytes);
    if (pixels == 0)
    {
        fclose(pFile);
        return 0;
    }

    // 读取像素数据
    if (fread(pixels, total_bytes, 1, pFile) <= 0)
    {
        free(pixels);
        fclose(pFile);
        return 0;
    }

    // 对就旧版本的兼容,如果图象的宽度和高度不是的整数次方,则需要进行缩放
    // 若图像宽高超过了OpenGL规定的最大值,也缩放
    {
        GLint max;
        glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
        if (!power_of_two(width)
            || !power_of_two(height)
            || width > max
            || height > max)
        {
            const GLint new_width = 256;
            const GLint new_height = 256; // 规定缩放后新的大小为边长的正方形
            GLint new_line_bytes, new_total_bytes;
            GLubyte* new_pixels = 0;

            // 计算每行需要的字节数和总字节数
            new_line_bytes = new_width * 3;
            while (new_line_bytes % 4 != 0)
                ++new_line_bytes;
            new_total_bytes = new_line_bytes * new_height;

            // 分配内存
            new_pixels = (GLubyte*)malloc(new_total_bytes);
            if (new_pixels == 0)
            {
                free(pixels);
                fclose(pFile);
                return 0;
            }

            // 进行像素缩放
            gluScaleImage(GL_RGB,
                width, height, GL_UNSIGNED_BYTE, pixels,
                new_width, new_height, GL_UNSIGNED_BYTE, new_pixels);

            // 释放原来的像素数据,把pixels指向新的像素数据,并重新设置width和height
            free(pixels);
            pixels = new_pixels;
            width = new_width;
            height = new_height;
        }
    }

    // 分配一个新的纹理编号
    glGenTextures(1, &texture_ID);
    if (texture_ID == 0)
    {
        free(pixels);
        fclose(pFile);
        return 0;
    }

    // 绑定新的纹理,载入纹理并设置纹理参数
    // 在绑定前,先获得原来绑定的纹理编号,以便在最后进行恢复
    GLint lastTextureID = last_texture_ID;
    glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastTextureID);
    glBindTexture(GL_TEXTURE_2D, texture_ID);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);
    glBindTexture(GL_TEXTURE_2D, lastTextureID);  //恢复之前的纹理绑定
    free(pixels);
    return texture_ID;
}

纹理映射的文件

 

绘制函数

//绘画
void display(void)
{
    //设置视角
        glLoadIdentity(); //加载单位矩阵
    glRotatef(angle, 1.0f, 1.0f, 1.0f); //旋转
    // 清除屏幕
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 加载纹理
    glBindTexture(GL_TEXTURE_2D, texWood);
    glBegin(GL_QUADS);
    for (int i = 0; i < 6; ++i) {
        // 有六个面,循环六次
        for (int j = 0; j < 4; ++j) {
            // 每个面有四个顶点,循环四次
            //纹理
            glTexCoord2f(coord_list[j][0], coord_list[j][1]);
            //正方体
            glVertex3fv(vertex_list[index_list[i][j]]);
        }
    }

    glEnd();
    glutSwapBuffers();
}

 改变角度和重画

void OnTimer(int value)
{
    //改变角度
    angle += 1;
    angle %= 360;
    glutPostRedisplay();//重画
    glutTimerFunc(16, OnTimer, 1);
}

 4.2完整代码

 

#include <Windows.h>
#include <GL/glut.h>
#include <cmath>
#include <cstdio>
#include <cstdlib>

#define WindowWidth  400
#define WindowHeight 400
#define WindowTitle  "OpenGL纹理测试"


//定义纹理对象编号
GLuint texWood;

#define BMP_Header_Length 54  //图像数据在内存块中的偏移量
int angle = 0;   //旋转角度

// 将立方体的八个顶点保存到一个数组里面
static const GLfloat vertex_list[][3] = {
    -0.5f, -0.5f, -0.5f,
     0.5f, -0.5f, -0.5f,
    -0.5f,  0.5f, -0.5f,
     0.5f,  0.5f, -0.5f,
    -0.5f, -0.5f,  0.5f,
     0.5f, -0.5f,  0.5f,
    -0.5f,  0.5f,  0.5f,
     0.5f,  0.5f,  0.5f,
};
//绘制次序
static const GLint index_list[][4] = {
    0, 2, 3, 1,
    0, 4, 6, 2,
    0, 1, 5, 4,
    4, 5, 7, 6,
    1, 3, 7, 5,
    2, 6, 7, 3,
};
//纹理绘制次序
static const GLfloat coord_list[][2] = {
    0,0,
    0,1,
    1,1,
    1,0,
};
// 函数power_of_two用于判断一个整数是不是2的整数次幂
int power_of_two(int n)
{
    if (n <= 0)
        return 0;
    return (n & (n - 1)) == 0;
}

/* 函数load_texture
* 读取一个BMP文件作为纹理
* 如果失败,返回0,如果成功,返回纹理编号
*/
GLuint load_texture(const char* file_name)
{
    GLint width, height, total_bytes;
    GLubyte* pixels = 0;
    GLuint last_texture_ID = 0, texture_ID = 0;

    // 打开文件,如果失败,返回
    FILE* pFile;
    int err = fopen_s(&pFile, file_name, "rb");
    if (err != 0) { return 0; }

    // 读取文件中图象的宽度和高度
    fseek(pFile, 0x0012, SEEK_SET);
    fread(&width, 4, 1, pFile);
    fread(&height, 4, 1, pFile);
    fseek(pFile, BMP_Header_Length, SEEK_SET);

    // 计算每行像素所占字节数,并根据此数据计算总像素字节数
    {
        GLint line_bytes = width * 3;
        while (line_bytes % 4 != 0)
            ++line_bytes;
        total_bytes = line_bytes * height;
    }

    // 根据总像素字节数分配内存
    pixels = (GLubyte*)malloc(total_bytes);
    if (pixels == 0)
    {
        fclose(pFile);
        return 0;
    }

    // 读取像素数据
    if (fread(pixels, total_bytes, 1, pFile) <= 0)
    {
        free(pixels);
        fclose(pFile);
        return 0;
    }

    // 对就旧版本的兼容,如果图象的宽度和高度不是的整数次方,则需要进行缩放
    // 若图像宽高超过了OpenGL规定的最大值,也缩放
    {
        GLint max;
        glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
        if (!power_of_two(width)
            || !power_of_two(height)
            || width > max
            || height > max)
        {
            const GLint new_width = 256;
            const GLint new_height = 256; // 规定缩放后新的大小为边长的正方形
            GLint new_line_bytes, new_total_bytes;
            GLubyte* new_pixels = 0;

            // 计算每行需要的字节数和总字节数
            new_line_bytes = new_width * 3;
            while (new_line_bytes % 4 != 0)
                ++new_line_bytes;
            new_total_bytes = new_line_bytes * new_height;

            // 分配内存
            new_pixels = (GLubyte*)malloc(new_total_bytes);
            if (new_pixels == 0)
            {
                free(pixels);
                fclose(pFile);
                return 0;
            }

            // 进行像素缩放
            gluScaleImage(GL_RGB,
                width, height, GL_UNSIGNED_BYTE, pixels,
                new_width, new_height, GL_UNSIGNED_BYTE, new_pixels);

            // 释放原来的像素数据,把pixels指向新的像素数据,并重新设置width和height
            free(pixels);
            pixels = new_pixels;
            width = new_width;
            height = new_height;
        }
    }

    // 分配一个新的纹理编号
    glGenTextures(1, &texture_ID);
    if (texture_ID == 0)
    {
        free(pixels);
        fclose(pFile);
        return 0;
    }

    // 绑定新的纹理,载入纹理并设置纹理参数
    // 在绑定前,先获得原来绑定的纹理编号,以便在最后进行恢复
    GLint lastTextureID = last_texture_ID;
    glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastTextureID);
    glBindTexture(GL_TEXTURE_2D, texture_ID);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);
    glBindTexture(GL_TEXTURE_2D, lastTextureID);  //恢复之前的纹理绑定
    free(pixels);
    return texture_ID;
}
//绘画
void display(void)
{
    //设置视角
        glLoadIdentity(); //加载单位矩阵
    glRotatef(angle, 1.0f, 1.0f, 1.0f); //旋转
    // 清除屏幕
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 加载纹理
    glBindTexture(GL_TEXTURE_2D, texWood);
    glBegin(GL_QUADS);
    for (int i = 0; i < 6; ++i) {
        // 有六个面,循环六次
        for (int j = 0; j < 4; ++j) {
            // 每个面有四个顶点,循环四次
            //纹理
            glTexCoord2f(coord_list[j][0], coord_list[j][1]);
            //正方体
            glVertex3fv(vertex_list[index_list[i][j]]);
        }
    }

    glEnd();
    glutSwapBuffers();
}
void OnTimer(int value)
{
    //改变角度
    angle += 1;
    angle %= 360;
    glutPostRedisplay();//重画
    glutTimerFunc(16, OnTimer, 1);
}

int main(int argc, char* argv[])
{
    // GLUT初始化
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(WindowWidth, WindowHeight);
    glutCreateWindow(WindowTitle);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_TEXTURE_2D);    // 启用纹理
    texWood = load_texture("D:\\Visual Studio\\MyOpenGL\\wood.bmp");//加载纹理文件
    glutDisplayFunc(&display);//注册函数 
    glutTimerFunc(16, OnTimer, 1);//计时器定时运行 60FPS
    glutMainLoop(); //循环调用
    return 0;
}

4.3结果展示 

实验课大作业:地球仪的绘制 

5.1实验思路

Step1:在 main()函数里,进行初始化

Step2:然后绘制窗口

Step3:设置用户操作

Step4:进行运动效果函数的引用

Step5:结束时,删除建模

Additionally

Step6:在 void initLight(){}函数里设置光照

Step7:函数 void handleKeypress(unsigned char key, int x, int y){}是按键操作

Step8:辅助函数 int power_of_two(int n){}以及函数 GLuint load_texture(const char* file_name){}来映射纹理

Step9:函数 void initRendering(){}定义建模的材质

Step10:函数 void deinitRendering(){}来删除建模

Step11:函数 void handleResize(int w, int h){}用以调整窗口

Step12:绘制地球的函数 void drawEarth(){}

Step13:在其中调用函数 void drawScene() {}绘制场景

Step14:实现自转的函数 void update(int value){}

Step15:在处理鼠标点击时间中函数 void handleMotion(int x, int y){}用以处理鼠标点击或活动事件,函数 void handleMouse(int button, int state, int x, int y){}用以处理鼠标点击事件

5.2实验步骤

Step1:在 main()函数里,进行初始化

//初始化
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(800, 480);

Step2:然后绘制窗口

//创建窗口
glutCreateWindow("地球仪");
initRendering();

Step3:设置用户操作

//设置用户操作
texEarth = load_texture("D:\\Visual Studio\\MyOpenGL\\earth.bmp");
glutDisplayFunc(drawScene);
glutKeyboardFunc(handleKeypress);
glutReshapeFunc(handleResize);
glutMotionFunc(handleMotion);
glutMouseFunc(handleMouse);

Step4:进行运动效果函数的引用

//运动效果
glutTimerFunc(16, update, 0);
glutMainLoop();

Step5:结束时,删除建模

//退出时删除建模
deinitRendering();
return 0;

Additionally

Step6:在 void initLight(){}函数里设置光照

//定义光照
void initLight()
{
GLfloat ambientLight[] = { 0.2,0.2,0.2,1 };
GLfloat diffuseLight[] = { 0.8,.8,0.8,1 };
GLfloat specularLight[] = { 0.5,0.5,0.5,1 };
GLfloat posLight[] = { 400,240,1,1 };
GLfloat specref[] = { 1,1,1,1 };
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);
//glLightfv(GL_LIGHT0,GL_POSITION,posLight); //指定光源位置
glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 128);
}

Step7:函数 void handleKeypress(unsigned char key, int x, int y){}是按键操作

//按按键操作
void handleKeypress(unsigned char key, int x, int y) {
switch (key) {
case 27: //Escape key
exit(0);break;
//按上则放大
case 'w':
//xyz[0] += 0.1;xyz[1] += 0.1;xyz[2] += 0.1;
r+=10;
glutPostRedisplay();break;
//按下则缩小
case 's':
//xyz[0] -= 0.1;xyz[1] -= 0.1;xyz[2] -= 0.1;
r-=10;
glutPostRedisplay();break;
}
}

Step8:辅助函数 int power_of_two(int n){}以及函数 GLuint load_texture(const char* file_name){}来映射纹理

// 函数power_of_two用于判断一个整数是不是2的整数次幂
int power_of_two(int n)
{
    if (n <= 0)
        return 0;
    return (n & (n - 1)) == 0;
}

/* 函数load_texture
* 读取一个BMP文件作为纹理
* 如果失败,返回0,如果成功,返回纹理编号
*/
GLuint load_texture(const char* file_name)
{
    GLint width, height, total_bytes;
    GLubyte* pixels = 0;
    GLuint last_texture_ID = 0, texture_ID = 0;

    // 打开文件,如果失败,返回
    FILE* pFile;
    int err = fopen_s(&pFile, file_name, "rb");
    if (err != 0) { return 0; }

    // 读取文件中图象的宽度和高度
    fseek(pFile, 0x0012, SEEK_SET);
    fread(&width, 4, 1, pFile);
    fread(&height, 4, 1, pFile);
    fseek(pFile, BMP_Header_Length, SEEK_SET);

    // 计算每行像素所占字节数,并根据此数据计算总像素字节数
    {
        GLint line_bytes = width * 3;
        while (line_bytes % 4 != 0)
            ++line_bytes;
        total_bytes = line_bytes * height;
    }

    // 根据总像素字节数分配内存
    pixels = (GLubyte*)malloc(total_bytes);
    if (pixels == 0)
    {
        fclose(pFile);
        return 0;
    }

    // 读取像素数据
    if (fread(pixels, total_bytes, 1, pFile) <= 0)
    {
        free(pixels);
        fclose(pFile);
        return 0;
    }

    // 对就旧版本的兼容,如果图象的宽度和高度不是的整数次方,则需要进行缩放
    // 若图像宽高超过了OpenGL规定的最大值,也缩放
    {
        GLint max;
        glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
        if (!power_of_two(width)
            || !power_of_two(height)
            || width > max
            || height > max)
        {
            const GLint new_width = 256;
            const GLint new_height = 256; // 规定缩放后新的大小为边长的正方形
            GLint new_line_bytes, new_total_bytes;
            GLubyte* new_pixels = 0;

            // 计算每行需要的字节数和总字节数
            new_line_bytes = new_width * 3;
            while (new_line_bytes % 4 != 0)
                ++new_line_bytes;
            new_total_bytes = new_line_bytes * new_height;

            // 分配内存
            new_pixels = (GLubyte*)malloc(new_total_bytes);
            if (new_pixels == 0)
            {
                free(pixels);
                fclose(pFile);
                return 0;
            }

            // 进行像素缩放
            gluScaleImage(GL_RGB,
                width, height, GL_UNSIGNED_BYTE, pixels,
                new_width, new_height, GL_UNSIGNED_BYTE, new_pixels);

            // 释放原来的像素数据,把pixels指向新的像素数据,并重新设置width和height
            free(pixels);
            pixels = new_pixels;
            width = new_width;
            height = new_height;
        }
    }

    // 分配一个新的纹理编号
    glGenTextures(1, &texture_ID);
    if (texture_ID == 0)
    {
        free(pixels);
        fclose(pFile);
        return 0;
    }

    // 绑定新的纹理,载入纹理并设置纹理参数
    // 在绑定前,先获得原来绑定的纹理编号,以便在最后进行恢复
    GLint lastTextureID = last_texture_ID;
    glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastTextureID);
    glBindTexture(GL_TEXTURE_2D, texture_ID);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);
    glBindTexture(GL_TEXTURE_2D, lastTextureID);  //恢复之前的纹理绑定
    free(pixels);
    return texture_ID;
}

Step9:函数 void initRendering(){}定义建模的材质

//定义D建模光照材质
void initRendering() {
//创建二次曲面对象
_earth = gluNewQuadric();
//初始化光照
initLight();
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LINE_SMOOTH);
//glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL,GL_SEPARATE_SPECUL
AR_COLOR);//镜面亮点
}

Step10:函数 void deinitRendering(){}来删除建模

//删除建模
void deinitRendering()
{
gluDeleteQuadric(_earth);
}

Step11:函数 void handleResize(int w, int h){}用以调整窗口

//窗口调整调用
void handleResize(int w, int h) {
if (h == 0)
{
h = 1;
}
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, w, 0, h, -200, 200);
glMatrixMode(GL_MODELVIEW);
}

Step12:绘制地球的函数 void drawEarth(){}

//画地球
void drawEarth()
{
static bool loaded = false;
//纹理绑定到目标
glBindTexture(GL_TEXTURE_2D, texEarth);
if (!loaded)
{
//纹理坐标自动生成
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
//表面生成纹理坐标
gluQuadricDrawStyle(_earth, GL_FILL);
gluQuadricNormals(_earth, GLU_SMOOTH);
gluQuadricTexture(_earth, GL_TRUE);
}
//生成球体
glPushMatrix();
{
glEnable(GL_TEXTURE_2D);
glRotatef(-90, 1, 0, 0);
gluSphere(_earth, r, 100, 100);
glDisable(GL_TEXTURE_2D);
}
glPopMatrix();
}

Step13:在其中调用函数 void drawScene() {}绘制场景

//绘制D场景
void drawScene() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW); //切换到绘图透视图
glLoadIdentity(); //重置绘图透视图
glPushMatrix();
//实现缩放
glScalef(xyz[0], xyz[1], xyz[2]);
//此处需要把地球往后方移动防止放大的时候中间出现黑色圆圈
glTranslatef(400, 240, -200);
//实现拖动旋转
glRotatef(_rotX / 100, 0, 1, 0);
glRotatef(-_rotY / 100, 1, 0, 0);
//实现自动旋转
glRotatef(_angle, 0, 1, 0);
drawEarth();
glPopMatrix();
glutSwapBuffers();
}

Step14:实现自转的函数 void update(int value){}

//实现自动旋转效果
void update(int value) {
_angle += 2.0f;
if (_angle > 360) {
_angle -= 360;
}
//重画
glutPostRedisplay();
//60FPS
glutTimerFunc(16, update, 0);
}

Step15:在处理鼠标点击时间中函数 void handleMotion(int x, int y){}用以处理鼠标点击或活动事件,函数 void handleMouse(int button, int state, int x, int y){}用以处理鼠标点击事件

void handleMotion(int x, int y)
{
int rx = x - oldX;
int ry = 480 - y - oldY;
_rotX += rx;
_rotY += ry;
//重画
glutPostRedisplay();
}
//处理鼠标点击事件
void handleMouse(int button, int state, int x, int y)
{
static bool done = false;
if (button == GLUT_LEFT_BUTTON)
{
oldX = x;
oldY = 480 - y;
}
}

5.3完整代码 

#include <Windows.h>
#include <GL/glut.h>
#include <cmath>
#include <cstdio>
#include <cstdlib>

using namespace std;

static float _angle = 30.0f;
static float _cameraAngle = 0.0f;

static int oldX;
static int oldY;
static float _rotX = 0;
static float _rotY = 0;
static float xyz[3] = { 1,1,1 };
static float r = 200;

static GLUquadric* _earth;

#define BMP_Header_Length 54  //图像数据在内存块中的偏移量

//定义纹理对象编号
GLuint texEarth;

//按按键操作
void handleKeypress(unsigned char key, int x, int y) {
	switch (key) {
	case 27: //Escape key
		exit(0);break;
		//按上则放大
	case 'w':
		//xyz[0] += 0.1;xyz[1] += 0.1;xyz[2] += 0.1;
		r+=10;
		glutPostRedisplay();break;
		//按下则缩小
	case 's':
		//xyz[0] -= 0.1;xyz[1] -= 0.1;xyz[2] -= 0.1;
		r-=10;
		glutPostRedisplay();break;
	}
}

//定义光照
void initLight()
{
	GLfloat	ambientLight[] = { 0.2,0.2,0.2,1 };
	GLfloat diffuseLight[] = { 0.8,.8,0.8,1 };
	GLfloat specularLight[] = { 0.5,0.5,0.5,1 };
	GLfloat posLight[] = { 400,240,1,1 };
	GLfloat	specref[] = { 1,1,1,1 };

	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight);
	//glLightfv(GL_LIGHT0,GL_POSITION,posLight); //指定光源位置
	glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
	glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight);

	glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 128);
}

// 函数power_of_two用于判断一个整数是不是2的整数次幂
int power_of_two(int n)
{
    if (n <= 0)
        return 0;
    return (n & (n - 1)) == 0;
}

/* 函数load_texture
* 读取一个BMP文件作为纹理
* 如果失败,返回0,如果成功,返回纹理编号
*/
GLuint load_texture(const char* file_name)
{
    GLint width, height, total_bytes;
    GLubyte* pixels = 0;
    GLuint last_texture_ID = 0, texture_ID = 0;

    // 打开文件,如果失败,返回
    FILE* pFile;
    int err = fopen_s(&pFile, file_name, "rb");
    if (err != 0) { return 0; }

    // 读取文件中图象的宽度和高度
    fseek(pFile, 0x0012, SEEK_SET);
    fread(&width, 4, 1, pFile);
    fread(&height, 4, 1, pFile);
    fseek(pFile, BMP_Header_Length, SEEK_SET);

    // 计算每行像素所占字节数,并根据此数据计算总像素字节数
    {
        GLint line_bytes = width * 3;
        while (line_bytes % 4 != 0)
            ++line_bytes;
        total_bytes = line_bytes * height;
    }

    // 根据总像素字节数分配内存
    pixels = (GLubyte*)malloc(total_bytes);
    if (pixels == 0)
    {
        fclose(pFile);
        return 0;
    }

    // 读取像素数据
    if (fread(pixels, total_bytes, 1, pFile) <= 0)
    {
        free(pixels);
        fclose(pFile);
        return 0;
    }

    // 对就旧版本的兼容,如果图象的宽度和高度不是的整数次方,则需要进行缩放
    // 若图像宽高超过了OpenGL规定的最大值,也缩放
    {
        GLint max;
        glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
        if (!power_of_two(width)
            || !power_of_two(height)
            || width > max
            || height > max)
        {
            const GLint new_width = 256;
            const GLint new_height = 256; // 规定缩放后新的大小为边长的正方形
            GLint new_line_bytes, new_total_bytes;
            GLubyte* new_pixels = 0;

            // 计算每行需要的字节数和总字节数
            new_line_bytes = new_width * 3;
            while (new_line_bytes % 4 != 0)
                ++new_line_bytes;
            new_total_bytes = new_line_bytes * new_height;

            // 分配内存
            new_pixels = (GLubyte*)malloc(new_total_bytes);
            if (new_pixels == 0)
            {
                free(pixels);
                fclose(pFile);
                return 0;
            }

            // 进行像素缩放
            gluScaleImage(GL_RGB,
                width, height, GL_UNSIGNED_BYTE, pixels,
                new_width, new_height, GL_UNSIGNED_BYTE, new_pixels);

            // 释放原来的像素数据,把pixels指向新的像素数据,并重新设置width和height
            free(pixels);
            pixels = new_pixels;
            width = new_width;
            height = new_height;
        }
    }

    // 分配一个新的纹理编号
    glGenTextures(1, &texture_ID);
    if (texture_ID == 0)
    {
        free(pixels);
        fclose(pFile);
        return 0;
    }

    // 绑定新的纹理,载入纹理并设置纹理参数
    // 在绑定前,先获得原来绑定的纹理编号,以便在最后进行恢复
    GLint lastTextureID = last_texture_ID;
    glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastTextureID);
    glBindTexture(GL_TEXTURE_2D, texture_ID);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);
    glBindTexture(GL_TEXTURE_2D, lastTextureID);  //恢复之前的纹理绑定
    free(pixels);
    return texture_ID;
}


//定义D建模光照材质
void initRendering() {
	//创建二次曲面对象
	_earth = gluNewQuadric();

	//初始化光照
	initLight();

	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHTING);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_LINE_SMOOTH);
	//glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL,GL_SEPARATE_SPECULAR_COLOR);//镜面亮点
}

//删除建模
void deinitRendering()
{
	gluDeleteQuadric(_earth);
}

//窗口调整调用
void handleResize(int w, int h) {
	if (h == 0)
	{
		h = 1;
	}
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, w, 0, h, -200, 200);
	glMatrixMode(GL_MODELVIEW);
}

//画地球
void drawEarth()
{
	static bool loaded = false;
	//纹理绑定到目标
	glBindTexture(GL_TEXTURE_2D, texEarth);
	if (!loaded)
	{
		//纹理坐标自动生成	
		glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
		glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
		//表面生成纹理坐标
		gluQuadricDrawStyle(_earth, GL_FILL);
		gluQuadricNormals(_earth, GLU_SMOOTH);
		gluQuadricTexture(_earth, GL_TRUE);

	}
	//生成球体
	glPushMatrix();
	{
		glEnable(GL_TEXTURE_2D);
		glRotatef(-90, 1, 0, 0);
		gluSphere(_earth, r, 100, 100);
		glDisable(GL_TEXTURE_2D);

	}
	glPopMatrix();

}

//绘制D场景
void drawScene() {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_MODELVIEW); //切换到绘图透视图
	glLoadIdentity(); //重置绘图透视图

	glPushMatrix();
	//实现缩放
	glScalef(xyz[0], xyz[1], xyz[2]);
	//此处需要把地球往后方移动防止放大的时候中间出现黑色圆圈
	glTranslatef(400, 240, -200);
	//实现拖动旋转
	glRotatef(_rotX / 100, 0, 1, 0);
	glRotatef(-_rotY / 100, 1, 0, 0);
	//实现自动旋转
	glRotatef(_angle, 0, 1, 0);
	drawEarth();
	glPopMatrix();

	glutSwapBuffers();
}

//实现自动旋转效果
void update(int value) {
	_angle += 2.0f;
	if (_angle > 360) {
		_angle -= 360;
	}

	//重画
	glutPostRedisplay();

	//60FPS
	glutTimerFunc(16, update, 0);
}

//处理鼠标点击旋转事件
//处理鼠标点击滑动事件
void handleMotion(int x, int y)
{
	int rx = x - oldX;
	int ry = 480 - y - oldY;
	_rotX += rx;
	_rotY += ry;
	//重画
	glutPostRedisplay();
}

//处理鼠标点击事件
void handleMouse(int button, int state, int x, int y)
{
	static bool done = false;
	if (button == GLUT_LEFT_BUTTON)
	{
		oldX = x;
		oldY = 480 - y;
	}
}

int main(int argc, char** argv) {
	//初始化
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
	glutInitWindowSize(800, 480);

	//创建窗口
	glutCreateWindow("地球仪");
	initRendering();

	//设置用户操作
	texEarth = load_texture("D:\\Visual Studio\\MyOpenGL\\earth.bmp");
	glutDisplayFunc(drawScene);
	glutKeyboardFunc(handleKeypress);
	glutReshapeFunc(handleResize);
	glutMotionFunc(handleMotion);
	glutMouseFunc(handleMouse);

	//运动效果
	glutTimerFunc(16, update, 0);

	glutMainLoop();
	//退出时删除建模
	deinitRendering();
	return 0;
}

纹理文件:

 

5.4结果展示 

  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值