OpenGL学习资料和记录

2019-06-11  本文已影响0人  hjm1fb

学习资料

OpenGL:
3D图形学:
OpenGL ES:
OpenGL 内存/性能优化:

VAO 和VBO的使用流程区别
OpenGLES顶点缓冲
android平台下OpenGL ES 3.0实例详解顶点缓冲区对象(VBO)和顶点数组对象(VAO)

VAO在OpenGL3.0上才能用。在OpenGL2.0上,使用VBO的步骤大致如下:

1. GenBuffer (called once in the whole rendering lifecycle)
2. glUseProgram(m_ProgId); (called on every draw for this one and the following commands)
3. BindBuffer to type GL_ARRAY_BUFFER 
4. glBufferData or glBufferSubData() (called only when data has changed)
5. EnableVertexAttribArray   
6. glVertexAttribPointer 
6. glBindBuffer to 0
7. glDrawXX
8. glDisableVertexAttribArray // 在iOS低端机上频繁调用会导致绘制无效,可以不调用或者在OpenGL环境销毁时调用
9. glUseProgram(0)
10. glDeleteBuffers  (called when the data is no longer used, ie. quit the Activity)

使用两个或以上的Buffer的时候注意: 调用glBufferData 和 glVertexAttribPointer前都要 BindBuffer 到对应的Buffer上. ( 比如顶点信息和参数信息分别使用 GLuint attribPositionBuffer 和 GLuint attribParamBuffer)

在做AE插件开发的时候,发现必须VBO结合VAO, 才能画粒子,并且要设置glEnable(GL_PROGRAM_POINT_SIZE);

VAO的使用示例:

1. GenBuffer (called once in the whole rendering lifecycle)
2. glUseProgram(m_ProgId); (called on every draw for this one and the following commands)
3. BindBuffer to type GL_ARRAY_BUFFER 
4. glBufferData or glBufferSubData() (called only when data has changed)
5. EnableVertexAttribArray   
6. glVertexAttribPointer 
6. glBindBuffer to 0
7. glDrawXX
8. glDisableVertexAttribArray // 在iOS低端机上频繁调用会导致绘制无效,可以不调用或者在OpenGL环境销毁时调用
9. glUseProgram(0)
10. glDeleteBuffers  (called when the data is no longer used, ie. quit the Activity)
#=========================配置VAO阶段===============================
unsigned int VBO[2], VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(2, VBO);
#=========================绑定VAO===============================
    glBindVertexArray(VAO); 
#=======================绑定第一个VBO============================
    glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
#===============================================================
        glBufferData(GL_ARRAY_BUFFER, sphereVertices.size() * sizeof(float), &sphereVertices[0], GL_STATIC_DRAW); //向第一个VBO中写入数据
#================告知VAO该如何解释第一个VBO的信息=================
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
#=======================解绑第一个VBO===========================
    glBindBuffer(GL_ARRAY_BUFFER, 0);
#===============================================================
#=======================绑定第二个VBO============================
    glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
#===============================================================
    glBufferData(GL_ARRAY_BUFFER, sizeof(texVertrices), texVertrices, GL_STATIC_DRAW);//向第二个VBO中写入数据
#================告知VAO该如何解释第二个VBO的信息=================
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(1);
#=======================解绑第二个VBO===========================
glBindBuffer(GL_ARRAY_BUFFER, 0);
#================解绑VAO=================
glBindVertexArray(0);
..............
#================绘制阶段使用VAO=================
    glBindVertexArray(mVAO);
    glDrawArrays(GL_POINTS, 0, mCount);
    glBindVertexArray(0);
# 如果是一个VAO 对应一个VBO, 但VBO里包含两个属性,则VAO解释VBO的两行改成如下形式:
    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // Color attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

