网站首页 > 技术文章 正文
课 程 实 验 报 告
目 录
实验一:分形图形绘制11
一、实验目的11
二、实验内容11
三、实验心得1111
实验二:三维场景绘制1212
一、实验目的1212
二、实验内容1212
三、实验心得1919
实验一:分形图形绘制
一、实验目的
- 理解OpenGL 中glut 程序框架;
- 掌握二维基本图形绘制算法;
- 利用二维基本图形绘制算法,扩展对其他复杂图形的绘制理解。
二、实验内容
1、实验算法
- 看懂“lineDDA模板.cpp”文件后,修改程序,将直线生成算法改为中点Bresenham算法,算法思想如下:
- 绘制分形图形。
绘制分形三角形算法思想:求出三角形ABC三条边AB,BC,AC的中点坐标D,E,F,绘制三角形DEF,再对三角形ADF,BDE,CEF重复进行上述操作即可得到分形三角形。
绘制Koch雪花的算法思想:计算得到一条边经过一次分形后的五个点的坐标,然后对分得的四条边反复进行变换,对三角形的三边进行上述变换即可得到Koch雪花。
绘制分形地毯图形思想:计算得到中心正方形的周围8个小正方形的左上顶点坐标与边长,绘制8个小正方形,然后对8个小正方形分别进行上述操作即可得到分形地毯。
2、源程序
源程序如下:
#include <stdio.h>
#include <time.h>
#include <math.h>
#include <GL/glut.h>
#define ROUND(a) ((int )(a+0.5)) // 求某个数的四舍五入值
#define PI 3.141592654 // pi的预定义
int xf = 100, yf = 200; // 定义测试数据坐标x0,y0
int xl = 500, yl = 200; // 定义测试数据坐标x1,y1
int xt = xf + (xl - xf) / 2.0; // 画等边三角形时的第三个坐标x0
int yt = yf + (xl - xf) / 2.0 * tan(3.1415926 / 3.0);
// 画等边三角形时的第三个坐标x0
// 窗口初始化函数,初始化背景色,投影矩阵模式和投影方式
void init(void)
{
glClearColor(0.396, 0.655, 0.890, 0.0); //指定窗口的背景色为蓝色
glMatrixMode(GL_PROJECTION); //对投影矩阵进行操作
gluOrtho2D(0.0, 600.0, 0.0, 600.0); //使用正投影
}
//绘制直线的函数
void lineDDA(GLint xa, GLint ya, GLint xb, GLint yb)
{
GLint dx = xb - xa, dy = yb - ya; //计算x,y方向的跨距
int steps, k; //定义绘制直线像素点的步数
float xIcre, yIcre, x = xa, y = ya; //定义步长的增量
//取X,Y方向跨距较大的值为步数
if (abs(dx) > abs(dy))
steps = abs(dx);
else
steps = abs(dy);
//根据步数来求步长增量
xIcre = dx / (float)steps;
yIcre = dy / (float)steps;
//从起点开始绘制像素点
for (k = 0;k <= steps; k++)
{
glBegin(GL_POINTS);
glVertex2f(x, y);
glEnd();
x += xIcre;
y += yIcre;
}
}
// 中点Bresenham算法绘制直线
void MidBresenhamLine(GLint xa, GLint ya, GLint xb, GLint yb)
{
// 斜率k的四个状态
// K01表示0<k<=1;
// KG1表示k>1;
// K_10表示-1<=k<0;
// KL_1表示k<-1
const int K01 = 0, KG1 = 1, K_10 = 2, KL_1 = 3;
int flag; // 标识斜率k的状态
GLint dx, dy, d, upIncre, downIncre, x, y;
// 使b的横坐标大于a的横坐标
if (xa > xb)
{
x = xb; xb = xa; xa = x;
y = yb; yb = ya; ya = y;
}
if (yb >= ya && yb - ya < xb - xa)
flag = K01; // K01表示0<k<=1;
else if (yb - ya > xb - xa)
flag = KG1; // KG1表示k>1;
else if (yb <= ya && yb - ya > xa - xb)
flag = K_10; // K_10表示-1<=k<0;
else
flag = KL_1; // KL_1表示k<-1
x = xa;
y = ya;
dx = xb - xa; // 计算增量dx
dy = yb - ya; // 计算增量dy
// 当0<k<=1时
if (flag == K01)
{
d = dx - 2 * dy; // 计算d初值
upIncre = 2 * dx - 2 * dy; // 计算步长增量
downIncre = -2 * dy;
}
// 当k>1时
if (flag == KG1)
{
d = 2 * dx - dy; // 计算d初值
upIncre = 2 * dx; // 计算步长增量
downIncre = 2 * dx - 2 * dy;
}
// 当-1<=k<0时
if (flag == K_10)
{
d = -dx - 2 * dy; // 计算d初值
upIncre = -2 * dy; // 计算步长增量
downIncre = -2 * dx - 2 * dy;
}
// 当k<-1时
if (flag == KL_1)
{
d = -2 * dx - dy; // 计算d初值
upIncre = -2 * dx - 2 * dy; // 计算步长增量
downIncre = -2 * dx;
}
// 开始绘制直线
glBegin(GL_POINTS);
// 斜率为无穷大,即为一条竖直线时单独考虑
if (dx == 0)
{
// 使B的纵坐标更大
if (ya > yb)
{
y = yb; yb = ya; ya = y;
}
y = ya;
// 从A点向上绘制直线
while (y < yb)
{
glVertex2i(x, y);
y++;
}
}
else
{
// 横向扫描,当扫描到B点横坐标时退出
while (x <= xb)
{
glVertex2i(x, y);
// 如果直线斜率满足(0,1]或[-1,0)时,X方向为最大位移方向且X增加
if (flag == K01 || flag == K_10)
x++;
// 如果直线斜率满足(1, ∞)时,Y方向为最大位移方向且Y增加
if (flag == KG1)
y++;
// 如果直线斜率满足(-∞, -1)时,Y方向为最大位移方向且Y递减
if (flag == KL_1)
y--;
// 当判据d<0时进入
if (d < 0)
{
// 如果直线斜率满足(0,1]时,y增加
if (flag == K01)
y++;
// 如果直线斜率满足(-∞, -1)时,x增加
if (flag == KL_1)
x++;
// 更新判据d
d += upIncre;
}
// d>0时
else
{
// 如果直线斜率满足[-1,0)时,y递减
if (flag == K_10)
y--;
// 如果直线斜率满足(1, ∞)时,x增加
if (flag == KG1)
x++;
// 更新判据d
d += downIncre;
}
}
}
glEnd(); // 结束绘制
}
// 递归绘制Koch分形图形的一条边,n为递归次数,inside为Koch图形的朝向
// inside为1表示朝上或朝内,为0表示朝下或朝外
void Koch(float x0, float y0, float x1, float y1, int n, int inside)
{
// n>0时继续递归
if (n > 0)
{
// A,B,C,D,E点为一条线上一次分形后的五个顶点
float xa, ya, xb, yb, xc, yc, xd, yd, xe, ye;
xa = x0; // A点的横坐标与X0相等
ya = y0; // A点的纵坐标与Y0相等
xb = x0 + (x1 - x0) / 3.0; // B点为靠近(X0, Y0)的三等分点
yb = y0 + (y1 - y0) / 3.0;
// 如果朝向为上或内,C点的坐标值计算如下
if (inside)
{
xc = (x1 + x0) / 2.0 + (y0 - y1) * sqrt(3.0) / 6.0;
yc = (y1 + y0) / 2.0 + (x1 - x0) * sqrt(3.0) / 6.0;
}
// 如果朝向为下或外,C点的坐标值计算如下
else
{
xc = (x1 + x0) / 2.0 - (y0 - y1) * sqrt(3.0) / 6.0;
yc = (y1 + y0) / 2.0 - (x1 - x0) * sqrt(3.0) / 6.0;
}
// D点为靠近(X1, Y1)的三等分点
xd = x0 + 2 * (x1 - x0) / 3.0;
yd = y0 + 2 * (y1 - y0) / 3.0;
xe = x1; // E点的横坐标等于X1
ye = y1; // E点的纵坐标等于Y1
Koch(xa, ya, xb, yb, n - 1, inside); // 对边AB进行递归
Koch(xb, yb, xc, yc, n - 1, inside); // 对边BC进行递归
Koch(xc, yc, xd, yd, n - 1, inside); // 对边CD进行递归
Koch(xd, yd, xe, ye, n - 1, inside); // 对边DE进行递归
}
// n=0时递归结束,绘制直线(x0, y0)到(x1, y1)
else
MidBresenhamLine(x0, y0, x1, y1);
}
// 递归绘制分形三角形,n为递归次数
void Triangle(float x0, float y0, float x1, float y1, float x2, float y2, int n)
{
// MID0为边(x1, y1)与(x2, y2)中点
// MID1为边(x0, y0)与(x2, y2)中点
// MID2为边(x0, y0)与(x1, y1)中点
float midx0, midy0, midx1, midy1, midx2, midy2;
// n>0时进入递归
if (n > 0)
{
// 计算MID0的坐标
midx0 = (x1 + x2) / 2.0;
midy0 = (y1 + y2) / 2.0;
// 计算MID1的坐标
midx1 = (x0 + x2) / 2.0;
midy1 = (y0 + y2) / 2.0;
// 计算MID2的坐标
midx2 = (x0 + x1) / 2.0;
midy2 = (y0 + y1) / 2.0;
// 对(x0, y0),MID1,MID2组成的三角形递归
Triangle(x0, y0, midx1, midy1, midx2, midy2, n - 1);
// 对MID0,(x1, y1),MID2组成的三角形递归
Triangle(midx0, midy0, x1, y1, midx2, midy2, n - 1);
// 对MID0,MID1,(x2, y2)组成的三角形递归
Triangle(midx0, midy0, midx1, midy1, x2, y2, n - 1);
}
// n=0时开始绘制三角型的三条边
else
{
MidBresenhamLine(x0, y0, x1, y1);
MidBresenhamLine(x1, y1, x2, y2);
MidBresenhamLine(x0, y0, x2, y2);
}
}
// 绘制内部填充的正方形,(x0, y0)为正方形左上方顶点,side为正方形边长
void DrawSquare(double x0, double y0, double side)
{
glBegin(GL_LINES);
// 从y=y0扫描到y=y0-side,绘制从(x0, y)到(x0+side, y)的直线
for (int y = y0; y > y0 - side; y--)
{
glVertex2d(x0, y);
glVertex2d(x0 + side, y);
}
glEnd();
}
// 绘制分形地毯图形,(x, y)为中心正方形的左上顶点,side为其边长,n为递归次数
void DrawCarpet(double x, double y, double side, int n)
{
// 中心正方形周围的8个小正方形的左上顶点坐标
double square0[2], square1[2], square2[2], square3[2],
square4[2], square5[2], square6[2], square7[2];
double len = side / 3.0; // 8个小正方形的边长
DrawSquare(x, y, side); // 以(x, y)为左上顶点,side为边长绘制实心正方形
// n>0时进入递归
if (n > 0)
{
// 计算第1个小正方形的左上顶点的坐标值
square0[0] = x - 2 * len;
square0[1] = y + 2 * len;
// 计算第2个小正方形的左上顶点的坐标值
square1[0] = x + len;
square1[1] = y + 2 * len;
// 计算第3个小正方形的左上顶点的坐标值
square2[0] = x + 4 * len;
square2[1] = y + 2 * len;
// 计算第4个小正方形的左上顶点的坐标值
square3[0] = x - 2 * len;
square3[1] = y - len;
// 计算第5个小正方形的左上顶点的坐标值
square4[0] = x + 4 * len;
square4[1] = y - len;
// 计算第6个小正方形的左上顶点的坐标值
square5[0] = x - 2 * len;
square5[1] = y - 4 * len;
// 计算第7个小正方形的左上顶点的坐标值
square6[0] = x + len;
square6[1] = y - 4 * len;
// 计算第8个小正方形的左上顶点的坐标值
square7[0] = x + 4 * len;
square7[1] = y - 4 * len;
// 对8个小正方形进行递归处理
DrawCarpet(square0[0], square0[1], len, n - 1);
DrawCarpet(square1[0], square1[1], len, n - 1);
DrawCarpet(square2[0], square2[1], len, n - 1);
DrawCarpet(square3[0], square3[1], len, n - 1);
DrawCarpet(square4[0], square4[1], len, n - 1);
DrawCarpet(square5[0], square5[1], len, n - 1);
DrawCarpet(square6[0], square6[1], len, n - 1);
DrawCarpet(square7[0], square7[1], len, n - 1);
}
}
// 绘制回调函数
void display()
{
glClear(GL_COLOR_BUFFER_BIT); // 设定颜色缓存中的值
glColor3f(1.0, 1.0, 1.0); // 设置直线颜色为白色
//lineDDA(50, 500, 300, 100); // 调用DDA算法函数绘制直线
//MidBresenhamLine(50, 500, 400, 200); // 调用中点Bresenham算法绘制直线
// 对三角形的三条边分别调用Koch分形图形绘制函数
//Koch(xf, yf, xl, yl, 4, 0);
//Koch(xf, yf, xt, yt, 4, 1);
//Koch(xt, yt, xl, yl, 4, 1);
//Triangle(xt, yt, xf, yf, xl, yl, 5); // 调用分形三角形绘制函数绘制图形
DrawCarpet(200, 400, 200, 4); // 调用分形地毯绘制函数绘制图形
glFlush(); //立即执行
}
// 主函数
int main(int argc, char ** argv)
{
glutInit(&argc, argv); // 初始化GLUT库OpenGL窗口的显示模式
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // 初始化窗口的显示模式
glutInitWindowSize(600, 600); // 设置窗口的尺寸
glutInitWindowPosition(100, 100); // 设置窗口的位置
glutCreateWindow("Hello OpenGL!"); // 创建窗口标题为“Hello OpenGL!”
init(); // 初始化
glutDisplayFunc(display); // 执行画图程序
glutMainLoop(); // 启动主GLUT事件处理循环
}
3、实验结果
- 中点Bresenham算法绘制直线如图1-1所示:
图1-1 中点Bresenham算法绘制直线结果
- 绘制分形三角形如图1-2:
图1-2 分型三角形绘制结果
- 绘制Koch雪花如图1-3所示:
图1-3 Koch雪花绘制结果
- 绘制分形地毯如图1-4所示:
图1-4 分型地毯绘制结果
三、实验心得
通过本次实验,我对图形学有了更深刻的认识,收获很大。
第一次使用OpenGL编程,因为有了C语言的基础,上手并不是特别困难,阅读理解了模板之后,便能自己修改程序完成中点Bresenham算法,对直线的绘制算法有了更全面的认识,把书本上的知识通过代码实现,并且能够看到相当不错的结果,这是很令人欣慰的;分形图形的绘制,是对数学变换以及递归算法的应用,通过自己的观察与计算,能够绘制出较为复杂的图形,分形三角形的绘制相对简单,找出中点即可,Koch图形的绘制需要找出变换后的五个顶点坐标,这需要进一步的计算才能实现,分形地毯的绘制是自己额外绘制的,也是不难实现的,分形图形的绘制让我体会到了数学与图形学的美,对课程有了更加浓厚的兴趣。
总的来说,完成本次实验,我的整体能力得到了提高,对计算机图形学这门课程的理解更加全面。
实验二:三维场景绘制
一、实验目的
- 掌握三维模型建立的一般方法;
- 理解OpenGL 中的变换过程;
- 理解透视投影与平行投影的不同;
- 掌握三维观察实现的基本原理。
二、实验内容
1、实验算法
三维场景绘制:先绘制出基本的三维图形,再通过简单的平移、缩放、旋转变换,得到一系列图形的组合,最后加入光照、材质等属性,设置合适的观测视角,即可实现简单三维场景的绘制。
本实验绘制三维简单城堡,对城堡的每个部分进行了分解,运用了OpenGL显示列表,再对每个部分进行各种变换,最后绘制出完整的城堡模型。
2、源程序
源程序如下:
#include <stdio.h>
#include <GL/glut.h>
#include <math.h>
#define PI 3.1415265359 // 定义常量π
static GLfloat MatSpec[] = {1.0, 1.0, 1.0, 1.0}; // 材料颜色
static GLfloat MatShininess[] = {50.0}; // 光泽度
static GLfloat LightPos[] = {-2.0, 1.5, 1.0, 0.0}; // 光照位置
static GLfloat ModelAmb[] = {0.5, 0.5, 0.5, 0.0}; // 环境光颜色
// 需要画出来的哨塔的数量和墙面的数量
GLint TowerListNum, WallsListNum;
GLint WallsList;
// 哨塔相关参数
int NumOfEdges = 30; // 细分度(其值越高,绘制越精细)
GLfloat LowerHeight = 3.0; // 哨塔上方倒圆台的下底面高度
GLfloat HigherHeight = 3.5; // 哨塔上方倒圆台的上底面高度
GLfloat HR = 1.3; // 哨塔上方倒圆台的上底面半径
// 城墙相关参数
GLfloat WallElementSize = 0.2; // 城墙上方凸起部分的尺寸
GLfloat WallHeight = 2.0; // 城墙上方凸起部分的高度
// 绘制城墙上方凸起部分
void DrawHigherWallPart(int NumElements)
{
glBegin(GL_QUADS); // 开始绘制四边形
// NumElements绘制凸起部分的数目,i小于其值时进入循环
for (int i = 0; i < NumElements; i++)
{
glNormal3f(0.0, 0.0, -1.0); // 设置法向量为Z轴负半轴方向
// 绘制四边形的四个顶点
glVertex3f(i * 2.0 * WallElementSize, 0.0, 0.0);
glVertex3f(i * 2.0 * WallElementSize, WallElementSize, 0.0);
glVertex3f((i * 2.0 + 1.0) * WallElementSize, WallElementSize, 0.0);
glVertex3f((i * 2.0 + 1.0) * WallElementSize, 0.0, 0.0);
}
glEnd(); // 结束绘制
}
// 绘制城墙
void DrawWall(GLfloat Length)
{
glBegin(GL_QUADS); // 开始绘制四边形
glNormal3f(0.0, 0.0, -1.0); // 设置法向量为Z轴负半轴方向
// 绘制四边形的四个点
glVertex3f(0.0, 0.0, 0.0);
glVertex3f(0.0, WallHeight, 0.0);
glVertex3f(Length, WallHeight, 0.0);
glVertex3f(Length, 0.0, 0.0);
glEnd(); // 结束绘制
// i为当前绘制的城墙上的凸起的数目
int i = (int)(Length / WallElementSize / 2);
// 保证凸起的总长度小于城墙的长度
if (i * WallElementSize > Length)
i--; // 如果凸起的总长度大于城墙长度,凸起数目减一
glPushMatrix();
glTranslatef(0.0, WallHeight, 0.0); // 平移变换
DrawHigherWallPart(i); // 绘制城墙上方凸起
glPopMatrix();
}
// 初始化函数,绘制哨塔,城墙
void Init(void)
{
/* 绘制哨塔 */
TowerListNum = glGenLists(1); // 生成哨塔显示列表
GLfloat x, z; // 绘制时的法向量x,z分量
GLfloat NVectY; // 哨塔上方部分倒圆台的法向量y分量
glNewList(TowerListNum, GL_COMPILE); // 用于创建和替换一个显示列表函数原型
glBegin(GL_QUADS); // 绘制四边形
// 创建塔身的圆柱体部分
int i = 0;
for (i = 0; i < NumOfEdges - 1; i++)
{
// 计算前两个点法向量的x,z坐标
x = cos((float)i / (float)NumOfEdges * PI * 2.0);
z = sin((float)i / (float)NumOfEdges * PI * 2.0);
glNormal3f(x, 0.0, z); // 设置法向量方向
// 以上述法线方向绘制前两个顶点
glVertex3f(x, LowerHeight, z);
glVertex3f(x, 0.0, z);
// 计算后两个点法向量的x,z坐标
x = cos((float)(i + 1) / (float)NumOfEdges * PI * 2.0);
z = sin((float)(i + 1) / (float)NumOfEdges * PI * 2.0);
glNormal3f(x, 0.0, z); // 设置法向量方向
// 以上述法线方向绘制后两个顶点
glVertex3f(x, 0.0, z);
glVertex3f(x, LowerHeight, z);
}
// 计算最后一个四边形的前两个点法向量的x,z坐标
x = cos((float)i / (float)NumOfEdges * PI * 2.0);
z = sin((float)i / (float)NumOfEdges * PI * 2.0);
glNormal3f(x, 0.0, z); // 设置法向量方向
// 以上述法线方向绘制前两个顶点
glVertex3f(x, LowerHeight, z);
glVertex3f(x, 0.0, z);
// 计算最后一个四边形的后两个点法向量的x,z坐标
x = cos(1.0 / (float)NumOfEdges * PI * 2.0);
z = sin(1.0 / (float)NumOfEdges * PI * 2.0);
glNormal3f(x, 0.0, z); // 设置法向量方向
// 以上述法线方向绘制后两个顶点
glVertex3f(x, 0.0, z);
glVertex3f(x, LowerHeight, z);
// 哨塔下方圆柱体部分绘制完成
// 绘制哨塔上方倒圆台部分
// 计算法向量NVect y分量的值
NVectY = (HR - 1.0) / (LowerHeight - HigherHeight) * (HR - 1.0);
for (i = 0; i < NumOfEdges - 1; i++)
{
// 计算四边形前两个顶点的法向量x,z值
x = cos((float)i / (float)NumOfEdges * PI * 2.0);
z = sin((float)i / (float)NumOfEdges * PI * 2.0);
glNormal3f(x, NVectY, z); // 设置法向量方向
// 绘制四边形前两个顶点
glVertex3f(x * HR, HigherHeight, z * HR);
glVertex3f(x, LowerHeight, z);
// 计算四边形后两个顶点的法向量x,z值
x = cos((float)(i + 1) / (float)NumOfEdges * PI * 2.0);
z = sin((float)(i + 1) / (float)NumOfEdges * PI * 2.0);
glNormal3f(x, NVectY, z); // 设置法向量方向
// 绘制四边形后两个顶点
glVertex3f(x, LowerHeight, z);
glVertex3f(x*HR, HigherHeight, z*HR);
}
// 计算最后一个四边形前两个顶点的法向量x,z坐标
x = cos((float)i / (float)NumOfEdges * PI * 2.0);
z = sin((float)i / (float)NumOfEdges * PI * 2.0);
glNormal3f(x, NVectY, z); // 设置法向量方向
// 绘制最后一个四边形前两个顶点
glVertex3f(x * HR, HigherHeight, z * HR);
glVertex3f(x, LowerHeight, z);
// 计算最后一个四边形后两个顶点的法向量x,z坐标
x = cos(1.0 / (float)NumOfEdges * PI * 2.0);
z = sin(1.0 / (float)NumOfEdges * PI * 2.0);
glNormal3f(x, NVectY, z); // 设置法向量方向
// 绘制最后一个四边形前两个顶点
glVertex3f(x, LowerHeight, z);
glVertex3f(x*HR, HigherHeight, z*HR);
glEnd(); // 绘制结束
glEndList(); // 标志显示列表的结束
/* 绘制哨塔 */
/* 绘制城墙和大门 */
WallsListNum = glGenLists(1); // 创建城墙显示列表
glNewList(WallsListNum, GL_COMPILE); // 说明显示列表的开始
DrawWall(10.0); // 调用城墙绘制函数绘制左侧城墙
glPushMatrix(); // 变换矩阵压栈
glTranslatef(10.0, 0.0, 0.0); // 平移变换
glPushMatrix(); // 变换矩阵压栈
glRotatef(270.0, 0.0, 1.0, 0.0); // 旋转变换
DrawWall(10.0); // 调用城墙绘制函数绘制后侧城墙
glPopMatrix(); // 恢复变换矩阵
glTranslatef(0.0, 0.0, 10.0); // 平移变换
glPushMatrix(); // 变换矩阵压栈
glRotatef(180.0, 0.0, 1.0, 0.0); // 旋转变换
DrawWall(5.0); // 调用城墙绘制函数绘制右侧后方城墙
glRotatef(90.0, 0.0, 1.0, 0.0); // 旋转变换
glTranslatef(0.0, 0.0, 5.0); // 平移变换
DrawWall(5.0); // 调用城墙绘制函数绘制右侧中间城墙
glPopMatrix(); // 恢复变换矩阵
glTranslatef(-5.0, 0.0, 5.0); // 平移变换
glPushMatrix(); // 变换矩阵压栈
glRotatef(180.0, 0.0, 1.0, 0.0); // 旋转变换
DrawWall(5.0); // 调用城墙绘制函数绘制最右侧城墙
glPopMatrix(); // 恢复变换矩阵
glPushMatrix(); // 变换矩阵压栈
glRotatef(90.0, 0.0, 1.0, 0.0); // 旋转变换
glTranslatef(0.0, 0.0, -5.0); // 平移变换
DrawWall(6.0); // 调用城墙绘制函数绘制前方大门右边城墙
// 绘制前方大门
glTranslatef(6.0, 0.0, 0.0); // 平移变换
glBegin(GL_QUADS); // 绘制四边形
glNormal3f(0.0, 0.0, -1.0); // 设置法向量
// 绘制四边形四个顶点
glVertex3f(0.0, WallHeight / 2.0, 0.0);
glVertex3f(0.0, WallHeight, 0.0);
glVertex3f(3.0, WallHeight, 0.0);
glVertex3f(3.0, WallHeight / 2.0, 0.0);
glEnd(); // 绘制结束
i = (int)(3.0 / WallElementSize / 2); // 计算大门上方凸起数目
if (i * WallElementSize > 3.0)
i--; // 如果凸起总长度大于大门长度,凸起数目减一
glPushMatrix(); // 变换矩阵压栈
glTranslatef(0.0, WallHeight, 0.0); // 平移变换
DrawHigherWallPart(i); // 绘制大门上方凸起
glPopMatrix(); // 恢复变换矩阵
glTranslatef(3.0, 0.0, 0.0); // 平移变换
DrawWall(6.0); // 绘制前方大门左侧城墙
glPopMatrix(); // 恢复变换矩阵
glPopMatrix(); // 恢复变换矩阵
glEndList(); // 标识显示列表的结束
/* 绘制城墙和大门 */
}
// 绘制函数
void Display(void)
{
glClearColor(1, 1, 1, 0); // 设置背景色
// 以上面颜色清屏并清除深度缓存
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); // 初始化矩阵
glLightfv(GL_LIGHT0, GL_POSITION, LightPos); // 指定光源的位置
glTranslatef(-7.0, -4.0, -20.0); // 平移变换
glRotatef(85.0, 0.0, 1.0, 0.0); // 旋转变换
glRotatef(15.0, 0.0, 0.0, 1.0); // 旋转变换
/* 绘制地面 */
glBegin(GL_POLYGON); // 绘制多边形
glNormal3f(0.0, 1.0, 0.0); // 设置法向量
// 绘制6个顶点
glVertex3f(0.0, 0.0, 0.0);
glVertex3f(10.0, 0.0, 0.0);
glVertex3f(10.0, 0.0, 10.0);
glVertex3f(5.0, 0.0, 15.0);
glVertex3f(0.0, 0.0, 15.0);
glVertex3f(0.0, 0.0, 0.0);
glEnd(); // 绘制结束
/* 绘制地面 */
// 设置全局环境光为双面光
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
// 执行城墙显示列表
glCallList(WallsListNum);
// 取消设置全局环境光为双面光
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
// 执行哨塔显示列表绘制第一个哨塔
glCallList(TowerListNum);
glTranslatef(10.0, 0.0, 0.0); // 平移变换
// 执行哨塔显示列表绘制第二个哨塔
glCallList(TowerListNum);
glTranslatef(0.0, 0.0, 10.0); // 平移变换
// 执行哨塔显示列表绘制第三个哨塔
glCallList(TowerListNum);
glTranslatef(-5.0, 0.0, 5.0); // 平移变换
// 执行哨塔显示列表绘制第四个哨塔
glCallList(TowerListNum);
glTranslatef(-5.0, 0.0, 0.0); // 平移变换
// 执行哨塔显示列表绘制第五个哨塔
glCallList(TowerListNum);
glFlush();// 立即执行
glutSwapBuffers();// 交换缓冲区
}
// 窗口改变函数
void Reshape(int x, int y)
{
glViewport(0, 0, x, y); // 设置视口
glMatrixMode(GL_PROJECTION); // 指定当前操作投影矩阵堆栈
glLoadIdentity(); // 重置矩阵函数,将之前的变换消除
gluPerspective(40.0, (GLdouble)x / (GLdouble)y, 1.0, 200.0); // 投影变换
glMatrixMode(GL_MODELVIEW); // 指定当前操作视景矩阵堆栈
}
// 主函数
int main(int argc, char **argv)
{
glutInit(&argc, argv); // 初始化
// 设置窗口初始显示模式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(1200, 600); // 显示框大小
glutInitWindowPosition(100, 100); // 确定显示框左上角的位置
glutCreateWindow("Castle"); // 窗口名字
glEnable(GL_DEPTH_TEST); // 打开深度检测
// 设置正反面都为填充方式
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glutDisplayFunc(Display); // 调用绘图函数
glutReshapeFunc(Reshape); // 调用窗口改变函数
// 定义镜面材料颜色
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, MatSpec);
// 定义材料的光泽度
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, MatShininess);
glEnable(GL_LIGHTING); // 启用光源
glEnable(GL_LIGHT0); // 使用指定灯光
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ModelAmb); // 设定全局环境光
Init(); // 调用初始化函数
glutMainLoop(); // 启动主GLUT事件处理循环
return 0;
}
3、实验结果
三维场景绘制结果如图2-1、2-2所示:
图2-1 三维场景绘制结果A
图2-2 三维场景绘制结果B
三、实验心得
通过第二次实验,我对三维场景绘制的认识更加全面,对OpenGL的运用也更加熟练。
三维场景的绘制,相比第一次实验较为复杂,对OpenGL的熟练使用要求更多,需要对OpenGL中的平移、缩放和旋转变换有足够的了解,在绘图过程中,运用了OpenGL的显示列表绘制,大大降低了代码的复杂度,绘制简单三维城堡的的过程中,先是绘制城堡的一小部分,如哨塔的上下两个部分,城墙上面的凸起等,在经过一系列变换,绘制出所有的哨塔,城墙以及城墙上方的凸起等等,绘制过程中,对法向量的计算尤为重要,因为绘制圆柱体和圆台时采用的是绘制四边形合成的方法,所以在绘制每个四边形时法向量的设置需要格外注意,对每个部分的定位也要准确,每一步的绘制都需要考虑到下一步的变换,所以在对绘制过程中的坐标变换时也需要注意。
综上,两次实验的完成,让我一点一滴的开始OpenGL编程,其中的收获是不言而喻的,对课程的学习也起到了重要作用。
- 上一篇: 圆生成算法 圆的生成方式有哪三种
- 下一篇: wu反走样画线算法的实现 wu反走样算法绘制直线
猜你喜欢
- 2024-10-31 如何实现鼠标绘制三角形? 鼠标三角形显示怎样才能增大
- 2024-10-31 CADCAM软件应用试题 cadcam题库
- 2024-10-31 Python pyecharts v1.x 绘制图形 python怎么绘制图形
- 2024-10-31 视觉SLAM中闭环检测算法的研究 什么是闭环测试
- 2024-10-31 |期刊分享|SLAM|不到200行C代码的CoreSLAM
- 2024-10-31 纹理贴图原理与实践【Texture Mapping】
- 2024-10-31 算力平台跑出200FPS!已知最快最精确的视觉SLAM!吊打ORB+VINS!
- 2024-10-31 利用html canvas实践三角形光栅化
- 2024-10-31 wu反走样画线算法的实现 wu反走样算法绘制直线
- 2024-10-31 圆生成算法 圆的生成方式有哪三种
你 发表评论:
欢迎- 最近发表
-
- 在 Spring Boot 项目中使用 activiti
- 开箱即用-activiti流程引擎(active 流程引擎)
- 在springBoot项目中整合使用activiti
- activiti中的网关是干什么的?(activiti包含网关)
- SpringBoot集成工作流Activiti(完整源码和配套文档)
- Activiti工作流介绍及使用(activiti工作流会签)
- SpringBoot集成工作流Activiti(实际项目演示)
- activiti工作流引擎(activiti工作流引擎怎么用)
- 工作流Activiti初体验及在数据库中生成的表
- Activiti工作流浅析(activiti6.0工作流引擎深度解析)
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)