OpenGL ES 帧缓冲对象介绍和使用示例

这篇具有很好参考价值的文章主要介绍了OpenGL ES 帧缓冲对象介绍和使用示例。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、介绍

1. 帧缓冲对象

默认情况下,OpenGL渲染的目标是屏幕,但如果你不想直接渲染到屏幕上,还需要对渲染结果做某些后期处理、渲染到纹理、阴影映射等操作,便可以使用帧缓冲对象,实现离屏渲染。

帧缓冲对象(Frame Buffer Object,FBO)是一个概念容器,它可以包含颜色缓冲区、深度缓冲区、模板缓冲区等,形成一个完整的渲染目标。

通过使用帧缓冲对象,可以实现离屏渲染、多重渲染目标(MRT)等高级渲染技术,而不必直接渲染到屏幕。

2. 相关概念

以下是帧缓冲对象的一些基本概念:

  • 颜色缓冲区(Color Buffer):存储渲染的颜色信息。一个帧缓冲对象可以包含多个颜色缓冲区,用于实现多重渲染目标。
  • 深度缓冲区(Depth Buffer): 存储每个像素的深度信息,用于实现深度测试,确保正确的渲染顺序。
  • 模板缓冲区(Stencil Buffer): 存储模板测试的结果,用于实现各种复杂的渲染效果。
  • 渲染缓冲区(Renderbuffer): 实际上是OpenGL ES 3.0中用于存储颜色、深度和模板信息的内存对象。它可以附加到帧缓冲对象的不同附着点。

3. 渲染缓冲对象

渲染缓冲对象(Render Buffer Object,RBO)是一种特殊类型的对象,用于存储图形渲染过程中的像素数据,优点是它的存储格式是优化过的,性能更好,适合渲染而不是读取,所以不可以直接读取。

用途一般是作为帧缓冲对象的附着点,提供一个高效的存储区域,用于存储渲染结果。 渲染缓冲对象通常用于只写入不读取的场景,而纹理可以作为可读取的附着点。于是渲染缓冲对象经常作为深度和模板附件来使用,因为大多数时候并不需要从深度和模板缓冲中读取数据。

4. 关系图

OpenGL ES 帧缓冲对象介绍和使用示例,OpenGL,android,计算机视觉
图源:OPENGL ES 3.0 编程指南 原书第2版

二、使用

帧缓冲对象的使用通常包括以下步骤:

  1. 创建帧缓冲对象: 使用 glGenFramebuffers 函数创建一个帧缓冲对象,其值是一个非零的无符号整数。
  2. 绑定帧缓冲对象: 使用 glBindFramebuffer 函数将帧缓冲对象绑定为当前渲染目标。
  3. 创建和附着附加点: 创建颜色缓冲区和深度缓冲区,然后将它们附着到帧缓冲对象的附着点上。
  4. 渲染: 执行渲染操作,渲染结果将存储在帧缓冲对象中。
  5. 解绑帧缓冲对象: 使用 glBindFramebuffer(GL_FRAMEBUFFER, 0) 将帧缓冲对象解绑,将渲染目标切换回屏幕,数字0即表示屏幕。
  6. 删除帧缓冲对象: 使用 glDeleteFramebuffers 函数创建一个帧缓冲对象。

代码示例:

#include <GLES3/gl3.h>

// 定义帧缓冲对象和渲染缓冲对象的标识符
GLuint framebuffer;
GLuint renderbuffer;

// 定义纹理对象的标识符
GLuint texture;

// 图像的宽度和高度
int screenWidth = 1080;
int screenHeight = 1440;

void init()
{
    // 创建并绑定帧缓冲对象
    glGenFramebuffers(1, &framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

    // 创建并绑定渲染缓冲对象用于深度和模板信息
    glGenRenderbuffers(1, &renderbuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);

    // 分配存储空间
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, screenWidth, screenHeight);

    // 将渲染缓冲对象附着到帧缓冲的深度和模板附着点
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderbuffer);

    // 创建并绑定纹理对象用于颜色附着点
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

    // 分配存储空间
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screenWidth, screenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);

    // 设置纹理参数
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    // 将纹理对象附着到帧缓冲的颜色附着点
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);

    // 检查帧缓冲完整性
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
        // 处理错误
        // ...
    }

    // 解绑帧缓冲对象
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

