A Simple OpenGL Shader Example
A Simple OpenGL Shader Example
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
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 f ...
- OpenGL Shader in OpenCASCADE
OpenGL Shader in OpenCASCADE eryar@163.com Abstract. As implementation of one of the strategic steps ...
- OpenGL Shader源码分享
Opengl shader程序,旗帜混合纹理加载,通过N张图片,能够组合出数百个:http://www.eyesourcecode.com/thread-39015-1-1.html 用GLSL做了一 ...
- 【玩转cocos2d-x之四十】怎样在Cocos2d-x 3.0中使用opengl shader?
有小伙伴提出了这个问题.事实上GLProgramCocos2d-x引擎自带了.全然能够直接拿来用. 先上图吧. 使用opengl前后的对照: watermark/2/text/aHR0cDovL2Js ...
- 「游戏引擎 浅入浅出」4.1 Unity Shader和OpenGL Shader
「游戏引擎 浅入浅出」从零编写游戏引擎教程,是一本开源电子书,PDF/随书代码/资源下载: https://github.com/ThisisGame/cpp-game-engine-book 4.1 ...
- OpenGL shader 中关于顶点坐标值的思考
今天工作中需要做一个事情: 在shader内部做一些空间距离上的计算,而且需要对所有的点进行计算,符合条件的显示,不符合条件的点不显示. 思路很简单,在vertex shader内知道顶点坐标,进行计 ...
- 【OpenGL】用OpenGL shader实现将YUV(YUV420,YV12)转RGB-(直接调用GPU实现,纯硬件方式,效率高)
这段时间一直在搞视频格式的转换问题,终于最近将一个图片的YUV格式转RGB格式转换成功了.下面就来介绍一下: 由于我的工程是在vs2008中的,其中包含一些相关头文件和库,所以下面只是列出部分核心代码 ...
- OpenGL shader渲染贴图
simple.vert #version core layout (location = ) in vec3 position; layout (location = ) in vec3 color; ...
- OpenGL Shader Key Points (2)
1. Uniform 1.1. Uniform变量 不是所有的变量都是跟顶点一一对应的,如变换矩阵,光源位置等. Uniform变量可以在任何类型的shader中使用,但只能作为输入值,不能在sh ...
随机推荐
- swift-Array(数组)
Swift 数组用于存储相同类型的值的顺序列表.Swift 要严格检查,不允许不同类型的值在同一个数组中 声明一个数组 var someArray = [SomeType]() var someArr ...
- Matlab读取数据中出现的问题
在运行Matlab读取一段数据并做处理的时候,常常会提示服务器错误,但是等待一会再次运行就会成功运行. 代码如下: clc; clear all; [~,~,rawdata] = xlsread('进 ...
- Python框架之Tornado(二)请求阶段
概述 上图是tornado程序启动以及接收到客户端请求后的整个过程,对于整个过程可以分为两大部分: 启动程序阶段,又称为待请求阶段(上图1.2所有系列和3.0) 接收并处理客户端请求阶段(上图3系列) ...
- windows service 1053错误 启动失败
做项目移植的时候发现一个项目的window service启动失败,最后试出来是启动时间超时 解决办法是给window service设置一个长一点的等待时间,步骤如下: 启动,输入regedit启动 ...
- 二、JSP、servlet、SQL三者之间的数据传递(前台与后台数据交互)
2.收信息来到表单提交时URL所指向的servlet文件,获取传递过来的参数值
- iOS中一些算法函数
rand() --- 随机数 求随机数 a-b arc4random()%(b-a+1)+a abs() labs() ---- 整数绝对值 fabs() fabsf() fabsl() ...
- ZOJ1913 Euclid's Game (第一道简单的博弈题)
题目描述: Euclid's Game Time Limit: 2 Seconds Memory Limit: 65536 KB Two players, Stan and Ollie, p ...
- asp.net 基礎部分一
过程: 客户端像服务器发送一个请求,iis服务器接收到请求的数据,并且将数据交给c#程序进行处理,并且对数据库进行操作,并且将处理到的结果响应给浏览器客户端 过程2:第一次浏览器请求,后端应该发一个表 ...
- Python 比较两个字符串大小
python 2中,有cmp(a,b)函数,用于比较两个字符串的大小. 如 >>>a='abc' >>>b='abd' >>>print cmp( ...
- OS中atomic的实现解析
OS中atomic的实现解析 转自:http://my.oschina.net/majiage/blog/267409 摘要 atomic属性线程安全,会增加一定开销,但有些时候必须自定义ato ...