OpenGL ES - 了解GLSL

前言

上个教程了解了苹果官方为减轻OpenGL ES开发提供的一个简单库GLKit。咱们也知道GLKit能提供的纹理通道只有两个,光照效果三个,在须要更多的纹理以及自定义效果只能经过GLSL(Open GL Shader Language)来实现。git

版本对比

也许不少学习的人会纠结OpenGL ES用啥版本,当真正使用的时候就不会有这种疑问。会发现各个版本有个版本的针对状况,实际使用能够根据需求来进行一个选择。github

OpenGL ES 版本 描述
1.x 针对固定管线硬件
2.x 针对可编程管线硬件
3.0 对2.0版本的扩充
3.1 在3.0的基础上增长了几何着色器

初步了解GLSL

GLSL就是为了开发者编写顶点着色器和片元着色器的语言。在以前的OpenGL中咱们知道可使用固定管线进行编程,也就是使用默认的顶点着色器和片元着色器,因此实现自定义的效果必须使用可编程管线。GLSL是一门C风格的语言,也有着控制结构(if、for、switch等),其实咱们了解了GLSL的规范后开发仍是很容易的,可是由于编译器不会提示,只会在一些关键字写对后会进行一个色彩显示,也就意味着咱们在写着色器的时候必须很仔细,否则会出现编译着色器失败。objective-c

GLSL的基本知识

向量类型

类型 描述
vec2 vec3 vec4 二维、三维、四维浮点向量
ivec2 ivec3 ivec4 二维、三维、四维整形向量
uvec2 uvec3 uvec4 二维、三维、四维无符号整形向量
bvec2 bvec3 bvec4 二维、三维、四维布尔型向量

矩形数据类型

类型 描述
mat2 mat2x2 两行两列
mat3 mat3x3 三行三列
mat4 mat4x4 四行四列
mat2x3 三行两列
mat2x4 四行两列
mat3x2 两行三列
mat3x4 四行三列
mat4x2 两行四列
mat4x3 三行四列

变量存储限定符

限定符 描述
<none> 普通的本地变量,外部不可见并访问
const 一个编译常量,只读属性
in/varying 从之前阶段传递过来的变量
in/vartying centroid 一个从之前处理阶段传递过来的变量,使用质心插值
out/attribute 传递下一处理阶段或者在一个函数中指定一个返回值
out/attribute centroid 传递到下一个处理阶段,质心插值
uniform 一个从客户端传递过来的变量,在顶点之间不作改变

顶点着色器代码

#version 300 es // 一般会写这个版本号,假如OpenGL ES 3.3.0 则为330
attribute vec4 position;		// 顶点数据都是用attribute修饰,用attribute通道进行传值
attribute vec2 textureCootdinate;		// 纹理顶点数据
varying lowp vec2 textureCoord;			// 须要传递给片元着色器的纹理顶点,且须要精度修饰符

void main(){
	textureCoord = textureCootdinate;
  gl_Position = position;
}

复制代码

咱们须要注意的点有如下这些:编程

①#version 是为了告诉OpenGL ES版本号,必须处于空行外的第一行。书写GLSL时每行结束须要添加分号和换行。数组

②顶点数据都须要使用attribute修饰,用于attribute通道进行传值。并且顶点数据须要用vec4修饰,由于顶点是RGBA四个数据,不然编译着色器将会报错。bash

③传递给片元着色器的纹理顶点须要使用varying修饰,只有使用了varying修饰的属性才能传递到片元着色器;其次还需使用精度修饰符,精度修饰符有lowpmediumphighp低中高三种精度。函数

④着色器源码中只能有一个main函数。性能

⑤在main函数中把须要传递给片元着色器的值进行赋值。例如纹理属性值学习

必须对内建变量gl_Position进行赋值spa

片断着色器代码

#version 300 es
varying lowp vec2 textureCoord;
uniform sampler2D textureSampler;

void main(){
  	gl_FragColor = texture2D(textureSampler, textureCoord);
}
复制代码

代码和顶点着色器差很少,由于片断着色器是处理纹理数据的,因此一些贴图、滤镜大部分都在这里进行。片元着色器的内建变量是gl_FragColor

常见的错误

咱们知道在OpenGL ES开发中会经过glGetError函数来获取错误代码,来排查错误缘由,其中在开发中若是粗心或者一不留神写错了值就容易形成12801286错误代码。

错误 代码 缘由
GL_NO_ERROR 0(0x0)
GL_INVALID_ENUM 1280(0x0500) 使用了不合法的枚举
GL_INVALID_VALUE 1281(0x0501) 数值参数超出范围
GL_INVALID_OPERATION 1282(0x0502) 操做在当前状态非法
GL_OUT_OF_MEMORY 1285(0x0505) 内存不足以执行命令
GL_INVALID_FRAMEBUFFER_OPERATION 1286(0x0506) 帧缓冲区不完整

