[OpenGL Insights] Ch1. Teching Computer Graphics Starting with Shader-based OpenGL

来源:互联网 发布:vmware虚拟机软件下载 编辑:程序博客网 时间:2024/06/07 11:08

本章主要是介绍老师该如何教导学生。如何从OpenGL3.1以前的教学思路转到shader-based思路。虽说不直接讲解OpenGL技术,但是对于理解OpenGL变化及变化的原因有益。还是需要好好看看。


1.1 Introduction

从OpenGL3.1开始,fixed function pipeline(固定功能流水线)被废弃并删除。每个OpenGL程序必须至少提供一个vertex shader(顶点着色器)和一个fragment shader(片断着色器)。

下面从历史的角度,编写程序的方式变化了,但是概念的变化不大。下面先用fixed function pipeline写个Hello World,再看在Shader-based中该如何写这个程序。最后,我将说明,shader-based OpenGL如何影响图形学标准课程中所要讲的话题。

1.2 A basic course

图形学所要讨论的主要话题是(这在过去和现在一致):

(1)modeling

(2)geometry

(3)transformations

(4)lighting and shading

(5)texture mapping and pixel processing

1.3. Hello World in OpenGL : Old style

我们先从一个简单程序开始,此程序常写在OpenGL3.1前。在黑色背景下画一个白色矩形,为了延迟讨论coordinate systems, transformations, giving vertex positions in clip coordinates这些概念,大部变量使用默认值。

// List1.1 Hello Worldvoid display(void){glClear(GL_COLOR_BUFFER_BIT);glBegin(GL_POLYGON);glVertex2f(-0.5, -0.5);glVertex2f(-0.5, 0.5);glVertex2f(0.5, 0.5);glVertex2f(0.5, -0.5);glEnd();//glutSwapBuffers();glFlush();}int main(int argc, char** argv){glutInit(&argc, argv);//glutInitDisplayMode(GL_RGBA | GLUT_DOUBLE);glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);glutInitWindowSize(500, 500);glutInitWindowPosition(100, 100);glutCreateWindow("Hello World");glutDisplayFunc(display);glutMainLoop();}
结果为:


Figure 1.1

原书使用的是双缓存,但我的实验电脑不支持,于是使用单缓存。

这里有三个主要问题:

(1)使用immediate mode(立即模式)

此程序中,除了glClear,其他的函数都被废弃(额)。理解为什么废弃这些函数对于理解为什么要使用更新的OpenGL很关键。OpenGL使用立即模式,当每个顶点被创建时,触发执行vertex shader。几何处理在GPU上由vertex shader执行,当每次我们想显示矩形时,这个简单的程序需要四次独立的向GPU发送顶点位置。但是,由于这个程序太过简单,未到CPU和GPU的瓶颈,故而看不出为什么要废弃掉老的函数。

(2)依赖于fixed function pipeline

让数据以可预测的方式被处理,让人感到舒服。但是,这也会造成一种误区:只有立即模式一种模式。于是,当几何体非常复杂而导致渲染速度极慢时,我们感到无措。

(3)state variables使用默认值(可以看到cleare color默认为黑色,而glColor默认为白色,类似于背景色和前影色)



1.4 Starting with programmable pipelines

虽说从2.0就支持了programmable pipeline,但是程序员一般还是习惯使用被废弃的函数。

OpenGL3.0宣称,从OpenGL3.1开始,将不再向后兼容。OpenGL3.1有一个shader-based的core,和一个对被废弃函数支持的扩展。我们选择使用完全的shader-based,与OpenGL3.1 core保持一致(实际上OpenGL2.0大部分也可以,因为3.1并非必要条件)。编写这样一个程序,先看看我们需要什么。

shader-based程序要求至少一个vertex shader和一个fragment shader。讲解者必须简要的介绍OpenGL Shading Language(GLSL)。因为GLSL是类C的,故而我们可以利用Shader-based写个Hello World,而不深入讨论GLSL。讲解者必须介绍些概念,如program objects和shader到底做了什么。关于shader到底做些什么,可以参见西川善司。虽然理解这些概念需要花点时间,但是他们都是理解现代graphics system的核心,越早了解越好。