用Direct Textures或PBO提高glReadPixels、glTexImage2D性能
Android 关于美颜/滤镜 利用PBO从OpenGL录制视频
如果想在不同的渲染图层或者渲染步骤复用glReadPixels的结果,可以把对应的计算结果存储到纹理的RGB值上(单独一个图层),此纹理就可以作为资源Map引用共享给同一GLContext的不同对象使用了。比如R和G通道存储Mask图glReadPixels处理后得到的目标方框->变换矩阵对应的每个像素的x/y目标坐标。即坐标纹理。
这里要注意两点,1是如果用Alpha存储数据,要考虑Alpha预乘,2是在后面读取此坐标纹理上的值时,可能有精度损失,
引起锯齿。一个解决方案是把坐标纹理用小尺寸再画一遍(比如16 * 16),然后再使用时,因为纹理过滤模式设置为双线性插值,每个像素原来的采样误差,会被双线性插值平滑,就没有锯齿了,只有很轻微的整体位置偏移

GPUImage(OpenGL ES)的性能优化、爬坑与架构改善(原文已删,我在Github私人仓库有备份)

gldrawelements比gldrawarrays更节省GPU消耗(vertex reuse, flexibility)
着色器里不需要精确的数据降低精度; 不需要3D绘制时vTextureCoord可以定义成二维;计算尽量在顶点着色器而不是片段着色器。
Overcome Incoherent Memory Access in OpenGL ES 3.0

如何打造一个高性能的前端智能推理引擎

在做亮度变换/色调分离等功能时,往往也会模拟Gamma矫正以有更符合预期的人眼感知效果:
posterize.frag(此Shader应该是预设输入是线性颜色空间的Texture)

调试:

GAPID
安卓图形调试利器-GAPID
GAPID Graphics Debugger (Android Game Developer Summit 2018)
如何快速定位Android端GPU问题之工具介绍
推荐一款强大的 Android OpenGL ES 调试工具
Android Studioh 和GAPID是不能同时用的,不然在GAPID中会因为都要占用adb而找不到device。可以打开资源管理器,确认没有adb的服务后再启动GAPID软件。
如果没有自动安装gapid-arm64-v8a.apk/gapid-armeabi-v7a.apk, 那么需要手动安装。
调试release版本的应用的话,需要root过的手机。
自行排查未检测到设备的原因(可以尝试重启,然后运行adb root, 再运行adb remount):
查看log文件: Couldn't get framebuffer attachment

GAPID_配置示例.png

RenderDoc
GPU分析工具RenderDoc使用 (作者不建议抓其他家应用的shader,但还是可以抓的。然后截止2020.07.07还没有Mac平台的稳定版本的安装包。
RenderDoc is only intended for capturing your own programs. Capturing copyrighted programs that you do not have the rights to is not endorsed and I will provide absolutely no support for it.)
使用后更新:RenderDoc的确很好用,抓取的数据非常全。但调试的条件是Root机或APP为debug模式。
鉴于逆向第三方应用越来越难,所以推荐用小米Root机来调试。不过我试了三台不同型号的小米Root机,只有小米SE 8是连接稳定的。其余两台连接不稳定,无法工作。
Command-line tool for converting RenderDoc CSV export to .OBJ

小米手机获取完整ROOT权限教程

Kodelife的实用安装指南

Shader:

优秀的Shader教程推荐
ShaderToy
边栏的小雨伞
shadertoy网站上的一些效果
KodeLife | Shader 实时编辑预览的强大工具使用实践
the Book of Shaders
Shader大神iq的教学网站

ShaderToy使用自定义视频纹理

粒子动画(点精灵):

OpenGL 点精灵效果
OPenGL点精灵
粒子
StarWars.Android 界面粉碎效果中的openGL操作解析
opengles绘制点精灵
smartGL

gl_PointCoord.png
(OpenGL标准纹理坐标的原点在左下角,但Android纹理坐标的原点在左上角,所以gl_fragcoord的原点也在左上角了)

对照:


OpenGL世界坐标系&Android纹理坐标系.png

