首页 > 其他分享 >FBO与PBuffer区别

FBO与PBuffer区别

时间:2024-08-14 20:27:06浏览次数:16  
标签:区别 int TEXTURE 纹理 2D PBuffer GL FBO GLES20

示例

渲染管线的最后一个阶段是到帧缓冲区。大部分OpenGL所做的渲染操作都是在默认的帧缓冲中进行的,这个默认的帧缓冲是我们创建一个Surface时自动创建和配置好的,默认情况下,我们使用OpenGL ES使用的窗口系统提供的帧缓冲区,这样绘制的结果是显示到屏幕上,然而实际中有很多情况并不需要渲染到屏幕上,那么使用窗口系统提供的帧缓冲区就不太好了,这个时候使用FBO(Frame Buffer Object)就可以很方便的实现这类需求。

我们知道显示到屏幕上的每一帧数据其实对应的就是内存中的数据,在内存中对应分配着存储帧数据的缓冲区,包括写入颜色的颜色缓冲,写入深度值的深度缓冲,以及基于一些条件丢弃片元的模板缓冲,这几种缓冲一起称之为帧缓冲。

帧缓冲区对象(FBO)是一组颜色、深度、模板附着点,纹理对象可以连接到帧缓冲区对象的颜色附着点,同时也可以连接到FBO的深度附着点,另外一种可以连接到深度附着点和模板附着点的一类对象叫做渲染缓冲区对象(RBO)

创建帧缓冲对象

和创建纹理类似,可以使用glGenFramebuffers函数创建帧缓冲。

public static native void glGenFramebuffers(int n, int[] framebuffers, int offset);

返回的framebuffers中包含了生成的帧缓冲对象,该帧缓冲对象是不为0的整数,0用来表示窗口系统生成的帧缓冲区,创建完帧缓冲对象后,接下来要将其绑定到当前帧缓冲。

使用glBindFramebuffer函数用于设置当前帧缓冲区。

public static native void glBindFramebuffer(
        int target, // 一般设置为GL_FRAME_BUFFER
        int framebuffer // 帧缓冲区对象
    );

绑定到GL_FRAMEBUFFER目标后,接下来所有的读、写帧缓冲的操作都会影响到当前绑定的帧缓冲。也可以把帧缓冲分开绑定到读或写目标上,分别使用GL_READ_FRAMEBUFFER或GL_DRAW_FRAMEBUFFER来做这件事。如果绑定到了GL_READ_FRAMEBUFFER,就能执行所有读取操作,像glReadPixels这样的函数使用了;绑定到GL_DRAW_FRAMEBUFFER上,就允许进行渲染、清空和其他的写入操作。大多数时候不必分开用,通常把两个都绑定到GL_FRAMEBUFFER上就行。

此时创建的帧缓冲对象其实只是一个“空壳”,它上面还包含一些附着,因此接下来还必须往它里面添加至少一个附着才可以使用。创建的帧缓冲必须至少添加一个附着点(颜色、深度、模板缓冲)并且至少有一个颜色附着点。
一个缓冲区就是一个内存空间,可以添加的附着点可以是纹理对象或者渲染缓冲对象

纹理附着

当把一个纹理扶着到FBO上后,所有的渲染操作就会写入到该纹理上,意味着所有的渲染操作会被存储到纹理图像上,这样做的好处是显而易见的,我们可以在着色器中使用这个纹理。

int [] textures = new int[1];
GLES20.glGenTextures(1, textures, 0);
int textureId = textures[0];
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_MIRRORED_REPEAT);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_MIRRORED_REPEAT);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, ShapeView.sScreenWidth, ShapeView.sScreenHeight, 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_SHORT_5_6_5, null);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, textureId, 0);

创建纹理的方式和前面学的一样,区别是使用了glTexImage2D函数,前面使用GLUtils#texImage2D函数加载一幅2D图像作为纹理对象,这里的glTexImage2D稍显复杂,这里重要的是最后一个参数,如果为null就会自动分配可以容纳相应宽高的纹理,然后后续的渲染操作就会存储到这个纹理上了。

就下来需要将这个纹理附着到帧缓冲中去

public static native void glFramebufferTexture2D(
        int target, // 创建的帧缓冲类型的目标,一般为GL_FRAMEBUFFER
        int attachment, // 附着点,这里附着的事一个纹理,需要传入参数为一个颜色附着点
        int textarget, // 希望附着的纹理类型
        int texture, // 附加的纹理对象ID
        int level // Mipmap level 一般设置为0
    );

