实验9- 3D编程基础

一、实验目的

1.熟悉3D基本编程
2.熟悉视点观察函数的设置和使用
3.熟悉投影变换函数的设置和使用
4.熟悉基本3D图元的绘制

二、实验内容

1.读懂以下3D物体程序,并结合本书内容理解一些新的绘制函数和投影变换函数的含 义:3D Cube.cpp (见后面参考程序)为正交投影下的旋转3D立方体,按下鼠标可实现不同方向的旋转,效果图参见实验图9-1,分析3D编程代码与程序结构。
图9-1
在这里插入图片描述
对于以下操作需要记录不同效果图和修改的相应参数:
1)让静止的立方体绕Z轴不停旋转。
gluLookAt(0, 0, 3, 0, 0, 0, 0, 1, 0);//视点设置函数
在这里插入图片描述
2)修改不同视点,目标点不变,观看显示效果。
gluLookAt(3, 0, 0, 0, 0, 0, 0, 1, 0);//视点设置函数
在这里插入图片描述
3)修改目标点,视点不动,观看显示效果。
gluLookAt(3, 3, 3, 0, 3, 0, 0, 1, 0);//视点设置函数
在这里插入图片描述
4)视点与目标点同时修改,观看显示效果。
gluLookAt(5, 0, 0, 3, 0, 0, 0, 1, 0); //视点设置函数
在这里插入图片描述
5)视点与目标点不变,修改观察体大小,观看显示效果。
gluPerspective(50, w / h, 10, 60); //定义透视投影投影观察体大小
在这里插入图片描述
6)将正交投影观察体改为透视投影观察体,并设置其大小,观察显示效果。
gluPerspective (120, w / h, 10, 30);
在这里插入图片描述
正交投影比透视投影观察体的亮度暗些,细节部分缺失。
7)将立方体替换为茶壶,观看显示效果。
核心代码:

void displaytadpot(void)  
{
    
      
    // 设置背景为白色  
    glClearColor(1.0, 1.0, 1.0, 1.0);  
    glClear(GL_COLOR_BUFFER_BIT);  
    glColor3f(1.0, 0.0, 0.0); //画笔红色  
    glLoadIdentity();  //加载单位矩阵    
    gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);  
    glRotatef(angle, 0.0f, 1.0f, 0.0f);  
    glRotatef(0.0, 1.0, 0.0, 0.0);  
    glRotatef(0.0, 0.0, 1.0, 0.0);  
    glRotatef(0.0, 0.0, 0.0, 1.0);  
    glutWireTeapot(1.5);  
    //rote += roate;  
    //glRotatef(angle, 0.0, 1.0, 0.0);  
    //angle += 1.0f;  
    glEnd();  
    glutSwapBuffers();  
}  

在这里插入图片描述
8)将立方体替换为圆环,观看显示效果。
核心代码:

//画圆环:  
void display()  
{
    
      
    // 设置背景为白色  
    glClearColor(1.0, 1.0, 1.0, 1.0);  
    glClear(GL_COLOR_BUFFER_BIT);  
    glColor3f(1.0, 0.0, 0.0); //画笔红色  
    glLoadIdentity();  //加载单位矩阵    
    //视角  
    gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);  
    glRotatef(angle, 0.0f, 1.0f, 0.0f);  
    glRotatef(0.0, 1.0, 0.0, 0.0);  
    glRotatef(0.0, 0.0, 1.0, 0.0);  
    glRotatef(0.0, 0.0, 0.0, 1.0);  
    glutWireTorus(0.2, 1.5, 20, 40);  
    glEnd();  
    glutSwapBuffers();  
}  

在这里插入图片描述
2.构思绘制茶壶和圆环造型程序Teapot Torus.cp在紧挨着茶壶下方添加一个平行的圆环,茶壶和圆环不停绕中心轴旋转,观看显示效果。
核心代码:

void displayTeapot_Torus()  
{
    
      
    // 设置背景为白色  
    glClearColor(1.0, 1.0, 1.0, 1.0);  
    glClear(GL_COLOR_BUFFER_BIT);  
    glColor3f(1.0, 0.0, 0.0); //画笔红色  
    glLoadIdentity();  //加载单位矩阵    
    //视角  
    gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);  
    glRotatef(angle, 0.0f, 1.0f, 0.0f);  
    glRotatef(0.0, 1.0, 0.0, 0.0);  
    glRotatef(0.0, 0.0, 1.0, 0.0);  
    glRotatef(0.0, 0.0, 0.0, 1.0);  
    glutWireTorus(0.2, 1.5, 20, 40);  
    glRotatef(90, 1, 0, 0);//旋转  
    glTranslatef(0, 0.9, 0);//平移  
    glutWireTeapot(1);  
    glEnd();  
    glutSwapBuffers();  
} 

