2024年最新七、帧缓冲离屏渲染(1),实践出真知

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

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

黑色表现显示的内容矩形

红色坐标表示顶点坐标[一般单位为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


![img](https://img-blog.csdnimg.cn/img_convert/eadf305294d019c0f4d67da36475c631.png)
![img](https://img-blog.csdnimg.cn/img_convert/307951b76730dbc74105fe9374fc0e79.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

 glBindTexture(GL_TEXTURE_2D, m_FboTextureId);//激活这个m_FboTextureId纹理绑定GL_TEXTURE_2D


[外链图片转存中...(img-UeLfUaGx-1715667868474)]
[外链图片转存中...(img-xj3E6dCn-1715667868474)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
离屏渲染是一种将图形渲染到与屏幕不直接关联的缓冲区的技术。在Cesium中,离屏渲染可以通过使用WebGL的缓冲对象(Framebuffer Object,FBO)来实现。 离屏渲染在Cesium中的应用场景包括: 1. 生成纹理:可以将场景渲染到一个纹理中,然后将该纹理用于其他的图形操作,例如后期处理、投影等。 2. 阴影计算:可以将场景渲染到一个深度纹理中,然后使用该深度纹理来计算阴影效果。 3. 屏幕空间反射(Screen Space Reflection,SSR):可以将场景渲染到一个颜色纹理和一个法线纹理中,然后使用这些纹理来计算屏幕空间反射效果。 具体实现离屏渲染的步骤如下: 1. 创建一个缓冲对象(Framebuffer Object,FBO)。 2. 创建一个纹理附件(Texture Attachment),用于存储渲染结果。 3. 将缓冲对象绑定到渲染管线中。 4. 渲染场景到缓冲对象中的纹理附件。 5. 解绑缓冲对象,将渲染结果用于其他的图形操作。 以下是一个使用Cesium进行离屏渲染的示例代码: ```javascript // 创建缓冲对象 var framebuffer = new Cesium.Framebuffer({ context: viewer.scene.context, colorTextures: [new Cesium.Texture({ context: viewer.scene.context })], depthTexture: new Cesium.Texture({ context: viewer.scene.context, format: Cesium.PixelFormat.DEPTH_COMPONENT }) }); // 将缓冲对象绑定到渲染管线中 viewer.scene.frameState.framebuffer = framebuffer; // 渲染场景到缓冲对象中的纹理附件 viewer.scene.render(); // 解绑缓冲对象 viewer.scene.frameState.framebuffer = undefined; // 获取渲染结果的纹理 var resultTexture = framebuffer.getColorTexture(0); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值