GLSL的简介(下)

来源:互联网 发布:神经网络 股票 知乎 编辑:程序博客网 时间:2024/04/29 11:21

着色器例子

到目前为止我们已经学习了一些关于GLSL的东西。但是着色器到底长什么样,下面是一些简单的例子:

环境光着色器

环境光着色器毫无疑问是最简单的着色器,每一个被渲染的像素都有一个具体的颜色:

顶点着色器:

void main() 

    // Transforming The Vertex 

    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; 

片段着色器:

Void main() 

    // Setting Each Pixel To Red 

    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); 

漫射光着色器

漫反射光照模型是一种被普遍使用的光照模型,它的实现稍微难一点:

顶点着色器:

varying vec3 normal; 

varying vec3 vertex_to_light_vector; 

  

void main() 

    // Transforming The Vertex 

    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; 

  

    // Transforming The Normal To ModelView-Space 

    normal = gl_NormalMatrix * gl_Normal;  

  

    // Transforming The Vertex Position To ModelView-Space 

    vec4 vertex_in_modelview_space = gl_ModelViewMatrx * gl_Vertex; 

  

    // Calculating The Vector From The Vertex Position To The Light Position 

    vertex_to_light_vector = vec3(gl_LightSource[0].position – vertex_in_modelview_space); 

片段着色器:

varying vec3 normal; 

varying vec3 vertex_to_light_vector; 

  

void main() 

    // Defining The Material Colors 

    const vec4 AmbientColor = vec4(0.1, 0.0, 0.0, 1.0); 

    const vec4 DiffuseColor = vec4(1.0, 0.0, 0.0, 1.0); 

  

    // Scaling The Input Vector To Length 1 

    vec3 normalized_normal = normalize(normal); 

    vec3 normalized_vertex_to_light_vector = normalize(vertex_to_light_vector); 

  

    // Calculating The Diffuse Term And Clamping It To [0;1] 

    float DiffuseTerm = clamp(dot(normal, vertex_to_light_vector), 0.0, 1.0); 

  

    // Calculating The Final Color 

    gl_FragColor = AmbientColor + DiffuseColor * DiffuseTerm; 

纹理映射着色器

这是一个简单的纹理映射着色器。

顶点着色器:

varying vec2 texture_coordinate;

void main() 

    // Transforming The Vertex 

    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; 

  

    // Passing The Texture Coordinate Of Texture Unit 0 To The Fragment Shader 

    texture_coordinate = vec2(gl_MultiTexCoord0); 

}

片段着色器:

varying vec2 texture_coordinate; uniform sampler2D my_color_texture;

void main() 

    // Sampling The Texture And Passing It To The Frame Buffer 

    gl_FragColor = texture2D(my_color_texture, texture_coordinate); 

如何在你的Opengl应用中使用GLSL

我们已经学习了如何编写着色器和它们的优点,但是如何在我们的OpenGL程序中使用GLSL?

GLSL通过4个扩展来使用:

GL_ARB_shader_objects

GL_ARB_shading_language_100

GL_ARB_vertex_shader

GL_ARB_fragment_shader

所有的扩展的文档在参考文献[3]中都可以找到。

GLSL非常类似于C/C++.所以如果你想使用GLSL,你必须完成下面的步骤:

将着色器源代码传送给着色器物件。

编译着色器源代码。

将着色器物件与一个程序物件连接。

一个着色器物件代表你的着色器源代码。你能够将着色器代码传送给一个着色器物件然后编译着色器物件。一个程序物件代表渲染管线的可用部分。

如何创建这些物件?

一个程序物件使用下面的语句创建:

GLhandleARB glCreateProgramObjectARB()

一个着色器物件使用下面的语句创建:

GLhandleARB glCreateShaderObjectARB(GLenum shaderType),其中shaderType为GL_VERTEX_SHADER_ARB 或GL_FRAGMENT_SHADER_ARB。

例如,如果你想创建一个顶点着色器物件,代码如下:

GLenum my_vertex_shader; 

my_vertex_shader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); 

如何将着色器源代码传送给着色器物件?

这可以通过下面的代码实现:
void glShaderSourceARB(GLhangleARB shader, GLuint number_strings, const GLcharARB** strings, Glint * length); 

