OpenGL Create Window

OpenGL 标准没有指定任何用于建立和使用窗口的 API。

glutInit(&argc,argv);

这个函数是为了初始化 GLUT。里面的参数可以直接从命令行中得到,同时可以包含其他有用的选项比如 '-sync' 和 '-gldebug',这样可以自动的检查GL的错误并独立的显示它们。

glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);

GLUT_DOUBLE 设置双缓冲(double buffering,即当一个 buffer 显示的时候,另一个 buffer 用来绘制)和 color buffer,后者是大多数渲染结束的地方(比如屏幕)。后面章节中我们经常用到这两个和其他的参数。

glutInitWindowSize(1024, 768);   

glutInitWindowPosition(100, 100); 

glutCreateWindow("Tutorial 01”);

glutDisplayFunc(RenderSceneCB);

因为我们工作在一个窗口系统中,可以通过事件回调函数与运行中的项目进行交互。GLUT 可以和基本的窗口系统进行交互,并且提供给我们一些回调函数。在这里我们仅仅使用了一个主回调函数,这个主回调函数完成了一帧中的所有渲染工作。这个函数被 GLUT 内部循环不断的调用。

glutMainLoop();

这个函数调用将控制传递给 GLUT,并且开启了它自己内部的循环。在这个循环中,它监听来自窗口系统的事件并通过我们设置的回调函数传递给 GLUT。

glClear(GL_COLOR_BUFFER_BIT);

glutSwapBuffers();

第一个函数的功能仅仅就是清除帧缓存(使用我们在上面指定过的颜色)。第二个函数调用是为了告诉 GLUT 在 backbuffer 和 frontbuffer 之间进行交换。在通过帧回调函数的下一个循环中,我们将场景渲染到当前的 frontbuffer 中,而 backbuffer 将被显示。

顶点缓冲区对象(VBOs)的使用。它们是被用来储存顶点数据的。加载顶点进入 GPU 最有效率的方法是 VBOs。它们是可以存储在显存中的缓冲区,使得 GPU 访问数据的速度最快。

渲染管线,现在在涉及到光栅器之前我们使用屏幕坐标系绘制点、线、和三角形,点的坐标用X,Y,Z表示并且他们的值都在[-1.0, 1.0]之间。光栅器将这些坐标映射到屏幕空间(比如,如果屏幕的宽度是1024,X 的坐标为-1.0,则 X 将映射到0;若 X 坐标为1.0,则将会被将映射到1023)。

最后,光栅器根据指定绘图命令(可以在下面的源代码解析中看到)的拓扑结构画出图元。因为我们没有绑定任何shader到管线中,所有顶点没有进行矩阵变化。这将意味着为了在屏幕中绘制出这些点我们仅仅需要传递给程序一些在上述范围之内的值就可以了。事实上,我们选取在屏幕中的零点作为x、y两条轴的中心点,换句话说,零点是在屏幕的正中心。

GLenum res =glewInit();

if (res != GLEW_OK) {

   fprintf(stderr, "Error: '%s'\n", glewGetErrorString(res));

   return 1;

}

Vector3f Vertices[1];

Vertices[0] =Vector3f(0.0f, 0.0f, 0.0f);

GLuint VBO;

我们分配一个全局变量 GLuint 储存顶点缓存区对象的句柄,你稍后将看到大部分 OpenGL 对象都是通过一个 GLuint 类型的变量来访问的。

glGenBuffers(1,&VBO);

OpenGL为了生成不同类型的对象定义几个 glGen* 函数。这些函数通常有两个参数,第一个参数指定你想要创建对象的数量,第二个参数是一个存放 GLuint 类型数组的地址,这个数组用于存放函数生成的句柄(确定数组是足够大来存放你的所申请的缓冲区句柄)。以后调用这个函数将不会生成相同的句柄,除非你使用 glDeleteBuffers 函数删除它。在这个地方,你不需要指定用这些 buffers 用于做什么,所以创建的是普通的 buffers,指定这个 buffer 的功能是下面一个函数的任务。

glBindBuffer(GL_ARRAY_BUFFER,VBO);

目标 GL_ARRAY_BUFFER 表示缓冲区用于存储顶点数组。另一个有用的目标是 GL_ELEMENT_ARRAY_BUFFER 表示缓冲区用于存储数组的索引。

glBufferData(GL_ARRAY_BUFFER,sizeof(Vertices),Vertices,GL_STATIC_DRA);

glVertexAttribPointer(0,3, GL_FLOAT, GL_FALSE, 0, 0);

这个函数调用告诉管线如何在缓冲区内部解释数据。第一个参数指定了属性的索引。我们都知道0是默认的,但是当我们开始使用 Shaders 的时候,我们在 Shader 中需要明确的设置索引。第二个参数是构成属性的分量的个数(X,Y,Z共三个分量)。第三个参数是每个分量的数据类型。第四个参数表明属性在管线中使用之前是否需要被规范化。第五个参数是在缓冲区中两个相同属性值之间的间隔的字节数,当只有一个属性时(比如缓冲区只包含顶点位置)并且数据是紧挨着的,那么我们设置这个值为0。如果我们有一个包含位置属性和法线属性的数组(每个属性都是一个float类型的三维向量),我们将这个值设置为6*4=24。最后一个参数在上一个例子中是有用的。我们需要指定在缓冲区中存储数据的偏移值,这样管线才会找到数据。当顶点的位置和法线相邻存储时时,我们设置顶点位置的偏移值为0而顶点法线的偏移值为12。

glDrawArrays(GL_POINTS,0, 1);

调用这个函数来画几何体。这里就是 GPU 真正开始工作的地方。它现在将结合绘图命令的参数,然后创建一个点并将结果渲染到屏幕。

glDisableVertexAttribArray(0);

当顶点不是要立即被使用的时候,禁用所有的顶点属性是一个很不错的方法。当着色器不使用顶点的时候将顶点属性激活。

Vector3f Vertices[3];

Vertices[0] =Vector3f(-1.0f, -1.0f, 0.0f);

Vertices[1] =Vector3f(1.0f, -1.0f, 0.0f);

Vertices[2] =Vector3f(0.0f, 1.0f, 0.0f);

glDrawArrays(GL_TRIANGLES,0, 3);

顶点处理器负责对传入渲染管线的每个顶点执行顶点着色器中的内容,(传入的顶点的数量由绘制函数确定),顶点着色器并不关心所要渲染的基本图元的拓扑结构。此外,你不能在顶点处理器中丢弃任何一个顶点。每个顶点都只被顶点处理器处理一次,在经过矩阵变换之后继续进入接下来的流水线。

渲染管线中的下一个阶段是裁剪,这是一个单一功能的固定功能单元——它通过我们前面课程中见过的规范化盒子对图元进行裁剪。同时它还通过近裁剪面和远裁剪面对其进行裁剪。同时他也支持用户自定义裁剪面对场景进行裁剪。未被裁剪掉的顶点会变换到屏幕坐标系之下,之后通过光栅化将顶点按照拓扑结构渲染到屏幕上。顶点着色器、片元着色器、几何着色器这三个可编程阶段是可选择的,如果我们不向其绑定 Shader 程序就会执行默认的固定管线的函数。

GLuint ShaderProgram = glCreateProgram();

我们通过创建一个shader程序对象来开始我们的着色器工程,我们将会把所有的shader程序都链接到这个sahder程序对象上。

GLuint ShaderObj = glCreateShader(ShaderType);

glAttachShader(ShaderProgram, ShaderObj);

我们将编译之后的 Shader 对象附加到程序对象上,这和在 Makefile 中链接一个对象链表类似。因为我们这儿没有 Makefile 所以通过编程来实现这种功能。只有被附加到程序对象上的 Shader 对象才会参与链接过程。

glLinkProgram(ShaderProgram);

在编译好所有 Shader 对象以及将他们附加到程序对象之后我们就可以进行链接操作。注意在链接程序对象之后你可以通过为每个 Shader 对象调用 glDetachShader 和 glDeleteShader 方法来删除其中的 Shader 对象。OpenGL驱动程序会为其生成的大部分对象维持一个引用计数。如果我们在创建一个 Shader 对象之后又将其删除则驱动程序会将这个 Shader 对象剔除掉,但是如果我们将 Shader 对象附加到程序对象之后调用 glDeleteShader 函数则只会将 Shader 对象标记为删除部分,你需要调用 glDetachShader 函数其引用计数才会下降到0之后才会被移除。

glUseProgram(ShaderProgram);

最后,我们调用上面的函数将链接之后的 Shader 程序对象添加到渲染管线中。除非我们使用其他的 Shader 程序对象来替换当前的程序对象或者通过调用 glUseProgram(NULL)显式的禁用它的使用(并且启用固定管线),否则这个 Shader 程序对象会对每次的绘制都会产生效果。如果你创建的 Shader 程序对象只包含一种类型的 Shader 程序,那么其他阶段的操作会默认的调用固定管线中的功能。

属性变量包含顶点特性数据所以每次调用shader都会从顶点缓冲区中重新导入新的数据,而一致变量中的值在整个绘制过程中都保持不变。这意味着我们在绘制过程之前就为一致变量赋值并且在着色器的每次调用中都可以访问这个相同的值。一致变量对于保存光照参数(光源位置和光照方向等),变换矩阵、纹理对象句柄等都是非常有用的。

GLUT 只有在如下列事件发生时才会强制调用渲染回调函数:最大化或者最小化窗口、被另一个窗口遮挡,遮挡窗口移除等。如果我们在程序运行之后对窗口的布局不做任何变化则渲染回调函数只会调用一次。你可以通过在渲染函数中添加一个打印函数来验证,你会看到打印函数只执行一次,如果你将窗口最大化或者最小化你可以发现打印函数会再次被执行。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值