attachment可以为一下几个枚举值:

  • GL_COLOR_ATTACHMENT0 : 颜色缓冲
  • GL_DEPTH_ATTACHMENT : 深度缓冲
  • GL_STENCIL_ATTACHMENT : 模板缓冲。

除了颜色附着,还可以附加深度和模板附着到帧缓冲对象上。附着深度缓冲可以使用GL_DEPTH_ATTACHMENT作为附着类型,此时纹理的内部类型为GL_DEPTH_COMPONENT(32位深),附着模板缓冲使用GL_STENCIL_ATTACHMENT附着点,对应文理类型为GL_STENCIL_INDEX。

最后使用glFramebufferTexture2D函数将2D纹理附着到帧缓冲对象。

public static native void glFramebufferTexture2D(
        int target,  // GL_FRAMEBUFFER
        int attachment, // GL_COLOR_ATTACHMENT、GL_DEPTH_ATTACHMENT或GL_STENCIL_ATTACHMENT
        int textarget, // 纹理目标,和glTeXImage2D中的参数target一致
        int texture,  // 纹理对象
        int level
    );

渲染缓冲对象附着

上面介绍的纹理附着都可以作为帧缓冲对象的附件,除此之外,还有一种可以作为帧缓冲对象的附着的对象为渲染缓冲对象(RBO),类似于纹理,它也是内存中的一片区域,渲染缓冲对象的优点是,它以OpenGL原生渲染格式储存它的数据,因此在离屏渲染到帧缓冲的时候,这些数据就相当于被优化过的了。

int [] renderbuffers = new int[1];
GLES20.glGenRenderbuffers(1, renderbuffers, 0);
int renderId = renderbuffers[0];
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, renderId);
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, ShapeView.sScreenWidth, ShapeView.sScreenHeight);  
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, renderId);

使用渲染缓冲对象和使用纹理附着类似,使用glGenRenderbuffers函数生成渲染缓冲对象,然后使用glBindRenderbuffer绑定为当前渲染缓冲区,glRenderbufferStorage函数和glTexImage2D很类似,初始化渲染缓冲对象的数据存储。

同样最后使用函数glFramebufferRenderbuffer将渲染缓冲对象附着到帧缓冲对象

public static native void glFramebufferRenderbuffer(
        int target,
        int attachment,
        int renderbuffertarget, // 必须为GL_RENDERBUFFER
        int renderbuffer // 渲染缓冲区对象
    );

纹理、渲染缓冲和帧缓冲区对象在使用结束后需要使用glDeleteXXX函数删除。

在完成所有附着的添加后,需要使用函数glCheckFramebufferStatus 函数检查帧缓冲区是否完整。

if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
  // error
}

渲染到纹理(Render to Texture)

了解了如何创建帧缓冲对象后,下面就需要将渲染操作作用于我们创建的帧缓冲对象,思路很简单,在绘制每一帧图像时,先将图像绘制到FBO中,然后再用该纹理绘制到屏幕上,会看到结果和没有使用FBO效果一样。而实际上在使用FBO时只是绘制了一个矩形而已,好处是我们拿到了屏幕上要显示的物体的所有数据,想要对其进行其他额外的操作就方便多了。
渲染到纹理的大致思路如下:

/*================================render2texture================================*/
glGenFramebuffers(1, framebuffers, 0);
glGenTextures(1, textures, 0);
// setTextureParams();
// 
glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, ShapeView.sScreenWidth, ShapeView.sScreenHeight, 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_SHORT_5_6_5, null);
glGenRenderbuffers(1, renderbuffers, 0);
glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, ShapeView.sScreenWidth, ShapeView.sScreenHeight);  
// attach to framebuffer
glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, textureId, 0);
glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, renderId);
// check if correct
if(GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) != GLES20.GL_FRAMEBUFFER_COMPLETE) {
    Log.i(TAG, "Framebuffer error");
}
// render to texture using FBO
// clear color and depth

// load uniforms for vertex and fragment shader used to render to FBO
set_fbo_texture_shader_and_uniforms();
// drawing commands to the framebuffer object
draw_to_fbo();
/*================================render2window================================*/
// 切换到窗口系统的缓冲区
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
// Use texture to draw to window system provided framebuffer
// draw a quad that is the size of the viewport
//
// set_screen_texture_shader_and_uniforms();
//
draw_screen_quad();

// clearup
GLES20.glDeleteTextures(1, textures, 0);
GLES20.glDeleteFramebuffers(1, framebuffers, 0);
GLES20.glDeleteRenderbuffers(1, renderbuffers, 0);

