OpenGL ES GLSL语言
EGL(Embedded Graphics Library)
-
OpenGL ES
命令需要渲染上下⽂
和绘制表面
才能完成图形图像的绘制 - 渲染上下⽂: 存储相关
OpenGL ES
状态,是一个状态机 - 绘制表面:⽤于绘制图元的表面,需要指定渲染的缓存区,例如颜⾊缓、深度和模板
-
OpenGL ES API
并没有提供如何创建渲染上下文或者上下文如何连接到原生窗口系 统.EGL
是Khronos
渲染API
(如OpenGL ES
) 和原⽣窗⼝系统之间的接⼝. 唯⼀支持OpenGL ES
却不支持EGL
的平台是iOS
.Apple
提供⾃己的EGL API
的iOS
实现,称为EAGL
- 因为每个窗⼝系统都有不同的定义,所以
EGL
提供基本的不透明类型—EGLDisplay
, 这 个类型封装了所有系统相关性,用于和原生窗⼝系统接⼝
主要功能
- 和本地窗口系统(
native windowing system
)通讯 - 查询可用的配置
- 创建
OpenGL ES
可用的绘图表面
(drawing surface
) - 同步不同类别的
API
之间的渲染,比如在OpenGL ES
和OpenVG
之间同步,或者在OpenGL
和本地窗口的绘图命令之间 - 管理
渲染资源
,比如纹理映射(Rendering map
)
GLSL语言
xcode
中不支持GLSL
语言对顶点/片元着色器的编译和连接,因此需要在项目中创建两个空文件,分别命名为shader.vsh
和shaderv.fsh
- 使用
vsh、fsh
后缀的原因是方便区分着色器,其本质就是一个字符串- 是否可以直接使用
NSString?
并不建议这样做,因为代码结构不清晰,不易读- 这两个文件中是否可以加中文注释?不建议加中文注释,会报奇怪的错误,由于在
xcode
中书写GLSL
,完全是纯手写,没有任何提示,排查问题不好排查
类型及API的总结
向量数据类型
常用的vec2、vec3、vec4
,默认是浮点类型
vec2,vec3,vec4
2分量、3分量、4分量浮点向量
ivec2,ivec3,ivec4
2分量、3分量、4分量整型向量
uvec2,uvec3,uvec4
2分量、3分量、4分量无符号整型向量
bvec2,bvec3,bvec4
2分量、3分量、4分量bool型向量
矩阵数据类型
mat2,mat2x2
两⾏两列
mat3,mat3x3
三行三列
mat4,mat4x4
四行四列
mat2x3
三行两列
mat2x4
四行两列
mat3x2
两行三列
mat3x4
四行三列
mat4x2
两行四列
mat4x3
三行四列
变量存储限定符
常用varying、attribute、uniform
-
varying
修饰符:当需要将顶点着色器的数据传递到片元着色器时,两个着色器中一模一样的纹理坐标变量就需要它来修饰 -
attribute
:数据只能从客户端中传递到顶点着色器,且只能在顶点着色器中使用- 修饰的数据:顶点、纹理、颜色、法线等
- API通常以
glVertex...
开头,例如glVertexAttribPointer
- 其中的纹理坐标,需要顶点着色器间接传递到片元着色器,需要在顶点与片元着色器中定义一个一模一样的纹理坐标,通过这个变量将纹理坐标数据间接传递到片元着色器,
varying lowp vec2 varyTextCoord
; - 顶点着色器计算之后的顶点结果需要赋值给
GLSL
的内建变量gl_Position
attribute vec4 position;
attribute vec2 textCoordinate;
varying lowp vec2 varyTextCoord;
void main()
{
varyTextCoord = textCoordinate;
gl_Position = position;
}
-
uniform
:从app
代码传递到vertex、fragment
中所用的变量- 在
vertex,fragment
中一般将uniform
当成常量 -
uniform
可以传的数据:视图矩阵、投影矩阵、投影视图矩阵 -
API
通常以glUniform...
开头 - 片元着色器中最终颜色,即拿到纹理对应坐标下的纹素。纹素是纹理对应像素点的颜色值,需要通过内建函数
texture2D
(纹理,纹理坐标)计算,将最终返回的颜色值赋值给内建变量gl_FragColor
- 在
//需要定义精度,否则可能会报错
precsion highp float;
//纹理坐标 必须与顶点着色器中一模一样,通过这个参数获取传递过来的值
varying lowp vec2 varyTextCoord;
//纹理
uniform sampler2D colorMap;
void main(){
//1、拿到纹理对应坐标下的纹素。纹素是纹理对应像素点的颜色值
lowp vec4 temp = texture2D(colorMap, varyTextCoord);
//2、非常重要且必须的内建变量:gl_FragColor
gl_FragColor = temp;
}
<none>
只是普通的本地变量,外部不见,外部不可访问
const
⼀个编译常量,或者说是⼀个对函数来说为只读的参数
in/varying
从以前阶段传递过来的变量
in/varying centroid
⼀个从以前的阶段传递过来的变量,使⽤质⼼插值
out/attribute
传递到下⼀个处理阶段或者在⼀个函数中指定⼀个返回值
out/attribute centroid
传递到下⼀个处理阶段,质心插值
uniform
⼀个从客户端代码传递过来的变量,在顶点之间不做改变
OpenGL ES 错误处理
如果不正确使用OpenGL ES
命令,应用程序就会产生一个错误编码,且会被记录,可以用glGetError
查询,一旦查询到错误代码,当前的错误代码就会复位为GL_NO_ERROR
GL_NO_ERROR
从上⼀次调⽤glGetError
以来没有生成任何错误
GL_INVALID_ENUM
GLenum
参数超出范围,忽略生成错误命令
GL_INVALID_VALUE
数值型 参数超出范围,忽略生成错误命令
GL_INVALID_OPERATION
特定命令在当前OpenGL ES
状态⽆法执⾏
GL_OUT_OF_MEMORY
内存不足时执⾏该命令,如果遇到这个错误,除⾮当前错误代码,否则OpenGL ES
管线的 状态被认为未定义
OpenGL ES 自定义着色器常用API
自定义着色器
自定义着色器一般有以下步骤:
- 创建顶点着色器/片元着色器 --
glCreateShader
- 指定
shader
的source
--glShaderSource
- 编译
shader
--glCompileShader
创建与编译一个着色器的相关API
自定义程序
自定义程序一般有以下步骤:
- 创建一个程序对象 --
glCreateProgram
- 着色器与程序连接/附着 --
glAttachShader
- 链接程序 --
glLinkProgram
- 使用程序 --
glUseProgram
创建与链接程序的相关API
着色器与程序的 编译 & 链接
- 需要创建2个基本对象才能使用着色器进行传染:着色器对象和程序对象
- 获取链接后着色器对象一般的编译&链接分为6步:
- 创建一个顶点着色器对象和一个片元着色器对象
- 将源代码链接到每个着色器对象
- 编译着色器对象
- 创建一个程序对象
- 将编译后的着色器对象连接到程序对象
- 链接程序对象