GLSL 在OpenGL中向shader传递信息【转】

http://blog.csdn.net/hgl868/article/details/7872219

引言

一个OpenGL程序可以用多种方式和shader通信。注意这种通信是单向的,因为shader的输出只能是渲染到某些目标,比如颜色和深度缓存。

OpenGL的部分状态可以被shader访问,因此程序改变OpenGL某些状态就可以与shader进行通信了。例如一个程序想把光的颜色传给shader,可以直接调用OpenGL接口,就像使用固定功能流水线时做的那样。

不过,使用OpenGL状态并不是设置shader中使用数据的直观方式。比如一个shader需要一个表示时间变化的变量来计算动画,在OpenGL状态中就没有现成的变量可用。当然,你可以使用没有用到的“镜面光截止角度(cutoffangle)”这样一个变量表示时间,但显然让人难以接受。

幸运的是,GLSL允许用户自定义变量,实现OpenGL应用程序与shader通信。有了这个功能,你就可以命名一个叫做timeElapsed的变量表示经过的时间。

上文的讨论涉及到了GLSL提供的两种类型修饰符(更多的类型将在后面提到):

·一致变量(Uniform)

·属性(Attribute)

在shader中定义的变量如果用这两种类型修饰符,表示对shader来说,它们是只读的。下面将详细讲述怎样使用这些类型的变量。

还有一种将变量送给shader的方法:使用纹理。一个纹理不止可以表示一张图片,它还可以表示一个数组。事实上,你完全可以决定如何在shader中解释纹理数据,即使它真是一幅图片。

数据类型和变量

下面是GLSL中的基本数据类型:

·float

·bool

·int

浮点类型与C中类似,布尔类型可以为true或false。这些基本类型可以组成2、3或4维向量,如下所示:

·vec{2,3,4} a vector of 2,3,or 4 floats

·bvec{2,3,4} bool vector

·ivec{2,3,4} vector of integers

GLSL还包括2×2、3×3或4×4型矩阵,因为这些矩阵类型在图形处理中很常用:

·mat2

·mat3

·mat4

此外,还有一组用来实现纹理访问的特殊类型,它们被称为采样器(sampler),在读取纹理值(也称为纹素texel)时用到。下面就是纹理采样用到的数据类型:

·sampler1D – for 1D textures

·sampler2D – for 2D textures

·sampler3D – for 3D textures

·samplerCube – for cube map textures

·sampler1DShadow – for shadow maps

·sampler2DShadow – for shadow maps

在GLSL中,可以像C一样声明和访问数组,但是不能在声明时初始化数组。GLSL还可以定义结构体:

struct dirlight
{
    vec3 direction;
    vec3 color;

};

变量声明一个基本类型变量的方法与C类似,你还可以在声明它的同时进行初始化。

float a,b;       // two vector (yes, the comments are like in C)
int c = 2;       // c is initialized with 2
bool d = true;  // d is true

声明其它类型变量也是按照这种方法,但是初始化与C语言有区别。GLSL非常依赖构造函数实现初始化和类型转换。、

float b = 2;          // incorrect, there is no automatic type casting
float e = (float)2; // incorrect, requires constructors for type casting
int a = 2;
float c = float(a); // correct. c is 2.0
vec3 f;                // declaring f as a vec3
vec3 g = vec3(1.0,2.0,3.0); // declaring and initializing g

在GLSL中使用一些变量初始化其它变量是非常灵活的。你只需要给出需要的数据成员即可。请看下面的例子:

vec2 a = vec2(1.0,2.0);
vec2 b = vec2(3.0,4.0);
vec4 c = vec4(a,b)   // c = vec4(1.0,2.0,3.0,4.0);
vec2 g = vec2(1.0,2.0);
float h = 3.0;
vec3 j = vec3(g,h);

矩阵的初始化也是类似方法,矩阵包含很多种构造函数,下面的例子给出了一些初始化矩阵的构造函数:

mat4 m = mat4(1.0)   // initializing the diagonal of the matrix with 1.0
vec2 a = vec2(1.0,2.0);
vec2 b = vec2(3.0,4.0);
mat2 n = mat2(a,b); // matrices are assigned in column major order
mat2 k = mat2(1.0,0.0,1.0,0.0); // all elements are specified

下面的例子给出了初始化结构体的方法:

struct dirlight     // type definition
{
    vec3 direction;
    vec3 color;
};
dirlight d1;
dirlight d2 = dirlight(vec3(1.0,1.0,0.0),vec3(0.8,0.8,0.4));

