A Simple OpenGL Shader Example

A Simple OpenGL Shader Example

eryar@163.com

Abstract. OpenGL Shading Language, the high-level programming language defined to allow application writers to write programs that execute on the programmable processors defined within OpenGL. Informally the language is sometimes referred to as GLSL. The GLSL has been made part of the OpenGL standard as of OpenGL2.0. The paper focus on a simple example of OpenGL Shader, which can be used as a guide of GLSL.

Key Words. OpenGL, OpenGL Shading Language, GLSL, Shader, Qt

1. Introduction

很早之前,从网上下载到这么一本书《OpenGL Shading Language》,翻看了几遍,终不得要领。后来看到一本由仇德元编写的《GPGPU编程技术—从GLSL、CUDA到OpenCL》,对GPU的了新的认识,对其性能刮目相看。书中给出的一个简单例子,也便于对Shader的入门。

Figure 1.1 OpenGL Shading Language and GPGPU

从OpenGL Shading Language的出现可以发现程序员的一个特点,那就是不喜欢一成不变的东西,希望自己有更多的控制权,有个性,向往自由。如果要有个性,那就要引入这个新的东西GLSL了,增加了学习成本。不过从OpenGL2.0之后,Shader已经成了OpenGL标准的一部分,新版的OpenGL的书籍中都少不了shader的内容。Shader成了真实感图形、高性能计算中不可或缺的技术,学习掌握新的工具是为了生活更轻松。

为了让例子简单,本文在Qt中实现了一个简单的Shader示例,通过这个例子入门后,再结合《OpenGL Shading Language》去实现更炫的效果。再者就是去理解OpenCASCADE中的shader应用。

2.OpenGL operation pipeline

原来有个问题一直困扰着我,那就是三维的模型怎么在二维的计算机屏幕上显示的。现在明白了这个就其实是显卡的主要工作,并按一定的流水线来实现的。图形流水线(pipeline)是GPU工作的通用模型,它以某种形式表示的三维场景为输入,输出二维的光栅图像(raster images),也就是位图。这样三维的模型就可以在二维的屏幕上进行显示了。下面依次解释流水线中的关键步骤:

v 图形流水线的起点是一个三维模型。这个三维模型可以是用软件设计出的三维游戏人物,也可以是逆向工程(Reverse Engineering)中用激光扫描仪(Laser Scanner)设备采集的顶点,也可以是几何造型内核(如OpenCASCADE)将模型网格化生成的顶点等。不论何种模型,在计算机处理之前都一定要经过采样而得到有限的离散顶点,每个顶点都可以被一个向量描述为一个三维坐标系里的点。这些可用来描述三维顶点组成了点云(Point Cloud)。如果采样频率足够高,得到的顶点就可以细致地描述模型的表面。点云中的点可以由一个列表表示,列表中每一项是某点的三维坐标值。同时,列表中每一点都带有该点的颜色信息。这个顶点列表即是流水线的输入数据,从起点进入流水线。

v 顶点可以用来形成多边形,从而拟合出近似的表面。由顶点形成多边形最常用的一种方法是三角化(triangulation),即每相邻的三个点组成一个三角形。接下来每个顶点要经过一系列的逐顶点操作(per-vertex operation),比如计算每个顶点的光照、坐标变换等。

v 由于显示输出的需要,用户会定义一个视口(viewport),即观察模型的位置和角度。然后,模型被投影到与视口观察方向垂直的平面上。这个投影变换也是硬件加速的。根据视口的大小,投影结果有可能被裁剪(clipping)掉一部分。

v 接受模型投影的平面是一个帧缓存(frame buffer),它是一个由像素(pixels)定义的光栅化平面。光栅化(rasterization)的过程,实际上就是决定帧缓存上的哪些像素该取怎么样的值。通过采样和插值,光栅化器(rasterizer)会决定一幅最接近原投影图像的位图。

v 这些像素或者由像素连成的片段还须经历一些逐片段操作(per-fragment operation),也就是说它们的颜色也可以根据算法改变。另外纹理映射(texturing or texture mapping)在这一阶段也会覆盖某些像素的值。对于投影和光栅化的结果,还要判断片段的可见性,也就是遮挡探测(occlusion detection)。

v 最后帧缓存里面的结果被刷新到显示器上。该过程以较高的频率重复,因为人的视频延迟,感觉到的就是连续的。