加载如图一个obj文件的立方体,然后通过先渲染到纹理在通过该纹理来绘制到窗口系统,效果和直接绘制到窗口系统是一样的。但是他们是有本质区别的。可以看到效果
关键的代码为:

public void draw(float[] mvpMatrix, float[] mMatrix) {
    /*================================render2texture================================*/
    // 生成FrameBuffer
    int [] framebuffers = new int[1];
    GLES20.glGenFramebuffers(1, framebuffers, 0);
    int framebufferId = framebuffers[0];
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebufferId);
    // 生成Texture
    int [] textures = new int[1];
    GLES20.glGenTextures(1, textures, 0);
    int textureId = textures[0];
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_MIRRORED_REPEAT);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_MIRRORED_REPEAT);
    GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, ShapeView.sScreenWidth, ShapeView.sScreenHeight, 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_SHORT_5_6_5, null);
    // 生成Renderbuffer
    int [] renderbuffers = new int[1];
    GLES20.glGenRenderbuffers(1, renderbuffers, 0);
    int renderId = renderbuffers[0];
    GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, renderId);
    GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, ShapeView.sScreenWidth, ShapeView.sScreenHeight);  
    // 关联FrameBuffer和Texture、RenderBuffer
    GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, textureId, 0);
    GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, renderId);
    if(GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) != GLES20.GL_FRAMEBUFFER_COMPLETE) {
        Log.i(TAG, "Framebuffer error");
    }
    GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
    int frameBufferVertexShader = loaderShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
    int frameBufferFagmentShader = loaderShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
    mFrameBufferProgram = GLES20.glCreateProgram();
    GLES20.glAttachShader(mFrameBufferProgram, frameBufferVertexShader);
    GLES20.glAttachShader(mFrameBufferProgram, frameBufferFagmentShader);
    GLES20.glLinkProgram(mFrameBufferProgram);
    int fbPositionHandle = GLES20.glGetAttribLocation(mFrameBufferProgram, "aPosition");
    int fbNormalHandle = GLES20.glGetAttribLocation(mFrameBufferProgram, "aNormal");
    int fbTextureCoordHandle = GLES20.glGetAttribLocation(mFrameBufferProgram, "aTextureCoord");
    int fbuMVPMatrixHandle = GLES20.glGetUniformLocation(mFrameBufferProgram, "uMVPMatrix");
    int fbuMMatrixHandle = GLES20.glGetUniformLocation(mFrameBufferProgram, "uMMatrix");
    int fbuLightLocationHandle = GLES20.glGetUniformLocation(mFrameBufferProgram, "uLightLocation");
    int fbuTextureHandle = GLES20.glGetUniformLocation(mFrameBufferProgram, "uTexture");
    GLES20.glUseProgram(mFrameBufferProgram);
    mVertexBuffer.position(0);
    GLES20.glVertexAttribPointer(fbPositionHandle, 3, GLES20.GL_FLOAT, false, 3 * 4, mVertexBuffer);
    mTexureBuffer.position(0);
    GLES20.glVertexAttribPointer(fbTextureCoordHandle, 2, GLES20.GL_FLOAT, false, 2 * 4, mTexureBuffer);
    mTexureBuffer.position(0);
    GLES20.glVertexAttribPointer(fbNormalHandle, 3, GLES20.GL_FLOAT, false, 3 * 4, mNormalBuffer);
    GLES20.glEnableVertexAttribArray(fbPositionHandle);
    GLES20.glEnableVertexAttribArray(fbTextureCoordHandle);
    GLES20.glEnableVertexAttribArray(fbNormalHandle);
    GLES20.glUniform3f(fbuLightLocationHandle, 0, 10, 10);
    GLES20.glUniformMatrix4fv(fbuMVPMatrixHandle, 1, false, mvpMatrix, 0);
    GLES20.glUniformMatrix4fv(fbuMMatrixHandle, 1, false, mMatrix, 0);
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mLoadedTextureId);
    GLES20.glUniform1i(fbuTextureHandle, 0);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, mVertexCount);

    /*================================render2window================================*/
    // 切换到窗口系统的缓冲区
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
    GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
    int vertexShader = loaderShader(GLES20.GL_VERTEX_SHADER, windowVertexShaderCode);
    int fragmentShader = loaderShader(GLES20.GL_FRAGMENT_SHADER, windowFragmentShaderCode);
    mWindowProgram = GLES20.glCreateProgram();
    GLES20.glAttachShader(mWindowProgram, vertexShader);
    GLES20.glAttachShader(mWindowProgram, fragmentShader);
    GLES20.glLinkProgram(mWindowProgram);
    GLES20.glUseProgram(mWindowProgram);
    int positionHandle = GLES20.glGetAttribLocation(mWindowProgram, "aPosition");
    int textureCoordHandle = GLES20.glGetAttribLocation(mWindowProgram, "aTextureCoord");
    int textureHandle = GLES20.glGetUniformLocation(mWindowProgram, "uTexture");
    mSqureBuffer.position(0);
    GLES20.glVertexAttribPointer(positionHandle, 2, GLES20.GL_FLOAT, false, (2+2) * 4, mSqureBuffer);
    mSqureBuffer.position(2);
    GLES20.glVertexAttribPointer(textureCoordHandle, 2, GLES20.GL_FLOAT, false, (2+2) * 4, mSqureBuffer);
    GLES20.glEnableVertexAttribArray(positionHandle);
    GLES20.glEnableVertexAttribArray(textureCoordHandle);
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
    GLES20.glUniform1i(textureHandle, 0);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
    GLES20.glDeleteTextures(1, textures, 0);
    GLES20.glDeleteFramebuffers(1, framebuffers, 0);
    GLES20.glDeleteRenderbuffers(1, renderbuffers, 0);
}