void render()
{
    // 绑定帧缓冲对象
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

    // 渲染操作,将渲染结果存储在帧缓冲对象中

    // 解绑帧缓冲对象
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    // 使用帧缓冲对象的渲染结果进行后期处理或直接显示在屏幕上
}

三、应用实例

1. 保存纹理数据

利用FBO绑定纹理到颜色附着上,便可以方便的把纹理数据读取出来了。

代码示例:

void textureToBuffer(int textureId, int x, int y, int width, int height, unsigned char *buffer) {
    // 创建FBO
    GLuint fbo[1];
    glGenFramebuffers(1, fbo);
    // 绑定
    glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0);
    // 读取数据
    glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);  // 这里的format和type需要和纹理一致
    // 解绑和释放
    glBindFramebuffer(GL_FRAMEBUFFER, 0);  // unbind
    glDeleteFramebuffers(1, fbo);
}

2. 渲染结果还需后处理

当需要多次渲染(渲染结果还需后处理)才能完成的任务时,此时无需次次都渲染到屏幕上,而是可以选择进行一次或多次离屏渲染,最终再渲染到屏幕上。

下面看一个图像虚化的例子来感受一下。(关于虚化方法这里不做过多解释,可以查看其他文章,如:opencv图像卷积操作和常用的图像滤波函数)

方案一:单次滤波完成虚化

使用 BoxFilter 的 fragment shader 文件如下:

#version 300 es
precision mediump float;
layout (location = 2) uniform sampler2D s_texture;
layout (location = 3) uniform int u_kernelSize;

in vec2 v_texCoor;
out vec4 fragColor

vec4 boxFilter() {
    if (u_kernelSize <= 1) {
        return texture(s_texture, v_texCoor);
    }
    ivec2 texSize = textureSize(s_texture, 0);
    float xStep = 1.0 / float(texSize.x);
    float yStep = 1.0 / float(texSize.y);
    vec4 sum = vec4(0.0);
    int num = 0;
    // 复杂度:N^2
    for (int i = -u_kernelSize; i <= u_kernelSize; i++) {
        for (int j = -u_kernelSize; j <= u_kernelSize; j++) {
            float x = v_texCoor.x + float(i) * xStep;
            float y = v_texCoor.y + float(j) * yStep;
            sum += texture(s_texture, vec2(x, y));
            num++;
        }
    }
    return sum / float(num);
}

void main() {
    fragColor = boxFilter();
}

我们通过对周围像素采样,并求平均值的方式获取当前点虚化后的值。由于实现上是嵌套的两层for循环,随着 u_kernelSize 的增大,其耗时将急剧增大。

方案二:横纵方向两次滤波完成虚化

之前我们是对 N*N 的区域进行采样并求平均值,其时间复杂度为 N^2。但如果我们先对横向进行一次采样求平均值,再对输出的结再进行一次纵向的采样求平均值,这样只需要 N*2 即可以达到相同的效果。在高通6225机器上测试 KernelSize = 7 时,耗时从44ms降低到9ms。

优化后的 fragment shader 文件如下:

#version 300 es
precision mediump float;
layout (location = 2) uniform sampler2D s_texture;
layout (location = 3) uniform int u_kernelSize;
layout (location = 4) uniform int u_boxFilterType;

in vec2 v_texCoor;
out vec4 fragColor

vec4 boxFilterHorizontal() {
    if (u_kernelSize <= 1) {
        return texture(s_texture, v_texCoor);
    }
    ivec2 texSize = textureSize(s_texture, 0);
    float xStep = 1.0 / float(texSize.x);
    vec4 sum = vec4(0.0);
    int num = 0;
    // 复杂度:N
    for (int i = -u_kernelSize; i <= u_kernelSize; i++) {
        float x = v_texCoor.x + float(i) * xStep;
        sum += texture(s_texture, vec2(x, v_texCoor.y));
        num++;
    }
    return sum / float(num);
}

vec4 boxFilterVertical() {
    if (u_kernelSize <= 1) {
        return texture(s_texture, v_texCoor);
    }
    ivec2 texSize = textureSize(s_texture, 0);
    float yStep = 1.0 / float(texSize.y);
    vec4 sum = vec4(0.0);
    int num = 0;
    // 复杂度:N
    for (int i = -u_kernelSize; i <= u_kernelSize; i++) {
        float y = v_texCoor.y + float(i) * yStep;
        if (y < 0.0 || y > 1.0) {
            continue;
        }
        sum += texture(s_texture, vec2(v_texCoor.x, y));
        num++;
    }
    return sum / float(num);
}