使用这个命令时你可以将多个字符串传送给一个着色器物件。shader为目标着色器物件,number_strings为你想传给OpenGL的字符串的个数,strings为字符的指针的指针(即字符串的指针),指向源代码存放的地址,length指向一个int数组的地址,这个数组保存每个字符串的长度,如果length等于NULL那么字符串被假设为以NULL结束。

下面是一个将源代码传送给顶点着色器的例子:

char * my_source; 

my_source = GetSource(); 

  

GLenum my_vertex_shader; 

my_vertex_shader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); 

  

glShaderSourceARB(my_vertex_shader, 1, &my_source, NULL); 

如何编译一个着色器物件?

这可以通过使用下面的代码实现:

void glCompileShader(GLhandleARB shader); 

其中shader是我们的着色器物件。

如何将着色器物件与程序物件连接?

首先我们要创建一个程序物件。然后用下面的代码将所有的着色器物件与程序物件挂接:
void glAttachObjectARB(GLhandleARB program, GLhandleARB shader); 

其中program为程序物件,而shader为着色器物件。

然后使用下面的代码完成连接:

void glLinkProgramARB(GLhandleARB program); 

其中program为程序物件。

如何使用程序物件?

使用程序物件用到下面的函数:

void glUseProgramObjectARB(GLhandleARB program); 

其中program为程序物件。如果program为0那么OpenGL标准的固定管线将被使用。

将上面的内容放在一起

下面是加载、编译、链接和使用着色器的例子:

char * my_fragment_shader_source; 

char * my_vertex_shader_source; 

  

// Get Vertex And Fragment Shader Sources 

my_fragment_shader_source = GetFragmentShaderSource(); 

my_vertex_shader_source = GetVertexShaderSource(); 

  

GLenum my_program; 

GLenum my_vertex_shader; 

GLenum my_fragment_shader; 

  

// Create Shader And Program Objects 

my_program = glCreateProgramObjectARB(); 

my_vertex_shader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); 

my_fragment_shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); 

  

// Load Shader Sources 

glShaderSourceARB(my_vertex_shader, 1, &my_vertex_shader_source, NULL); 

glShaderSourceARB(my_fragment_shader, 1, &my_fragment_shader_source, NULL); 

  

// Compile The Shaders 

glCompileShaderARB(my_vertex_shader); 

glCompileShaderARB(my_fragment_shader); 

  

// Attach The Shader Objects To The Program Object 

glAttachObjectARB(my_program, my_vertex_shader); 

glAttachObjectARB(my_program, my_fragment_shader); 

  

// Link The Program Object 

glLinkProgramARB(my_program); 

  

// Use The Program Object Instead Of Fixed Function OpenGL 

glUseProgramObjectARB(my_program); 

如果所有的着色器成功编译并且成功连接到程序物件,那么在glUseProgramObjectARB 之后的所有渲染将使用自定义的着色器。

其他一些重要的函数

当然你可以使用下面的函数删除着色器物件和程序物件:

void glDeleteObjectARB(GLhandleARB object) 

另外一个非常重要的函数:

glGetInfoLogARB(GLhandleARB object, GLsizei maxLenght, GLsizei *length, GLcharARB *infoLog) 

其中object为一个着色器物件或者程序物件。

maxLenght为可以写进infoLog的字符的最大个数。

length为写进infoLog的字符的个数。

infoLog指向保存日志信息的字符串的地址。

这个函数提供着色器物件和程序物件的信息。例如,如果你在着色器编译失败之后调用这个函数就可以得到编译失败的信息。

常量(Uniforms)

你可以使用下面的方法将Uniforms传给OpenGL:

void glUniform{1|2|3|4}{f|i}ARB(GLint location, TYPE val)

void glUniform{1|2|3|4}{f|i}vARB(GLint location, GLuint count, const TYPE * vals)

void glUniformMatrix{2|3|4|}fvARB(GLint location, GLuint count, GLboolean transpose, const GLfloat * vals)

location为uniform的地址。

count为你想传递的类型TYPE中的值的数量。

val为TYPE类型的一个值。

Vals指向TYPE类型的一个值(对于矩阵只能用float类型)。

transpose指明被传递的矩阵是否被转置。

取得uniform的位置可以使用下面的函数:

GLint glGetUniformLocationARB(GLhandleARB program, const GLcharARB * name) 

其中program程序物件,name为想得到位置的uniform的名字。

下面是一个使用uniform的例子:

glUseProgramObjectARB(my_program); 

int my_vec3_location = glGetUniformLocationARB(my_program, “my_3d_vector”); 

