声明:
opengl是一个跨平台的图形,广泛采用的3D api;opengl es 是一个为手持和嵌入设备为目标,对opengl相应标准进行精简而来的;实现了具有可编程着色功能的图形管线,所以它包括es 和es着色器语言(我个人对着色器语言的初步理解);管线流程,这个确实有必要,但是,一个入门应该是先有个直接的印象,所以,我先上个程序来看下
参考书籍:opengl es编程指南3.0
示例
对于android来说,使用可以是ndk,也可以是java层
java api提供了GLSurfaceView,它的绘制主要在Render回调中
1、检测opengl es版本
应用市场声
明:低于3.0版本的手机不可安装
<uses-feature android:glEsVersion="0x00030000" android:required="true"/>
opengl es 3.0 对应android 4.3
opengle es 2.0 对应android 2.2 (有人说使用2.0,因为可以兼容3.0, 如果需要使用3.0的新特性,可以判断使用)
opengl es 1.0 已经退出历史舞台了。。。
如果不是使用真机测试,支持情况,请左转
0x2000是2.0版本
2、设置布局和声明界面刷新回调
设置版本:setEGLContextClientVersion
设置刷新回调:setRenderer
3、回调
onSurfaceCreated:
每次创建都会回调一次,也就是创建后只有一次,但不一定只有一次创建
里面主要的操作是,生成包括vetex shader和fragment shader的程序
shader: 创建---- 添加shader程序-----编译
program:创建---- 添加shader ---- 链接
onSurfaceChanged:
横竖屏变化时,调用;设置展示区域
onDrawFrame:绘制每一帧内容,可以跟随系统,也可以自己控制
绘制图形
这是一个绘制矩形的示例,代码github
是不是感觉都什么方法啊,我都不会啊,不急,下面慢慢来,上面只是让看官感受下
opengl es内容
opengl es的api对于所有的平台都应该是相同的方法,具体平台可能参数,返回值略有不同;而由于android开发,ndk和java-api,所以,我们讲常用api方法
图片来源其它博客,不想自己绘制了;这就是api绘制的总过程;
方法:以gl前缀,还有可能采用参数数量,数据类型
数据类型: GL开头
常量:GL_开头
方法执行成功与否均需要自己判断
android平台 java层,都封装到GLES30(3.0版本)中
特别注意:介绍的api全部是c语言的形式,其它平台方法名一致,功能一致,参数并不一定一致,参数类型也有区别,意义还是说了的
指针传入,可以获取结果;c中一般都这样搞,java语言引用对象作参数也可以,但很少这样搞
c中方法返回值,表示成功与否,所以才这样搞;返回false表示失败
有几个特别的函数
glGetError() :获取当前错误代码,并复位
glEnable:启用某个功能
glDisenable:禁用某个功能
glIsEnabled:查询某个功能
a、EGL
opengl es需要渲染上下文和绘制表面,这就是EGL干的事,它是opengl ES到具体系统的中间件,具体实现和硬件有关;渲染上下文存储相关状态,绘制表面是用于绘制图元的表面,需要指定缓存区类型、缓存区位深度等;总体来说主要干三件事
查询并初始化可用显示器
创建渲染表面:屏幕内表面,连接到系统窗口;屏幕外表面,是可以用作渲染的像素缓存取
创建渲染上下文
EGL命名规则和GL类似,以egl, EGL, EGL_开头
eglGetDisplay(displayId):打开EGL显示服务器的链接,参数使用EGL_DEFAULT_DISPLAY
eglInitalize(diaplayId, *majorVersion, *minorVersio):初始化,后两个参数是返回的版本号结果,可能为null
eglGetConfigs(displayId,*configs, max, *numConfigs):获取可用表面配置;1、configs传入null,这是numConfigs返回可用数目;2、configs未初始化数组,max传入数组大小,numConfigs传回实际可用个数(为了使用更少的内存,可以先使用1获取可用个数,2再获取具体可用值);EGL包括颜色,缓存区,表面类型等特性信息
eglGetConfigAttrib(displayId, config, attrib, *value):获取某个特性信息的值
eglChooseConfig(diaplayId,*attriList, *config, max, *num):让EGL自己根据attrlist属性列表选择匹配的config信息,max最多返回个数,num实际返回个数
eglCreateWindowSurface(displayId, config, window, *attribList):创建屏幕内渲染区域;需要原生窗口系统先创建一个窗口window,attribList为单一属性,即指出渲染表面是前台缓存区还是后台缓存区 eg:attribList[] = {EGL_RENDER_BUFFR, EGL_BACK_BUFFER, EGL_NONE}
EGL_NONE:结束,其它都是值对的形式
eglCreatePbufferSurface(displayId, config, *attribList):创建屏幕外渲染区域,也叫pbuffer(像素缓存区),attribList和上个方法书写类似,只不过属性不同
eglCreateContext(displayId, config, contexFlag, *attrbList):创建上下文,attrbList只有一个属性,即版本号,需要和使用openGL es的版本好一致
eglMakeCurrent(displayId, drawSurface, readSurface, context):指定context为当前上下文
同步渲染(不会。。。)
eglWaitClient()和eglWaitNative()
b、着色器和程序
OpenGL ES中每个程序必须有一个vetex shader和fragment shader
glCreateShader(type):创建shader,type有两种:GL_VETEX_SHADER, GL_FRAGMENT_SHADER
glDeleteShader(shader):删除
glShaderSource(shader, count, *str, *length):提供着色器代码,count着色器源字符串数量,为1,str字符串,length字符串长度
glCompileShader(shader):编译
对于shader编译成功与否需要校验:
glGetShaderiv(shader, name, *params):name查询着色器相关状态:一般我们用编译状态:GL_COMPILE_STATUS
glGetShaderInfoLog(shader,maxL, *len, *info):log缓存区大小maxL,写入len长度,写入信息info
glReleaseShaderCompiler:释放着色器编译使用的资源
glCreateProgram():创建程序
glDeleteProgram(program):删除程序
glAttachShader(program,shader):添加shader到程序program
glDetachShader(program, shader):断开
glLinkProgram(program):链接,生成最终可执行程序
对于程序链接成功需要校验:
glGetProgramiv(program, name, *param):相似不? name一般取GL_LINK_STATUS
glGetProgramInfoLog(program,maxL, *len, *info):相似不?
链接成功,并不代表有效,所有还要进行最后一步:
glValidateProgram(program)
保存到系统中多次使用:
glGetProgramBinary glProgramBinary
校验在开始联系阶段,debug版本都是要做的,为了更好的验证shader编写
glUseProgram(program):设置为活动程序进行渲染
现在对于shader还没有完。。。如果我们不对shader交互,直接后台处理上面这些就ok了啊,我们只需要设置shader程序不是很简单
与着色器交互
统一变量(uniform):存储应用程序通过gl es-api传递给着色器的只读常数值变量
多个统一变量可以组合为统一变量块,类似c中struct(关键字变换而已。。。)
特别注意:如果在vetex和fragment着色器中均有声明,必须完全一致
被使用的统一变量块,是活动的,不使用会被优化掉。。。
统一变量
glGetUniformLocation(program, *name):查找name的统一变量位置(位置可以直接用layout(location = 数值)来指定)
设置uniform值
glUniform+数字+类型首字母+v(向量,可以省略,这时,需要传入数字个相应类型的值)
glUniformMatrix+数字m+x+数字n+类型+v:m×n矩阵
参数:位置,加载数组元素个数,行优先(GL_TRUE)或者列优先,统一变量值,元素数组
如果shader是你写的,上面几个就够用了,但是如果相互独立开发,那么你需要取定类型
glGetActiveUniform(program, index, bufSize, *len,*size, *type, *name):index索引,名称数组的字符数,名字数组写入字符数(输出),统一变量大小,统一变量类型(输出),统一变量名字(输出);用于统一变量
glGetActiveUniformsiv(program, count, *indices, name, *params):啥东西,不太懂。。。
检索统一变量块
统一变量缓存区:存储统一变量块的,单独统一变量没有这个东东。。没用过,不太懂,大致写下,以后便于研读
glGetUniformBlockIndex(program, *name)
glGetActiveUniformBlockName(program, index, bufSize, *len, *name)
glGetActiveUniformBlockiv(program, index, name, *param)
glUniformBlockBinding(program, index, binding):将索引和程序中统一变量块绑定点关联
绑定到目标:GL_UNIFORM_BUFFER
glBindBufferRange(target, index, buffer, offset, size)
glBindBufferBase(target, index, buffer)
buffer:缓存区句柄,offset:字节计算,对象起始偏移量,size能够读取和写入数据量大小
还有些方法:glBufferData, glBufferSubData, glMapBufferRange, glUnmapBuffer
b、顶点属性、数组和缓存区对象
vetex着色器中以in修饰符作为vetex属性,只读,不能修改
glGetActiveAttrib
glVertexAttrib+数字+类型+v:设置常量定点属性
glVertexAttribPointer(position, size, type, normalized, stride, *ptr):size每个定点的大小维度(1,2,3,4),normalized表示非浮点转换为浮点是否规范化,stride:顶点数据间隔步长,ptr数据
glEnableVertexAttribArray:启动顶点属性数组
glDisableVertexAttribArray:禁用
glGetAttribLocation(program,*name):获取位置
glBindAttribLocation(program, index, *name)绑定到位置
glDrawArrays 或glDrawElements 调用时,顶点数据从客户内存复制到图形内存
顶点缓存区对象
顶点数据缓存-GL_ARRAY_BUFFER 和图元数据缓存-GL_ELEMENTS_ARRAY_BUFFER
glGenBuffers(n, *buffers):生成n个条目的数组指针buffers
glBindBuffer(target, buffer):target类型缓存区使用buffer缓存
glBufferData(target, size, *data, usage):创建和初始化;data客户区数据指针??
glBufferSubData(target, offset,size, *data)创建和初始化;data客户区数据指针
glDeleteBuffers(n, *buffers)
顶点缓存区优于顶点数组
顶点数组对象(VAO)
glGenVetexArrays(n, *arrays)
glBindVetexArray(array)
glDeleteVetexArrays(n, *arrays)
映射缓存区对象 由于普通缓存区
*glMapBufferRange(target, offset, len, access):返回缓存区存储范围指针,access访问标志位域组合 GL_MAP_READ_BIT,GL_MAP_WRITE_BIT等,只不过这两个至少包括一个,其它标志可选
glUnmapBuffer(target) :取消映射
*glFlushMappedBufferRange(target, offset, length)
glCopyBufferSubData(readTarget,writeTarget,readOffset, writeOffset, size)
c、图元和光栅化
图元就是使用绘制方法绘制的几何形状对象,由一组表示顶点位置的顶点描述,每个顶点可以关联颜色,纹理坐标、几何法线
opengl es可渲染三种图元:点,线,三角形, 颜色对于图元来说是填充
三角形:
GL_TRIANGLES(3个点一组,组成各自的三角形)
GL_TRIANGLE_STRIP(按照顺序相邻的三个点组成三角形)
GL_TRIANGLE_FAN(第一个点参与每个三角形的连接, 其它两个点为相邻的两个点)
直线图元:
GL_LINES(两个点一条线,一系列不相连的线)、GL_LINE_STRIP(相邻点均以直线连接)、GL_LINE_LOOP(相邻两点相连,头尾也连)
glLineWidth(width):线宽,像素值
点图元
GL_POINTS:一系列点
内建变量:
gl_PointSize 点半径或者点尺寸;有限制范围,可以查询
glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, *range):range尺寸为2的一维数组
gl_PointCoord:作用点,在fragment shader中使用,vec2类型,float精度mediump
绘制图元
glDrawArrays(mode, first, count):图元类型mode,顶点数据中起始点first,数量count
glDrawElements(mode, count, type, *indices)
glDrawRangeElements(mode, start, end, count, type, *indices):数据指针indices
type:保存元素索引类型:有效值:GL_UNSIGNED_BYTE、GL_UNSIGNED_SHORT,GL_UNSIGNED_INT
图元重启:使用索引类型的最大值隔开多个不相连的图元
几何形状实例化:
glDrawArraysInstanced(mode, ffirst, count, instanceCount):instanceCount绘制图元的实际数量
glDrawElementsInstanced(mode, count, type, *indecs, instanceCount)
glVetexAttribDivisor(index, divisor):index索引,divisor指定index位置属性更新之间传递实例数量
图元装配:
裁剪、透视分割、视口变换
坐标系:规范坐标系(左x正,上y正,左边半轴长度为1),窗口坐标(设备实际坐标,android左上角远点,右下正)
视口变换:视口是一个二维窗口区域
glViewport(x, y, w, h)
glDepthRangef(n, f) 深度范围设置n~f,默认0f~1f
光栅化:
图元裁剪之后,光栅化管道取得单独图元,并为其生成对应片段
剔除:
glFrontFace(dir):dir方向,取值GL_CW或者GL_CCW
glCullFace(mode):剔除的面,GL_FRONT,GL_BACK,GL_FRONT_AND_BACK
glEnable/glDisable(cap): cap = GL_CULL_FACE 启用或者禁用剔除
多边形偏移:
由于光栅化精度问题,会造成多个多边形重叠,造成伪像;偏移就是解决这个问题的
glPolygonOffset(factor, unit)
禁用启动标志:GL_POLYGON_OFFSET_FILL
遮挡查询
用查询对象来跟踪通过深度测试的任何片段和样本
glBeginQuery(target, id)
glEndQuery(target)
glGenQueries(n, *ids)
glDeleteQueries(n, *ids)
glGetQueryObjetuiv(id, name, *params)
target:有效值:GL_ANY_SAMPLE_PASSED, GL_ANY_SAMPLE_PASSED_CONSERVATIVE, GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN
d、vetex shader
提供顶点操作的通用编程方法
其输入包括:属性、统一变量和统一变量缓存区、采样器、着色器程序
内建变量
gl_VertexID:输入,highp,顶点的整数索引,什么鬼。。。
gl_InstanceId:输入,实例化绘图调用中图元实例编号,默认为0
gl_Position gl_PointSize
内建常量:。。。
常用技术:矩阵变换、生成纹理坐标、顶点蒙皮
变换反馈:glTransformFeedbackVaryings glBeginTransformFeedback glEndTransformFeedback等方法
这一节是对编写shader的一个功底考验,也是opengl es绘制各种图形的关键
e、纹理
表现出从网格几何图形无法得到的附加细节,2D纹理,2D纹理数组,3D纹理,立方图纹理
2D纹理
一个图像数据的二维数组,一个纹理单独的数据元素称为纹素
立方图纹理
由6个2D纹理组成的纹理
3D纹理
看作2D纹理多个切片的一个数组,体纹理
2D纹理数组
素组的每个切片表示纹理动画的一帧
纹理对象和纹理加载
创建纹理对象,是一个容器对象,保存渲染所需的纹理数据,例如图像数据、过滤模式、包装模式,用无符号整数表示,也就是纹理对象句柄
glGenTextures(n, *textures)
glDeteleTextures(n, *texture)
glBindTexture(target, texture): target取值:GL_TEXTURE_2D、GL_TEXTURE_3D、 GL_TEXTURE_2D_ARRAY 、GL_TEXTURE_CUBE_MAP
glTexture2D(target, level, iformat, w, h, border, format, type, *pixs)
ifromat:纹理保存的内部格式,format:输入纹理数据格式,type:输入像素数据类型
glPixelStorei(name, param)
过滤包装 glTexParameter+[i|f][v](target, name, param)
glGenerateMipmap(target):在绑定纹理对象上生成mip贴图
glActiveTexture(texture):设置为当前纹理单元,为后续绑定作准备
着色器内建函数:texture(sampler, coord [, bias]):sampler采样器, coord从纹理贴图中读取的[2|3]D纹理坐标, bias可选,mip贴图偏置
glTexImage3D(target, level, iformat, w, h [, depth], border, imgeSize, *data)
glCompressedTexImage[2|3]D(target, level, iformat, w, h [, depth], border, imgeSize, *data)
gl[Compressed]TexSubImage2D(target, level ,xoffset, yoffset, w, h, format, type, *pixs)
gl[Compressed]TexSubImage3D(target, level ,xoffset, yoffset,zoffset, w, h,depth, format, type, *pixs)
glReadBuffer
glCopyTexImage2D
glCopyTexSubImage2D
glCopyTexImage3D
glCopyTexSubImage3D
采样器对象
glGenSamples(n, *samples)
glDeleteSamples(n, *samples)
glBindSampler(unit , sampler): unit 纹理单元
glSamplerParameter[i|f][v](sampler, name ,param)
不可变纹理:
glTextStorage2D(target, levels,format, w, h)
glTextStorage3D(target, levels,format, w, h, depth)
f、fragment shader
内建变量:gl_FragCoord、 gl_FrontFacing 、gl_PointCoord 、gl_FragDepth等
必须使用精度限定符
技术:多重纹理,雾化,alpha测试,用户裁剪平面
g、片段操作
清除缓存区
glClear(mask): 清除缓存区,mast GL_[COLOR|DEPTH|STENCIL]_BUFFER_BIT,不同标志位使用不同的清理方式
glClearColor(red, green, blue, alpha):指定颜色清除缓存区
glClearDepthf(depth):指定深度清理
glClearStencil(s):指定模板清理
多个绘图缓存区清理
glClearBuffer[iv|uiv|fv](buffer, drawbuffer, *value):buffer缓存区类型,drawbuffer缓存区名字,一个思维向量(颜色)或者一个值的指针(深度,模板)
glClearBufferfi(buffer, drawbuffer, depth, stencil):buffer必须为GL_DEPTH_STENCIL, drawbuffer必须为0,清除缓存区depth深度值,stencil模板值
使用标志位控制
glColorMask (bred, bgreen, bblue, blpha)
glDepthMask(bdepth)
glStencilMast(bs)
glStencilMaskSeparate(face, mask):face图元的面顶点
片段测试:默认所有片段测试和操作都被禁用,可以使用glEnable开启
裁剪测试:glScissor(x, y, w, h); 启用标志GL_SCISSOR_TEST
模板缓存区测试:glStencilFunc(func,ref, mask)
glStencilFuncSeparate(face, func, ref, mask)
启动标志:GL_STENCIL_TEST
face:面类型,func比较函数,ref比较值
深度缓存区测试:深度缓存区通常用与隐藏表面的消除 glDepthFunc(func)-GL_DEPTH_TEST
混合测试:
glBlendFunc(sfactor, dfactor): sfactor输入系数,dfactor目标像素系数
glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha)
glBlendColor(red, green, blue, apha)
glBlendEquation(mode):mode 混合元算
glBlendEquationSeparate(modeRgb, modeAlpha)
抖动: 模拟最大色深
启动标志:GL_SAMPLE_COVERAGE
glSampleCoverage(value, invert):value 样本掩码[0,1],invert指定掩码中所有位被反转
质心采样: centroid修饰符修饰
读取写入像素: glReadPixels(x, y, w, h, format, type, *pixels)
多重渲染目标:涉及帧缓存区对象
h、帧缓存区对象(FBO)
一组颜色、深度、和模板纹理或者渲染目标;仅支持单缓存区
glGenRenderbuffers(n, *renderbuffer):分配渲染缓存区
glGenFramebuffers(n, *ids)
glBindRenderbuffer(target, buffer):target= GL_RENDERBUFFER
glRenderbufferSorage(target, iformat, w, h)
glRenderbufferStorageMultisample(target, samples, iformat, w, h):samples:样本数
glBindFramebuffer(target, buffer): target取值: GL[_READ|_DRAW]_FRAMEBUFFER
连接渲染区作为帧缓存区的附着点
glFramebufferRenderbuffer(target, attachment, rendertarget, renderbuffer):帧缓存区目标,attachment必须为GL_[COLOE|DEPTH|STENCIL|DEPTH_STENCIL]_ATTACHMENT
glFramebufferTexture2D(target, attachment, textarget, texture, level)
glFramebufferTextureLayer(target, attachment, texture, level ,layer)
检查缓存区完整性:glCheckFramebufferStatus(target)
帧缓存区块传输: glBlitFramebuffer(srcx0, srcy0, srcx1, srcy1, dstx0, sdty0,dstx1, dsty1, mask, filter)
缓存区失效:
glInvalidateFramebuffer(target, nums, *attachments)
glInvalidateSubFramebuffer(target, num , *attachments, x y, w, h)
删除帧缓存区
glDeleteRenderBuffers(n, *renderbuffer)
glDeleteFrameBuffers(n, *framebuffers)
i、同步对象和栅栏
与glFinish命令比更高效
glFenceSync(condition, flags):condition = GL_SYNC_GPU_COMMANDS_COMPLETE,
flags当前必须为0;返回sync句柄
glDeleteSync(sync):删除
glClientWaitSync(sync, flags, timeout):阻塞,timeout单位纳秒;返回结果表示当前状态
glWaitSync(sync, flags, timeout):立即返回结果, flags必须为0
哈哈,终于大致过一遍了,上面的我大部分只有写概念,具体什么鬼,只能问上帝了,写了这些,就一行一行的撸代码了,体会后,再写续集