void main() {
    if (u_boxFilterType == 1) {
        fragColor = boxFilter();
    } else if (u_boxFilterType == 2) {
        fragColor = boxFilterHorizontal();
    } else if (u_boxFilterType == 3){
        fragColor = boxFilterVertical();
    } else {
        fragColor = texture(s_texture, v_texCoor); // origin
    }
}

如上我们通过指定 u_boxFilterType 的值来实现使用不同的函数来完成滤波(当然你也可以写几个不同的 fragment shader 来实现)。

部分调用代码如下(完整代码链接在文章末尾):

public void drawBitmapUseFBO() {
    if (mGLProgramBlur <= 0) {
        Log.e(TAG, "mGLProgram not create!");
        return;
    }
    GLES30.glFinish();
    long startTime = System.currentTimeMillis();

    GLES30.glUseProgram(mGLProgramBlur); // 指定使用的program
    GLES30.glEnable(GLES30.GL_CULL_FACE); // 启动剔除
    // init vertex
    GLES30.glEnableVertexAttribArray(0);
    GLES30.glEnableVertexAttribArray(1);
    GLES30.glVertexAttribPointer(0, VERTEX_SIZE, GLES30.GL_FLOAT, false, VERTEX_STRIDE, mVertexBuffer);
    GLES30.glVertexAttribPointer(1, VERTEX_SIZE, GLES30.GL_FLOAT, false, VERTEX_STRIDE, mTextureBitmapBuffer);

    // 先进行boxFilterHorizontal,渲染到FBO上,绑定颜色附着的纹理
    GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextures[0]);
    GLES30.glUniform1i(2, 0);
    GLES30.glUniform1i(3, mKernelSize);  // set u_kernelSize
    GLES30.glUniform1i(4, 2);  // set u_boxFilterType to boxFilterHorizontal
    GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBO[0]);
    GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, mTextures[1], 0);
    GLES30.glDrawElements(GLES30.GL_TRIANGLE_FAN, VERTEX_ORDER.length, GLES30.GL_UNSIGNED_BYTE, mDrawListBuffer);

    // 再进行boxFilterVertical,渲染到屏幕
    GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);  // 解绑,即重新绑定回屏幕
    // GLES30.glVertexAttribPointer(0, VERTEX_SIZE, GLES30.GL_FLOAT, false, VERTEX_STRIDE, mVertexBuffer);
    // 这次不需要再对纹理进行上下翻转了,重新设置下纹理坐标的值
    GLES30.glVertexAttribPointer(1, VERTEX_SIZE, GLES30.GL_FLOAT, false, VERTEX_STRIDE, mTextureRotation0Buffer);
    GLES30.glActiveTexture(GLES30.GL_TEXTURE1);
    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextures[1]);
    GLES30.glUniform1i(2, 1);
    GLES30.glUniform1i(3, mKernelSize);  // set u_kernelSize
    GLES30.glUniform1i(4, 3);  // set u_boxFilterType to boxFilterVertical
    GLES30.glDrawElements(GLES30.GL_TRIANGLE_FAN, VERTEX_ORDER.length, GLES30.GL_UNSIGNED_BYTE, mDrawListBuffer);
    GLES30.glDisableVertexAttribArray(0);
    GLES30.glDisableVertexAttribArray(1);

    GLES30.glFinish();
    long endTime = System.currentTimeMillis();
    Log.i(TAG, "drawBitmapUseFBO time(ms): " + (endTime - startTime));
}

先进行 boxFilterHorizontal,渲染到FBO上,再进行 boxFilterVertical,渲染到屏幕。

3. 多重渲染目标(MRT)

通常情况下,片段着色器只有一个输出颜色。如果想要同时输出多个颜色,则可以使用多重渲染目标来实现,通过多重渲染目标,片段着色器输出多个颜色(用于保存RGBA颜色、法线、深度或纹理坐标)。MRT 用于多种高级渲染算法中,例如延迟着色和快速环境遮蔽估算(SSAO)。

使用示例:

