A Simple OpenGL Shader Example II
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. GLSL provides three qualifiers which form the interfaces of the shaders to their outside world.
Key Words. OpenGL, GLSL, Qualifiers,
1. Introduction
GLSL的特性与C/C++非常类似,包括它的数据类型。GLSL有三种基本数据类型:float, int和bool,及由这些数据类型组成的数组和结构体。需要注意的是GLSL并不支持指针。
GLSL 中有4个限定符(Qualifier)可供使用,它们限定了被标记的变量不能被更改的“范围”。及通过这几个限定符可以与OpenGL的程序来通信,即为 OpenGL程序提供了一个将数据传递给Shader的界面(Interface to a Shader)。
OpenCASCADE中使用GLSL实现了Ray Tracing效果,刚开始使用第三方库OpenCL来使用GPU加速,最新版本统一使用GLSL。
Figure 1.1 OpenGL Training
在《OpenGL高级编程技术培训教材》中,GLSL也是一个重要内容。虽然当时听得云里雾里,还是要感谢公司提供这样的培训机会。
2.GLSL Data Types
GLSL内置了许多数据类型,使图形操作的表达式计算更方便。布尔类型、整型、矩阵、向量及结构、数组等都包括在内。
Scalars |
|
float |
Declares a single floating-point number. |
int |
Declares a single integer number. |
bool |
Declares a single Boolean number. |
这三种是GLSL的基本类型。
Vectors |
|
vec2 |
Vector of two floating-point numbers |
vec3 |
Vector of three floating-point numbers |
vec4 |
Vector of four floating-point numbers |
ivec2 |
Vector of two integers |
ivec3 |
Vector of three integers |
ivec4 |
Vector of four integers |
bvec2 |
Vector of two booleans |
bvc3 |
Vector of three booleans |
bvc4 |
Vector of four booleans |
向量非常有用,可以用来存储和操作颜色、位置、纹理坐标等等。GLSL内置的很多变量及函数中就大量使用了向量。
Matrices |
|
mat2 |
2x2 matrix of floating-point numbers |
mat3 |
3x3 matrix of floating-point numbers |
mat4 |
4x4 matrix of floating-point numbers |
矩阵主要用来实现线性变换。
Samplers |
|
sampler1D |
Accesses a one-dimensional texture |
sampler2D |
Accesses a two-dimensional texture |
sampler3D |
Accesses a three-dimensional texture |
samplerCube |
Accesses a cube-map texture |
sampler1DShadow |
Accesses a one-dimensional depth texture with comparison |
sampler2DShadow |
Accesses a two-dimensional depth texture with comparison |
3.Qualifiers
GLSL有4个限定符可供使用,它们限定了被标记的变量不能被更改的范围:
Qualifiers |
|
attribute |
For frequently changing information, from the application to a vertex shader |
uniform |
For infrequently changing information, from the application to either a vertex shader or a fragment shader |
varying |
For interpolated information passed from a vertex shader to a fragment shader |
const |
For declaring nonwritable, compile-time constant variables as in C |
const限定符和C/C++里的相同,表示限定的变量在编译时不可被修改,即它标记了一个常量。const限定符是4个限定符中被标记变量不可被更改的范围最大的。其余3个限定符是GLSL特有的,所以它们都用在着色器内部声明变量。
attribute限定符标记的是一种全局变量,该变量被用作从OpenGL应用程序向顶点着色器中传递参数,因此该限定符仅用于顶点着色器。
uniform限定符也标也一种全局变量,该变量对于一个图元来说是不可改变的。同attribute限定符一样,uniform可以从OpenGL应用程序中接收传递过来的数据。uniform限定符可以用于顶点着色器和像素着色器。
最后GLSL还提供了从顶点着色器向片段着色器传递数据的方法,即使用varying限定符。
4.Code Example
在《A Simple OpenGL Shader Example》 中已经成功实现了一个带Shader的OpenGL程序。事实上这是两个相对独立的Shader,它们只能使用OpenGL内置的变量从外部OpenGL 程序中获取一些数据。比如当前顶点坐标、当前像素颜色等。这些Shader还没有自定义的变量,以便从OpenGL程序中传递数据。通常程序的设计者需要 在OpenGL程序中更好地控制shader的行为,这就需要从OpenGL程序向shader传递数据。
如上述的4个限定符,可以用来声明变量帮助shader从外部获取数据。其中uniform变量可以用来从OpenGL程序中给vertex shader或fragment shader传递数据,最很常用的一个限定符变量。将《A Simple OpenGL Shader Example》中的程序稍做修改,使得片段shader可以收到一个来自OpenGL程序里面的数据。
实现的主要代码在这两个函数中:
void ShaderWidget::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); mAngle += 0.1; glRotatef(mAngle, 0.0, 1.0, 1.0); // update uniform variable value mShaderProgram->setUniformValue(mTimeId, mAngle); glutSolidTeapot(1.0); //glutWireTeapot(1.0); } void ShaderWidget::setShader() { if (!isValid()) { return; } const QGLContext* aGlContext = context(); mShaderProgram = new QGLShaderProgram(aGlContext); //mShaderProgram->addShaderFromSourceFile(QGLShader::Vertex, "vertex.vs"); mShaderProgram->addShaderFromSourceFile(QGLShader::Fragment, "uniform.fs"); mShaderProgram->link(); mShaderProgram->bind(); QString aLog = mShaderProgram->log(); // save the location of the uniform variable name within the shader program. mTimeId = mShaderProgram->uniformLocation("v_time"); }
// time(passed in from the application) uniform float v_time; void main() { float fr = 0.9 * sin(0.0 + v_time*0.05) + 1.0; float fg = 0.9 * cos(0.33 + v_time*0.05) + 1.0; float fb = 0.9 * sin(0.67 + v_time*0.05) + 1.0; gl_FragColor = vec4(fr/2.0, fg/2.0, fb/2.0, 1.0); }
Figure 4.1 Test uniform variable in GLSL
当将uniform.fs中的v_time改名后,就会发现视图一片漆黑,说明shader已经起作用了。
5.Conclusion
综上所述,GLSL中通过限定符Qualifiers来实现OpenGL程序与GLSL的数据传递。其中uniform变量可以用来从OpenGL程序向片段着色器和顶点传递数据,是很常用的一种方式。
本文在Qt中测试了uniform变量效果,可以发现Qt对OpenGL的面向对象封装还是很方便使用,也很容易找到与之对应的OpenGL函数。通过学习使用Qt中的OpenGL来方便学习理解OpenGL相关知识点。
6. References
1. san. Shader support in OCCT6.7.0. http://dev.opencascade.org/index.php?q=node/902
2. Qt Assistant.
PDF version and Source code: A Simple OpenGL Shader Example II