(OpenGL标准纹理坐标的原点在左下角,但Android纹理坐标的原点在左上角)
update:其实是数据的原因,Android的Bitmap的坐标原点是左上角,Android端的OpenGL ES的glReadPixels等接口也没有对此适配,所以读入的图像数据就是颠倒的了,需要纹理坐标y轴颠倒一下来负负得正。所以理解为“Android纹理坐标的原点在左上角”,其实不准确。只是说上下颠倒坐标系,就刚好把数据又转正了。也就是只需要在bitmap 作为纹理输入的时候,需要手动上下颠倒一下,后面就按正常的OpenGL纹理处理就可以了

OpenGL ES 3.0 新增的实例渲染接口,可以增强粒子动画的表现能力,以及优化传输和绘制的性能:
glVertexAttribDivisor
glDrawArraysInstanced
glBeginTransformFeedback

待学习的粒子效果:
unity-optical-flow
PixelFlow

  1. NPOT纹理与平铺模式

OpenGL规范从2.0开始支持显示边长为非2次幂的Texture,但限制条件是需要环绕模式为CLAMP_TO_EDGE并且过滤模式为NEAREST或者LINEAR。
解除限制的条件是硬件支持OES_texture_npot的扩展。
获取硬件扩展列表的代码如下:

    std::string GetGLString(unsigned int pname) {
        const char* gl_string =
                reinterpret_cast<const char*>(glGetString(pname));
        if (gl_string)
            return std::string(gl_string);
        return "";
    }
...
const char* gl_extensions = GetGLString(GL_EXTENSIONS).c_str();
ULOGE("gl_extensions %s", gl_extensions);
//看打印的字符串里是否包含OES_texture_npot

OpenGL 2.0规范里有这个限制,是因为NPOT纹理设置REPEAT等模式会造成在图像连接处有接缝:
Seamless tilemap rendering (borderless adjacent images)

OpenGL ES 3.0 has full NPOT support in core; ES 2.0 has limited NPOT support (no mipmaps, no Repeat wrap mode) in core; and ES 1.1 has no NPOT support.

OpenGL对NPOT纹理的支持情况:
For ES 1.1 and 2.0, full NPOT support comes with GL_ARB_texture_non_power_of_two orGL_OES_texture_npot extension. In practice, iOS devices don’t support this; and on Android side there’s support on Qualcomm Adreno and ARM Mali. Possibly some others.

For ES 1.1, limited NPOT support comes with GL_APPLE_texture_2D_limited_npot (all iOS devices) or GL_IMG_texture_npot (some ImgTec Android devices I guess).

而谷歌从Android4.3开始强制要求设备支持OpenGL3.0:Android 4.3兼容性定义的“本机API兼容性”一栏

从5.0开始设备必须支持OpenGL3.1

Android 兼容性计划概览

所以可以认为,Android 4.3以上的设备以及iOS 7.0(iPhone系列 5S)以上的设备,启用OpenGL3.0后,支持对NPOT纹理应用GL_REPEAT等功能。而Android的4.3以前的设备,如果硬件支持OES_texture_npot扩展,那么也同样支持。
但这个只是理论上,Android的兼容性问题众所周知。

iOS升级3.0的文档:Adopting OpenGL ES 3.0

参考:
NPOT texture in iOS can't be repeat mode!
PowerVR Supported Extensions OpenGL ES and EGL

update:
实践的结论是,用shader在OpenGL2.0上实现NPOT的GL_REPEAT模式,预览效果也还好。在几台Android和iOS设备上测试,连接处的缝隙看不出来。
所以OpenGL2.0不支持的原因,我猜是在mipmap的场景下可能有问题。


记录

精度修饰符
OpenGL精度修饰符
顶点着色器默认精度为highp.
片元着色器没有默认精度, 所以需要专门指定默认精度或者给每个数值类型变量指定精度.
半精度浮点数Half
由于一个浮点型数在计算机内由符号位/指数位/尾数表示,如
1000.1 =1.0001 * 2的3次
1110110.1 = 1.1101101 * 2的6次
所以数值越大,float的精度误差也随着指数的增加而增加
有效值