fragment shader 中指定多个输出颜色

#version 300 es
precision mediump float;
layout (location = 2) uniform sampler2D s_texture;

in vec2 v_texCoor;
out vec4 fragColor1;  // 输出到第一个颜色附着点
out vec4 fragColor2;  // 输出到第二个颜色附着点
out vec4 fragColor3;  // 输出到第三个颜色附着点

void main() {
    vec4 color = texture(s_texture, v_texCoor); // origin
    fragColor1 = vec4(1.0) - color; // inverted
    fragColor2 = mix(color, vec4(1.0, 0.0, 0.0, 1.0), 0.5); // mix with color red
    fragColor3 = mix(color, vec4(0.0, 0.0, 1.0, 1.0), 0.5); // mix with color blur
}

部分调用代码如下(完整代码链接在文章末尾):

public void drawBitmapUseMRT(int targetIndex) {
    if (mGlProgramMRT <= 0) {
        Log.e(TAG, "mGLProgram not create!");
        return;
    }
    GLES30.glFinish();
    long startTime = System.currentTimeMillis();

    Log.d(TAG, "drawBitmapUseMRT mGLProgram: " + mGlProgramMRT);
    GLES30.glUseProgram(mGlProgramMRT); // 指定使用的program
    GLES30.glEnable(GLES30.GL_CULL_FACE); // 启动剔除
    // init vertex
    GLES30.glEnableVertexAttribArray(0);
    GLES30.glEnableVertexAttribArray(1);
    GLES30.glVertexAttribPointer(0, VERTEX_SIZE, GLES30.GL_FLOAT, false, VERTEX_STRIDE, mVertexBuffer);
    GLES30.glVertexAttribPointer(1, VERTEX_SIZE, GLES30.GL_FLOAT, false, VERTEX_STRIDE, mTextureBitmapBuffer);
    // bind texture
    GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
    GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextures[0]);
    GLES30.glUniform1i(2, 0);

    // bind FBO and color attachment
    GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBO[0]);
    GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, mTextures[1], 0);
    GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT1, GLES30.GL_TEXTURE_2D, mTextures[2], 0);
    GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT2, GLES30.GL_TEXTURE_2D, mTextures[3], 0);
    // 假设有两个颜色附着点,分别对应 GL_COLOR_ATTACHMENT0 和 GL_COLOR_ATTACHMENT1
    int drawBuffers[] = { GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_COLOR_ATTACHMENT1, GLES30.GL_COLOR_ATTACHMENT2 };
    // 将颜色附着点指定给帧缓冲对象
    GLES30.glDrawBuffers(drawBuffers.length, drawBuffers, 0);
    GLES30.glDrawElements(GLES30.GL_TRIANGLE_FAN, VERTEX_ORDER.length, GLES30.GL_UNSIGNED_BYTE, mDrawListBuffer);

    GLES30.glFinish();
    long endTime = System.currentTimeMillis();
    Log.i(TAG, "drawBitmapUseMRT time(ms): " + (endTime - startTime));
}

四、示例代码地址

https://github.com/afei-cn/OpenGLSample/tree/master/fbodemo

其中shader代码在 fbodemo/src/main/assets 文件夹下,调用代码在 fbodemo/src/main/java/com/afei/fbodemo/JavaDrawer.java 中。文章来源地址https://www.toymoban.com/news/detail-768272.html