在GLSL中还有一些实用的选择子(selector),可以简化我们的操作并让代码更简洁。访问一个向量可以使用如下的方法:

vec4 a = vec4(1.0,2.0,3.0,4.0);
float posX = a.x;
float posY = a[1];
vec2 posXY = a.xy;
float depth = a.w

在上面的代码片段中,可以使用x、y、z、w来访问向量成员。如果是颜色的话可以使用r、g、b、a,如果是纹理坐标的话可以使用s、t、p、q。注意表示纹理坐标通常是使用s、t、r、q,但r已经表示颜色中的红色(red)了,所以纹理坐标中需要使用p来代替。矩阵的选择子可以使用一个或两个参数,比如m[0]或者m[2][3]。第一种情况选择了第一列,第二种情况选择了一个数据成员。

对于结构体来说,可以像在C语言中一样访问其成员。所以访问前面定义的结构体,可以使用如下的代码:

d1.direction = vec3(1.0,1.0,1.0);

变量修饰符

修饰符给出了变量的特殊含义,GLSL中有如下修饰符:

·const – 声明一个编译期常量。

·attribute– 随不同顶点变化的全局变量,由OpenGL应用程序传给顶点shader。这个修饰符只能用在顶点shader中,在shader中它是一个只读变量。

·uniform– 随不同图元变化的全局变量(即不能在glBegin/glEnd中设置),由OpenGL应用程序传给shader。这个修饰符能用在顶点和片断shader中,在shader中它是一个只读变量。

·varying
–用于顶点shader和片断shader间传递的插值数据,在顶点shader中可写,在片断shader中只读。

一致变量(Uniform Variables)

不同于顶点属性在每个顶点有其自己的值,一个一致变量在一个图元的绘制过程中是不会改变的,所以其值不能在glBegin/glEnd中设置。一致变量适合描述在一个图元中、一帧中甚至一个场景中都不变的值。一致变量在顶点shader和片断shader中都是只读的。

首先你需要获得变量在内存中的位置,这个信息只有在连接程序之后才可获得。注意,对某些驱动程序,在获得存储位置前还必须使用程序(调用glUseProgram)。

获取一个一致变量的存储位置只需要给出其在shader中定义的变量名即可:

GLint glGetUniformLocation(GLuint program, const char *name);
参数:
·program – the hanuler to the program
·name – the name of the variable

返回值就是变量位置,可以用此信息设置变量的值。根据变量的数据类型不同,有一系列函数可以用来设置一致变量。用来设置浮点值的一组函数如下:

void glUniform1f(GLint location, GLfloat v0);
void glUniform2f(GLint location, GLfloat v0, GLfloat v1);
void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
或者
GLint glUniform{1,2,3,4}fv(GLint location, GLsizei count, GLfloat *v);
参数:
·location – the previously queried location
·v0,v1,v2,v3 – float values
·count – the number of elements in the array
·v – an array of floats

对integer类型也有一组类似的函数,不过要用i替换函数中的f。对bool类型没有专门的函数,但可以使用整数的0和1来表示真假。一旦你使用了一致变量数组,那么就必须使用向量版本的函数。

对sampler变量,使用函数glUniform1i和glUniform1iv。

矩阵也是一种GLSL的数据类型,所以也有一组针对矩阵的函数:

GLint glUniformMatrix{2,3,4}fv(GLint location, GLsizei count, GLboolean transpose, GLfloat *v);
参数:
location – the previously queried location.
count – the number of matrices. 1 if a single matrix is being set, or n for an array of n matrices.
transpose – wheter to transpose the matrix values. A value of 1
indicates that the matrix values are specified in row major order, zero
is column major order
v – an array of floats.

还有一点要注意的是:使用这些函数之后,变量的值将保持到程序再次连接之时。一旦进行重新连接,所有变量的值将被重置为0。

最后是一些示例代码。假设一个shader中使用了如下变量:

uniform float specIntensity;
uniform vec4 specColor;
uniform float t[2];
uniform vec4 colors[3];

在OpenGL程序中可以使用下面的代码设置这些变量:

GLint loc1,loc2,loc3,loc4;
float specIntensity = 0.98;
float sc[4] = {0.8,0.8,0.8,1.0};
float threshold[2] = {0.5,0.25};
float colors[12] = {0.4,0.4,0.8,1.0,
                0.2,0.2,0.4,1.0,
                0.1,0.1,0.1,1.0};

