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. 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");
}
首先通過QShaderProgram的函數uniformLocation()給GLSL中的變量用一個整數標記,對應在OpenGL中的函數是 GLint glGetUniformLocation(GLuint program, const char* name);再通過函數setUniformValue()來更新GLSL中變量的值,對應OpenGL中的函數為:glUniform{1234}(if,ui}。最后只用了一個片段著色器,代碼如下所示:
// 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.