在这里插入图片描述
3.编写或改写程序,构造自己的3D物体场景造型。
核心代码:

void displayTeapot_Torus()  
{
    
      
    // 设置背景为白色  
    glClearColor(1.0, 1.0, 1.0, 1.0);  
    glClear(GL_COLOR_BUFFER_BIT);  
    glColor3f(1.0, 0.0, 0.0); //画笔红色  
    glLoadIdentity();  //加载单位矩阵    
    //视角  
    gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);  
    glRotatef(angle, 0.0f, 1.0f, 0.0f);  
    glRotatef(0.0, 1.0, 0.0, 0.0);  
    glRotatef(0.0, 0.0, 1.0, 0.0);  
    glRotatef(0.0, 0.0, 0.0, 1.0);  
    glutWireTorus(0.2, 1.5, 20, 40);  
    glRotatef(90, 1, 0, 0);//旋转  
    glTranslatef(0, 0.9, 0);//平移  
    glutWireTeapot(1);
	glColor3f(0.0, 0.0, 1.0);
	glRotatef(angle, 0.0f, 1.0f, 0.0f);  
    glRotatef(0.0, 1.0, 0.0, 0.0);  
    glRotatef(0.0, 0.0, 1.0, 0.0);  
    glRotatef(0.0, 0.0, 0.0, 1.0);  
    glutWireTorus(0.2, 1.5, 20, 40); 
    glEnd();  
    glutSwapBuffers();  
}  

在这里插入图片描述

三、函数参考

1.视点设置函数:

void gluLookAt(GLdouble eyex, GLdouble eyey,GLdouble eyezz GLdouble atx,GLdouble aty,GLdouble atz,GLdouble upx,GLdouble upy,GLdouble upz)

2.正交投影变换设置函数:

void glOrtho(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top,GLdouble near,GLdouble far)

3.透视投影变换设置函数:

void gluPerspective(GLdouble fov, GLdouble aspect, GLdouble near,GLdouble far)

4.三维基本图形绘制函数:
1)立方体绘制函数:

void glutWireCube(GLdouble size) //线框模式
void glutSolidCube(GLdouble size)//实体模式

功能:绘制一个边长为size的线框或实心立方体,立方体的中心位于原点。
2)小球绘制函数:

扫描二维码关注公众号,回复: 15410349 查看本文章

void glutWireSphere(GLdouble Radius, Glint slices,Glint stacks) ;//线框模式
void glutSolidSphere(GLdouble Radius, Glint slices,Glint stacks);//实体模式

功能:绘制一个半径为Radius的线框或实心小球,小球的中心点位于原点,slices为小 球的经线数目,stacks为小球的纬线数目。
gluSphere(GLUquadricObj *obj,GLdouble radius,GLint slices,GLint stacks);
用法如下:

GLUquadricObj *sphere;//定义二次曲面对象
sphere = gluNewQuadric();//生成二次曲面对象
gluSphere(Sphere,8,50,50);//半径为8,球心在原点,经线和纬线数目为50的小球

3)茶壶绘制函数:

void glutWireTeapot(GLdouble size);//线框模式
void glutSolidTeapot(GLdouble size);//实体模式

功能:绘制一个半径为size的线框或实心茶壶,茶壶的中心位于原点。
参数说明:参数size为茶壶的近似半径,以size为半径的球体可完全包容这个茶壶。
4)圆环绘制函数:

void glutWireTorus(GLdouble innerRadius, GLdouble outerRadius,Glint slices,Glint 	stacks) ;	//线框模式
void glutSolidTorus(GLdouble innerRadius, GLdouble outerRadius,Glint slices,Glint 	stacks) ;	//实体模式

功能:绘制一个半径为size的线框或实心圆环体,圆环体的中心位于原点,圆环的内径 和外径由参数innerRadius、outerRadius指定。
参数说明:innerRadius为圆环体的内径;outerRadius为圆环体的外径;slices为圆环体 的经线数目;stacks为圆环体的纬线数目。
5)正八面体绘制函数:

void glutWireOctahedron (void) ;	// 线框模式
void glutSolidOctahedron (void);	//实体模式

功能:绘制一个线框的或实心的正八面体,其中心位于原点,半径为1。
6)正十二面体绘制函数:

void glutWireDodehedron (void) ;	// 线框模式
void glutSolidDodehedron (void);	//实体模式

功能:绘制一个线框的或实心的正十二面体,其中心位于原点,-半径为3的平方根。
7)正二十面体绘制函数:

void glutWirelcosahedron (void);	//线框模式
void glutSolidlcosahedron (void);	//实体模式

功能:绘制一个线框的或实心的正二十面体,其中心位于原点,半径为1。
8)正四面体绘制函数:

void glutWireTetrahedron (void);	//线框模式
void glutSolidTetrahedron (void);	//实体模式

功能:绘制一个线框的或实心的正四面体,其中心位于原点,半径为3的平方根。
9)裁剪平面函数:
void glClipPlane(GLenum plane, const GLdouble *equation)
定义一个附加的裁减平面。plane指岀要定义的附加裁减平面名称,取值为GL_CLIP_ PLANEi,i=0 ~ 5 ; equation指向由平面方程Ax+By+Cz+D=0的4个系数刀、B、C、O构成的 数组,以定义一个裁减平面。调用此函数,先启用glEnable(GL_CLIP_PLANEi),需要时用 glDisable(GL_CLIP_PLANEi)关闭某裁减平面。
5.图形变换函数:
1)glTranslatef(xz y,z)
2)gLRotatef(Q,x, y, z)
3)glScalef(x,y,z)
6.void glPushMatrix (void)函数
7.void glPopMatrix (void)函数
8.void glutIdleFunc( (*f) (void)) 〃注册闲置响应函数
9.void myidle () //闲置响应回调函数
{
//当时间空闲时系统要做的事情
}

四、思考题

1.修改目标点与视点,显示结果有何不同?
答:修改目标点会使物体在程序框的相对位置改变,修改视点观察角度不同。
2.视点与目标点不变,修改观察体大小,显示结果有什么规律?
答:视场角变大时,物体反而变小。
3.正交投影与透视投影有何不同?
答:在正交投影中,图形沿平行线变换到投影面上;对透视投影,图形沿收敛于某一点的直线变换到投影面上,此点称为投影中心,相当于观察点,也称为视点。
  正交投影和透视投影区别在于透视投影的投影中心到投影面之间的距离是有限的,而正交投影的投影中心到投影面之间的距离是无限的。
  当投影中心在无限远时,投影线互相平行,所以定义正交投影时,给出投影线的方向就可以了,而定义透视投影时,需要指定投影中心的具体位置
  正交投影保持物体的有关比例不变,这是三维绘图中产生比例图画的方法。物体的各个面的精确视图可以由平行投影得到。
另一方面,透视投影不保持相关比例,但能够生成真实感视图。对同样大小的物体,离投影面较远的物体比离投影面较近物体的投影图象要小,产生近大远小的效果。

五、课后加分题

如何编写程序从不同角度观看物体模型?如按“A”键,视点越来越远;按“S”键视点 越来越近?如何切换俯视、侧视或正视?
答:将视点设为变量,按相应的键进行改变。切换俯视、侧视或正视,设置键位使视点切换至相应的方向。

六、最终程序

