首先是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.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;
}
纹理文件: