七、帧缓冲离屏渲染

news2024/11/23 19:05:57

第一部分基础概念

1)两种帧缓冲的由来

首先opengl能够显示到屏幕,也是有一个默认的framebuffer由窗口系统创建并管理的,将数据放到默认framebuffer 中就可以显示到屏幕上。但是应用程序也想创建额外的非可显示的framebuffer。
应用程序自己创建FBO也是与系统framebuffer一样的,需要有一系列的缓冲区(至少一个颜色缓冲区[因为多个颜色附件点可以实现同时将颜色缓冲区渲染到多个目的地],最多一个深度缓冲区,最多一个模板缓冲区),连接这些缓冲区的叫附着点。FBO本身不存放数据,相当于是有几个指针(指向这些缓冲区)。
可以附着framebuffer的两个类型,纹理和渲染缓冲区,纹理附加到FBO叫opengl将执行渲染到纹理,renderbuffer 被附着到 FBO,则 OpenGL 将执行“离屏渲染”,就是将操作的数据传输到纹理或者渲染缓冲区里面去。
默认帧缓冲自己创建的帧缓冲其实是一样的,只是作用不同,默认的是窗口系统提供直接渲染到屏幕的,而自己创建的则可以重定向,做许多特殊效果再渲染。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mkksX0GB-1686050452782)(C:\Users\CreatWall_zhouwen\Desktop\pic\pic\wenli.png)]

2)FBO概念:

帧缓冲区对象,就是一个可以添加纹理或者渲染缓冲区对象RBO的一个容器,默认帧缓冲区是直接添加到屏幕所对应的缓冲区,而自定义的可以指定要输出的对象中,纹理或者渲染缓冲区对象中。
FBO如需要渲染则需要附着也就是添加纹理或者渲染缓冲区对象之后才能成为渲染目标,它提供了三种附着方式:颜色附着,深度附着,模板附着。

3)FBO的两个作用:

1、因为默认提供的帧缓冲区只适合纹理尺寸小于或等于帧缓冲区才有效,因此可以通过链接到纹理的pBuffer来实现渲染到纹理,但是上下文和窗口系统提供的可会制面切换开销大,所以引入FBO
2、因为一般GLSurfaceView 是绘制到屏幕的,但是有时候部分场景是不需要渲染到屏幕,只是利用GPU进行计算,则使用FBO可以完成,FBO可以让渲染操作不再渲染到屏幕而是离屏Buffer中。

4)语法基础

使用一个叫做glGenFramebuffers的函数来创建一个帧缓冲对象(简称FBO)

GLuint fbo;
glGenFramebuffers(1, &fbo);

与之前的顶点数组和顶点缓冲差不多,也是首先我们要创建一个帧缓冲对象,把它绑定到当前帧缓冲,做一些操作,然后解绑帧缓冲。我们使用glBindFramebuffer来绑定帧缓冲:

glBindFramebuffer(GL_FRAMEBUFFER, fbo);
绑定到GL_FRAMEBUFFER目标后,接下来所有的读、写帧缓冲的操作都会影响到当前绑定的帧缓冲

帧缓冲的附加:包括纹理附件和渲染缓冲区附件

这种附加其实就是最后完成操作之后将数据结果输出到纹理或者渲染缓冲区的。

纹理附件:

当把一个纹理附加到帧缓冲上的时候,所有渲染命令会写入到纹理上,就像它是一个普通的颜色/深度或者模板缓冲一样。使用纹理的好处是,所有渲染操作的结果都会被储存为一个纹理图像,这样我们就可以简单的在着色器中使用了。

创建帧缓冲附件的纹理,与普通纹理创建差不多
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);

//主要这里绑定的数据是NULL,对于这个纹理,我们只分配内存,而不去填充它。纹理填充会在渲染到帧缓冲的时候去做。同样,要注意,我们不用关心环绕方式或者Mipmap,因为在大多数时候都不会需要它们的。
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 800, 600, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
//你打算把整个屏幕渲染到一个或大或小的纹理上,你需要用新的纹理的尺寸作为参数再次调用glViewport(要在渲染到你的帧缓冲之前做好),否则只有一小部分纹理或屏幕能够绘制到纹理上。
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

纹理附加到帧缓冲上

附加的适合是只用创建内存的纹理,但是再利用离屏渲染进行操作时绑定的是有图像的纹理,离屏渲染操作完成之后就会将结果输出到这里附加的空数据纹理中。再渲染这个纹理就是将离屏渲染操作后的结果渲染到屏幕上。

glBindTexture(GL_TEXTURE_2D, texture);//激活纹理
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D, texture, 0);
参数解释
	target:我们所创建的帧缓冲类型的目标(绘制、读取或两者都有GL_READ_FRAMEBUFFER或GL_DRAW_FRAMEBUFFER)。
	attachment:我们所附加的附件的类型。现在我们附加的是一个颜色附件。需要注意,最后的那个0是暗示我们可以附加1个以上颜色的附件。我们会在后面的教程中谈到。
注意还可以附加GL_DEPTH_ATTACHMENT深度缓冲,但是注意需要将纹理格式和内部格式类型(internalformat)就成了 GL_DEPTH_COMPONENT去反应深度缓冲的存储格式。
模板格式同样:附加一个模板缓冲,你要使用 GL_STENCIL_ATTACHMENT作为第二个参数,把纹理格式指定为 GL_STENCIL_INDEX。
	textarget:你希望附加的纹理类型。
	texture:附加的实际纹理。
	level:Mipmap level。我们设置为0。

渲染缓冲区对象附件

渲染缓冲对象的一大优点是,它以OpenGL原生渲染格式储存它的数据,因此在离屏渲染到帧缓冲的时候,这些数据就相当于被优化过的了。渲染缓冲对象通常是只写的,不能修改它们(就像获取纹理,不能写入纹理一样)。

因为它们的数据已经是原生格式了,所以在写入或把它们的数据简单地到其他缓冲的时候非常快。

当使用渲染缓冲对象时,像切换缓冲这种操作变得异常高速。我们在每个渲染迭代末尾使用的那个glfwSwapBuffers函数,同样以渲染缓冲对象实现:我们简单地写入到一个渲染缓冲图像,最后交换到另一个里

渲染缓冲对象

GLuint rbo;
glGenRenderbuffers(1, &rbo);

渲染缓冲对象绑定,这样所有后续渲染缓冲操作都会影响到当前的渲染缓冲对象

glBindRenderbuffer(GL_RENDERBUFFER, rbo);

调用glRenderbufferStorage函数可以创建一个深度和模板渲染缓冲对象:

glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 800, 600);
创建一个渲染缓冲对象与创建纹理对象相似,不同之处在于这个对象是专门被设计用于图像的,而不是通用目的的数据缓冲,比如纹理。这里我们选择GL_DEPTH24_STENCIL8作为内部格式,它同时代表24位的深度和8位的模板缓冲。

帧缓冲对象附加

glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);

对比渲染缓冲区对象和纹理附加:貌似RBO比纹理有点多,但也不是万能的,纹理自有纹理的优点,纹理能够在shader中进行操作或者需要读取像素时,做一些处理,此时RBO就无能为力了。

5)介绍三个顶点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WZWv2r4s-1686050452791)(C:\Users\CreatWall_zhouwen\Desktop\pic\pic\zuobiao.png)]

真实验证这个好像有些问题,如果按这个顶点编写,最后离屏渲染出来的图形是反的。

黑色表现显示的内容矩形

红色坐标表示顶点坐标[一般单位为0.5]

黄色坐标表示正常纹理坐标,原点在左上角【一般单位为1】

蓝色坐标表示FBO的纹理坐标,原点为左下角【一般单位为1】

紫色的描述表示索引,012,132.三角形凑矩形

第二部分编写代码

主要是C++这边的代码;

因为工程完成了使用离屏渲染操作后还将结果显示了,所以这边是需要创建两个opengles的工程的CreateGLProgram。然后主要是围绕这两个工程来实现的。

m_ProgramObj普通工程:与之前纹理工程一致,需要绑定VAO,VBO以及创建绑定纹理,注意最后绘画的时候绑定离屏渲染的那个纹理进行显示。

m_FboProgramOb离屏渲染的工程:跟普通工程差不多,只是创建绑定纹理那里暂时不需要绑定数据。还有需要创建FBO帧缓冲这块。与普通工程有不同的片段着色器代码,这块离屏渲染可以做一些特殊操作,当前工程就是将图片灰度画。

1)GLUtil.h文件将创建着色器和创建工程接口抽象化

//
// Created by CreatWall_zhouwen on 2023/4/21.
//

#ifndef FORTHDRAWFBO_GLUTIL_H
#define FORTHDRAWFBO_GLUTIL_H


#include "Util.h"
#include <GLES3/gl3.h>
#define TAG "GLUTIL_H"

void CheckGLerror(GLuint shader, GLenum pname, GLint *params)
{
    glGetShaderiv(shader, pname, params);
}

GLuint LoadShader(GLenum shaderType, const char *pSource)
{
    LOGD("LoadShader Entern");
    //创建对应类型的着色器
    GLuint ShaderHandle = glCreateShader(shaderType);//创建shaderType类型的着色器
    if (ShaderHandle)
    {
        //附加着色器源码
        glShaderSource(ShaderHandle, 1, &pSource, NULL);//shader源码为pSource
        //编译着色器
        glCompileShader(ShaderHandle);
        //获取编译结果是否成
        GLint compiled = 0;
        glGetShaderiv(ShaderHandle, GL_COMPILE_STATUS, &compiled);
        if (!compiled){
            glDeleteShader(ShaderHandle);
            LOGD("glCompileShader ShaderHandle error");
            return 0;
        }
    }
    LOGD("LoadShader Leave");
    return ShaderHandle;
}

GLuint CreateGLProgram(const char *pVertexShaderSource, const char *pFragShaderSource, GLuint &vertexShaderHandle, GLuint &fragShaderHandle)
{
    LOGD("CreateProgram Entern");

    GLuint program = 0;
    GLint AttachStatus = GL_FALSE;
    //创建顶点着色器
    vertexShaderHandle = LoadShader(GL_VERTEX_SHADER, pVertexShaderSource);
    if (!vertexShaderHandle) return program;
    LOGD("vertexShaderHandle success");

    //创建片段着色器
    fragShaderHandle = LoadShader(GL_FRAGMENT_SHADER, pFragShaderSource);
    if (!fragShaderHandle) return program;
    LOGD("fragShaderHandle success");

    //创建程序
    program = glCreateProgram();
    if (program)
    {
        AttachStatus = 0;
        glAttachShader(program, vertexShaderHandle);
        glGetShaderiv(vertexShaderHandle, GL_ATTACHED_SHADERS, &AttachStatus);
        if(AttachStatus != 0 ){
            LOGD("glAttachShader vertexShaderHandle error %d",AttachStatus);
            return 0;
        }
        AttachStatus = 0;
        glAttachShader(program, fragShaderHandle);
        glGetShaderiv(fragShaderHandle, GL_ATTACHED_SHADERS, &AttachStatus);
        if(AttachStatus != 0 ){
            LOGD("glAttachShader fragShaderHandle error %d",AttachStatus);
            return 0;
        }

        glLinkProgram(program);
        GLint linkStatus = GL_FALSE;
        glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
        if (linkStatus != GL_TRUE){
            LOGD("glLinkProgram error AttachStatus = %d", linkStatus);
            glDeleteProgram(program);
            program = 0;
            return 0;
        }
        glDetachShader(program, vertexShaderHandle);
        glDeleteShader(vertexShaderHandle);
        vertexShaderHandle = 0;
        glDetachShader(program, fragShaderHandle);
        glDeleteShader(fragShaderHandle);
        fragShaderHandle = 0;
        LOGD("glCreateProgram success");
    }
    LOGD("CreateProgram Leave");
    return program;
}


#endif //FORTHDRAWFBO_GLUTIL_H

2)离屏渲染操作类

FBOSample.h

//
// Created by CreatWall_zhouwen on 2023/4/21.
//

#ifndef FORTHDRAWFBO_FBOSAMPLE_H
#define FORTHDRAWFBO_FBOSAMPLE_H
#include <GLES3/gl3.h>

class FBOSample {
public:
    FBOSample();
    ~FBOSample();
    void CreateProgram(const char *ver, const char *frag, const char * fragfbo);//创建工程并初始化
    void getTexturedata(unsigned char *data, int width, int height);//从NDK那边获取图片数据传过来
    void OnSurfaceChanged(int width, int height);//获取屏幕大小
    static FBOSample* GetInstance();
    static void DestroyInstance();
    void Draw();//绘画 就是执行离屏渲染再将离屏渲染的结果显示
private:
    GLuint m_FboVertexShader;//FBO的顶点着色器和片段着色器
    GLuint m_FboFragmentShader;
    GLuint m_VertexShader;//普通渲染的顶点着色器和片段着色器
    GLuint m_FragmentShader;
    GLuint m_ProgramObj;//普通工程ID
    GLuint m_FboProgramObj;//FBO工程ID
    GLuint m_ImageTextureId;//图片数据的纹理ID
    GLuint m_FboTextureId;//FBO绑定的空数据纹理ID
    GLint m_SamplerLoc;//普通片段着色器中的采样器值的位置
    GLint m_FboSamplerLoc;//FBO片段着色器中的采样器值的位置
    GLuint m_FboId;//FBO的ID
    unsigned char *texturedata;
    int texturewidth, textureheight;
    GLuint m_VaoIds[2];//存放顶点数据 0表示普通渲染的顶点缓冲区 1表示离屏渲染的顶点缓冲区
    GLuint m_VboIds[4];//0表示顶点坐标缓冲区,1表示普通纹理坐标缓冲区,2表示离屏渲染纹理坐标缓冲区,3表示纹理索引坐标缓冲区

    int srceenWidth, srceenHeight;//屏幕宽高
};


#endif //FORTHDRAWFBO_FBOSAMPLE_H

FBOSample.cpp

//
// Created by CreatWall_zhouwen on 2023/4/21.
//

#include "FBOSample.h"
#include "Util.h"
#include "GLUtil.h"
FBOSample* m_pContext = nullptr;
#define TAG "DRAWTEXTURE"
//顶点坐标
GLfloat vVertices[] = {
        -1.0f, -1.0f, 0.0f,
        1.0f, -1.0f, 0.0f,
        -1.0f,  1.0f, 0.0f,
        1.0f,  1.0f, 0.0f,
};

//正常纹理坐标
GLfloat vTexCoors[] = {
        0.0f, 0.0f,
        1.0f, 0.0f,
        0.0f, 1.0f,
        1.0f, 1.0f,
};

//fbo 纹理坐标与正常纹理方向不同  实践并不相反
GLfloat vFboTexCoors[] = {
        0.0f, 0.0f,
        1.0f, 0.0f,
        0.0f, 1.0f,
        1.0f, 1.0f,
};

GLushort indices[] = { 0, 1, 2, 1, 3, 2 };//三角形的索引数组

FBOSample::FBOSample() {
    m_VaoIds[0] = GL_NONE;
    m_VboIds[0] = GL_NONE;

    m_ImageTextureId = GL_NONE;
    m_FboTextureId = GL_NONE;
    m_SamplerLoc = GL_NONE;
    m_FboId = GL_NONE;
    m_FboProgramObj = GL_NONE;
    m_FboVertexShader = GL_NONE;
    m_FboFragmentShader = GL_NONE;
    m_FboSamplerLoc = GL_NONE;
}

FBOSample::~FBOSample() {
    if (m_ProgramObj)
    {
        glDeleteProgram(m_ProgramObj);
    }

    if (m_FboProgramObj)
    {
        glDeleteProgram(m_FboProgramObj);
    }

    if (m_ImageTextureId)
    {
        glDeleteTextures(1, &m_ImageTextureId);
    }

    if (m_FboTextureId)
    {
        glDeleteTextures(1, &m_FboTextureId);
    }

    if (m_VboIds[0])
    {
        glDeleteBuffers(4, m_VboIds);
    }

    if (m_VaoIds[0])
    {
        glDeleteVertexArrays(2, m_VaoIds);
    }

    if (m_FboId)
    {
        glDeleteFramebuffers(1, &m_FboId);
    }
}

void FBOSample::CreateProgram(const char *ver, const char *frag, const char * fragfbo) {
    LOGD("CreateProgram Enter");
    // 编译链接用于普通渲染的着色器程序
    m_ProgramObj = CreateGLProgram(ver, frag, m_VertexShader, m_FragmentShader);
    // 编译链接用于离屏渲染的着色器程序
    m_FboProgramObj = CreateGLProgram(ver, fragfbo, m_FboVertexShader, m_FboFragmentShader);
    if (m_ProgramObj == GL_NONE || m_FboProgramObj == GL_NONE)
    {
        LOGD("FBOSample::Init m_ProgramObj == GL_NONE");
        return;
    }
    LOGD("CreateGLProgram Success");
    //获取片段着色器中s_TextureMap的属性位置,编译后期指定是哪个纹理
    m_SamplerLoc = glGetUniformLocation(m_ProgramObj, "s_TextureMap");
    m_FboSamplerLoc = glGetUniformLocation(m_FboProgramObj, "s_TextureMap");
    LOGD("glGetUniformLocation Success");
    //生成VBO 加载顶点数据和索引数据
    glGenBuffers(4, m_VboIds);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices), vVertices, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vTexCoors), vTexCoors, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[2]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vFboTexCoors), vFboTexCoors, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[3]);//最后一个为纹理的索引缓冲数据
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    LOGD("glGenBuffers Success");
    // 生成 2 个 VAO顶点数据对象,一个用于普通渲染,另一个用于离屏渲染
    glGenVertexArrays(2, m_VaoIds);
    //初始化用于普通渲染
    glBindVertexArray(m_VaoIds[0]);//绑定0顶点缓冲区 那么接下来的操作就是绑定0顶点缓冲区的初始化
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);//绑定属性缓冲区0并设置顶点数据属性
    glEnableVertexAttribArray(0);//0对应的顶点着色器的location
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (const void *)0);
    glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[1]);//绑定属性缓冲区1并设置纹理坐标数据属性
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (const void *)0);
    glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[3]);//绑定属性缓冲区3并设置纹理索引属性
    glBindVertexArray(GL_NONE);
    LOGD("m_VaoIds[0] Success");
    //初始化离屏渲染的VAO
    glBindVertexArray(m_VaoIds[1]);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (const void *)0);
    glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[2]);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (const void *)0);
    glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[3]);
    glBindVertexArray(GL_NONE);
    LOGD("m_VaoIds[1] Success");
    //创建两个纹理,创建图片文件就是作为渲染操作的源数据,另外一个FBO的纹理就是FBO操作离屏渲染,就是将GPU处理结果放到FBO纹理存储
    //创建并初始化图形纹理  
    glGenTextures(1, &m_ImageTextureId);
    glBindTexture(GL_TEXTURE_2D, m_ImageTextureId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);//重复纹理的填充方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);//缩小时线性插值
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//放到就是线性
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texturewidth, textureheight, 0, GL_RGB, GL_UNSIGNED_BYTE, texturedata);
    LOGD("CreateProgram %s", texturedata);
    glGenerateMipmap(GL_TEXTURE_2D);//为当前绑定的纹理自动生成所有需要的多级渐远纹理
    glBindTexture(GL_TEXTURE_2D, GL_NONE);
    LOGD("m_ImageTextureId Success");
    //创建离屏的纹理,不绑定数据值申请内存
    glGenTextures(1, &m_FboTextureId);
    glBindTexture(GL_TEXTURE_2D, m_FboTextureId);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texturewidth, textureheight, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
    glBindTexture(GL_TEXTURE_2D, GL_NONE);
    LOGD("m_FboTextureId Success");
    //创建并初始化FBO,帧缓冲
    glGenFramebuffers(1, &m_FboId);
    glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);//绑定帧缓冲
    glBindTexture(GL_TEXTURE_2D, m_FboTextureId);//激活这个m_FboTextureId纹理绑定GL_TEXTURE_2D
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_FboTextureId, 0);//纹理附加到帧缓冲
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER)!= GL_FRAMEBUFFER_COMPLETE) {
        LOGD("FBOSample::CreateFrameBufferObj glCheckFramebufferStatus status != GL_FRAMEBUFFER_COMPLETE");
        return ;
    }
    glBindTexture(GL_TEXTURE_2D, GL_NONE);
    glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);
    LOGD("m_FboId Success");
}

void FBOSample::Draw() {
    LOGD("Draw Enter");
    //先执行离屏渲染操作将数据存放到纹理中
    glViewport(0, 0, texturewidth, textureheight);
    glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);//绑定缓冲帧,不是默认的而是绑定到自定义的
    glUseProgram(m_FboProgramObj);//使用离屏渲染的工程
    glBindVertexArray(m_VaoIds[1]);//绑定顶点数据
    glActiveTexture(GL_TEXTURE0);//激活纹理
    glBindTexture(GL_TEXTURE_2D, m_ImageTextureId);//绑定纹理数据
    glUniform1i(m_FboSamplerLoc, 0);//赋值纹理采样器
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);//绘画
    glBindVertexArray(0);
    glBindTexture(GL_TEXTURE_2D, 0);
    LOGD("m_FboProgramObj Success");
    //渲染离屏渲染出来的纹理结果
    glViewport(0, 0, srceenWidth, srceenHeight);//这行就是把这个纹理数据扩展到全屏,没有话那么就只有图片大小
    glBindFramebuffer(GL_FRAMEBUFFER, 0);//0帧缓冲表示默认帧缓冲渲染到屏幕的
    glUseProgram(m_ProgramObj);
    glBindVertexArray(m_VaoIds[0]);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, m_FboTextureId);
    glUniform1i(m_SamplerLoc, 0);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
    glBindTexture(GL_TEXTURE_2D, GL_NONE);
    glBindVertexArray(GL_NONE);
    LOGD("m_ProgramObj Success");
}

FBOSample *FBOSample::GetInstance() {
    if (m_pContext == nullptr)
    {
        m_pContext = new FBOSample();
    }
    return m_pContext;
}

void FBOSample::DestroyInstance() {
    if (m_pContext)
    {
        delete m_pContext;
        m_pContext = nullptr;
    }
}

void FBOSample::getTexturedata(unsigned char *data, int width, int height) {
    texturedata = data;
    texturewidth = width;
    textureheight = height;
    LOGD("getTexturedata Success %s", texturedata);
}

void FBOSample::OnSurfaceChanged(int width, int height) {
    glViewport(0, 0, width, height);
    srceenWidth = width;
    srceenHeight = height;
}

#

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/617196.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【2023RT-Thread全球技术峰会】一套全新的物联网多应用框架xiotman,助你解决多应用的难题

写在前面 就在上周&#xff0c;我作为讲师参与了2023RT-Thread全球技术峰会的主题演讲&#xff0c;我给大家带来了一套全新的解决物联网终端应用多样化的软件架构解决方案&#xff0c;在这里我再次以图文的形式介绍一下给社区的小伙伴&#xff0c;希望借此机会找到更多的同频小…

Nginx优化、Nginx+Tomcat实现负载均衡、动静分离集群部署

Nginx优化、NginxTomcat实现负载均衡、动静分离集群部署 一、Tomcat 优化二、Tomcat多实例部署1、安装好jdk2、安装tomcat3、配置tomcat环境变量4、修改tomcat2中的server.xml文件&#xff0c;要求各tomcat实例配置不能有重复的端口号5、修改各tomcat实例中的startup.sh和shutd…

ARM--$2$驱动模块

目录 1.驱动模块&#xff08;驱动程序的框架&#xff09; 2.内核中的打印函数&#xff08;编写第一个驱动程序&#xff09; Source Insight 使用&#xff1a; 打印函数编写 分析 3.驱动的多文件编译 4.模块传递参数 安装好驱动之后如何传参&#xff1f; 多驱动之间调用…

js内存管理与闭包

JavaScript内存管理 ◼ 不管什么样的编程语言&#xff0c;在代码的执行过程中都是需要给它分配内存的&#xff0c;不同的是某些编程语言需要我们自己手动的管理内存&#xff0c; 某些编程语言会可以自动帮助我们管理内存&#xff1a; ◼ 不管以什么样的方式来管理内存&#xf…

扩展系统功能——装饰模式(二)

装饰模式概述 装饰模式可以在不改变一个对象本身功能的基础上给对象增加额外的新行为&#xff0c;在现实生活中&#xff0c;这种情况也到处存在&#xff0c;例如一张照片&#xff0c;我们可以不改变照片本身&#xff0c;给它增加一个相框&#xff0c;使得它具有防潮的功能&…

安装Unity Hub和Unity Editor

1、首先下载UnityHub的安装包&#xff1a; https://unity.com/cn/download 开始您的创意项目并下载 Unity Hub | Unity 2、运行安装包 3、运行Unity Hub 第一次运行Unity Hub会默认弹出登录Unity提示&#xff0c;正常登录就可以了&#xff0c;登录后会在浏览器提示打开 Unity…

nginx+lua(openresty) 安装及使用(一)

前言 OpenResty —— 通过 Lua 扩展 NGINX 实现的可伸缩的 Web 平台。 OpenResty&#xff08;也称为 ngx_openresty&#xff09;是一个基于 Nginx 与 Lua 的高性能 Web 平台&#xff0c;其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超…

全志V3S嵌入式驱动开发(lcd屏幕驱动)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 对于一些设备&#xff0c;人们是希望从屏幕上面获取结果信息的&#xff0c;这样也显得更直观一些。另外&#xff0c;也有一些设备&#xff0c;它本…

阿里云OSS实现图片上传(后端接口)

文章目录 1、阿里云oss服务开通2、java操作阿里云oss&#xff0c;上传文件到阿里云oss1. 创建操作阿里云oss许可证2. 安装SDK3.修改配置文件4.创建常量类&#xff0c;读取配置文件中的内容5、OssController6、OssService7、OssServiceImpl8、改进1. 多次上传相同名称文件&#…

Segment Anything学习小结

论文地址&#xff1a;Segment Anything 项目地址&#xff1a;https://github.com/facebookresearch/segment-anything 在线Demo&#xff1a; https://segment-anything.com/demo 前言 近日&#xff0c;MetaAI团队开源了一个用于分割的10亿个masks&#xff0c;1100w张图片数…

测试新手如何晋升为月入过万的软件测试工程师?“我“的测试之路不简单...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 测试工程师这个岗…

记一次binlog恢复Mysql某张表数据的过程

1、备份数据库&#xff0c;非常重要 2、要用户不能操作&#xff08;如果不能停止&#xff0c;可以新建一个库&#xff0c;所有的binlog执行操作在新库执行&#xff09;。 3、登录服务器&#xff1b; 4、获取Mysql数据路径 cat /etc/my.cnf.d/mysql-server.cnf5、进入当前目录…

【SpinalHDL快速入门】4.2、基本类型之Bits

文章目录 1.1、描述1.2、声明1.3、运算符1.3.1、逻辑运算&#xff08;Logic&#xff09;1.3.2、比较&#xff08;Comparison&#xff09;1.3.3、类型转换&#xff08;Type cast&#xff09;1.3.4、部分赋值/提取操作符&#xff08;Bit extraction&#xff09;1.3.5、杂项&#…

Redis的使用规范小建议

Redis 核心技术与实战 笔记 作者&#xff1a; 蒋德钧 毕竟&#xff0c;高性能和节省内存&#xff0c;是我们的两个目标&#xff0c;只有规范地使用Redis&#xff0c;才能真正实现这两个目标。如果说之前的内容教会了你怎么用&#xff0c;那么今天的内容&#xff0c;就是帮助你用…

ChatGPT的学习过程【分析ChatGPT原理】+如何高效使用GPT

ChatGPT的学习过程【分析ChatGPT原理】如何高效使用GPT 此处借鉴&#xff1a;台湾大学李宏毅老师的讲解 资料&#xff1a;pan.baidu.com/s/1Jk1phne3ArfOERYNTPL12Q?pwd1111 GPTGenerative Pre-trained Transformer生成式预训练转换模型 ChatGPT共有四个学习阶段 学习文字接龙…

Java性能权威指南-总结7

Java性能权威指南-总结7 垃圾收集算法理解Throughput收集器堆大小的自适应调整和静态调整理解CMS收集器 垃圾收集算法 理解Throughput收集器 Throughput收集器有两个基本的操作&#xff1b;其一是回收新生代的垃圾&#xff0c;其二是回收老年代的垃圾。 下图展示了堆在新生代…

Python配置MySQL数据库使用

创建配置文件 config.ini [MySQL] host 172.xxx.xxx.xxx port 3306 user root password ****** db bgp_routing charset utf8创建读取配置文件 readConfig.py import configparser from pathlib import Pathclass ReadConfig():def __init__(self):configDir Path.cwd…

【学习日记2023.6.6】之 Linux环境下部署Java项目

文章目录 5. 项目部署5.1 手动部署项目5.2 基于Shell脚本自动部署5.2.1 介绍5.2.2 推送代码到远程5.2.3 Git操作5.2.4 Maven安装5.2.5 Shell脚本准备5.2.6 Linux权限5.2.7 授权并执行脚本5.2.8 设置静态IP 5. 项目部署 开发的项目绝大部分情况下都需要部署在Linux系统中。下面通…

springboot+vue多维的知识分类管理系统

随着国内市场经济这几十年来的蓬勃发展&#xff0c;突然遇到了从国外传入国内的互联网技术&#xff0c;互联网产业从开始的群众不信任&#xff0c;到现在的离不开&#xff0c;中间经历了很多挫折。本次开发的多维分类的知识管理系统有管理员和用户两个角色。管理员可以管理用户…

Dozzle-解决通过命令方式查看Docker 日志的神器

对于程序员们来说&#xff0c;Docker 一定是不陌生了。Docker 为我们的工作带来的巨大的便利&#xff0c;你可以使用它快速部署和扩展应用程序&#xff0c;并保证隔离性和可移植性&#xff0c;使应用程序在容器内独立运行&#xff0c;而且可以轻松地在不同的主机和操作系统上移…