实验1、OpenGL初步
学习红宝书思考:
(1)回调函数是什么意思?GLUT中有哪些回调函数?
(2)用鼠标改变窗口大小会发生什么?哪个函数在影响整个过程?
(3)试着画画其他图形?
(1)回调函数是什么意思?GLUT中有哪些回调函数?
回调函数:
当指定的事件发生时,这些函数就会被调用
开发人员所要做的仅仅是编写一些定义了程序对具体时间响应方式的回调函数.
GLUT中的回调函数:
void glutDisplayFunc(void (*func)(void));
注册当前窗口的显示回调函数
参数:
func:形为void func()的函数,完成具体的绘制操作
这个函数告诉GLUT当窗口内容必须被绘制时,那个函数将被调用.当窗口改变大小或者从被覆盖的状态中恢复,或者由于调用glutPostRedisplay()函数要求GLUT更新时,执行func参数指定的函数.
例如:
//*******************************************************************************
void glutReshapeFunc(void (*func)(int width, int height));
指定当窗口的大小改变时调用的函数
参数:
func:形如void func(int width, int height)
处理窗口大小改变的函数.
width,height:为窗口改变后长宽.
这个函数确定一个回调函数,每当窗口的大小或形状改变时(包括窗口第一次创建),GLUT将会调用这个函数,这个回调函数接受这个窗口新的长宽作为输入参数.
//*********************************************************************************
void glutKeyboardFunc(void (*func)(unsigned char key, int x, int y));
注册当前窗口的键盘回调函数
参数:
func:形如void func(unsigned char key, int x, int y)
key:按键的ASCII码
x,y:当按下键时鼠标的坐标,相对于窗口左上角,以像素为单位
当敲击键盘按键(除了特殊按键,即glutSpecialFunc()中处理的按键,详见glutSpecialFunc())时调用.
//******************************************************************************
void glutMouseFunc(void (*func)(int button, int state, int x, int y));
注册当前窗口的鼠标回调函数
参数:
func:形如void func(int button, int state, int x, int y);
button:鼠标的按键,为以下定义的常量
GLUT_LEFT_BUTTON:鼠标左键
GLUT_MIDDLE_BUTTON:鼠标中键
GLUT_RIGHT_BUTTON:鼠标右键
state:鼠标按键的动作,为以下定义的常量
GLUT_UP:鼠标释放
GLUT_DOWN:鼠标按下
x,y:鼠标按下式,光标相对于窗口左上角的位置
当点击鼠标时调用.
(2)用鼠标改变窗口大小会发生什么?哪个函数在影响整个过程?
如果没有gluOrtho2D函数和glViewport函数,那么由于像glVertex3f(0.29, 0.3, 0.6);
glVertex3f(0.19, 0.4, 0.7);表示的是这个点位于窗口的什么位置,是比例关系,因此用鼠标拖动窗体口时候,就会一直变化,物体会被拉伸, 为了保持一样的比例.
- gluOrtho2D
而如果有gluOrtho2D函数: void gluOrtho2D(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top)左下角坐标为(left,bottom),右上角为(right,top)
例如:设置gluOrtho2D(0,500,500,0);
原图
如果向右拖动,那么会改变right,即宽,所以物体会跟着变化,同理拖动别的也会改变某个数值,因此会改变他的大小或形状.
glOrtho是创建一个正交平行的视景体。一个物体的大小不随它离屏幕的距离而改变的情况. 工程制图等需要比较精确的显示。简单理解起来,就是一个物体摆在那里,怎么去截取他, 就是一个摄像机怎么去拍他. 假设有一个物体, 用一个框正对着中心去截取他可能他就会出现在整个屏幕里, 把框向左移动,可能就只有物体的左半部分出现,和平时拍照是一个道理.
- glViewport
void glViewport(GLine x, GLine y, GLsizei w, GLsizei h)
将窗口的宽度设置为w, 高度为h, 左下角顶点坐标是(x,y),单位均为像素.默认情况下,视口为整个初始窗口. 因此如果拖动窗口,视口以屏幕为基准 而屏幕已经固定了.
例如:设置:
void shikou(int x, int y)//x代表屏幕长度 y代表高
{
int dis = x > y ? y : x;//m取小的
glViewport(0, 0, dis, dis);/*
0,0是指视口左下角的坐标,后两个width,heigh表示视口的宽高
这里选择大小改变后的窗口较小的边,作为新的视口的边长
*/
//在没设定视口的时候 只是设定了屏幕有多大,
//画的时候glRectf(-0.1,-0.1,0.1,0.1)是定了按照窗口的比例多少来画
//视口以屏幕为基准 而屏幕已经固定了
}
主函数中:glutReshapeFunc(shikou);
此时原图:
向右拖动:图形比例不变,这里选择大小改变后的窗口较小的边,作为新的视口的边长
往上拖,图形变小,因为视口的长宽改变
它负责把视景体截取的图像按照怎样的高和宽显示到屏幕上,
我的理解是,glOrtho是相机,拍摄一个照片,决定各个图形的哪些部分屏幕中会出现,
而glViewport是将照片画布(假设是弹性的)以怎样的方式拉伸,最终呈现在用户面前.
无论在刚打开的时候,还是在移动窗口或者改变窗口大小的时候,窗口系统都会发送一个事件作为通知,. 如果使用的是GLUT,他会自动的产生通知,在glutReshapeFunc()中注册的那个函数会被调用. 需要注册一个回调函数,完成:1)重新建立一个矩形区域,把它作为新的渲染画布. 2)定义一个用于绘制物体的坐标系统.
- 试着画画其他图形?
//---连起来的三角形
glColor3f(0.5, 0.0, 0.0);
glBegin(GL_TRIANGLE_STRIP);
glVertex2f(0.2 + 0.3, 0.1 + 0.3);
glVertex2f(0.3 + 0.3, 0.2 + 0.3);
glVertex2f(0.4 + 0.3, 0.1 + 0.3);
glVertex2f(0.5 + 0.3, 0.1 + 0.3);
glVertex2f(0.6 + 0.3, 0.3 + 0.3);
glVertex2f(0.7 + 0.3, 0.1 + 0.3);
glEnd();
//------GL_TRIANGLE_FAN----
glColor3f(0.5, 0.25, 0.0);
glBegin(GL_TRIANGLE_FAN);
glVertex2f(0.2, 0.1);
glVertex2f(0.3, 0.2);
glVertex2f(0.4, 0.1);
glVertex2f(0.5, 0.1);
glVertex2f(0.6, 0.3);
glVertex2f(0.7, 0.1);
glEnd();
//---四边形面---------
glColor3f(0.5, 0.9, 0.6);
glBegin(GL_QUADS);
glVertex2f(0.4 + 0.2, 0.1);
glVertex2f(0.5 + 0.2, 0.1);
glVertex2f(0.6 + 0.2, 0.3);
glVertex2f(0.7 + 0.2, 0.1 + 0.2);
glEnd();
//---多边形面
glColor4f(0.6, 0.8, 0.4, 0.5);
glBegin(GL_POLYGON);
glVertex2f(0.2 - 0.2, 0.1 - 0.3);
glVertex2f(0.6 - 0.2, 0.3 - 0.2);
glVertex2f(0.7 - 0.2 - 0.2, 0.1 + 0.2 + 0.2);
glVertex2f(0.3, 0.2 - 0.2);
glVertex2f(0.4 - 0.2, 0.1);
glVertex2f(0.5 - 0.2 - 0.2, 0.1 + 0.2);
glVertex2f(0.2, 0.1);
glVertex2f(0.5 - 0.2 - 0.6, 0.1 + 0.2);
glEnd();
//加了光源的球
//设置灯光
GLfloat am[] = { 0.8,0.8,0.8,1.0 };//环境光
GLfloat di[] = { 0.8,0.0,0.8,1.0 };//紫色//反射光
GLfloat sp[] = { 1.0,0.0,1.0,1.0 };//;亮紫色
//GLfloat po[] = { 0.6,0.5,0.1,0.0 };//位置 光源
GLfloat po[] = { 1.0,1.0,1.0,0.0 };//位置 光源
glLightfv(GL_LIGHT0,GL_AMBIENT,am);//用指针就加v不用就不加
glLightfv(GL_LIGHT0,GL_DIFFUSE,di);
glLightfv(GL_LIGHT0,GL_SPECULAR,sp);
glLightfv(GL_LIGHT0, GL_POSITION, po);
glEnable(GL_LIGHTING);//开灯
glEnable(GL_LIGHT0);
glDepthFunc(GL_LESS);//深度缓存
glEnable(GL_DEPTH_TEST);
//设置材质
//反射系数 设置材质的各种光的颜色成分反射比率
GLfloat mat_ambient[] = { 0.3,0.3,0.3,1.0 };
GLfloat mat_di[] = { 0.7,0.7,0.7,1.0 };//紫色
GLfloat mat_sp[] = { 0.5,0.5,0.5,1.0};//;亮紫色
GLfloat mat_shininess[] = { 50.0 };//天哪这个没有加会变成没有过渡的样子
//如果没有设置光的颜色 则默认为白光
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_di);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_sp);
glMaterialfv(GL_FRONT,GL_SHININESS, mat_shininess);
glClearColor(0.1, 0, 0.1, 0.5);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);//*/*/*
glutSolidSphere(1.0, 200.0, 200.0);//半径,经线,纬线
//线框四面体
glutWireTetrahedron();//线框四面体
glutWireCube(0.2);//线框正方体
glutWireIcosahedron();//线框二十面体
glutWireSphere(0.8, 8, 6);//线框球体(半径,经线,纬线)
glutWireCone(0.8, 0.9,8, 6);//线框圆锥面(底面半径,高,经线,纬线)
glutWireTetrahedron();//线框四面体
- Task3. 绘制一个实心的圆。要求圆分成若干个扇形,每个扇形颜色不同。例如下图:
我画这个圆的思路是生成无数个顶点在同一点的三角形, 无数个就近似一个扇形.七个扇形形成一个多色圆盘.
```
代码:
#include<GL/glut.h>
#include<Windows.h>
#include<iostream>
using namespace std;
GLsizei WIDTH = 500;//屏幕宽度
GLsizei HEIGHT = 500;//屏幕高度
//GLdouble ZHANBI[7] = { 10,10,10,10,10,10,10 };//比例相同 均匀圆盘
//GLfloat ZHANBI[7] = { 20,33,40,40,30,20,45 };//占比 比例不相同 不均匀圆盘
GLfloat ZHANBI[7] = {60, 55,50,45,40,35,30 };//占比 比例不相同 不均匀圆盘
//GLfloat ZHANBI[7] = {60,40,50,45,35, 55,30 };//占比 比例不相同 不均匀圆盘
//GLfloat ZHANBI[7] = {60,20,50,27,35, 55,20 };//占比 比例不相同 不均匀圆盘
const GLdouble PI = 3.1415926;
class pOINT//表示点的类,有点的x,y
{
public:
GLint x, y;
};
void init(void)
{
glClearColor(1.0, 1.0, 1.0, 1.0);//背景色
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 600.0, 0.0, 600.0);
}
void paintCircle()
{
pOINT center;
GLdouble jd[7], jjd[8], sum = 0.0;//jd存储(单个扇形)角度,jjd存储累加角度
GLint i, n = 7, r = WIDTH / 4;//半径是屏幕宽度的四分之一
center.x = WIDTH / 2;//原点是屏幕宽度的而分之一二
center.y = HEIGHT / 2;
for (i = 0; i < n; i++)
{
sum += ZHANBI[i];//所有的圆盘占比的总和
}
GLdouble du, tempdu = 0.0;
//生成关键点的(x,y) 坐标
for (i = 0; i < n; i++)
{
du = 2 * PI*ZHANBI[i] / sum + tempdu;
jjd[i] = du;//每个扇形累加后的角度
jd[i] = 2 * PI*ZHANBI[i] / sum;//单个扇形的角度
//jjd[7] = jjd[0];
tempdu = du;
}
//开始画扇形
//形成第一个扇形
glColor3f(0.6, 0.4, 0.5);
glBegin(GL_TRIANGLE_FAN);//画无数个小小扇形
glVertex2f(center.x, center.y);//确定顶点
//生成无数个顶点在同一点的三角形,无数个就近似一个扇形
GLdouble j;
for (j = 0; j < 1000000; j++)
{
//角度t
GLdouble t = j * jjd[0] / 1000000;
glVertex2f(center.x - r * cos(t), center.y + r * sin(t));
}
glEnd();
//形成另外六个扇形
for (i = 1; i < 7; i++)
{
//确定颜色用的
GLfloat a, b, c = 0.0;
if (i == 1)
{
a = 0.7;
b = 0.6;
c = 0.7;
}
if (i == 2)
{
a = 0.5;
b = 0.4;
c = 0.7;
}
if (i == 3)
{
a = 0.3;
b = 0.2;
c = 0.6;
}
if (i == 4)
{
a = 0.3;
b = 0.2;
c = 0.4;
}
if (i == 5)
{
a = 0.4;
b = 0.2;
c = 0.215;
}
if (i == 6)
{
a = 0.5;
b = 0.2;
c = 0.3;
}
glColor3f(a, b, c);
glBegin(GL_TRIANGLE_FAN);
glVertex2f(center.x, center.y);
for (j = 0; j < 1000000; j++)
{
//角度t
GLdouble t = jjd[i - 1] + j * jd[i] / 1000000;
glVertex2f(center.x - r * cos(t), center.y + r * sin(t));
}
glEnd();
}
}
void dispaly()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0, 0.0, 0.0);
paintCircle();
glFlush();
}
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowPosition(300, 150);
glutInitWindowSize(WIDTH, HEIGHT);
glutCreateWindow("多色扇形组成的圆盘");
init();
glutDisplayFunc(dispaly);
glutMainLoop();
return 0;
}
```
部分逻辑示意图:
实验结果:
均匀的多色圆盘(7色):
自定义占比的不均匀多色圆盘: