openGL(一)
由于近期需要参加个比赛,需要一个demo,主题框架使用openGL来搭,刚好来学习下openGL,参考的工程为来自CMU,但是其使用的openGL都是老古董了,准备使用最近的翻修下。
PS:openGL现在都更新到4.5了,而且不能向下兼容。因此根据oprnGL3.3 tutorail 来进行学习。没有使用其中的窗口库(GLFW),使用FLTK的窗口建立(自我感觉更好用)。
首先是显示窗口的搭建
主要思想就是先封装窗口,包括菜单、显示窗口等,然后是界面操作,包括按键、鼠标等操作,最后就是建立简单的世界。
- 主界面
- 菜单
- 键盘、鼠标操作
- 世界设置(世界坐标系,地面等)
主界面
就是显示一个窗口界面,十分简单
Fl_Window *form = NULL; // Global form
form = make_window();
form->show();
就是定义一个窗口,建造一个窗口,然后显示。其中,make_window函数构造为:
Fl_Window * make_window()
{
Fl_Window * w;
//main window
{
Fl_Window * o = main_window = new Fl_Window(741, 622, "Motion Player");
w = o;
//add other components
{
}
o->end();
}
return w;
}
结果如下:
菜单
接着就是往窗口内添加菜单按钮了。
就是在make_window() 函数中添加其他组件:
部分代码如下:
//单选按钮
{
Fl_Light_Button * o = record_button = new Fl_Light_Button(380, 575, 40, 25, "R");
o->callback((Fl_Callback *)record_callback, (void*)(0));
}
//开始按钮
{
Fl_Button * o = play_button = new Fl_Button(500, 575, 35, 25, "@>");
o->labeltype(FL_SYMBOL_LABEL);
o->labelsize(12);
o->callback((Fl_Callback *)play_callback, (void*)(0));
}
注意:由于绘制块需要连续绘制画面,需要定义绘制函数,因此我们定义一个类来继承Fl_Gl_Window类(一个与openGL绘制的类)。需要重写绘制和窗口调用函数:
class Draw_Gl_Window : public Fl_Gl_Window
{
public:
//使用父类构造器
inline Draw_Gl_Window(int x, int y, int w, int h, const char *l = 0) :
Fl_Gl_Window(x, y, w, h, l){};
/*重载父类函数*/
//绘制函数
void draw();
//窗口调用函数,当空间发生事件时被Anim_Gl_Window调用,一般为键盘和鼠标事件
int handle(int event);
}
最后结果如下:
键盘鼠标操作
键盘和鼠标操作需要在继承Fl_Gl_Window类中的handle() 方法操作,用于获取和处理键盘、鼠标操作。其代码如下:
int Draw_Gl_Window::handle(int event)
{
int handled = 1;
//鼠标键盘事件(左键移动 右键选择 滚轴缩放)
switch (event)
{
//鼠标滚轴
case FL_MOUSEWHEEL:
break;
//鼠标释放(包括左右键)
case FL_RELEASE:
break;
//鼠标按下(包括左右键)button表示按的键 左键1 中间2 右键3
case FL_PUSH:
break;
//鼠标按下移动(包括左右键)
case FL_DRAG:
break;
default:
// 其他事件使用父类
break;
}
return handled;
}
世界坐标系
openGL绘制
首先需要绘制一个平面作为地面。
主要就是对GLEW的初始化,然后就是绘制。
初始化
// Initialize GLEW
if (glewInit() != GLEW_OK) {
fprintf(stderr, "Failed to initialize GLEW\n");
getchar();
exit(-1);
}
// Dark blue background
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
initGround(); // 初始化陆地
对GLEW首先初始化,然后初始化地面,代码主要如下:
//定义形状
static const GLfloat g_vertex_buffer_data[] = {
-0.8f, -0.8f, 0.0f,
...
};
//定义颜色
static const GLfloat g_color_buffer_data[] = {
1.0f, 0.0f, 0.0f,
...
};
// 定义第一个缓冲 加载形状
glGenBuffers(1, &vertexbuffer); //创建
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); //绑定
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW); //填充
glEnableVertexAttribArray(0); //设置定点属性
//定义第二个缓冲 加载颜色
glGenBuffers(1, &colorbuffer);
glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_color_buffer_data), g_color_buffer_data, GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,0, (void*)0);
//加载着色器
bool sv = shVertex.LoadShader("shader.vert", GL_VERTEX_SHADER);
bool sf = shFragment.LoadShader("shader.frag", GL_FRAGMENT_SHADER);
if (!sv || !sf)
{
printf("No File of shader!\n");
exit(-1);
}
//创建着色器
spMain.CreateProgram();
spMain.AddShaderToProgram(&shVertex);
spMain.AddShaderToProgram(&shFragment);
spMain.LinkProgram();
spMain.UseProgram();
openGL把图形位置和颜色都读入到缓冲中,然后使用着色器把颜色赋给图形。
最后只需要在绘制的回调函数中绘制图像:
//绘制陆地
void Draw_Gl_Window::drawGround(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
其中glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 根据不同的形状进行调整参数。
总结:
openGL中文学习这个是之前那文章的中文翻译,自我感觉讲的有些讲的不是很清楚,比如Shader(着色器),建议结合OpenGL 3.3 - Tutorials 一起看,最主要把Shader搞懂,这个是openGL重点。