GLSL. Basic Function
n
OpenGL Shader的运行步骤:
第一步:创建程序。我们通过以下接口创建一个Shader程序:
这个函数返回一个程序对象
第二步:编译Shader。这里我们有两个shader,一个是vertex shader(顶点着色器);一个是fragment shader(片断着色器)。
我们使用自定义的- (BOOL) compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file方法分别对顶点着色器和片断着色器进行编译。对于OpenGL着色器创建的基本步骤而言,我们要调用的OpenGL API的接口依次是:
这个接口用于创建一个着色器。这里的参数shaderType是个枚举值,用于指定要创建哪种类型的着色器。GL_VERTEX_SHADER表示顶点着色器;GL_TESS_CONTROL_SHADER用于细分曲面的控制阶段(流水线级,OpenGL4.1);GL_TESS_EVALUATION_SHADER(OpenGL4.1)用于细分曲面的计算阶段;GL_GEOMETRY_SHADER用于几何着色器;GL_FRAGMENT_SHADER则用于片断处理器。这个函数的返回可看作为是一个着色器的句柄。
这个接口用于指定着色器源代码。参数介绍:
shader:指定着色器句柄
count:用于指定第三个参数string中含有多少个const GLchar*的元素
string:源代码文本,以字符串指针的形式给出
length:分别指定string中每个子串的长度
如果length为NULL,那么OpenGL假定每个子串均以NIL('\0')结尾.
这个接口用于编译着色器代码。
我们可以通过下列两个接口来获取编译是否成功,若不成功,则获得错误信息:
这个接口可以用于获得编译结果信息的长度。第二个参数,我们传入GL_INFO_LOG_LENGTH,那么长度值就会被写入params所指向的地址。
这个接口用于获得具体到日志信息。
maxLength:用于指定你所提供的存放日志信息的缓存的最大长度
length:用于返回实际日志的长度
infoLog:写日志信息的缓存
这里对上面的着色器类型再补充说明一下,GL_GEOMETRY_SHADER,即几何着色器是从OpenGL 3.3开始才引入的,因此OpenGL2.1没有支持这个着色器特征。因此,我们目前只能使用顶点着色器和片断着色器。但是这两个着色器足以满足我们绝大多数的需求。而在OpenGL手册中也并不十分推荐几何着色器的使用。
另外,我们编译完着色器后必须调用glGetShaderiv接口。此时,pname要传GL_COMPILE_STATUS进去,以获得编译状态。如果得到的结果为0说明编译失败,否则为成功。
第三步:分别将顶点着色器和片断着色器关联到程序对象上:
program是我们先前创建好的程序对象;而shader则是我们创建并编译完之后的着色器对象。
第四步:绑定属性位置。
program:指定程序对象
index:指定要被绑定的通用顶点属性的索引
name:指定index所绑定的通用顶点属性变量名
glBindAttribLocation用于将程序中一个用户定义的属性变量与一个通用属性索引相关联。当所指定的程序对象变为当前状态的一部分时,通过通用顶点属性index提供的将会修改通过name所指定的用户自定义属性变量的值。
我们可以看一下我们这边的代码例子,以下是顶点着色器的代码:
我们可以看到,这边定义了两个属性变量,一个是position,一个是color。然后再看一下主机端上绑定这两个属性变量的代码:
这里,ATTRIB_VERTEX和ATTRIB_COLOR是我们自定义的枚举值,分别是0和1。我们在后面将会看到如何通过glVertexAttribPointer将顶点数据和颜色数据分别传递给顶点着色器中的position和color
第五步:连接程序。
在我们的代码中是用- (BOOL) linkProgram:(GLuint)prog这个方法来完成连接的。
而这个方法主要调用的接口是:
调用完成后。我们可以通过使用以下接口对来获取连接信息:
上面这两个接口的使用与着色器的日志获取的方法一样。
最后同样再调用一次glGetProgramiv,此时pname传递的是GL_LINK_STATUS来获取连接状态。如果返回为0表示失败,否则表示成功。
在程序运行中,我们可以根据当前的OpenGL状态来查询设备端的Shader程序是否有效:
我们可以用与获得连接日志相同的方法来获取日志信息。
接着,我们可以获取着色器程序中uniform变量的位置:
其实我们这个代码非常简单,没有用到uniform变量。但是我们保留了一个,仅用于描述这个获取步骤,
第六步:释放着色器对象。
当我们成功地把程序构建完之后,我们先前创建的着色器对象已经没有用了,此时我们通过以下接口释放着色器对象:
我们接下去就可以进行安装程序:
这个接口用于安装指定的程序作为当前绘制上下文的一部分。
n
顶点着色器取代的是OpenGL固定功能流水线中对每个顶点的变换阶段。这部分的操作包括:顶点变换(投影变换、视图模型变换)、法线变换与规格化、纹理坐标生成、纹理坐标变换、光照、颜色材质应用。
下面看一个简单的Vertex Shader的代码:
//
// Shader.vsh
// GLSLTest
attributevec4position;
attributevec4color;
varying vec4 colorVarying;
uniform mat4 translate;
void main()
{
gl_Position = position * translate; //mat4(0.816, 0.433, 0.0, 0.0, -0.5, 0.616, 0.0, 0.0, 0.0, 0.0, -0.5, 0.0, 0.0, 0.0, -0.5, 1.0);
colorVarying = color;
}
最上面两个attribute变量的数据是从主机端输入。attribute在后期Shader Language版本中被废弃,取而代之的是in关键字。主机端通过以下接口把相关数据与vertex shader所对应的attribute变量进行绑定:
index:自己定义的一个id,与下面的name进行绑定
name:指定顶点着色器中所要绑定的变量名
在我们的主机端程序中,与上述position变量进行绑定的代码为:
// attribute index
enum{
ATTRIB_VERTEX,
ATTRIB_COLOR,
NUM_ATTRIBUTES
};
glBindAttribLocation(program, ATTRIB_VERTEX, "position");
与color绑定的代码为:
glBindAttribLocation(program, ATTRIB_COLOR, "color");
这里要注意的是,顶点着色器是可以与主机端应用进行直接交互的。而片断着色器则不能与主机端程序进行直接交互。因此,我们可以把片断着色器所要用到的数据先通过顶点着色器输入进来,然后通过varying属性传给片断着色器(Fragment Shader)。注意,在以后版本中varying关键字也被废弃。取而代之的是在顶点着色器部分用out关键字,而在片断着色器中相应地使用in关键字。
因此,attribute不仅仅是指顶点属性,也可以是颜色属性或用户自定义的一些变量,而我们用主机端的数据与这些attribute变量绑定时,都用glBindAttribLocation这一个接口。
而我们通过以下接口把主机端的数据发送给Shader端:
index:我们刚才所指定的attribute变量所绑定的id。
size:指定每个元素有多少分量,可以是1,2,3,4
type:指定数据类型
normalized:指定定点数据是否被规格化
stride:相邻两个元素之间的跨度(字节数)
pointer:指向数组首地址
下面将给出示例代码:
首先定义顶点坐标和颜色值:
static const GLfloat squareVertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f,
0.5f, 0.5f, 0.0f
};
static const GLubyte squareColors[] = {
255, 255, 0, 255,
0, 255, 255, 255,
0, 0, 0, 0,
255, 0, 255, 255,
};
然后,下面就是分别传送顶点坐标数据和颜色数据:
// Update attribute values
glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT, 0, 0, squareVertices);
glEnableVertexAttribArray(ATTRIB_VERTEX);
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, 1, 0, squareColors);
glEnableVertexAttribArray(ATTRIB_COLOR);
uniform变量:
uniform变量与attribute类似,都是通过主机端传递到shader,而且它们都不能在Shader端被修改。但是,attribute变量数据依赖于glDrawArray等绘制函数所提供的顶点个数,并且类型不能是矩阵类型,而uniform变量类型可以非常灵活,你甚至能通过uniform block定义自己的数据结构。并且uniform变量能够在Vertex Shader端与Fragment Shader端共享,这点,attribute则做不到。attribute只能把数据传给varying变量才能在Fragment Shader端使用。
要使用uniform变量,首先在主机端对shader中所用的uniform变量进行绑定,通过以下接口:
返回的是指定的uniform变量在shader中的索引,可以认为是一个id。
在本示例程序中,对我们定义的uniform mat4 translate;变量所绑定的代码如下:
这里,将主机端的数据传递到shader的uniform的代码是:
另外要强调一点的是,glUniformMatrix4fv是默认将数组以列向量的形式存放在矩阵中的,即对于四个向量——(a, b, c, d);(e, f, g, h);(i, j, k, l);(m, n, o, p),存放在一个数组中,构成——{ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p },那么通过调用glUniformMatrix4fv函数,并且第三个参数传递为GL_FALSE,那么获得的矩阵其实是:[a, e, i, m; b, f, j, n; c, g, k, o; d, h, l, p]。如果要将数组中的元素作为行向量传递到uniform的矩阵中,那么第三个形参transpose应该传给它GL_TRUE值。
上面要注意的是glVertexAttribPointer接口只指定attribute属性变量每个元素的长度和类型,但并没有指定元素个数。
元素个数都是通过glDrawArrays这样的绘制接口传递的。因此如果是自己自定义的attribute变量必须注意顶点个数,元素个数必须与顶点个数挂钩。
以上程序,我们是事先计算好变换矩阵,然后在顶点着色器中直接与顶点坐标值相乘。下面将提供实时计算坐标顶点的方法。
首先我们把vertex shader改成如下形式:
//
// Shader.vsh
// GLSLTest
//
// Created by Zenny Chen on 4/11/10.
// Copyright GreenGames Studio 2010. All rights reserved.
//
attribute vec4 position;
attribute vec4 color;
varying vec4 colorVarying;
uniform mat4 translate;
uniform mat4 projection;
uniform float degree;
mat4 rotation = mat4(
0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
);
void main()
{
float radian = radians(degree);
//gl_Position = position * translate; //mat4(0.816, 0.433, 0.0, 0.0, -0.5, 0.616, 0.0, 0.0, 0.0, 0.0, -0.5, 0.0, 0.0, 0.0, -0.5, 1.0);
mat4 transMatrix = translate;
rotation[0][0] = cos(radian);
rotation[0][1] = -sin(radian);
rotation[1][0] = sin(radian);
rotation[1][1] = cos(radian);
transMatrix = rotation * transMatrix;
transMatrix = projection * transMatrix;
gl_Position = position * transMatrix;
colorVarying = color;
}
我们可以看到,这里增加了两个uniform变量,一个用于投影变换,一个用于输入角度。另外,这里又增加了一个shader普通的全局变量rotation,并且为其初始化。
在main()函数中,我们先将输入的角度换成弧度,然后按照模型视图变换-》投影变换这一次序构成变换矩阵。最后用顶点坐标与变换矩阵相乘,获得变换后的顶点坐标值,输出给内建的gl_Position变量,这个内建属性将会被传递给光栅化阶段进行使用。
而在代码中,我们需要为添加的uniform变量进行绑定:
// uniform index
enum {
UNIFORM_TRANSLATE,
UNIFORM_PROJECTION,
UNIFORM_DEGREE,
NUM_UNIFORMS
};
GLint uniforms[NUM_UNIFORMS];
在方法中,在- (BOOL) loadShaders原来的地方添加:
// get uniform locations
uniforms[UNIFORM_TRANSLATE] = glGetUniformLocation(program, "translate");
uniforms[UNIFORM_PROJECTION] = glGetUniformLocation(program, "projection");
uniforms[UNIFORM_DEGREE] = glGetUniformLocation(program, "degree");
然后,我们再添加一个定时器相应方法:
static GLfloat degree = 0.0f;
- (void)timerFireMethod:(NSTimer*)theTimer
{
degree += 1.0f;
if(degree >= 360.0f)
degree = 0.0f;
[self setNeedsDisplay:YES];
}
接着,在- (void)prepareOpenGL方法中绑定新的uniform变量:
#if 0
my_transform(transformMatrix);
// Update uniform value
glUniformMatrix4fv(uniforms[UNIFORM_TRANSLATE], 1, GL_FALSE, transformMatrix);
#else
GLfloat translation[4 * 4] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
// x, y, z, w
0.0f, 0.0f, -2.0f, 1.0f
};
GLfloat projection[4 * 4] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, -0.5f, 0.0f,
0.0f, 0.0f, -1.5f, 1.0f
};
glUniformMatrix4fv(uniforms[UNIFORM_TRANSLATE], 1, GL_FALSE, translation);
glUniformMatrix4fv(uniforms[UNIFORM_PROJECTION], 1, GL_FALSE, projection);
#endif
由于角度是在每个单位时间内变化的,因此我们在- (void)drawRect:(NSRect)dirtyRect方法中修改degree变量:
- (void)drawRect:(NSRect)dirtyRect {
glClear(GL_COLOR_BUFFER_BIT);
glUniform1f(uniforms[UNIFORM_DEGREE], degree);
// Draw
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glFlush();
}
参考
http://blog.csdn.net/zenny_chen/article/details/5921633
OpenGL Shader的运行步骤:
第一步:创建程序。我们通过以下接口创建一个Shader程序:
1
|
GLuint glCreateProgram(
void
);
|
这个函数返回一个程序对象
第二步:编译Shader。这里我们有两个shader,一个是vertex shader(顶点着色器);一个是fragment shader(片断着色器)。
我们使用自定义的- (BOOL) compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file方法分别对顶点着色器和片断着色器进行编译。对于OpenGL着色器创建的基本步骤而言,我们要调用的OpenGL API的接口依次是:
1
|
GLuint glCreateShader(GLenum shaderType);
|
这个接口用于创建一个着色器。这里的参数shaderType是个枚举值,用于指定要创建哪种类型的着色器。GL_VERTEX_SHADER表示顶点着色器;GL_TESS_CONTROL_SHADER用于细分曲面的控制阶段(流水线级,OpenGL4.1);GL_TESS_EVALUATION_SHADER(OpenGL4.1)用于细分曲面的计算阶段;GL_GEOMETRY_SHADER用于几何着色器;GL_FRAGMENT_SHADER则用于片断处理器。这个函数的返回可看作为是一个着色器的句柄。
1
2
3
4
|
void
glShaderSource( GLuint shader,
GLsizei count,
const
GLchar ** string,
const
GLint * length);
|
这个接口用于指定着色器源代码。参数介绍:
shader:指定着色器句柄
count:用于指定第三个参数string中含有多少个const GLchar*的元素
string:源代码文本,以字符串指针的形式给出
length:分别指定string中每个子串的长度
如果length为NULL,那么OpenGL假定每个子串均以NIL('\0')结尾.
1
|
void
glCompileShader(GLuint shader);
|
我们可以通过下列两个接口来获取编译是否成功,若不成功,则获得错误信息:
1
2
3
|
void
glGetShaderiv(GLuint shader,
GLenum pname,
GLint * params);
|
这个接口可以用于获得编译结果信息的长度。第二个参数,我们传入GL_INFO_LOG_LENGTH,那么长度值就会被写入params所指向的地址。
1
2
3
4
|
void
glGetShaderInfoLog(GLuint shader,
GLsizei maxLength,
GLsizei * length,
GLchar * infoLog);
|
maxLength:用于指定你所提供的存放日志信息的缓存的最大长度
length:用于返回实际日志的长度
infoLog:写日志信息的缓存
这里对上面的着色器类型再补充说明一下,GL_GEOMETRY_SHADER,即几何着色器是从OpenGL 3.3开始才引入的,因此OpenGL2.1没有支持这个着色器特征。因此,我们目前只能使用顶点着色器和片断着色器。但是这两个着色器足以满足我们绝大多数的需求。而在OpenGL手册中也并不十分推荐几何着色器的使用。
另外,我们编译完着色器后必须调用glGetShaderiv接口。此时,pname要传GL_COMPILE_STATUS进去,以获得编译状态。如果得到的结果为0说明编译失败,否则为成功。
第三步:分别将顶点着色器和片断着色器关联到程序对象上:
1
2
|
void
glAttachShader( GLuint program,
GLuint shader);
|
第四步:绑定属性位置。
1
2
3
|
void
glBindAttribLocation( GLuint program,
GLuint index,
const
GLchar *name);
|
program:指定程序对象
index:指定要被绑定的通用顶点属性的索引
name:指定index所绑定的通用顶点属性变量名
glBindAttribLocation用于将程序中一个用户定义的属性变量与一个通用属性索引相关联。当所指定的程序对象变为当前状态的一部分时,通过通用顶点属性index提供的将会修改通过name所指定的用户自定义属性变量的值。
我们可以看一下我们这边的代码例子,以下是顶点着色器的代码:
1
2
3
4
5
6
7
8
9
10
11
|
attribute vec4 position;
attribute vec4 color;
varying vec4 colorVarying;
uniform
float
translate;
void
main()
{
gl_Position = position;
colorVarying = color;
}
|
1
2
3
4
|
// bind attribute locations
// this needs to be done prior to linking
glBindAttribLocation(program, ATTRIB_VERTEX,
"position"
);
glBindAttribLocation(program, ATTRIB_COLOR,
"color"
);
|
第五步:连接程序。
在我们的代码中是用- (BOOL) linkProgram:(GLuint)prog这个方法来完成连接的。
而这个方法主要调用的接口是:
1
|
void
glLinkProgram(GLuint program);
|
调用完成后。我们可以通过使用以下接口对来获取连接信息:
1
2
3
4
5
6
7
8
|
void
glGetProgramiv( GLuint program,
GLenum pname,
GLint *params);
void
glGetProgramInfoLog( GLuint program,
GLsizei maxLength,
GLsizei *length,
GLchar *infoLog);
|
上面这两个接口的使用与着色器的日志获取的方法一样。
最后同样再调用一次glGetProgramiv,此时pname传递的是GL_LINK_STATUS来获取连接状态。如果返回为0表示失败,否则表示成功。
在程序运行中,我们可以根据当前的OpenGL状态来查询设备端的Shader程序是否有效:
1
|
void
glValidateProgram( GLuint program);
|
我们可以用与获得连接日志相同的方法来获取日志信息。
接着,我们可以获取着色器程序中uniform变量的位置:
1
2
|
GLint glGetUniformLocation( GLuint program,
const
GLchar * name);
|
其实我们这个代码非常简单,没有用到uniform变量。但是我们保留了一个,仅用于描述这个获取步骤,
第六步:释放着色器对象。
当我们成功地把程序构建完之后,我们先前创建的着色器对象已经没有用了,此时我们通过以下接口释放着色器对象:
1
|
void
glDeleteShader(GLuint shader);
|
1
|
void
glUseProgram( GLuint program);
|
这个接口用于安装指定的程序作为当前绘制上下文的一部分。
n
顶点着色器取代的是OpenGL固定功能流水线中对每个顶点的变换阶段。这部分的操作包括:顶点变换(投影变换、视图模型变换)、法线变换与规格化、纹理坐标生成、纹理坐标变换、光照、颜色材质应用。
下面看一个简单的Vertex Shader的代码:
//
// Shader.vsh
// GLSLTest
attributevec4position;
attributevec4color;
varying vec4 colorVarying;
uniform mat4 translate;
void main()
{
gl_Position = position * translate; //mat4(0.816, 0.433, 0.0, 0.0, -0.5, 0.616, 0.0, 0.0, 0.0, 0.0, -0.5, 0.0, 0.0, 0.0, -0.5, 1.0);
colorVarying = color;
}
最上面两个attribute变量的数据是从主机端输入。attribute在后期Shader Language版本中被废弃,取而代之的是in关键字。主机端通过以下接口把相关数据与vertex shader所对应的attribute变量进行绑定:
- void glBindAttribLocation( GLuint program,
- GLuint index,
- const GLchar * name);
index:自己定义的一个id,与下面的name进行绑定
name:指定顶点着色器中所要绑定的变量名
在我们的主机端程序中,与上述position变量进行绑定的代码为:
// attribute index
enum{
ATTRIB_VERTEX,
ATTRIB_COLOR,
NUM_ATTRIBUTES
};
glBindAttribLocation(program, ATTRIB_VERTEX, "position");
与color绑定的代码为:
glBindAttribLocation(program, ATTRIB_COLOR, "color");
这里要注意的是,顶点着色器是可以与主机端应用进行直接交互的。而片断着色器则不能与主机端程序进行直接交互。因此,我们可以把片断着色器所要用到的数据先通过顶点着色器输入进来,然后通过varying属性传给片断着色器(Fragment Shader)。注意,在以后版本中varying关键字也被废弃。取而代之的是在顶点着色器部分用out关键字,而在片断着色器中相应地使用in关键字。
因此,attribute不仅仅是指顶点属性,也可以是颜色属性或用户自定义的一些变量,而我们用主机端的数据与这些attribute变量绑定时,都用glBindAttribLocation这一个接口。
而我们通过以下接口把主机端的数据发送给Shader端:
- void glVertexAttribPointer( GLuint index,
- GLint size,
- GLenum type,
- GLboolean normalized,
- GLsizei stride,
- const GLvoid * pointer);
index:我们刚才所指定的attribute变量所绑定的id。
size:指定每个元素有多少分量,可以是1,2,3,4
type:指定数据类型
normalized:指定定点数据是否被规格化
stride:相邻两个元素之间的跨度(字节数)
pointer:指向数组首地址
下面将给出示例代码:
首先定义顶点坐标和颜色值:
static const GLfloat squareVertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f,
0.5f, 0.5f, 0.0f
};
static const GLubyte squareColors[] = {
255, 255, 0, 255,
0, 255, 255, 255,
0, 0, 0, 0,
255, 0, 255, 255,
};
然后,下面就是分别传送顶点坐标数据和颜色数据:
// Update attribute values
glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT, 0, 0, squareVertices);
glEnableVertexAttribArray(ATTRIB_VERTEX);
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, 1, 0, squareColors);
glEnableVertexAttribArray(ATTRIB_COLOR);
uniform变量:
uniform变量与attribute类似,都是通过主机端传递到shader,而且它们都不能在Shader端被修改。但是,attribute变量数据依赖于glDrawArray等绘制函数所提供的顶点个数,并且类型不能是矩阵类型,而uniform变量类型可以非常灵活,你甚至能通过uniform block定义自己的数据结构。并且uniform变量能够在Vertex Shader端与Fragment Shader端共享,这点,attribute则做不到。attribute只能把数据传给varying变量才能在Fragment Shader端使用。
要使用uniform变量,首先在主机端对shader中所用的uniform变量进行绑定,通过以下接口:
- GLint glGetUniformLocation( GLuint program,
- const GLchar * name);
返回的是指定的uniform变量在shader中的索引,可以认为是一个id。
在本示例程序中,对我们定义的uniform mat4 translate;变量所绑定的代码如下:
- // uniform index
- enum {
- UNIFORM_TRANSLATE,
- NUM_UNIFORMS
- };
- GLint uniforms[NUM_UNIFORMS];
- // get uniform locations
- uniforms[UNIFORM_TRANSLATE] = glGetUniformLocation(program, "translate");
这里,将主机端的数据传递到shader的uniform的代码是:
复制代码
|
上面要注意的是glVertexAttribPointer接口只指定attribute属性变量每个元素的长度和类型,但并没有指定元素个数。
元素个数都是通过glDrawArrays这样的绘制接口传递的。因此如果是自己自定义的attribute变量必须注意顶点个数,元素个数必须与顶点个数挂钩。
以上程序,我们是事先计算好变换矩阵,然后在顶点着色器中直接与顶点坐标值相乘。下面将提供实时计算坐标顶点的方法。
首先我们把vertex shader改成如下形式:
//
// Shader.vsh
// GLSLTest
//
// Created by Zenny Chen on 4/11/10.
// Copyright GreenGames Studio 2010. All rights reserved.
//
attribute vec4 position;
attribute vec4 color;
varying vec4 colorVarying;
uniform mat4 translate;
uniform mat4 projection;
uniform float degree;
mat4 rotation = mat4(
0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
);
void main()
{
float radian = radians(degree);
//gl_Position = position * translate; //mat4(0.816, 0.433, 0.0, 0.0, -0.5, 0.616, 0.0, 0.0, 0.0, 0.0, -0.5, 0.0, 0.0, 0.0, -0.5, 1.0);
mat4 transMatrix = translate;
rotation[0][0] = cos(radian);
rotation[0][1] = -sin(radian);
rotation[1][0] = sin(radian);
rotation[1][1] = cos(radian);
transMatrix = rotation * transMatrix;
transMatrix = projection * transMatrix;
gl_Position = position * transMatrix;
colorVarying = color;
}
我们可以看到,这里增加了两个uniform变量,一个用于投影变换,一个用于输入角度。另外,这里又增加了一个shader普通的全局变量rotation,并且为其初始化。
在main()函数中,我们先将输入的角度换成弧度,然后按照模型视图变换-》投影变换这一次序构成变换矩阵。最后用顶点坐标与变换矩阵相乘,获得变换后的顶点坐标值,输出给内建的gl_Position变量,这个内建属性将会被传递给光栅化阶段进行使用。
而在代码中,我们需要为添加的uniform变量进行绑定:
// uniform index
enum {
UNIFORM_TRANSLATE,
UNIFORM_PROJECTION,
UNIFORM_DEGREE,
NUM_UNIFORMS
};
GLint uniforms[NUM_UNIFORMS];
在方法中,在- (BOOL) loadShaders原来的地方添加:
// get uniform locations
uniforms[UNIFORM_TRANSLATE] = glGetUniformLocation(program, "translate");
uniforms[UNIFORM_PROJECTION] = glGetUniformLocation(program, "projection");
uniforms[UNIFORM_DEGREE] = glGetUniformLocation(program, "degree");
然后,我们再添加一个定时器相应方法:
static GLfloat degree = 0.0f;
- (void)timerFireMethod:(NSTimer*)theTimer
{
degree += 1.0f;
if(degree >= 360.0f)
degree = 0.0f;
[self setNeedsDisplay:YES];
}
接着,在- (void)prepareOpenGL方法中绑定新的uniform变量:
#if 0
my_transform(transformMatrix);
// Update uniform value
glUniformMatrix4fv(uniforms[UNIFORM_TRANSLATE], 1, GL_FALSE, transformMatrix);
#else
GLfloat translation[4 * 4] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
// x, y, z, w
0.0f, 0.0f, -2.0f, 1.0f
};
GLfloat projection[4 * 4] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, -0.5f, 0.0f,
0.0f, 0.0f, -1.5f, 1.0f
};
glUniformMatrix4fv(uniforms[UNIFORM_TRANSLATE], 1, GL_FALSE, translation);
glUniformMatrix4fv(uniforms[UNIFORM_PROJECTION], 1, GL_FALSE, projection);
#endif
由于角度是在每个单位时间内变化的,因此我们在- (void)drawRect:(NSRect)dirtyRect方法中修改degree变量:
- (void)drawRect:(NSRect)dirtyRect {
glClear(GL_COLOR_BUFFER_BIT);
glUniform1f(uniforms[UNIFORM_DEGREE], degree);
// Draw
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glFlush();
}
参考
http://blog.csdn.net/zenny_chen/article/details/5921633