loc1 = glGetUniformLocation(p,"specIntensity");
glUniform1f(loc1,specIntensity);

loc2 = glGetUniformLocation(p,"specColor");
glUniform4fv(loc2,1,sc);

loc3 = glGetUniformLocation(p,"t");
glUniform1fv(loc3,2,threshold);

loc4 = glGetUniformLocation(p,"colors");
glUniform4fv(loc4,3,colors);

例子代码的下载地址:

http://lighthouse3d.com/wptest/wp-content/uploads/2011/03/glutglsl2_2.0.zip

注意设置一个数组(例子中的t)与设置四元向量(例子中的colors和specColor)的区别。中间的count参数指在shader中声明的数组元素数量,而不是在OpenGL程序中声明的。所以虽然specColor包含4个值,但glUniform4fv函数中的参数是1,因为它只是一个向量。另一种设置specColor的方法:

loc2 = glGetUniformLocation(p,"specColor");
glUniform4f(loc2,sc[0],sc[1],sc[2],sc[3]);

GLSL中还可以获取数组中某个变量的地址。比如,可以获得t[1]的地址。下面的代码片段展示了设置t数组元素的另一种方法:

loct0 = glGetUniformLocation(p,"t[0]");
glUniform1f(loct0,threshold[0]);

loct1 = glGetUniformLocation(p,"t[1]");
glUniform1f(loct1,threshold[1]);

注意在glGetUniformLocation中使用方括号指示的变量。


属性变量(Attribute Variables)

在前一节提到,一致变量只能针对一个图元全体,就是说不能在glBegin和glEnd之间改变。

如果要针对每个顶点设置变量,那就需要属性变量了。事实上属性变量可以在任何时刻更新。在顶点shader中属性变量是只读的。因为它包含的是顶点数据,所以在片断shader中不能直接应用。

与一致变量相似,首先你需要获得变量在内存中的位置,这个信息只有在连接程序之后才可获得。注意,对某些驱动程序,在获得存储位置前还必须使用程序。

GLint glGetAttribLocation(GLuint program,char *name);
参数:
program – the handle to the program.
name – the name of the variable

上述函数调用的返回变量在存储器中的地址。下面就可以为它指定一个值,类似一致变量,每种数据类型都有对应的函数。

void glVertexAttrib1f(GLint location, GLfloat v0);
void glVertexAttrib2f(GLint location, GLfloat v0, GLfloat v1);
void glVertexAttrib3f(GLint location, GLfloat v0, GLfloat v1,GLfloat v2);
void glVertexAttrib4f(GLint location, GLfloat v0, GLfloat v1,,GLfloat v2, GLfloat v3);
或者
GLint glVertexAttrib{1,2,3,4}fv(GLint location, GLfloat *v);
参数:
location – the previously queried location.
v0,v1,v2,v3 – float values.
v – an array of floats.

对于integer类型,也有一组类似的函数。与一致变量不同,这里向量版的函数并不支持对向量数组的赋值,所以函数参数用向量或是分别指定的效果没有太大区别,就好像OpenGL中glColor3f和glColor3fv的关系。下面是一个简单的例子,假定顶点shader中声明了一个名为height的浮点属性变量,在程序连接之后可以进行如下操作:

loc = glGetAttribLocation(p,"height");

在执行渲染的代码中间可以为shader中的变量赋值:

glBegin(GL_TRIANGLE_STRIP);
    glVertexAttrib1f(loc,2.0);
    glVertex2f(-1,1);
    glVertexAttrib1f(loc,2.0);
    glVertex2f(1,1);
    glVertexAttrib1f(loc,-2.0);
    glVertex2f(-1,-1);
    glVertexAttrib1f(loc,-2.0);
    glVertex2f(1,-1);
glEnd();

例子代码的下载地址:

http://lighthouse3d.com/wptest/wp-content/uploads/2011/03/glutglsl3_2.0.zip、

顶点数组和属性变量也可以一起使用。首先需要使能数组,使用如下函数:

void glEnableVertexAttribArray(GLint loc);
参数:
loc – the location of the variable.

接下来使用函数提交包含数据的数组指针:

void glVertexAttribPointer(GLint loc, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
参数:
loc – the location of the variable.
size – the number of components per element, for instance: 1 for float; 2 for vec2; 3 for vec3, and so on.
type – The data type associated: GL_FLOAT is an example.
normalized – if set to 1 then the array values will be normalized,
converted to a range from -1 to 1 for signed data, or 0 to 1 for
unsigned data.
stride – the spacing between elements. Exactly the same as in OpenGL.
pointer – pointer to the array containing the data.

下面是示例代码,首先执行初始化,定义了顶点数组和属性数组。

float vertices[8] = {-1,1, 1,1, -1,-1, 1,-1};
float heights[4] = {2,2,-2,-2};
...
loc = glGetAttribLocation(p,"height");

glEnableClientState(GL_VERTEX_ARRAY);
glEnableVertexAttribArray(loc);
glVertexPointer(2,GL_FLOAT,0,vertices);
glVertexAttribPointer(loc,1,GL_FLOAT,0,0,heights);

接下来的渲染步骤与OpenGL中的通常做法一致,比如调用glDrawArrays。示例源代码下载地址:

http://lighthouse3d.com/wptest/wp-content/uploads/2011/03/glutglsl4_2.0.zip

易变变量(Varying Variables)

前面说过,shader包括两种类型:顶点shader和片断shader。为了计算片断的值,往往需要访问顶点的插值数据。例如,当使用逐片断光照时,我们需要知道当前片断的法线,但是在OpenGL中只为每个顶点指定了法线。顶点shader可以访问这些法线,而片断shader不能,因为法线是OpenGL程序作为属性变量指定的。

顶点变换后的数据移动到流水线的下一个阶段,在这个阶段通过使用连接信息,生成了所有图元并完成片断化。对每个片断,有一组变量会被自动进行插值并提供给片断shader,这些都是固定功能。片断的颜色就是这么处理的,到达片断shader的颜色就是组成图元的顶点颜色插值的结果。

像片断shader接收到的这种插值产生的变量,就是“易变变量”类型。GLSL包含一些预先定义的易变变量,例如前面提到的颜色。用户也可以自己定义易变变量,它们必须同时在顶点shader和片断shader中声明:

varying float intensity;

一个易变变量必须先在顶点shader中声明,然后计算每个顶点的变量值。在片断shader中,接收这个变量通过插值得到的结果,注意此时这个变量是只读的。

语句和函数

控制流语句

与C语言类似,GLSL中有类似if-else的条件语句,for,while,do-while等循环语句。

if (bool expression)
    ...
else
    ...

for (initialization; bool expression; loop expression)
    ...

while (bool expression)
    ...

do
    ...
while (bool expression)

GLSL也有跳转语句:

·continue – available in loops, causes a jump to thenext iteration of the loop

·break – available in loops, causes an exit of theloop

·discard

最后的discard关键字只能在片断shader中使用,它将在不写入帧缓存或者深度缓存的情况下,终止当前片断的shader程序。

函数

与C语言类似,shader也是由函数组成的结构化程序。至少每类shader都必须包含一个如下方式声明的主函数:

void main()

此外用户还可以自定义函数。这些函数像C函数一样,一般都会有返回值,返回值的类型没有限制,但不能是数组。

函数参数可以有如下修饰符:

·in – for input parameters

·out – for outputs of the function. The returnstatement is also an option for sending the result of a function.

·inout – for parameters that are both input andoutput of a function

如果没有指定修饰符,默认情况下为in类型。

最后还有两点要注意:

·允许函数重载,只要参数不同。

·在标准中没有定义递归行为。

结束本节之前来看一个函数的例子:

vec4 toonify(in float intensity)
{
    vec4 color;
    if (intensity > 0.98)
       color = vec4(0.8,0.8,0.8,1.0);
    else if (intensity > 0.5)
       color = vec4(0.4,0.4,0.8,1.0);
    else if (intensity > 0.25)
       color = vec4(0.2,0.2,0.4,1.0);
    else
       color = vec4(0.1,0.1,0.1,1.0);

return(color);
}

时间: 2024-12-20 07:28:16

GLSL 在OpenGL中向shader传递信息【转】的相关文章

OpenGL中的Shader

http://blog.csdn.net/huangcanjun187/article/details/52474365 学习总结自:http://learnopengl.com/#!Getting-started/Hello-Triangle http://learnopengl.com/#!Getting-started/Shaders 继上篇文章中提到,OpenGL是为了在GPU上同时跑成千上万个程序,在GPU上跑的这些小程序,称为Shader. 准备 我们在运行GPU程序前,得准备几样东

OpenGL中使用Shader的基本步骤

在OpenGL中,创建和使用Shader的基本步骤如下: 1.通过glCreateShader创建一个或多个着色器对象: 2.使用glShaderSource加载着色器的源代码,和着色器对象关联: 3.glCompileShader编译每个着色器对象 4.使用glCreateProgram创建程序对象 5.通过glAttachShader将所有着色器对象绑定到程序对象上: 6.glLinkProgram链接程序对象: 7.调用glUseProgram使用着色器程序对象,使得着色器可执行程序成为O

cocos2dx(3.X)中使用shader

原文链接:http://blog.csdn.net/xufeng0991/article/details/47256583 一 shader的基本概念 1 什么是shader shader即着色器,就是专门用来渲染3D图形的一种技术. 通过shader,可以自己编写显卡渲染画面的算法,使画面更漂亮.更逼真. 2 shader分类 shader又分两种,一种是顶点shader(3D图形是由三角形组成的,顶点shader就是计算顶点位置,并为后期像素渲染做准备的), 另一种是像素shader,就是以

OpenGL中使用着色器

OpenGL中使用GLSL着色器步骤 GLSL既适用于顶点着色器,也适用于片段着色器. 使用着色器对象的步骤: 1.创建着色器对象: GLuint glCreateShader(GLenum type); //创建一个着色器对象,type值必须是GL_VERTEX_SHADER或GL_FRAGMENT_SHADER.error返回0 2.把着色器的源码与着色器对象相关联: glShaderSource(GLuint shader, GLsizei count, const GLChar** st

OpenGL中glVertex、显示列表(glCallList)、顶点数组(Vertex array)、VBO及VAO区别

OpenGL中glVertex.显示列表(glCallList).顶点数组(Vertex array).VBO及VAO区别 1.glVertex 最原始的设置顶点方法,在glBegin和glEnd之间使用.OpenGL3.0已经废弃此方法.每个glVertex与GPU进行一次通信,十分低效. glBegin(GL_TRIANGLES); glVertex(0, 0); glVertex(1, 1); glVertex(2, 2); glEnd(); 2.显示列表(glCallList) 每个gl

qml 中 使用 shader

使用绘制工具如Photoshop .Flash已经可以创建许多效果非常绚丽的图像.动画等. Qt/QML 的努力其实是在这些工具发展的后面, 因此很多效果在Qt中无法实现. 不得不佩服Qt小组的才智, 他们为了解决这个矛盾, 采取了让qt 5.x 跟opengl 紧密结合的方法, openGL是现代图形学的一个有力的编程工具,很多图形学的概念都已经在opengl中实现: 因此虽然实现一些特效虽然需要比较丰富的图形学知识, 但已经让以前很多的不可能变为可能. Qt 中可以有多种方法使用opengl

OpenGL中实现双缓冲技术

在OpenGL中实现双缓冲技术的一种简单方法: 1.在调用glutInitDisplayMode函数时, 开启GLUT_DOUBLE,即glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);.这里将我们惯用的GLUT_SINGLE替换为GLUT_DOUBLE,意为要使用双缓冲而非单缓冲. 2. 调用glutDisplayFunc(display)注册回调函数时, 在回调函数中所有绘制操作完成后调用glutSwapBuffers()交换两个缓冲区指针. 3. 调用

什么是OpenGL中的深度、深度缓存、深度测试?

原文来自http://blog.csdn.net/xiaoquanhuang/article/details/6613705 1)直观理解 深度其实就是该象素点在3d世界中距离摄象机的距离,深度缓存中存储着每个象素点(绘制在屏幕上的)的深度值!深度测试决定了是否绘制较远的象素点(或较近的象素点),通常选用较近的,而较远优先能实现透视的效果!!! 2)Z值(深度值).Z buffer(深度缓存) 下面先讲讲Z坐标.Z坐标和X.Y坐标一样.在变换.裁减和透视除法后,Z的范围为-1.0~1.0.Dep

openGL中的混合

    之前在项目中就使用过混合,但是研究的不深入,近期美术的一个需求让我下决心重新深入的研究了一下混合以及它在cocos2d-x中的使用,在这里分享给大家. 混合(blend,有些翻译书上把它称作混融,以下简称混合),在openGL中,当一个输入的片元通过了所有相关的片元测试,就可以在与颜色缓存中当前的内容通过某种方式进行合并了.最简单的,也是默认的方式,就是直接覆盖已有的值,实际上不能称作是合并.除此之外,我们也可以将帧缓存中已有的颜色与输入的片元颜色进行混合.这是在openGL流程上的定义