到了这里,关于OpenGL ES 帧缓冲对象介绍和使用示例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • OpenGL ES入门介绍

    OpenGL ES入门介绍

    目录 1.OpenGL ES的简介 2. 基本流程和概念 2.1 渲染的基本流程 2.2 管线 2.3 顶点 2.4 纹理 2.5 顶点着色器(VertexShader) 2.6 图元装配 2.7 光栅化 2.8 片段着色器(FragmentShader) 2.9 逐片段操作         第一次接触OpenGL ES是两年前,但是看到OpenGL中各种专业名词和专业术语,

    2024年01月22日
    浏览(7)
  • Android OpenGL ES 学习(四) -- 正交投影

    Android OpenGL ES 学习(四) -- 正交投影

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学习(三) – 绘制平面图形 Android OpenGL ES 学习(四) – 正交投屏 Android OpenGL ES 学习(五) – 渐变色 Android OpenGL ES 学习(六) – 使用 VBO、VAO 和 EBO/IBO 优化程序 Android OpenG

    2024年02月13日
    浏览(5)
  • Android OpenGL ES 学习(五) -- 渐变色

    Android OpenGL ES 学习(五) -- 渐变色

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学习(三) – 绘制平面图形 Android OpenGL ES 学习(四) – 正交投屏 Android OpenGL ES 学习(五) – 渐变色 Android OpenGL ES 学习(六) – 使用 VBO、VAO 和 EBO/IBO 优化程序 Android OpenG

    2024年02月07日
    浏览(7)
  • Android OpenGL ES 学习(一) -- 基本概念

    Android OpenGL ES 学习(一) -- 基本概念

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学习(三) – 绘制平面图形 Android OpenGL ES 学习(四) – 正交投屏 Android OpenGL ES 学习(五) – 渐变色 Android OpenGL ES 学习(六) – 使用 VBO、VAO 和 EBO/IBO 优化程序 Android OpenG

    2024年01月23日
    浏览(9)
  • Android OpenGL ES 学习(八) –矩阵变换

    Android OpenGL ES 学习(八) –矩阵变换

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学习(三) – 绘制平面图形 Android OpenGL ES 学习(四) – 正交投影 Android OpenGL ES 学习(五) – 渐变色 Android OpenGL ES 学习(六) – 使用 VBO、VAO 和 EBO/IBO 优化程序 Android OpenG

    2023年04月08日
    浏览(6)
  • Android OpenGL ES实现简单绿幕抠图

    Android OpenGL ES实现简单绿幕抠图

    目录 正文 OES Filter BlendShader Filter 最后的效果 缺陷 实现绿幕抠图,其实想法很简单。 这里简单粗暴的使用着色器替换。 OES Filter 直接实现在相机预览上的Shader ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #extension GL_OES_EGL_image_external : require precision mediump float ;        

    2024年02月13日
    浏览(12)
  • LearnOpenGL - Android OpenGL ES 3.0 绘制三角形

    LearnOpenGL - Android OpenGL ES 3.0 绘制三角形

    LearnOpenGL 笔记 - 入门 01 OpenGL LearnOpenGL 笔记 - 入门 02 创建窗口 LearnOpenGL 笔记 - 入门 03 你好,窗口 LearnOpenGL 笔记 - 入门 04 你好,三角形 OpenGL - 如何理解 VAO 与 VBO 之间的关系 经过一段时间 OpenGL 的学习,我们已经掌握了如何使用 glwf 在桌面端绘制简单图形。现在让我们把目光

    2024年02月12日
    浏览(24)
  • Android OpenGL ES 学习(九) – 坐标系统和实现3D效果

    Android OpenGL ES 学习(九) – 坐标系统和实现3D效果

    OpenGL 学习教程 Android OpenGL ES 学习(一) – 基本概念 Android OpenGL ES 学习(二) – 图形渲染管线和GLSL Android OpenGL ES 学习(三) – 绘制平面图形 Android OpenGL ES 学习(四) – 正交投影 Android OpenGL ES 学习(五) – 渐变色 Android OpenGL ES 学习(六) – 使用 VBO、VAO 和 EBO/IBO 优化程序 Android OpenG

    2024年01月25日
    浏览(11)
  • Android OpenGl 介绍(一)

    一、OpenGl 介绍 看到这个介绍,相信大家都不会陌生,因为在平时的工作中,或多或少大家都会听说过 openGl 这个东西,而且对它的印象基本都是觉得比较高深难懂。其实这个东西也不是那么难,那么无从下手,首先必须要了解一些基本的背景和搞懂一些基本概念,然后就可

    2024年02月16日
    浏览(8)
  • Android APP OpenGL ES应用(01)GLSurfaceView 2D/3D绘图基础

    Android APP OpenGL ES应用(01)GLSurfaceView 2D/3D绘图基础

    OpenGL本身是开放图形库的一种标准,定义了一个跨语言、跨平台的编程规范,主要用于3D图形编程。OpenGLES是OpenGL的裁剪版本,主要是针对嵌入式设备/移动设备 (像手机、游戏机这种等等。。。) 进行裁剪后的库。对于Android设备来说主要是用OpenGL ES。从初学者角度来讲 Ope

    2023年04月20日
    浏览(7)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包