glUniform3fARB(my_vec3_location, 1.0f, 4.0f, 3.0f); 

注意你不能通过glUniform传递内建的uniform!

一般的属性(Generic Attributes)

一般的属性的使用就像常量:

void glVertexAttrib{1|2|3|4}{s|f|d}ARB(GLuint index, TYPE val)

void glVertexAttrib{1|2|3|4}{s|f|d}vARB(GLuint index, const TYPE * vals)

index为attribute的位置。

val为TYPE类型的一个值。

vals为TYPE类型的指针(对于矩阵只能用float类型)。

获取attribute的位置可以使用下面的函数:

GLint glGetAttribLocationARB(GLhandleARB program, const GLcharARB* name) 

Program为程序物件,name为我们想得到位置的attribute的名字。

一个例子:

glUseProgramObjectARB(my_program); 

int my_vec3_location = glGetAttribLocationARB(my_program, “my_3d_vector”); 

  

glBegin(GL_TRIANGLES); 

    glVertexAttrib3f(my_vec3_location, 4.0f, 2.0f, 7.0f); 

    glVertex3f(1.0f, 1.0f, 1.0f); 

  

    glVertexAttrib3f(my_vec3_location, 2.0f, 9.0f, 2.0f); 

    glVertex3f(-1.0f, 1.0f, 1.0f); 

  

    glVertexAttrib3f(my_vec3_location, 1.0f, 0.0f, 5.0f); 

    glVertex3f(1.0f, -1.0f, 1.0f); 

glEnd(); 

在GLSL中使用纹理

因为许多GLSL的初学者在纹理方面遇到困难,所以我说一下这方面的一些事项。

正如我先前说过的,在GLSL中通过采样器(sampler)访问纹理,而且采样器必须是归一化的。但是我们如何告诉GLSL哪个采样器该使用哪张纹理?

纹理并没有直接传给GLSL,而是传递绑定纹理的纹理单位,通过以下几步:

得到采样器的uniform位置。

将纹理绑定到纹理单元i。

通过glUniform将i作为整数传递。

这是一个例子:

glUseProgramObjectARB(my_program); 

int my_sampler_uniform_location = glGetUniformLocationARB(my_program, “my_color_texture”); 

  

glActiveTexture(GL_TEXTURE0 + i); 

glBindTexture(GL_TEXTURE_2D, my_texture_object); 

  

glUniform1iARB(my_sampler_uniform_location, i); 

其中i为绑定纹理的纹理单元。

在GLSL中还有其他一些有用的函数。可以参考[4]。

着色器硬件/驱动 概述

OK,我们学了不少关于着色器和GLSL的知识。但是哪些硬件可以运行着色器?

GLSL非常类似于DirectX Shader Model 3.0。

到目前为止GLSL在以下的显卡中是可用的:

使用Catalyst 4.5 驱动(这个驱动bug较少)的ATI Radeon 9500, 9600, 9700 和9800 

使用forceware 60 系列驱动的nVidia GeForce FX 5200, 5600, 5700, 5800, 5900, 5950,你可以在这个网址下载驱动http://www.3dchipset.com/drivers/beta/nvidia/nt5/6111.php

因为这些显卡实际上只支持Shader Model 2.0,GLSL的一些功能不能使用,比如loops, vertex shader sampler。

我不认为更老的显卡支持GLSL片段着色器,但是我想在这些老显卡的驱动软件中存在对于顶点着色器的模拟。

GLSL着色器开发工具

有一些不错的GLSL开发工具:

Typhoon Labs有一个不错的开发工具叫做Shader Designer,你可以从这个网址得到:http://www.typhoonlabs.com

ATI和3Dlabs也合作开发过一个RenderMonkeyde GLSL版本,不过现在已经不更新了。

 

References:

[1] OpenGL Shading Language Master Class Notes, 3DLabs, March/April 2004
http://www.3dlabs.com/support/developer/ogl2/presentations/GLSLMasterClass2004.pdf
[2] The OpenGL Shading Language Specification Version 1.051, 3DLabs, February 2003
http://www.3dlabs.com/support/developer/ogl2/downloads/ShaderSpecV1.051.pdf
[3] The OpenGL extension registry
http://oss.sgi.com/projects/ogl-sample/registry/
[4] OpenGL shading language “The orange book”, Randi J. Rost, ISBN 0-321-19789-5
http://www.3dshaders.com/

 

原创粉丝点击