FBO和RBO

1.为什么使用帧缓冲区对象(FBO)?

**在应用调用任何OpenGL ES命令以前,须要首先建立一个渲染上下文和绘图表面,并使之成为现行的上下文和表面。**渲染上下文和绘图表面一般由原生窗口系统经过EGL等API提供,咱们知道在iOS中是EAGL。由原生系统提供的绘图表面能够是屏幕上显示的表面(称为系统提供的帧缓冲区);也能够是屏幕外的表面(称为pbuffer)。建立CAEAGLLayer绘图表面的咱们能够指定宽度、高度以及是否使用颜色、深度和模板缓冲区以及这些缓冲区的位深。

在默认状况,在iOS中OpenGL ES使用CAEAGLayer做为绘图表面。若是应用程序只在屏幕上的表面绘图,则窗口系统提供的帧缓冲区童话村那个很高效。可是,由于许多应用程序须要渲染到纹理,为此,使用系统提供的帧缓冲区做为绘图表面一般不是理想的选择。渲染到纹理的实例由阴影贴图、动态反射和环境贴图、多道景深技术、运动模糊效果和处理后特效。

应用程序能够经过如下两种方式渲染到纹理:

经过绘制窗口系统提供的缓冲区,而后将缓冲区的对应区域复制到纹理来实现渲染到纹理。这能够用glCopyTexImage2DglCopyTexSubImage2D API实现。这些API执行从帧缓冲区到纹理缓冲区的复制,之一复制操做每每对性能有不利影响。此方法只适用于纹理的尺寸小于或者等于帧缓冲区尺寸的时候才有效。

经过使用链接到纹理的pbuffer来实现渲染到纹理。咱们知道,窗口系统提供的表面必须链接到一个渲染上下文。这在某些对每一个pbuffer和窗口表面须要不一样的上下文实现中可能效率低下。可绘制表面切换有时候须要OpenGL ES清除全部切换以前渲染的图像。

可是以上两种方式对于渲染到纹理或者其余屏幕外表面来讲都不理想。做为替代,咱们须要容许程序直接渲染到纹理的API,或者在OpenGL ES API中具有建立屏幕外表面的能力,并将它做为渲染目标。帧缓冲区对象和渲染缓冲区对象容许应用程序完成这些操做,不须要额外建立渲染上下文。由于咱们在使用窗口系统提供了渲染到纹理或者屏幕外表面的更好、更有效的方法。

2.帧缓冲区对象API支持以下操做

①仅使用OpenGL ES命令建立帧缓冲区对象。

②在单一EAGLContext中建立和使用多个帧缓冲区对象。也就是说不须要每一个帧缓冲区都有一个渲染上下文。

③建立屏幕外颜色、深度、或者模板渲染缓冲区和纹理,并将它们链接到帧缓冲区对象。

④在每一个帧缓冲区之间共享颜色、深度或者模板缓冲区。

⑤将纹理直接链接到帧缓冲区做为颜色或深度,从而避免了进行复制操做的必要操做。

⑥在帧缓冲区之间复制并使用帧缓冲区内容失效

3.FBO和RBO关系

帧缓冲区对象(FBO)是一组颜色、深度、模板纹理或者渲染目标对象。各类2D图像能够链接到帧缓冲区对象中的颜色附着点。这些附着点包括一个渲染缓冲区对象,他保存颜色值、2D纹理或者立方图面的mip级别、2D数组纹理的层次甚至3D纹理中一个2D切片的mip级别;一样的包含深度值得各类2D图像能够链接到FBO的深度附着点;能够链接到FBO模板附着点的惟一2D图像是保存模板值得渲染缓冲区对象。并且一个帧缓冲区只有一个颜色、深度、模板附着点。

渲染缓冲区对象(RBO)是一个有应用程序分配的2D图像缓冲区。渲染缓冲区能够用于分配和存储颜色、深度或者模板值,能够用做帧缓冲区对象中的颜色、深度或者模板附着。渲染缓冲区相似于屏幕外的窗口系统提供的可绘制表面,例如pbuffer可是渲染缓冲区不能直接做为GL的纹理。

案例:使用glsl纹理贴图

根据上面的流程图咱们知道和GLKit不一样的是使用自定义GLSL代替了GLKBaseEffect部份内容。值得注意的是咱们程序正常的加载纹理是倒像,咱们能够经过处理顶点数据或者处理纹理映射方式来处理,可是都不是很好的处理办法。较好的作法是在加载纹理的时候重绘位图时进行一个转换操做:

CGRect rect = CGRectMake(0, 0, imageWidth, imageHeight);
CGContextTranslateCTM(context, 0, imageHeight);
CGContextScaleCTM(context, 1.0f, -1.0f);
复制代码

实现的效果图:

案例demo地址:Swift版本OC版本

相关文章
相关标签/搜索