面使用了RenderBuffer作为深度缓冲附着,可以看到在绘制的时候分为两部分,第一部分负责渲染到纹理,在创建好我们的FBO后,执行的绘制操作和前面学的没有任何区别,接下来切换到系统窗口上再进行绘制,第一次绘制到帧缓冲时,使用的纹理对象是一幅图片,通过渲染,附着到FBO上面的纹理附件就会被填充,这样在后面往窗口渲染时只需要这个纹理去渲染即可。
需要注意的是,前面说过,必须至少附着一个附件,可以只附着一个颜色附着,深度附着可以不使用,也就是我们创建的FBO只有颜色缓冲区,那么结果是,渲染到纹理的操作使得FBO里面只有颜色缓冲区有值,由于没有深度缓冲区,所有的深度测试都会通过,于是效果就是下面的样子,和没有就开启深度测试的效果是一样的。

渲染到深度纹理

另外,也可以使用纹理作为深度附着,很简单,只需要把上面例子中使用RenderBuffer的地方换成纹理即可,可以达到相同的效果。此外,还可以渲染到深度纹理,在绘制到屏幕时,使用深度纹理,效果如下面的样子,事实上,如果预先知道图像没有被用作纹理使用时,还是使用渲染到缓冲区比较好可以提高性能。
深度纹理中每个像素所记录的深度值是从0 到1 非线性分布的。精度通常是 24 位或16 位,这主要取决于所使用的深度缓冲区。当读取深度纹理时,我们可以得到一个0-1范围内的高精度值。下面的示例展示出了深度值的可视化。近的物体偏向于黑色,远的物体偏向于白色。并且近处的变化幅度大,远处的变化幅度,也就是近处随着z序的变化深度值变化大,远处变化小,这也是深度值的非线性特性。

public void draw(float[] mvpMatrix, float[] mMatrix) {
    /*================================render2texture================================*/
    // 生成FrameBuffer
    int [] framebuffers = new int[1];
    GLES20.glGenFramebuffers(1, framebuffers, 0);
    int framebufferId = framebuffers[0];
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebufferId);
    // 生成Texture
    int [] textures = new int[2];
    GLES20.glGenTextures(2, textures, 0);
    int colorTxtureId = textures[COLOR_TEXTURE];
    int depthTxtureId = textures[DEPTH_TEXTURE];
    // 颜色纹理 :纹理作为颜色附着
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, colorTxtureId);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_MIRRORED_REPEAT);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_MIRRORED_REPEAT);
    GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, ShapeView.sScreenWidth, ShapeView.sScreenHeight, 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_SHORT_5_6_5, null);
    // 深度纹理:纹理作为深度附着
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, depthTxtureId);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_MIRRORED_REPEAT);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_MIRRORED_REPEAT);
    GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_DEPTH_COMPONENT, ShapeView.sScreenWidth, ShapeView.sScreenHeight, 0, GLES20.GL_DEPTH_COMPONENT, GLES20.GL_UNSIGNED_SHORT, null);
    // 关联FrameBuffer和Texture
    GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, colorTxtureId, 0);
    GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_TEXTURE_2D, depthTxtureId, 0);
    if(GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) != GLES20.GL_FRAMEBUFFER_COMPLETE)    {
        Log.i(TAG, "Framebuffer error");
    }
    // set_fbo_texture_shader_and_uniforms();
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, mVertexCount);

    /*================================render2window================================*/
    // 切换到窗口系统的缓冲区
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
    GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
    // set_screen_texture_shader_and_uniforms();
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    // 使用颜色纹理和使用深度纹理
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, depthTxtureId/*colorTxtureId*/);
    GLES20.glUniform1i(textureHandle, 0);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

    GLES20.glDeleteTextures(2, textures, 0);
    GLES20.glDeleteFramebuffers(1, framebuffers, 0);
}

