实验环境:win10、VS 2017、OPenGL库
实验一 基于OpenGL的二维图形绘制
要求
编程实现绘制一个五角星(基于OpenGL),效果如下图所示
问题分析
可以将一个五角星划分为10个三角形,假设五角星的各边长,分别计算出10个定点的坐标,然后逐个绘制三角形,将其拼接为五角星;
也可以利用参数方程,如下图所示,设大圆和小圆的半径分别为R、r,已知夹角为37度,在循环中计算绘制三角形。
代码
ProjectOne.cpp
代码语言:javascript复制#include <GL/glew.h>//OpenGL库
#include <GL/glut.h>//OpenGL辅助库
#include <math.h>
#pragma comment(linker, "/subsystem:"windows" /entry:"mainCRTStartup"") //这句是不让控制台窗体出现,如果想要出现,去掉即可。
// 明确 计算三角函数 需要 theta * pi / 180
// 由于 两个三角形的theta角有36度的差值
// 点的坐标为 x = x0 r(R) * sin(cos) theta
// 对于大圆 theta 为( 0 72 * i )* pi/180 == 0.4i * pi
// 小圆theta 为 (36 72 * i) * pi / 180 == 0.2*pi 0.4i * pi
// 两者相差只有 0.2 * pi
void init(void)
{
glClearColor(1.0, 1.0, 1.0, 1.0);//设置背景色白色
}
void DrawStar1(float cx, float cy, float R, float r, float o)//五角星中心坐标x,y,大圆半径,小圆半径,初始角度
{
float x0, y0, x1, y1;//
float Theta = o;//大圆对应角度
float theta = o 0.2 * 3.14;//小圆对应角度
for (int i = 0; i < 10; i )
{
x0 = cx R * cos(Theta);//大圆对应的x坐标
y0 = cy R * sin(Theta);//大圆对应y坐标
x1 = cx r * cos(theta);//小圆对应x坐标
y1 = cy r * sin(theta);//小圆对应y坐标
glBegin(GL_LINES);//绘制
glColor3f(1, 0, 0);
glVertex2f(x0, y0);
glVertex2f(x1, y1);
glEnd();
if (i % 2 == 0)
{
Theta = Theta 0.4 * 3.14;//大圆对应角度变换
}
else
{
theta = theta 0.4 * 3.14;//小圆对应角度变换
}
}
}
void DrawStar2(float cx, float cy, float R, float r, float o)//五角星中心坐标x,y,大圆半径,小圆半径,初始角度
{
float x0, y0, x1, y1;//
float Theta = o;//大圆对应角度
float theta = o 0.2 * 3.14;//小圆对应角度
for (int i = 0; i < 10; i )
{
x0 = cx R * cos(Theta);//大圆对应的x坐标
y0 = cy R * sin(Theta);//大圆对应y坐标
x1 = cx r * cos(theta);//小圆对应x坐标
y1 = cy r * sin(theta);//小圆对应y坐标
glColor3f(1, 0, 0);
glBegin(GL_POLYGON);//绘制
glVertex2f(x0, y0);
glVertex2f(x1, y1);
glVertex2f(cx, cy);
glEnd();
if (i % 2 == 0)
{
Theta = Theta 0.4 * 3.14;//大圆对应角度变换
}
else
{
theta = theta 0.4 * 3.14;//小圆对应角度变换
}
}
}
void DrawStar3(float cx, float cy, float R, float r, float o)//五角星中心坐标x,y,大圆半径,小圆半径,初始角度
{
float x0, y0, x1, y1;
float Theta = o; //大圆对应角度
float theta = o 0.2 * 3.14; //小圆对应角度
for (int i = 0; i < 10; i )
{
x0 = cx R * cos(Theta);//大圆对应的x坐标
y0 = cy R * sin(Theta);//大圆对应y坐标
x1 = cx r * cos(theta);//小圆对应x坐标
y1 = cy r * sin(theta);//小圆对应y坐标
if (i % 2 == 0)
{
glColor3f(1, 0, 0);
}
else
{
glColor3f(0, 1, 1);
}
glBegin(GL_POLYGON);//绘制
glVertex2f(x0, y0);
glVertex2f(x1, y1);
glVertex2f(cx, cy);
glEnd();
if (i % 2 == 0)
{
Theta = Theta 0.4 * 3.14; //大圆对应角度变换
}
else
{
theta = theta 0.4 * 3.14; //小圆对应角度变换
}
}
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT); //清除帧缓存
DrawStar1(150, 300, 120, 50, 0); //glFlush();//单缓冲时必须要,说明绘图命令(函数)结束
DrawStar2(400, 300, 120, 50, 0);
DrawStar3(650, 300, 120, 50, 0);
glutSwapBuffers();//交换缓冲(双缓冲时使用)
}
void reshape(int width, int height)
{
glViewport(0, 0, width, height);//设置视区(窗口内绘图区域)
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, width, 0, height);//设置图形数据范围
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void keyboard(unsigned char key, int x, int y)
{
}
int main(int argc, char* argv[])
{
glutInitWindowPosition(200, 200);//应用程序窗口位置
glutInitWindowSize(800, 600);//窗口大小
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);//双缓冲,单缓冲为GLUT_SINGLE
glutCreateWindow("五角星绘制");//创建窗口,参数为窗口标题
init();
glutDisplayFunc(display);//图形绘制
glutReshapeFunc(reshape);//窗口大小变化
glutKeyboardFunc(keyboard);//键盘交互
glutMainLoop();//必须,进入GLUT事件处理循环
return 0;
}
效果展示
实验二 二维图形的几何变换
要求
- 实现一个五角星以任意角度在矩形四条边内部滚动,与边界碰撞时发生的反弹,参考台球在桌案上的滚动效果。
- 实现矩形框内一个五角星的连续放缩(大小变化)
- 注意:两个五角星在同一矩形内;放缩五角星的参照点为五角星的中心,五角星位置固定;滚动五角星的旋转角度和平移距离尽量一致。
问题分析
- 将滚动五角星看做半径为R的圆,滚动五角星的碰撞条件判断为:它的圆心到矩形边界的距离是否大于半径R。图示如下
红色矩形所框选的范围即为滚动五角星圆心所运动的区域。
- 旋转五角星只需要不停地调用放缩函数即可。
代码
ProjectTwo.cpp
代码语言:javascript复制// OpenGLOld.cpp : 定义控制台应用程序的入口点。
//
#include <GL/glew.h>//OpenGL库
#include <GL/glut.h>//OpenGL辅助库
#include <math.h>
#pragma comment( linker, "/subsystem:"windows" /entry:"mainCRTStartup"" ) //这句是不让控制台窗体出现,如果想要出现,去掉即可。
void init(void)
{
glClearColor(0.2, 0.3, 0.3, 1.0);//设置背景色
}
float alpha=0.0f;
float x = 0.0f, y = 0.0f;
double Scale = 0;
void DrawStar3(float cx, float cy, float R, float r, float o)//五角星中心坐标x,y,大圆半径,小圆半径,初始角度
{
float x0, y0, x1, y1;
float Theta = o; //大圆对应角度
float theta = o 0.2 * 3.14; //小圆对应角度
for (int i = 0; i < 10; i )
{
x0 = cx R * cos(Theta);//大圆对应的x坐标
y0 = cy R * sin(Theta);//大圆对应y坐标
x1 = cx r * cos(theta);//小圆对应x坐标
y1 = cy r * sin(theta);//小圆对应y坐标
if (i % 2 == 0)
{
glColor3f(1, 0, 0);
}
else
{
glColor3f(0, 1, 1);
}
glBegin(GL_POLYGON);//绘制
glVertex2f(x0, y0);
glVertex2f(x1, y1);
glVertex2f(cx, cy);
glEnd();
if (i % 2 == 0)
{
Theta = Theta 0.4 * 3.14; //大圆对应角度变换
}
else
{
theta = theta 0.4 * 3.14; //小圆对应角度变换
}
}
}
void MyDrawRect()
{
glBegin(GL_LINE_LOOP);
glVertex2f(0, 0);
glVertex2f(0, 700);
glVertex2f(1000, 700);
glVertex2f(1000, 0);
glEnd();
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);//清除帧缓存
//模型观察矩阵初始化
glMatrixMode(GL_MODELVIEW);
// 旋转与碰撞
glPushMatrix();
glTranslatef(150.0f x,300.0f y,0.0f);
glRotatef(alpha, 0, 0, 1);
glTranslatef(-150.0f-x, -300.0f-y, 0.0f);
DrawStar3(150 x, 300 y, 100, 40, 0);
//glLoadIdentity();
glPopMatrix();
//缩放
glPushMatrix();
glTranslatef(300.0, 200.0, 0.0);
glScalef(Scale, Scale, 1);
glTranslatef(-300.0, -200.0, 0.0);
DrawStar3(300, 200, 100, 40, 0.1*3.14);
//glLoadIdentity();
glPopMatrix();
MyDrawRect();
//glFlush();//单缓冲时必须要,说明绘图命令(函数)结束
glutSwapBuffers();//交换缓冲(双缓冲时使用)
}
void reshape(int width, int height)
{
glViewport(0, 0, width, height);//设置视区(窗口内绘图区域)
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, width, 0, height);//设置图形数据范围
// glMatrixMode(GL_MODELVIEW);
// glLoadIdentity();
}
int flag = 0, flagX = 0, flagY = 0;
void change()
{
alpha = 0.2f;
if (alpha > 360) alpha -= 360;
// 放缩判断
if (flag == 0) {
Scale = 0.002;
if (Scale > 2.5) {
flag = 1;
}
} else if (flag == 1) {
Scale -= 0.002;
if (Scale < 0.2) {
flag = 0;
}
}
// X取值范围判断 (x1, x2)
// x1 = x - R
// x2 = x R
if (flagX == 0) {
x = 0.2f;
if (x 150 >= 900) {
flagX = 1;
}
}
else if (flagX == 1) {
x -= 0.2f;
if (x 150 <= 100) {
flagX = 0;
}
}
// Y值取值范围判断 (y1, y2)
// y1 = y - R
// y2 = y R
if (flagY == 0) {
y = 0.2f;
if (y 300 >= 600) {
flagY = 1;
}
}else if (flagY == 1) {
y -= 0.2f;
if (y 300 <= 100) {
flagY = 0;
}
}
glutPostRedisplay();
}
void keyboard(unsigned char key, int x, int y)
{
}
int main(int argc, char* argv[])
{
glutInitWindowPosition(200, 200);//应用程序窗口位置
glutInitWindowSize(800, 600);//窗口大小
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);//双缓冲,单缓冲为GLUT_SINGLE
glutCreateWindow("Project Two");//创建窗口,参数为窗口标题
init();
glutDisplayFunc(display);//图形绘制
glutReshapeFunc(reshape);//窗口大小变化
glutKeyboardFunc(keyboard);//键盘交互
glutIdleFunc(change);//键盘交互
glutMainLoop();//必须,进入GLUT事件处理循环
return 0;
}
效果展示
实验三 & 实验四
要求
- 构建一个丰富的三维场景,包括立方体、球体、圆柱体;
- 为它们添加合适的纹理;
- 在场景中添加光源;
- 可以使用按键控制你视角的移动及光源的移动。
代码
ProjectFour.cpp
代码语言:javascript复制// OpenGLOld.cpp : 定义控制台应用程序的入口点。
//
#include <Windows.h>
#include <GL/glew.h>//OpenGL库
#include <GL/glut.h>//OpenGL辅助库
#include <math.h>
#include "SOIL/SOIL.h"
#include "cgSphere.h"
#include "cgCylinder.h"
#include "cgCube.h"
#include "cgQuad.h"
#include "Bitmap.h"
#pragma comment( linker, "/subsystem:"windows" /entry:"mainCRTStartup"" ) //这句是不让控制台窗体出现,如果想要出现,去掉即可。
float alpha=0.0f;
float pos[]={0.0,1.5,0.0};
float headdir[]={0.0f,0.0f,-1.0f};
float rightdir[]={1.0f,0.0f,0.0f};
float step = 0.10f;
float beta = 180.0f;//与z轴正向夹角
float betastep = 1.0f;
cgSphere sphere, sun;
cgCylinder cylinder;
cgCube cube;
//光照变量
GLfloat light_position[] = { 0.0, 2.0, 1.0, 0.1 };
GLfloat light_color[] = { 1.0, 0.0, 0.0, 0.0 };
// 添加的光照
GLfloat sun_position[] = { -10.0, 5.0, 0.0, 0.0 };
GLfloat sun_color[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat aMaterial[] = { .20, 0.20, 0.20, 1.0 }; //环境光反射系数
GLfloat dMaterial[] = { .60, 0.60, 0.60, 1.0 }; //漫反射光反射系数
GLfloat sMaterial[] = { 0.4, 0.4,0.4, 1 }; //镜面反射光反射系数
GLfloat shiniess = 20; //高光指数
int rendermode = 2;//0:填充; 1:线框;2:纹理
GLuint textureID;
int frames=0;
GLuint LoadTexture(char* fname)
{
GLuint tID;
int width, height, nrComponents;
unsigned char *data;
data = SOIL_load_image(fname, &width, &height, &nrComponents,SOIL_LOAD_RGB);//
if (data)
{
GLenum internalFormat;
GLenum dataFormat;
bool gammaCorrection = false;
if (nrComponents == 1)
{
internalFormat = dataFormat = GL_RED;
}
else if (nrComponents == 3)
{
internalFormat = gammaCorrection ? GL_SRGB : GL_RGB;
dataFormat = GL_RGB;
}
else if (nrComponents == 4)
{
internalFormat = gammaCorrection ? GL_SRGB_ALPHA : GL_RGBA;
dataFormat = GL_RGBA;
}
//生成纹理
glGenTextures(1, &tID);
glBindTexture(GL_TEXTURE_2D, tID);
//glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);//这个函数居然不能用
gluBuild2DMipmaps(GL_TEXTURE_2D, internalFormat, width, height, dataFormat, GL_UNSIGNED_BYTE, data);
//glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
SOIL_free_image_data(data);
return tID;
}
return -1;
}
void init(void)
{
glClearColor(0.5, 1.0, 1.0, 1.0);//设置背景色白色
glEnable(GL_DEPTH_TEST);
/*GL_AMBIENT、GL_DIFFUSE、GL_SPECULAR属性。这三个属性与光源的三个对应属性类似,每一属性都由四个值组成。
GL_AMBIENT表示各种光线照射到该材质上,经过很多次反射后最终遗留在环境中的光线强度(颜色)。
GL_DIFFUSE表示光线照射到该材质上,经过漫反射后形成的光线强度(颜色)。
GL_SPECULAR表示光线照射到该材质上,经过镜面反射后形成的光线强度(颜色)。
通常,GL_AMBIENT和GL_DIFFUSE都取相同的值,可以达到比较真实的效果。
使用GL_AMBIENT_AND_DIFFUSE可以同时设置GL_AMBIENT和GL_DIFFUSE属性。*/
//光照
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glLightfv(GL_LIGHT0, GL_AMBIENT, light_color);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_color);
glLightfv(GL_LIGHT0, GL_SPECULAR, light_color);
glLightfv(GL_LIGHT1, GL_POSITION, sun_position);
glLightfv(GL_LIGHT1, GL_AMBIENT, sun_color);
glLightfv(GL_LIGHT1, GL_DIFFUSE, sun_color);
glLightfv(GL_LIGHT1, GL_SPECULAR, sun_color);
// void glMaterial{if}(GLenum face, GLenum pname, TYPE param);
// 参数pname的可能取值。
// GL_AMBIENT_AND_DIFFUSE让您能够同时设置材质的环境颜色和散射颜色,并将它们设置为相同的RGBA值。
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, aMaterial);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, dMaterial);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, sMaterial);
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &shiniess);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHT1);
///////////////////////////
//地球仪
sphere.InitData(1.0f);
sphere.SetPos(cgPoint3D(0.0f, 2.5f,-8.0f));
sphere.SetTexture(LoadTexture("Textures/earth2.jpg"));
// 太阳光照
sun.InitData(3.0f);
sun.SetPos(cgPoint3D(sun_position[0], sun_position[1], sun_position[2]));
sun.SetTexture(LoadTexture("Textures/sun.jpeg"));
// 正方体
cube.InitData(1.0f);
cube.SetPos(cgPoint3D(1.0f, 1.0f, -5.0f));
cube.SetTexture(LoadTexture("Textures/timg.bmp"));
// 圆柱体
cylinder.InitData(1.0f, 5.0f);
cylinder.SetPos(cgPoint3D(3.0f, 4.0f, -3.0f));
cylinder.SetTextureID(LoadTexture("Textures/desert.bmp"));
//地面纹理
textureID = LoadTexture("Textures/timg.jpg");
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT );//清除帧缓存和深度缓存
//模型观察矩阵初始化
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
float at[3];
for (int i=0; i<3; i )
at[i] = pos[i] headdir[i];
gluLookAt(pos[0], pos[1], pos[2], at[0], at[1], at[2], 0.0, 1.0, 0.0);
if (rendermode==1)
glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
else
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
//地面
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureID);
glColor3f(1.0f,1.0f,1.0f);
glBegin(GL_QUADS);
glTexCoord2f(0.0f,0.0f);//指定顶点纹理坐标
glNormal3f(0,1,0);//指定顶点法向量
glVertex3f(-50.0f,0.0f,50.0f);//指定顶点坐标
glTexCoord2f(10.0f,0.0f);
glNormal3f(0,1,0);
glVertex3f(50.0f,0.0f,50.0f);
glTexCoord2f(10.0f,10.0f);
glNormal3f(0,1,0);
glVertex3f(50.0f,0.0f,-50.0f);
glTexCoord2f(0.0f,10.0f);
glNormal3f(0,1,0);
glVertex3f(-50.0f,0.0f,-50.0f);
glEnd();
glDisable(GL_TEXTURE_2D);
glColor3f(0.25,0.5,0.5);
sphere.Render();
glColor3f(1.0,0.0,0.0);
cube.Render();
sun.Render();
cylinder.Render();
glutSwapBuffers();//交换缓冲(双缓冲时使用)
}
void reshape(int width, int height)
{
glViewport(0, 0, width, height);//设置视区(窗口内绘图区域)
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60,(GLfloat) width/(GLfloat)height, 1.0, 200.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void change()
{
glutPostRedisplay();
}
void keyboard(unsigned char key, int x, int y)
{
switch (key)
{
case 'W': //上移
case 'w':
for (int i=0; i<3; i )
pos[i] = step*headdir[i];
break;
case 'S': //下移
case 's':
for (int i=0; i<3; i )
pos[i] -= step*headdir[i];
break;
case 'A': //左移
case 'a':
for (int i=0; i<3; i )
pos[i] -= step*rightdir[i];
break;
case 'D': //右移
case 'd':
for (int i=0; i<3; i )
pos[i] = step*rightdir[i];
break;
case 'Z':
case 'z':
//抬高相机
pos[1] = .30f;
break;
case 'X':
case 'x':
//降低相机
pos[1] -= .30f;
break;
case 'T': //修改绘制模式
case 't':
rendermode = ( rendermode)%2;
break;
// 光源移动
case '1':
sun_position[0] = 1.0f;
glLightfv(GL_LIGHT1, GL_POSITION, sun_position);
sun.SetPos(cgPoint3D(sun_position[0], sun_position[1], sun_position[2]));
break;
case '2':
sun_position[0] -= 1.0f;
glLightfv(GL_LIGHT1, GL_POSITION, sun_position);
sun.SetPos(cgPoint3D(sun_position[0], sun_position[1], sun_position[2]));
break;
case '3':
sun_position[1] = 1.0f;
glLightfv(GL_LIGHT1, GL_POSITION, sun_position);
sun.SetPos(cgPoint3D(sun_position[0], sun_position[1], sun_position[2]));
break;
case '4':
sun_position[1] -= 1.0f;
glLightfv(GL_LIGHT1, GL_POSITION, sun_position);
sun.SetPos(cgPoint3D(sun_position[0], sun_position[1], sun_position[2]));
break;
case '5':
glEnable(GL_LIGHT1);
break;
case '6':
glDisable(GL_LIGHT1);
break;
case '7':
glEnable(GL_LIGHT0);
break;
case '8':
glDisable(GL_LIGHT0);
break;
case 'n':
case 'N':
cube.UpdateTexCord();
break;
}
glutPostRedisplay();
}
void SpecialKey(GLint key,GLint x,GLint y)
{
switch (key)
{
case GLUT_KEY_UP:
headdir[1] = 0.05f;
break;
case GLUT_KEY_DOWN:
headdir[1] -= 0.05f;
break;
case GLUT_KEY_LEFT:
//修改前进方向
beta = betastep;
headdir[0] = sin(beta / 180 * 3.14);
headdir[2] = cos(beta / 180 * 3.14);
headdir[1] = headdir[1];
//修改右向方向
rightdir[0] = -cos(beta / 180 * 3.14);
rightdir[2] = sin(beta / 180 * 3.14);
rightdir[1] = rightdir[1];
break;
case GLUT_KEY_RIGHT:
//修改前进方向
beta -= betastep;
headdir[0] = sin(beta / 180 * 3.14);
headdir[2] = cos(beta / 180 * 3.14);
headdir[1] = headdir[1];
//修改右向方向
rightdir[0] = -cos(beta / 180 * 3.14);;
rightdir[2] = sin(beta / 180 * 3.14);;
rightdir[1] = rightdir[1];
break;
}
glutPostRedisplay();
}
int main(int argc, char* argv[])
{
glutInitWindowPosition(200, 200);//应用程序窗口位置
glutInitWindowSize(800, 600);//窗口大小
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE| GLUT_DEPTH );//双缓冲,单缓冲为GLUT_SINGLE,深度缓存
glutCreateWindow("Project Four");//创建窗口,参数为窗口标题
init();
glutDisplayFunc(display);//图形绘制
glutReshapeFunc(reshape);//窗口大小变化
glutKeyboardFunc(keyboard);//键盘交互
glutSpecialFunc(&SpecialKey);//方向键
glutIdleFunc(change);//空闲时间执行
glutMainLoop();//必须,进入GLUT事件处理循环
return 0;
}