#include "stdafx.h"
#include "Shiyan9-1.h"
#define MAX_LOADSTRING 100
#define GLUT_DISABLE_ATEXIT_HACK
#include <windows.h>
#include <gl/glut.h>
#include <math.h>
#include<iostream>  
#define COLOR 8.0  
#define PIX ((GLfloat)(1.0 / COLOR))  
#define STEP 1.0f  
#define PI acos(-1)   
using namespace std;  
// 旋转初始的角度  
GLfloat angle = 0.0f;  
// 设置旋转轴:两个三维的点确定的旋转轴  
GLfloat axis[][3] = {
    
      
    0.5f,0.5f, 0.0f,  
    0.5f, 0.5f, 1.0f  
};  
void display_2()  
{
    
      
    // 设置逆时针排列的点围成的平面为正面  
    glFrontFace(GL_CCW);  
    // 设置不绘制背面,节省算力同时不会出现背面覆盖正面的情况  
    glCullFace(GL_BACK);  
    glEnable(GL_CULL_FACE);  
    // 设置背景为白色  
    glClearColor(1.0, 1.0, 1.0, 1.0);  
    glClear(GL_COLOR_BUFFER_BIT);  
    // 加载单位阵  
    glLoadIdentity();  
    // 设置相机的位置和视角  
    // 有关gluLookAt:https://blog.csdn.net/Augusdi/article/details/20470813  
    //gluLookAt(2, 2, 2, 0.0, 1, 0.0, -1, -1, 1);//修改目标点  
    //gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, -1, -1, 1);//修改视点  
     //gluLookAt(1, 2, 0, 0.0, 1, 0.0, -1, -1, 1);//修改目标点和视点  
    gluLookAt(2, 2, 2, 0.0, 0.0, 0.0, -1, -1, 1);  
    // 设置绕给定的轴旋转  
    glTranslatef(axis[0][0], axis[0][1], axis[0][2]);  
    glRotatef(angle, axis[1][0] - axis[0][0], axis[1][1] - axis[0][1], axis[1][2] - axis[0][2]);  
    glTranslatef(-axis[0][0], -axis[0][1], -axis[0][2]);  
    // 设置立方体的八个顶点坐标  
  
    static const GLfloat vertex[][3] = {
    
      
        0.0f, 0.0f, 0.0f,  
        1.0f, 0.0f, 0.0f,  
        0.0f, 1.0f, 0.0f,  
        1.0f, 1.0f, 0.0f,  
        0.0f, 0.0f, 1.0f,  
        1.0f, 0.0f, 1.0f,  
        0.0f, 1.0f, 1.0f,  
        1.0f, 1.0f, 1.0f  
    };  
    /* 
//修改正方体大小 
static const GLfloat vertex[][3] = { 
    0.0f, 0.0f, 0.0f, 
    1.5f, 0.0f, 0.0f, 
    0.0f, 1.5f, 0.0f, 
    1.5f, 1.5f, 0.0f, 
    0.0f, 0.0f, 1.5f, 
    1.50f, 0.0f, 1.5f, 
    0.0f, 1.5f, 1.5f, 
    1.5f, 1.5f, 1.5f 
}; 
    */  
    // 设置绘制六个面时顶点的顺序  
    static const GLint index[][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  
    };  
    // 绘制六个面  
    glBegin(GL_QUADS);  
    for (unsigned int i = 0; i < 6; i++)  
        for (unsigned int j = 0; j < 4; j++) {
    
      
            // 每个顶点的RGB颜色值和其顶点位置坐标一致  
            glColor3fv(vertex[index[i][j]]);  
            glVertex3fv(vertex[index[i][j]]);  
        }  
    glEnd();  
    // 双缓冲下的刷新帧缓存  
    glutSwapBuffers();  
}  
/*void displaytadpot(void)  
{  
    // 设置背景为白色  
    glClearColor(1.0, 1.0, 1.0, 1.0);  
    glClear(GL_COLOR_BUFFER_BIT);  
    glColor3f(1.0, 0.0, 0.0); //画笔红色  
    glLoadIdentity();  //加载单位矩阵    
    gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);  
    glRotatef(angle, 0.0f, 1.0f, 0.0f);  
    glRotatef(0.0, 1.0, 0.0, 0.0);  
    glRotatef(0.0, 0.0, 1.0, 0.0);  
    glRotatef(0.0, 0.0, 0.0, 1.0);  
    glutWireTeapot(1.5);  
    //rote += roate;  
    //glRotatef(angle, 0.0, 1.0, 0.0);  
    //angle += 1.0f;  
    glEnd();  
    glutSwapBuffers();  
}  */
//画圆环:  
/*void display()  
{  
    // 设置背景为白色  
    glClearColor(1.0, 1.0, 1.0, 1.0);  
    glClear(GL_COLOR_BUFFER_BIT);  
    glColor3f(1.0, 0.0, 0.0); //画笔红色  
    glLoadIdentity();  //加载单位矩阵    
    //视角  
    gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);  
    glRotatef(angle, 0.0f, 1.0f, 0.0f);  
    glRotatef(0.0, 1.0, 0.0, 0.0);  
    glRotatef(0.0, 0.0, 1.0, 0.0);  
    glRotatef(0.0, 0.0, 0.0, 1.0);  
    glutWireTorus(0.2, 1.5, 20, 40);  
    glEnd();  
    glutSwapBuffers();  
}  */
//画圆环和茶狐混合绕一个轴旋转  
void displayTeapot_Torus()  
{
    
      
    // 设置背景为白色  
    glClearColor(1.0, 1.0, 1.0, 1.0);  
    glClear(GL_COLOR_BUFFER_BIT);  
    glColor3f(1.0, 0.0, 0.0); //画笔红色  
    glLoadIdentity();  //加载单位矩阵    
    //视角  
    gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);  
    glRotatef(angle, 0.0f, 1.0f, 0.0f);  
    glRotatef(0.0, 1.0, 0.0, 0.0);  
    glRotatef(0.0, 0.0, 1.0, 0.0);  
    glRotatef(0.0, 0.0, 0.0, 1.0);  
    glutWireTorus(0.2, 1.5, 20, 40);  
    glRotatef(90, 1, 0, 0);//旋转  
    glTranslatef(0, 0.9, 0);//平移  
    glutWireTeapot(1);
	glColor3f(0.0, 0.0, 1.0);
	glRotatef(angle, 0.0f, 1.0f, 0.0f);  
    glRotatef(0.0, 1.0, 0.0, 0.0);  
    glRotatef(0.0, 0.0, 1.0, 0.0);  
    glRotatef(0.0, 0.0, 0.0, 1.0);  
    glutWireTorus(0.2, 1.5, 20, 40); 
    glEnd();  
    glutSwapBuffers();  
}
void timer_function(GLint value)  
{
    
      
    // 旋转角度增加  
    angle += STEP;  
    // 若角度大于360转完一圈则清零  
    if (angle > 360.0) angle -= 360.0;  
    glutPostRedisplay();  
    glutTimerFunc(50, timer_function, value);  
}  
void reshape(int w, int h)  
{
    
      
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);  
    glMatrixMode(GL_PROJECTION);//投影矩阵  
    glLoadIdentity();  
    gluPerspective(60.0, (GLfloat)w / (GLfloat)h, 1.0, 20.0);  
    glMatrixMode(GL_MODELVIEW);//切换回模型视图矩阵  
    //glLoadIdentity();  
    //gluLookAt(2, 2, 2, 0.0, 0.0, 0.0, -1, -1, 1);  
} 
//透视投影  
void reshape(int w, int h)  
{
    
      
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);  
    glMatrixMode(GL_PROJECTION);//投影矩阵  
    glLoadIdentity();  
    gluPerspective(60.0, (GLfloat)w / (GLfloat)h, 1.0, 20.0);  
    glMatrixMode(GL_MODELVIEW);//切换回模型视图矩阵  
    //glLoadIdentity();  
    //gluLookAt(2, 2, 2, 0.0, 0.0, 0.0, -1, -1, 1);  
}  
void reshape_2(int w, int h)  
{
    
      
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);  
    glMatrixMode(GL_PROJECTION);//投影矩阵  
    glLoadIdentity();  
    //使用正交投影  
    if (w <= h)  
        glOrtho(-2.0, 2.0, -2.0 * (GLfloat)h / (GLfloat)w,  
            2.0 * (GLfloat)h / (GLfloat)w, -10.0, 10.0);  
    else  
        glOrtho(-2.0 * (GLfloat)w / (GLfloat)h,  
            2.0 * (GLfloat)w / (GLfloat)h, -2.0, 2.0, -10.0, 10.0);  
    glMatrixMode(GL_MODELVIEW);  
}  
int APIENTRY _tWinMain(HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPTSTR    lpCmdLine,
	int       nCmdShow)  
{
    
      
    UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);
	char* argv[] = {
    
     (char*)"hello ",(char*)" " };
	int argc = 2;		//argv中的字符串数

	glutInit(&argc, argv);                  
    // 设置双缓冲和RGB颜色模式  
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);  
    // 设置窗口大小、位置和名称  
    glutInitWindowSize(500, 500);  
    glutInitWindowPosition(100, 100);  
    glutCreateWindow("实验九WYM");  
    // 设置绘制函数、窗口大小自适应函数和定时器回调函数  
    glutDisplayFunc(displayTeapot_Torus);  
    glutReshapeFunc(reshape);  
    //glEnable(GL_DEPTH_TEST); //使用了深度缓存这行不能掉  
    glutTimerFunc(500, timer_function, 1);  
    // 进入主循环  
    glutMainLoop();  
    return 0;  
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_52030647/article/details/130729315