glGetXXLocation(比如glGetUniformLocation/glGetAttribLocation) 有效值为>=0,失败会返回-1;
GenXX(比如glGenBuffers) 有效值为>0,失败name会是0;
纹理 Texture ID大于0 等于0表示此纹理不合法;
MEDIUMP_FLT_MAX 65504.0
MEDIUMP_FLT_MIN 0.00006103515625
MEDIUMP小数点最大误差为0.048%,平均误差0.018%

其他记录

但因为四字节对齐的问题,可能这个vec4数组的最大个数也是float数组的最大个数:
GLSL float/vec3/vec4 array max size = GL_MAX_VERTEX_UNIFORM_VECTORS?
因此vec4或者4xn的mat,对内存的利用效率是最高的

OpenGL 3.3 guarantees a minimum uniform size of 1024 bytes 也就是16个uniform mat4

  1. With the texture object, the content drawn into the texture object can be used as a texture image.The renderbuffer object is a more general-purpose drawing area, allowing a variety of data types to be written.
  2. Renderbuffer is simply a data storage object containing a single image of a renderable internal format. It is used to store OpenGL logical buffers that do not have corresponding texture format, such as stencil or depth buffer.
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_nowFboId);
    ULOGD("glGetIntegerv filter before draw: %d", _nowFboId);

MAX_VARYING_VECTORS的解释(WebGL shaders: maximum number of varying variables)

OpenGL 关于深度测试
绘制粒子时往往需要 glDisable(GL_DEPTH_TEST);

  1. vector在左边,右边乘以Martix是把vector当做行向量,运算结果与vector在右边,左边乘以Martix的转置矩阵相同GLSL里的矩阵乘法

  2. 已经GLSL里的Martix是以列主序存储,所以vector在左边,右边乘以Martix的好处是提高运算效率:矩阵:行主序、列主序、行向量、列向量

  3. GLSL里初始化矩阵时,同样以列优先顺序赋值:

vec2 v = vec2(10., 20.);
mat2 m = mat2(1., 2.,  3., 4.);
vec2 w = v * m; // = vec2(1. * 10. + 2. * 20., 3. * 10. + 4. * 20.)
  1. 举例:
// 第二行是vector在左边,右边乘以Martix,所以要转置旋转矩阵
mat2 rmat = mat2(cos(angle),-sin(angle),sin(angle),cos(angle)); 
vec2 sampleCoord = vec2(u,v) * rmat;
  1. 矩阵顺序与执行顺序的关系:
    当前矩阵为A,变换矩阵为B,A执行B变换后新矩阵为AB,和顶点坐标v相乘,从而构成新的顶点坐标ABv。上述过程说明,程序中绘制顶点前的最后一个变换命令最先作用于顶点之上。这同时也说明,OpenGL编程中,实际的变换顺序与指定的顺序是相反的。Shader里顶点坐标v为列向量,如果和顶点坐标相乘的方式为vAB,此时其实相当于把顶点坐标v当做行向量,计算结果与v在右边,左边乘以Martix的转置矩阵相同

Filament
Filament解析1·主体逻辑
Filament专题 by Night_Aurora liujing7256
技术文章/渲染引擎-技术文章/filament

数据驱动渲染Data Driven Rendering: Pipelines

  1. 用Renderdoc设置连抓10张,有助于提高抓取成功率。
  2. 抓shader的时候,如果抓不到:
    a. 图片视频编辑页面抓不到,也可以去相机实时预览页面试试
    b. 可以把编辑结果放到草稿箱,然后设置连抓10帧,然后点击进入草稿箱,在APP应用草稿结果的时候,可能会抓到shader。
    c. 有时候RenderDoc和Gapid抓不到的shader, SnapDragon能抓到。SnapDragon里要一行一行看执行命令来推测渲染流程,稍微麻烦一点,但uniform和纹理等资源都是可以抓到的
    d. 如果是一张纹理图抓不到,可以试试Gapid, 因为Gapid的纹理列表里有整个OpenGL环境里的所有纹理,甚至是系统相册里的图片纹理。所以颜色查找表之类的纹理抓不到,可以试试去Gapid的纹理列表里找找。

噪声性能比较:


8-Table2-1.png
上一篇下一篇

猜你喜欢

热点阅读