介绍Shader的一个难点是,OpenGL应用程序必须读取,编译,链接Shader(好像Shader是一个外部模块一样)。这些操作需要一个OpenGL函数,并且这些函数对于理解基本的图形概念没有意义。先介绍第一个函数:

GLuint program = InitShaders("vertex_shader_file", "fragment_shader_file")
从函数读入shader文件并且编译链接他们。如果成功,则返回一个program object。此函数的源代码是可被利用的,我们在后面讲解。

另外,需要一个小的C++包,提供了一维,二维,三维,四维矩阵或者向量类。即使OpenGL可以用C和Shader编写,但是GLSL依赖额外的矩阵和向量操作,使用了C++的构造函数,运算符重载。

1.5 Hello World: New Style

即使最简单的应用程序也需要三个部分:配置shader的初始化程序和窗口系统接口,形成数据并把数据发送到GPU,在GPU中渲染数据。第一个部分使用InitShader可以轻松实现。第三个部分只需要清除缓存,并调用glDrawArrays。第二部分与立即模式不同。即使最简单的程序,我们也需要介绍vertex buffer objects和vertex array objects。我们用一个新的程序来讨论这些话题,此程序与List1.1的输出相同。List1.2为OpenGL程序,List1.3为vertex shader,List1.4为fragment shader。

List1.2中使用了一个"Angel.h"的头文件(此头文件好像不公开,我没找到,所以程序先不测试),里面有InitShader的源码和一些数学操作。我们注意到,此程序使用了两个三角形而不是一个四边形,从OpenGL3.1开始,四边形是唯一被支持的filled type。使用vec2只初始化一个数组,说明了为什么限制为三角形。我们还可以讨论其他可选择的如triangle stripe和triangle fan。

下面是最难的部分,即使他只有5行代码。我们分配了一个vertex array obejct(VAO)和一个vertex buffer object(VBO)。

接下来的代码与立即模式一样,除了glDrawArrays的使用。

不需要花太多时间去讨论Shaders,他不要求很多的GLSL知识。

1.5.1 OpenGl ES and WebGL

OpenGL ES 2.0是shader-based,并且支持多种设备,包括iPhones。WebGl是JavaScript实现了OpenGL ES 2.0,可以在最新的浏览器上运行。

1.6 The rest of course

下面介绍些所谓的高级图形的核心。

1.6.1. Geometry

CG是建立在一些基本的几何概念上的。如basic types(scalars, points, vectors),simple objects(triangles, planes, polylines),methods of representation(coordinates system, frame)。我们花一些时间讨论如何通过vertex buffer建立几何模型。

考虑建一个立方体模型。这个模型在讨论transformations, viewing, lighting, texture mapping很有用。由于我们只能渲染三角形,因而我们有多种方式渲染立方体。我们已经介绍了使用vertex buffer,作为扩展我们可以讨论glDrawElements。在课程中,Color经常被附加在vertex上。因为老的built-in状态变量(包括当前颜色和顶点位置)已经不是OpenGl状态的一部分,因而这些状态变量需要在程序中定义并传入到GPU。

1.6.2 Transformations and viewing

这部分内容花较多时间。先介绍标准的affine transformations(仿射变换):translation, rotation, scaling,以及如何使用他们。接下来介绍projective transformation(投影变换),包括standard orthographic(标准正交投影)和perpective transformations(透视投影)。OpenGL3.1前,API使用简单的矩阵函数来实现变换,如glMatrixMode, glLoadMatrix, glLoadIdentity, glMultiMatrix,标准变换函数,如glTranslate, glRotate, glScale,矩阵堆栈函数,如glPushMatrix, glPopMatrix,投影函数,如glOrtho, glFrustum。所有这些函数都被废弃。并且,由于许多状态变量都被废弃,当前矩阵的概念就没有了。