Figure 2.1 A simplified version of the OpenGL pipeline

Figure 2.2 Overview of OpenGL operation

可以将上述OpenGL的渲染管线想成有两个机器来完成主要的工作:一个机器处理顶点;一个处理片段。对于早期的OpenGL而言,只是两个机器是内置的,程序员不能改变他们的工作方式。

Figure 2.3 The OpenGL Fixed-Function Pipeline[3]

然而可编程的Shader(programmable shader)则是可以对这两个机器的工作进行干预。

3.Using GLSL Shaders

当你想在程序中使用一个顶点Shader或片段Shader时,需要按如下步骤进行:

v 创建一个Shader对象;

v 将Shader的源文件编译到这个对象;

v 验证源文件是否编译成功;

然后将这些shader链接到一个Shader程序:

v 创建一个Shader程序;

v 将创建的Shader对象绑定到这个Shader程序;

v 链接Shader程序;

v 验证链接是否成功;

v 将shader对象应用到顶点及片段的处理;

如下图所示:

Figure 3.1 Shader Creation Flowchart

4.The simplest Shader Example

本来想用glut来写个简单的例子的,但是识别不了glCreateShader这种函数,发现要下载一些第三方库才可以,如glew。看到Qt中已经有封装好的QGLShader和QGLShaderProgram,所以还是决定用Qt来写个简单的例子,从而来对OpenGL的shader有个感性的认识。其中关于Shader部分的主要是这个函数:

void ShaderWidget::setShader()
{
    if (!isValid())
    {
        return;
    }

    const QGLContext* aGlContext = context();

    QGLShaderProgram* aShaderProgram = new QGLShaderProgram(aGlContext);

    aShaderProgram->addShaderFromSourceFile(QGLShader::Vertex, "vertex.vert");
    aShaderProgram->addShaderFromSourceFile(QGLShader::Fragment, "fragment.frag");

    aShaderProgram->link();
    aShaderProgram->bind();
    QString aLog = aShaderProgram->log();
}

先看一下没有使用Shader之前的程序的效果图:

Figure 4.1 A teapot model without shader

其中添加两个shader,一个是vertex shader: vertex.vert,一个是fragment shader:fragment.frag。在vertex shader中对顶点的坐标进行变换,代码如下所示:

void main()
{
    vec4 a = gl_ModelViewProjectionMatrix * gl_Vertex;
    gl_Position.x = 0.4 * a.x;
    gl_Position.y = 0.1 * a.y;
}

OpenGL内置变量gl_Position保存了当前顶点的位置信息,上面的顶点着色器修改了每个顶点的X坐标和Y坐标,使得输出了一个扭曲的teapot。

片段着色器当前片段的颜色改成紫色,片段着色器代码如下:

void main()
{
    gl_FragColor = vec4(0.627, 0.125, 0.941, 1.0);
}

为了验证程序是使用了着色器,运行程序得到如下图所示:

Figure 4.2 A Teapot with shader

在不重新编译程序的情况下,只修改片段着色器中的代码,改成如下所示内容:

void main()
{
    gl_FragColor = vec4(0.627, 0.125, 0.0, 1.0);
}

保存片段着色器代码后直接运行程序,可得到如下图所示的红色teapot:

Figure 4.3 Change Fragment Shader

可以看到shader的确是起了作用。本文最后将给出程序的完整代码及shader的代码,便于测试。

5.Conclusion

OpenGL的Shader给了程序员对OpenGL的更多的控制权,可对其顶点处理和片段处理进行更个性化的配置以达到炫酷的效果。

Shader的使用步骤是先创建shader对象,再将源码编译到shader对象。最后通过shader程序,将shader添加并编译、链接和使用。

最后在Qt中以一个简单的例子来验证了shader的效果,入门之后便于理解GLSL更详细的功能,以使自己的可视化程序具有更高的性能,更酷的效果。

6. References

1. 仇德元. GPGPU编程技术—从GLSL、CUDA到OpenCL. 机械工业出版社. 2012

2. Randi J. Rost. OpenGL Shading Language. Addison Wesley. 2006

3. Dave Shreiner. OpenGL Programming Guide(7th). Addison-Wesley. 2009

 

Source Code and PDF Version: A Simple OpenGL Shader Example

时间: 2024-09-14 01:26:04

A Simple OpenGL Shader Example的相关文章

