初窥OpenGL Shaders

我们现在大部分人在使用 OpenGL 的时候用的是古老的 OpenGL 固定功能流水线接口。然后,现代的 GPU,包括移动 GPU 都具有可编程特性,并且大部分都具有统一着色单元,使得 OpenCL 也能实现。
  那么可编程的GPU能带给我们什么好处呢?
  我们可以利用可编程的着色器实现局部光照,绘制更复杂的图形,实现更炫的特效等等。下面我将利用一个最最简单的例子来给出如何在 Mac OS X Snow Leopard 上通过 XCode 来创建一个 OpenGL Shader 的工程。
  在附件中,这个工程已经能很好地工作了。这里我们会看到一个 Shaders 目录。这里我们在项目中添加这个引用时不要把它关联到你的构建目标上,即构造器不需要对它进行解析。但是要在 Target 中添加“拷贝文件”。方法是:在你左侧的项目管理视图中找到 Targets,然后点一下三角,再点一下你的目 标名左侧的小三角,将会看到 Copy Bundle Resource 组,将你工程中刚才引用的两个 shader 文件拖到 Resource 组的名称上,测试会出现椭圆的高亮,然后松手。我们在构建整个项目时 就会把这两个 Shader 文件作为资源拷贝到我们的应用的 Bundle 中了。



  下面介绍一下 OpenGL Shader 的运行步骤:
  第一步:创建程序。

  我们通过以下接口创建一个 Shader 程序:

 
GLuint glCreateProgram(void);
 

  这个函数返回一个程序对象。
  第二步:编译 Shader。

  这里我们有两个 Shader,一个是 vertex shader(顶点着色器);一个是 fragment shader(片断着色器)。我们使用自定义的- (BOOL) compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file 方法分别对顶点着色器和片断着色器进行编译。对于 OpenGL 着色器创建的基本步骤而言,我们要调用的 OpenGL API 的接口依次是:

 
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 则用于片断处理器。这个函数的返回可看作为是一个着色器的句柄。


 
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')结尾。


 
void glCompileShader(GLuint shader);
 

  这个接口用于编译着色器代码。我们可以通过下列两个接口来获取编译是否成功,若不成功,则获得错误信息:


 
void glGetShaderiv(GLuint shader,
GLenum pname,
GLint * params);
 

  这个接口可以用于获得编译结果信息的长度。第二个参数,我们传入 GL_INFO_LOG_LENGTH,那么长度值就会被写入 params 所指向的地址。


 
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 说明编译失败,否则为成功。
  通过依次调用这些接口,我们就完成了对着色器的编译过程。

第三步:分别将顶点着色器和片断着色器关联到程序对象上:

 
void glAttachShader(    GLuint program,
     GLuint shader);
 

program是我们先前创建好的程序对象;而shader则是我们创建并编译完之后的着色器对象。



第四步:绑定属性位置。


 
void glBindAttribLocation(    GLuint program,
     GLuint index,
     const GLchar *name);
 

program:指定程序对象


index:指定要被绑定的通用顶点属性的索引


name:指定index所绑定的通用顶点属性变量名



  glBindAttribLocation 用于将程序中一个用户定义的属性变量与一个通用属性索引相关联。当所指定的程序对象变为当前状态的一部分时,通过通用顶点属性index提供的将会修改通过name所指定的用户自定义属性变量的值。


我们可以看一下我们这边的代码例子,以下是顶点着色器的代码:


 
attribute vec4 position;
attribute vec4 color;
varying vec4 colorVarying;
 
uniform float translate;
 
void main()
{
    gl_Position = position;
    colorVarying = color;
}
 

  我们可以看到,这边定义了两个属性变量,一个是 position,一个是 color。然后再看一下主机端上绑定这两个属性变量的代码:


 
    // bind attribute locations
    // this needs to be done prior to linking
    glBindAttribLocation(program, ATTRIB_VERTEX, "position");
    glBindAttribLocation(program, ATTRIB_COLOR, "color");
 

  这里,ATTRIB_VERTEX和ATTRIB_COLOR 是我们自定义的枚举值,分别是 0 和 1。我们在后面将会看到如何通过 glVertexAttribPointer 将顶点数据和颜色数据分别传递给顶点着色器中的 position 和 color。

第五步:连接程序。
  在我们的代码中是用 - (BOOL) linkProgram:(GLuint)prog 这个方法来完成连接的。
  而这个方法主要调用的接口是:

 
void glLinkProgram(GLuint program);
 

  调用完成后。我们可以通过使用以下接口对来获取连接信息:


 
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 程序是否有效:


 
void glValidateProgram(    GLuint program);
 

  我们可以用与获得连接日志相同的方法来获取日志信息。接着,我们可以获取着色器程序中uniform变量的位置:


 
GLint glGetUniformLocation(    GLuint      program,
     const GLchar *     name);
 

  其实我们这个代码非常简单,没有用到uniform变量。但是我们保留了一个,仅用于描述这个获取步骤,呵呵。



第六步:释放着色器对象。


  当我们成功地把程序构建完之后,我们先前创建的着色器对象已经没有用了,此时我们通过以下接口释放着色器对象:


 
void glDeleteShader(GLuint shader);
 

  我们接下去就可以进行安装程序:


 
void glUseProgram(    GLuint program);
 

  这个接口用于安装指定的程序作为当前绘制上下文的一部分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值