标签:区别,int,TEXTURE,纹理,2D,PBuffer,GL,FBO,GLES20
From: https://blog.csdn.net/sjw890821sjw/article/details/141160863

相关文章

  • 可观测性与传统监控的区别和联系
    什么是可观测性?可观测性(Observability)是一种软件开发和系统构建的哲学,是对系统内部状态及行为的度量和推断能力,通常包括日志、指标、链路追踪等多个度量维度。也就是说,在软件开发和运维领域中,可观测性是指对于一个复杂的系统,能够通过监控、日志、指标、追踪等手段,快速地发现、诊......
  • SQL中exists和in的用法以及区别
    SQL中exists和in的用法以及区别  目录一、in用法二、exists用法三、in与exists的区别in语句:只执行一次exists语句:执行n次(外表行数)区别和应用场景notin和notexists四、结论 一、in用法in 语法为:select*fromtable_namewherecol_namei......
  • jpg和png的区别
    jpg和png是两种常用的图像文件格式,它们在用途、特点以及压缩方法上有所不同:JPEG(JointPhotographicExpertsGroup):用途:适用于照片或含有较多连续色调的内容,如风景、人像等。因为JPEG主要依赖于有损压缩算法,对于复杂细节和颜色渐变处理较好。优点:占用空间较小,适合网络传输和......
  • HashMap和Hashtable的区别 day15
    /*Map:存储元素的特点是每一个元素是一个键值对{【name:"魏一民"】,【age:18】}Map集合的共同拥有的特点:1、Map集合中的元素,键是唯一的,不会在一个Map集合发现两个相同的键1001:魏一民1002:陈真1001:小虎2......
  • 事件和委托的区别
    在C#中,事件(Event)和委托(Delegate)是紧密相关但具有不同含义的概念。以下是它们的主要区别:委托(Delegate):委托是一种类型,它表示一个方法的签名(即方法的参数列表和返回类型)。委托可以持有对一个或多个方法的引用,并且可以像普通方法一样被调用。委托是多播的,意味着一个委托......
  • Session的工作原理、Session与Token的区别
    Session是一种在无状态的HTTP协议中用来实现用户状态管理的机制。它通过在服务器端保存用户的状态信息,并通过客户端在每次请求时传递一个唯一的标识符(通常称为SessionID),实现了在多个请求之间维持用户的会话状态。一、Session的工作原理:客户端发送请求:用户首次访问网站......
  • 【C++ Allocator】 详解C++的空间配置器和vector的底层实现以及push_back()和empalce_
    空间配置器用于管理动态内存分配和释放,STL容器类(如std::vector,std::list,std::map等)都使用配置器来管理内存。它有非常重要的特点:将容器的内存开辟和对象构造分离开将容器的对象析构和内存释放分离开这样能够高效的插入元素以及删除元素vectorSTL中典型的容器vec......
  • 浅述TSINGSEE青犀EasyCVR视频汇聚平台与海康安防平台的区别对比
    在我们的很多项目中都遇到过用户的咨询:TSINGSEE青犀EasyCVR视频汇聚平台与海康平台的区别在哪里?确实,在安防视频监控领域,EasyCVR视频汇聚平台与海康威视平台是两个备受关注的选择。它们各自具有独特的功能和优势,适用于不同的应用场景和需求。今天我们将从多个方面对这两个平台进......
  • 在K8S中,flannel和calico的区别?
    在Kubernetes(K8S)中,Flannel和Calico是两种常用的网络插件,它们各自具有不同的特点和应用场景。以下是Flannel和Calico在多个方面的详细区别:1.技术基础与数据平面实现Flannel:使用虚拟网络(如VXLAN或UDP)来实现数据平面。通过封装和解封装技术,将容器的网络流量封装在UDP或VXLAN包......
  • 接口与抽象类区别
    接口:接口主要用来约束某个实物实现,制定规范抽象类:抽象类可以理解用来复用代码。  区别。1.接口不考虑default方法,则只需要声明方法,不需要实现。而类可以写具体实现。2.抽象类可以有构造函数,接口不能有3.单继承,多实现,一个类只能一次继承指定类,而接口可以多实现。  使......