A Simple OpenGL Shader Example II

A Simple OpenGL Shader Example II eryar@163.com Abstract. The OpenGL Shading Language syntax comes from the C family of programming languages. Tokes, identifiers, semicolons, nesting with curly braces, control-flow, and many key words look like C. GL

OpenGL Shader in OpenCASCADE

OpenGL Shader in OpenCASCADE eryar@163.com Abstract. As implementation of one of the strategic steps in OpenCASCADE visualization component development road-map, support for GLSL shader programs has been added in OpenCASCADE Technology 6.7.0.  Key Wo

OpenCASCADE Outline

OpenCASCADE Outline eryar@163.com      有网友反映blog中关于OpenCASCADE的文章比较杂乱,不太好找,最好能提供一个大纲,这样方便查找.于是决定将这些学习时写的文章整理下,方便对OpenCASCADE的学习理解.其实在http://www.cnblogs.com/opencascade中,已经将文章按目录重新发表了一遍.可以按OpenCASCADE的模块的顺序来学习,也可以挑选自己感兴趣的部分来学习.      由于本人水平所限,文中的错误不妥之处

最简单的视音频播放示例6:OpenGL播放YUV420P(通过Texture,使用Shader)

本文记录OpenGL播放视频的技术.上一篇文章中,介绍了一种简单的使用OpenGL显示视频的方式.但是那还不是OpenGL显示视频技术的精髓.和Direct3D一样,OpenGL更好的显示视频的方式也是通过纹理(Texture).本文介绍OpenGL通过纹理的方式显示视频的技术. OpenGL中坐标和Direct3D坐标的不同 OpenGL中的纹理的坐标和Direct3D中的坐标是不一样的. 在Direct3D中.纹理坐标如下图所示.取值是0到1.坐标系原点在左上角. 物体表面坐标如下图所示.取

opengl-OpenGL Shader 递归处理像素

问题描述 OpenGL Shader 递归处理像素 一个图片处理的OpenGL程序,处理当前像素需要使用前一个像素值,但是不是原图中的像素值,而是经过处理后的前一个像素值,请问这样的递归在fragment shader里面能做吗? 解决方案 Fragment shaders应用是没有固定的顺序的,在全局状态也没有副作用.所以你认为是前一个像素值的问题. 如果你想写一个算法涉及从左到右,从上到下的图像扫描,然后你需要明确需要生成输出的所有源像素.如果像素确定了,那么左像素加3,这样你就能明确得到左

经典CUDA教程

CUDA从入门到精通(零):写在前面 在老板的要求下,本博主从2012年上高性能计算课程开始接触CUDA编程,随后将该技术应用到了实际项目中,使处理程序加速超过1K,可见基于图形显示器的并行计算对于追求速度的应用来说无疑是一个理想的选择.还有不到一年毕业,怕是毕业后这些技术也就随毕业而去,准备这个暑假开辟一个CUDA专栏,从入门到精通,步步为营,顺便分享设计的一些经验教训,希望能给学习CUDA的童鞋提供一定指导.个人能力所及,错误难免,欢迎讨论.   PS:申请专栏好像需要先发原创帖超过15篇.

CUDA从入门到精通(一):环境搭建

NVIDIA于2006年推出CUDA(Compute Unified Devices Architecture),可以利用其推出的GPU进行通用计算,将并行计算从大型集群扩展到了普通显卡,使得用户只需要一台带有Geforce显卡的笔记本就能跑较大规模的并行处理程序.   使用显卡的好处是,和大型集群相比功耗非常低,成本也不高,但性能很突出.以我的笔记本为例,Geforce 610M,用DeviceQuery程序测试,可得到如下硬件参数: 计算能力达48X0.95 = 45.6 GFLOPS.而笔

WebGL 规范(WebGL Specification)

WebGL 规范(WebGL Specification) 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS.Android.Html5.Arduino.pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作. WebGL 规范 WebGL Specification 1.0.2 版,发布于 20

opengl-OpenGL uniform block有index 而没有location

问题描述 OpenGL uniform block有index 而没有location 在opengl shader 里面,有独立uniform和uniform block两个概念,每个独立的uniform,又有一个uniform index和uniform location, glGetActiveUniform通过index来获取uniform type, name信息,再通过name 来获取uniform location, 接着根据type的不同选取不同函数填充uniform数据.但是在