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++非常類似,包括它的數(shù)據(jù)類型。GLSL有三種基本數(shù)據(jù)類型:float, int和bool,及由這些數(shù)據(jù)類型組成的數(shù)組和結(jié)構(gòu)體。需要注意的是GLSL并不支持指針。
GLSL中有4個(gè)限定符(Qualifier)可供使用,它們限定了被標(biāo)記的變量不能被更改的“范圍”。及通過(guò)這幾個(gè)限定符可以與OpenGL的程序來(lái)通信,即為OpenGL程序提供了一個(gè)將數(shù)據(jù)傳遞給Shader的界面(Interface to a Shader)。
OpenCASCADE中使用GLSL實(shí)現(xiàn)了Ray Tracing效果,剛開始使用第三方庫(kù)OpenCL來(lái)使用GPU加速,最新版本統(tǒng)一使用GLSL。
Figure 1.1 OpenGL Training
在《OpenGL高級(jí)編程技術(shù)培訓(xùn)教材》中,GLSL也是一個(gè)重要內(nèi)容。雖然當(dāng)時(shí)聽得云里霧里,還是要感謝公司提供這樣的培訓(xùn)機(jī)會(huì)。
2.GLSL Data Types
GLSL內(nèi)置了許多數(shù)據(jù)類型,使圖形操作的表達(dá)式計(jì)算更方便。布爾類型、整型、矩陣、向量及結(jié)構(gòu)、數(shù)組等都包括在內(nèi)。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 |
向量非常有用,可以用來(lái)存儲(chǔ)和操作顏色、位置、紋理坐標(biāo)等等。GLSL內(nèi)置的很多變量及函數(shù)中就大量使用了向量。
Matrices |
mat2 | 2x2 matrix of floating-point numbers |
mat3 | 3x3 matrix of floating-point numbers |
mat4 | 4x4 matrix of floating-point numbers |
矩陣主要用來(lái)實(shí)現(xiàn)線性變換。
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個(gè)限定符可供使用,它們限定了被標(biāo)記的變量不能被更改的范圍:
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++里的相同,表示限定的變量在編譯時(shí)不可被修改,即它標(biāo)記了一個(gè)常量。const限定符是4個(gè)限定符中被標(biāo)記變量不可被更改的范圍最大的。其余3個(gè)限定符是GLSL特有的,所以它們都用在著色器內(nèi)部聲明變量。
attribute限定符標(biāo)記的是一種全局變量,該變量被用作從OpenGL應(yīng)用程序向頂點(diǎn)著色器中傳遞參數(shù),因此該限定符僅用于頂點(diǎn)著色器。
uniform限定符也標(biāo)也一種全局變量,該變量對(duì)于一個(gè)圖元來(lái)說(shuō)是不可改變的。同attribute限定符一樣,uniform可以從OpenGL應(yīng)用程序中接收傳遞過(guò)來(lái)的數(shù)據(jù)。uniform限定符可以用于頂點(diǎn)著色器和像素著色器。
最后GLSL還提供了從頂點(diǎn)著色器向片段著色器傳遞數(shù)據(jù)的方法,即使用varying限定符。
4.Code Example
在《A Simple OpenGL Shader Example》中已經(jīng)成功實(shí)現(xiàn)了一個(gè)帶Shader的OpenGL程序。事實(shí)上這是兩個(gè)相對(duì)獨(dú)立的Shader,它們只能使用OpenGL內(nèi)置的變量從外部OpenGL程序中獲取一些數(shù)據(jù)。比如當(dāng)前頂點(diǎn)坐標(biāo)、當(dāng)前像素顏色等。這些Shader還沒(méi)有自定義的變量,以便從OpenGL程序中傳遞數(shù)據(jù)。通常程序的設(shè)計(jì)者需要在OpenGL程序中更好地控制shader的行為,這就需要從OpenGL程序向shader傳遞數(shù)據(jù)。
如上述的4個(gè)限定符,可以用來(lái)聲明變量幫助shader從外部獲取數(shù)據(jù)。其中uniform變量可以用來(lái)從OpenGL程序中給vertex shader或fragment shader傳遞數(shù)據(jù),最很常用的一個(gè)限定符變量。將《A Simple OpenGL Shader Example》中的程序稍做修改,使得片段shader可以收到一個(gè)來(lái)自O(shè)penGL程序里面的數(shù)據(jù)。
實(shí)現(xiàn)的主要代碼在這兩個(gè)函數(shù)中:
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");
}
首先通過(guò)QShaderProgram的函數(shù)uniformLocation()給GLSL中的變量用一個(gè)整數(shù)標(biāo)記,對(duì)應(yīng)在OpenGL中的函數(shù)是 GLint glGetUniformLocation(GLuint program, const char* name);再通過(guò)函數(shù)setUniformValue()來(lái)更新GLSL中變量的值,對(duì)應(yīng)OpenGL中的函數(shù)為:glUniform{1234}(if,ui}。最后只用了一個(gè)片段著色器,代碼如下所示:
// 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);
}
運(yùn)行程序,當(dāng)程序視圖重繪時(shí)就會(huì)改變茶壺的顏色,如下圖所示:

Figure 4.1 Test uniform variable in GLSL
當(dāng)將uniform.fs中的v_time改名后,就會(huì)發(fā)現(xiàn)視圖一片漆黑,說(shuō)明shader已經(jīng)起作用了。
5.Conclusion
綜上所述,GLSL中通過(guò)限定符Qualifiers來(lái)實(shí)現(xiàn)OpenGL程序與GLSL的數(shù)據(jù)傳遞。其中uniform變量可以用來(lái)從OpenGL程序向片段著色器和頂點(diǎn)傳遞數(shù)據(jù),是很常用的一種方式。
本文在Qt中測(cè)試了uniform變量效果,可以發(fā)現(xiàn)Qt對(duì)OpenGL的面向?qū)ο蠓庋b還是很方便使用,也很容易找到與之對(duì)應(yīng)的OpenGL函數(shù)。通過(guò)學(xué)習(xí)使用Qt中的OpenGL來(lái)方便學(xué)習(xí)理解OpenGL相關(guān)知識(shí)點(diǎn)。
6. References
1. san. Shader support in OCCT6.7.0. http://dev.opencascade.org/index.php?q=node/902
2. Qt Assistant.