原理
reshape函数细节
改变窗口形状,所绘制的物体不变形,而只是大小发生变化
void reshape (int w, int h)
{
glviewport (0, 0, (glsizei) w, (glsizei) h);
glmatrixmode (gl_projection);
glloadidentity ();
if (w <= h)
gluortho2d (0.0, 30.0, 0.0, 30.0 * (glfloat) h/(glfloat) w);
else
gluortho2d (0.0, 30.0 * (glfloat) w/(glfloat) h, 0.0, 30.0);
glmatrixmode(gl_modelview);
}
看这个reshape函数当窗口发生变化,窗口的w(宽度)和h(高度)参数传给reshape函数,
glviewport (0, 0, (glsizei) w, (glsizei) h);
把视口设置为铺满整个窗口, if (w <= h)
gluortho2d (0.0, 30.0, 0.0, 30.0 * (glfloat) h/(glfloat) w);
else
gluortho2d (0.0, 30.0 * (glfloat) w/(glfloat) h, 0.0, 30.0);
把投影得到的视景体按照视口变化的比例变化视景体的比例;简单地说,本来视景体宽高比是1:1,视口宽高比也是1:1,这时绘制了一个物体。然后因为窗口发生变化,视口也变化,假如变成了2:1;这时如果用1:1的视景体放到2:1的视口上肯定会变形,所以要把视景体变成2:1,这样,虽然物体看起来变大(或变小)了,但长宽比例不变,也就看起来不变形了。注意这个参数(glfloat) w/(glfloat) h。
原文链接:
glMatrixMode()函数
这两个都是glMatrixMode()函数的参数,那就先说说glMatrixMode吧~,这个函数其实就是对接下来要做什么进行一下声明,也就是在要做下一步之前告诉计算机我要对“什么”进行操作了,这个“什么”在glMatrixMode的“()”里的选项(参数)有,GL_PROJECTION,GL_MODELVIEW和GL_TEXTURE;
如果参数是GL_PROJECTION,这个是投影的意思,就是要对投影相关进行操作,也就是把物体投影到一个平面上,就像我们照相一样,把3维物体投到2维的平面上。这样,接下来的语句可以是跟透视相关的函数,比如glFrustum()或gluPerspective();
如果参数是GL_MODELVIEW,这个是对模型视景的操作,接下来的语句描绘一个以模型为基础的适应,这样来设置参数,接下来用到的就是像gluLookAt()这样的函数;
若是GL_TEXTURE,就是对纹理相关进行操作;
顺便说下,OpenGL里面的操作,很多是基于对矩阵的操作的,比如位移,旋转,缩放,所以,这里其实说的规范一点就是glMatrixMode是用来指定哪一个矩阵是当前矩阵,而它的参数代表要操作的目标,GL_PROJECTION是对投影矩阵操作,GL_MODELVIEW是对模型视景矩阵操作,GL_TEXTURE是对纹理矩阵进行随后的操作
OpenGL常用函数介绍
需要包含头文件#include<GL/glut.h>,这是GLUT的头文件。
注意main函数中的各语句,除了最后的return之外,其余全部以glut开头。这种以glut开头的函数都是GLUT工具包所提供的函数,下面对用到的几个函数进行介绍。
1、glutInit,对GLUT进行初始化。其格式比较死板,照抄glutInit(&argc, argv)就可以。
2、glutInitDisplayMode,设置显示方式,其中GLUT_RGB表示使用RGB颜色,与之对应的还有GLUT_INDEX(表示使用索引颜色)。GLUT_SINGLE表示使用单缓冲,与之对应的还有GLUT_DOUBLE(使用双缓冲)。
3、glutInitWindowPosition,设置窗口在屏幕中的位置。
4、glutInitWindowSize,设置窗口的大小。
5、glutCreateWindow,创建窗口。参数将被作为窗口的标题。注意:窗口被创建后,并不立即显示到屏幕上,需要调用glutMainLoop才能看到窗口。
6、glutDisplayFunc,设置一个函数,当需要进行画图时,这个函数就会被调用。
7、glutMainLoop,进行一个消息循环。
以gl开头的函数都是OpenGL的标准函数。
1、glClear,清除。
2、glBegin与glEnd是一对,中间写的是要画的内容。
3、glFlush,保证前面的OpenGL命令立即执行(而不是让它们在缓冲区中等待)。
原文链接:
OpenGL框架实现
窗口初始化
glutInit(&argc, argv); // 初始化glut窗口
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // 设定显示窗口的缓存和颜色模型
glutInitWindowPosition(50, 100); // 决定窗口左上角离屏幕左上角的位置
glutInitWindowSize(400, 300); // 设定窗口大小
glutCreateWindow("The Hello World Window!"); // 窗口名
glutInit(int *argc, char **argv);
glut初始化API
void glutInit(int *argc, char *argv);
这个函数用来初始化 GLUT 库.这个函数从 main 函数获取其两个参数.对应main 函数的形式应是:int main(int argc,char argv[]);
在这个部分我们将在我们的程序里建立一个main函数,这个main函数将完成必须的初始化和开启事件处理循环。所有的GLUT函数都有glut前缀并且那些完成一些初始化的函数有glutInit前缀。你首先要做的是调用函数glutInit()。
Void glutInit(int*argc,char**argv);
参数:
Argc:一个指针,指向从main()函数传递过来的没更改的argc变量。
Argv:一个指针,指向从main()函数传递过来的没更改的argv变量。
用GetSystemMetrics获取屏幕的宽度和高度
int x, y;
x = GetSystemMetrics(SM_CXSCREEN); //屏幕宽度
y = GetSystemMetrics(SM_CYSCREEN); //屏幕高度
glClearColor,glShadeModel
glClearColor ( )
就是用来设置这个 “ 底色 ” 的,即所谓的背景颜色。glClearColor ( ) 只起到Set 的作用,并不Clear 任何。
void glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
//红、绿、蓝和 alpha 值,指定值范围均为[ 0.0f,1.0f ]
glShadeModel ( )
函数用于控制 opengl 中绘制指定两点间其他点颜色的过渡模式。
参数一般为 GL_SMOOLH ( 默认 ) 或 GL_FLAT。
如果两点的颜色相同,则使用这两个参数效果相同;
如果两点颜色不同,GL_SMOOLH 会出现过渡效果; GL_FLAT 则以指定的某一点的单一色绘制其他所有点
鼠标交互
要将鼠标有关的数据发送给应用程序,则需要将鼠标事件注册到glutMouseFunc(myMouse)。其中myMouse(int button, int state, int x,int y);
函数名可以自己制定,在回调函数glutMouseFunc()中替换成相应的函数头就好。
但是其中的四个参数是不可缺少的:
button:GLUT_LEFT_BUTTON,GLUT_MIDDLE_BUTTON,GLUT_RIGHT_BUTTON,分别指代鼠标左键,中间键(鼠标滚轮),右键;
state:鼠标的状态GLUT_UP,GLUT_DOWN;
x和y指明鼠标在窗口中的位置(x为鼠标距离窗口左边的像素数,y为鼠标距离窗口顶端的像素数,在计算是为(x,screenLength-y)。)
glutpostredisplay,mianloop用法理解
glutPostRedisplay 标记当前窗口需要重新绘制。通过glutMainLoop下一次循环时,窗口显示将被回调以重新显示窗口的正常面板。多次调用glutPostRedisplay,在下一个显示回调只产生单一的重新显示回调。
为了便于理解,用下面的例子来说明:
假设有绘制代码:
glutDisplayFunc(display); // opengl drawing goes here
glutTimerFunc(30, drive, -1); // physics, movement equations here
glutMainLoop();
另外有代码如下:
void drive (int data)
{
glutTimerFunc(30, drive, -1);// call drive() again in 30 milliseconds
/callback function moves the car. … move x, y, z, etc
前面的代码不用理解,就是移动了物体,绘制的画面改变了/
glutPostRedisplay();
}
如果有glutpostredisplay,mianloop运行的过程会像下面这样:
drive (-1);
display();
drive (-1);
display();
drive (-1);
display();
…
如果没有glutpostredisplay,mianloop运行的过程会像下面这样:
drive (-1);
drive (-1);
drive (-1);
…
Bezier部分代码解释
3次Beizer曲线绘制代码
#include<glut.h>
#include<math.h>
#include<windows.h>
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
#define N 1000 //绘制Bezier曲线的点数设置为1000
struct Point
{
int x, y;
Point(){};
Point(int tx, int ty)
{
x = tx;
y = ty;
}
};
vector<Point> p;//下标从0开始
double getRatio(double t,double a,double b,double c,double d)
{
return a * pow(t, 3) + b * pow(t, 2) + c * t + d;
}
void Bezier()
//画Bezier曲线
{
double derta = 1.0 / N;
glPointSize(2);//设置点的大小为2
glColor3d(0, 0, 1);//设置Bezier曲线(点)颜色为蓝 RGB(0,0,255)
glBegin(GL_POINTS);//开始画点
for (int i = 1; i <= N; i++)
{
double t = derta * i;
double ratio[4];
ratio[0] = getRatio(t, -1, 3, -3, 1);
ratio[1] = getRatio(t, 3, -6, 3, 0);
ratio[2] = getRatio(t, -3, 3, 0, 0);
ratio[3] = getRatio(t, 1, 0, 0, 0);
double x=0, y=0;
for (int j = 0; j < 4; j++)
{
x += ratio[j] * p[j].x;
y += ratio[j] * p[j].y;
}
glVertex2d(x, y);//画点
}
glEnd();//结束画点
}
void myDisplay()
//画鼠标 自选点和折线
{
glClear(GL_COLOR_BUFFER_BIT); //清除颜色缓存和深度缓存
//画点
glPointSize(5);
glColor3d(1, 0, 0);//设置颜色为红 RGB(255,0,0)
glBegin(GL_POINTS);
for (int i = 0; i < p.size(); i++)
glVertex2d(p[i].x, p[i].y);
glEnd();
//画线
glLineWidth(2);//设置线的宽度为2
glColor3d(0, 0, 0);//设置线段颜色为黑色
glBegin(GL_LINE_STRIP); //开始画线
for (int m = 0; m < p.size(); m++)//这里防止i变量重复定义,把变量改为m
glVertex2d(p[m].x, p[m].y);
glEnd();//结束画线
if (p.size() == 4)//已经选了4个点了,开始画Bezier曲线
Bezier();
glFlush();//清空缓冲区,将指令送往缓硬件立即执行
}
void mouse(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN && p.size() < 4)//按下左键,且次数小于4次
{
Point t(x, y);
p.push_back(t); //记录鼠标按下位置
glutPostRedisplay();//标记当前窗口需要重新绘制
}
}
void Reshape(int w, int h) //当窗口发生变化,窗口的w(宽度)和h(高度)参数传给reshape函数,
{
glViewport(0, 0, w, h); //width,height 指定视口矩形的宽度和高度.
glMatrixMode(GL_PROJECTION);//指定哪一个矩阵是当前矩阵,GL_PROJECTION是对投影矩阵操作
glLoadIdentity(); //重置当前指定的矩阵为单位矩阵。
gluOrtho2D(0, w, h, 0); //将当前的可视空间设置为正投影空间。
glMatrixMode(GL_MODELVIEW); //GL_MODELVIEW是对模型视景矩阵操作
glLoadIdentity();
}
void initWindow(int &argc, char *argv[], int width, int height, char *title) //初始化并显示到屏幕中央
{
glutInit(&argc, argv); // 初始化glut窗口
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // 设定显示窗口的缓存和颜色模型
glutInitWindowPosition((GetSystemMetrics(SM_CXSCREEN) - width) >> 1, (GetSystemMetrics(SM_CYSCREEN) - height) >> 1);
//GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN)是获取显示设备的分辨率,分别为屏幕宽度,屏幕高度
// 决定窗口左上角离屏幕左上角的位置
glutInitWindowSize(width, height); //指定窗口大小
glutCreateWindow(title); // 窗口名
glClearColor(1, 1, 1, 0); //设置背景色为白色
glShadeModel(GL_FLAT); //设置指定两点间其他点颜色的过渡模式。GL_FLAT以指定的某一点的单一色绘制其他所有点
}
int main(int argc, char *argv[])
{
initWindow(argc, argv, 600, 600, "三次Bezier曲线");
puts("\n\t用鼠标在窗口选择四个点来绘制三次Bezier曲线");
glutDisplayFunc(myDisplay);
glutReshapeFunc(Reshape);
glutMouseFunc(mouse);
glutMainLoop();
return 0;
}