现在,讲解者在齐次坐标中讲解affine transformation。即使,关于坐标轴的translation, rotation, scaling函数都比较好写,但是关于任意轴的就比较困难,可以用多种方式来搞。不使用这些API的一个好处是学生可以自己参考而不依赖于API。不过,我们已经加了函数到矩阵/向量类中(这里可能是作者写的工具类),包括标准viewing matrices。这样做的原因是我们经常需要比较在程序中实现变换和在shader中实现变换。

1.6.3 Lighting and shading

这部分说明了shader-based的好处。过去,在fixed function pipeline中,学生只能用Blinn-Phong模型。其他的模型可以被讨论但是只能在offline manner中实现。另一个等价的问题是只有vertex lighting可以用。因此当学习Phong and Gouraud shading时,不能在pipeline中实现Phong shading。而在shader-based中,我们可以自己实现per-vertex shading和per-fragment shading。

被废弃的立即模式状态变量可能引起问题。最大的问题发生在法向的变换。而现在,当实现lighting shader时,必须提供normal matrix,因为状态变量gl_NormalMatrix已经被废弃。学生可以自已经实现normal matrix,在程序中或者shader中。

1.6.4 Texturing and discrete processing

OpenGL3.1前的大部分纹理函数都没有变化。应用程序在程序中配置texture object。纹理坐标可以在程序中作为vertex attribute指定,也可以在vertexshader中指定,然后由rasterizer进行插值。最后在fragment shader中使用sampler为每一个fragment上颜色。

从OpenGL3.1开始,pixel processing变得很难。bitmap-writing和pixel-writing函数以及一些相关函数,如使用accumulation buffer,都被废弃。虽然这些函数使用起来简单,但是低效。下面有个例子,编码简单但是导致使用GPU的利用率不高,并且由于反复的在CPU和GPU间传送大量数据使用效率达到瓶颈。一个好的代替方案是使用fragment shader管理纹理。例如,List1.5的一个简单的fragment shader已经足够说明足够说明image smoothing,并且可以进行简单的调整就可以适用别的图像操作。

in vec2 texCoord;out vec4 FragColor;uniform float d;uniform Sampler2D image;void main(){    FragColor=    (texture(image, vec2(texCoord.x+d, texCoord.y))    + texture(image, vec2(texCoord.x, texCoord.y+d))    + texture(image, vec2(texCoord.x-d, texCoord.y))    + texture(image, vec2(texCoord.x, texCoord.y-d))/4.0;}// List 1.5 Image-Smoothing shader
1.6.5 Advanced Topics

这里讨论三个问题。

一是curves 和 surfaces。即使evaluator(求值器)已经被废弃,但是在应用程序端还是很容易创建。如果使用较新的OpenGL,有一种更有意思的方法来生成parametric cubic curves:使用geometry shaders。Geometry shaders并不会增加编程复杂度,并且可以用来介绍subdivision curve和surface。Tesselation shaders用来介绍parametric polynomial surfaces更好,但是对于第一课来讲比较难。

二是framebuffer obejct(FOBs)。理解FOBs需要了解更多的OpenGL细节,他会打开许多领域,用以创建一个优秀的程序。有时候需要动态的图像操作,可以将图像先渲染到一个off-screen buffer中,然后在下一次迭代使用此buffer作为一个纹理。

三是,由于graphics system如OpenGl将诸如rasterization, clipping, hidden-surface removal封装起来,使用学习的人不用再考虑这些细节,故而对图形学理论理解不深。而programmable shader可以使用学生更好的学习并实现graphics algorithms。

1.6.6 Issues

原先我们关心的重点是CPU和GPU,而但是我们关心的是GPU本身。如何使用窗口系统是个问题。GLUT提供了对许多标准窗口系统的接口。他允许创建管理多个窗口,支持鼠标键盘交互。GLUT已经10年未变,所以不适应shader-based OpenGL,因为GLUT使用了很多废弃的函数。但是,依赖于显卡及驱动,很多GLUT还是可以使用。例如,Mac OS X Lion支持OpenGL3.2, 但是3.2profile与GLUT框架不兼容。

现在有些替代方案可使用,虽然最好的还有待开发。


参考:

1. 《OpenGL编程指南E7》CH15。

原创粉丝点击