Android下Opengl ES实现单屏幕双眼显示

12 篇文章 0 订阅

默认情况下,Opengl ES使用系统提供的帧缓冲区作为绘图表面,一般情况下,如果只在屏幕的表面绘图的话,系统提供的默认帧缓冲区很高效,但是很多应用程序需要渲染到纹理。此时,就需要我们自己来创建帧缓冲区对象。比如在VR中就需要使用帧缓冲对象在同一块屏幕上来实现双眼显示模式。

 

首先,说下渲染到纹理的概念。

渲染到纹理(Render to Texture),将我们要绘制的场景保存到一张纹理当中,打个比喻,类似于利用手机的截图操作,手机的画面就好比是已经渲染好的内容,截图得到的图片相当于纹理,然后将它附加到缓冲区对象上。

 

Opengl中,帧缓冲区是渲染管线的最后一步,即可以理解为将在屏幕上显示的信息都保存在这里,它像一个容器,里面有颜色附着、纹理、模板、深度等信息,下图就是帧缓冲区、渲染缓冲区对象、纹理的关系。

                                                                                                


当我们自己不创建帧缓冲区对象时,使用的是系统默认提供的,此时,当前我们的渲染操作都是针对系统的帧缓冲区对象,就像向该容器中放信息,而在显示时,也是由该容器提供信息。

 

所以想要在一块屏幕上显示左右两块区域的图像时,就需要再增加2个“容器”,来暂时保存纹理等信息,显示时再将这两个容器的信息交给系统的容器,即将2个自己创建的帧缓冲区对象的纹理作为系统缓冲区对象的纹理,然后进行绘图显示

 

有个疑问是,既然都是容器,为什么还要用自己创建呢?

假设同一个屏幕上的两个区域显示两幅图像,左右场景不同,渲染管线出来只有一张纹理放到“容器”中,两个管线得到两张纹理,但是系统的帧缓冲区只对应一个渲染管线,不能直接使用两张纹理,所以就需要弄两个“容器”来缓存一下两张纹理,然后在屏幕显示每一帧的过程中,在左右两部分分别使用两张纹理进行绘制,再得到一个整合后的纹理,然后交给系统的帧缓冲区。这样,就能实现同一个屏幕上的两个区域显示两幅图像,利用两个“容器”(自己创建的帧缓冲区对象)类似于卸磨杀驴。



使用帧缓冲对象渲染到纹理的基本操作,其中,初始化缓冲区对象最为关键,主要有以下几个步骤

 

a)创建帧缓冲对象
b)创建与帧缓冲对象一起使用的纹理对象
c)创建渲染缓冲区对象
d)绑定帧缓冲区对象
e)绑定纹理对象
f)设置纹理的参数格式尺寸等设置
g)绑定渲染缓冲区对象并设置尺寸
h)连接纹理对象到缓冲区对象的颜色附着上
i)连接渲染缓冲区对象到深度附着

 

上面是基本的理论部分,下面直接上代码

 

int[] temp = new int[1];

//generate fbo id

GLES20.glGenFramebuffers(1, temp, 0);

fboId = temp[0];

//generate texture

GLES20.glGenTextures(1, temp, 0);

fboTex = temp[0];

//generate render buffer

GLES20.glGenRenderbuffers(1, temp, 0);

renderBufferId = temp[0];

//Bind Frame buffer

GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);

//Bind texture

GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, fboTex);

//Define texture parameters

GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, fboWidth, fboHeight, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,null);

GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);

GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);

GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);

//Bind render buffer and define buffer dimension

GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, renderBufferId);

GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, fboWidth, fboHeight);

//Attach texture FBO color attachment

GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, fboTex, 0);

//Attach render buffer to depth attachment

GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, renderBufferId);

//we are done, reset

GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);

GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, 0);

GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

 

以上就是初始化帧缓冲对象的过程,基本的操作相对比较固定

 

下面的函数是安卓中利用Opengl es绘制的每一帧,大致过程:在你想要得到的纹理前先绑定初始化好的帧缓冲对象设置窗口然后开始渲染你的纹理渲染完之后解除绑定切换到系统的帧缓冲区再进行绘制,最简单的就是你可以画一个矩形,然后将纹理贴到上面,最终显示到屏幕上

 

public void RenderToTexture (GL10 arg0){

GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);

GLES20.glViewport(0, 0, fboWidth, fboHeight);

        ******Rendering Code*******

       

GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

}

 

public void onDrawFrame(GL10 arg0)

{

    //call FBORenderer to render to texture

    fbor.RenderToTexture();//调用RenderToTexture 方法,将纹理保存在fbor的缓冲区中

    //reset the projection, because viewport is set by FBO renderer is different

    GLES20.glViewport(0, 0, vwidth, vheight);

    float ratio = (float)vwidth/(float)vheight;

    float a = 5f;

    Matrix.orthoM(m_fProj, 0, -a*ratio, a*ratio, -a*ratio, a*ratio, 1, 10);   

    //multiply view matrix with projection matrix

    Matrix.multiplyMM(m_fVPMat, 0, m_fProj, 0, m_fView, 0);

    //below procedure is same as any other rendering

    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT|GLES20.GL_DEPTH_BUFFER_BIT);

   

    GLES20.glUseProgram(iProgId);

   

    vertexBuffer.position(0);

    GLES20.glVertexAttribPointer(iPosition, 3, GLES20.GL_FLOAT, false, 0, vertexBuffer);

    GLES20.glEnableVertexAttribArray(iPosition);

   

    texBuffer.position(0);

    GLES20.glVertexAttribPointer(iTexCoords, 2, GLES20.GL_FLOAT, false, 0, texBuffer);

    GLES20.glEnableVertexAttribArray(iTexCoords);

   

    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);

    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, iTexId);

    GLES20.glUniform1i(iTexLoc, 0);

    //since I'm multi-texturing, bind fboId to texture1

    GLES20.glActiveTexture(GLES20.GL_TEXTURE1);

    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, fboId);

    GLES20.glUniform1i(iTexLoc1, 1);

    //for rotating cube

    Matrix.setIdentityM(m_fModel, 0);

    Matrix.rotateM(m_fModel, 0, -xAngle, 0, 1, 0);

    Matrix.rotateM(m_fModel, 0, -yAngle, 1, 0, 0);

    //multiply model matrix with view-projection matrix

    Matrix.multiplyMM(m_fMVPMat, 0, m_fVPMat, 0, m_fModel, 0);   

    //pass model-view-projection matrix to shader

    GLES20.glUniformMatrix4fv(iMVPMat, 1, false, m_fMVPMat, 0);   

    //draw cube

GLES20.glDrawElements(GLES20.GL_TRIANGLES, cube.m_nIndeces, GLES20.GL_UNSIGNED_SHORT, indexBuffer);

}

 

上面给出的仅仅是一个缓冲区对象的主要代码部分,对于实现双眼显示的话,要创建两个缓冲区对象,分别渲染纹理到这两个缓冲区中,

然后在onDraw()函数中可以绘制两个矩形,每个矩形分别贴上这两张纹理,递交给系统的帧缓冲区(解绑定时已经切换到系统缓冲区对象啦,

所以按照正常的绘制流程就行了) ,此时,屏幕上就显示了左右两张图像了。


  

 

参考:http://opengles2learning.blogspot.kr/2014/02/render-to-texture-rtt.html

参考:Opengl ES 3.0编程指南

参考:http://learnopengl-cn.readthedocs.io/zh/latest/04%20Advanced%20OpenGL/05%20Framebuffers/



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要在 Android 环境下使用 C++ 和 OpenGL ES 显示图片,你需要进行以下步骤: 1. 首先,你需要创建一个 Android 项目。你可以使用 Android Studio 或其他 IDE 来创建项目。 2. 在项目中,你需要创建一个新的 C++ 文件,用于编写 OpenGL ES 的代码。 3. 在 C++ 文件中,你需要包含以下头文件: ``` #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> ``` 4. 接着,你需要编写 OpenGL ES 的初始化代码。这包括创建一个 OpenGL ES 上下文、创建一个渲染缓冲区和一个帧缓冲区、以及编译和链接着色器程序等。 5. 然后,你需要加载图片,并将其绑定到一个纹理对象上。你可以使用 SOIL 或其他库来加载图片。 6. 最后,你需要编写渲染代码,在 OpenGL ES 上下文中绘制纹理。这包括激活纹理元、绑定纹理对象、绘制图形等。 这里是一个简的示例代码,可以用来在 Android 环境下显示一张图片: ``` #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> #include <SOIL/SOIL.h> GLuint texture_id; void init_opengl() { // 创建 OpenGL ES 上下文 // 创建渲染缓冲区和帧缓冲区 // 编译和链接着色器程序等 // ... // 加载图片并绑定到纹理对象 int width, height; unsigned char *image = SOIL_load_image("image.png", &width, &height, 0, SOIL_LOAD_RGB); glGenTextures(1, &texture_id); glBindTexture(GL_TEXTURE_2D, texture_id); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); SOIL_free_image_data(image); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } void render() { // 清除屏幕并设置视口 glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, screen_width, screen_height); // 激活纹理元并绑定纹理对象 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture_id); // 绘制图形 GLfloat vertices[] = { -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; GLuint indices[] = { 0, 1, 2, 3 }; glUseProgram(shader_program); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), vertices); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), vertices + 2); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, indices); // 交换帧缓冲区和显示缓冲区 eglSwapBuffers(display, surface); } ``` 这只是一个简的示例,实际上在使用 OpenGL ES 显示图片时,你需要更多的代码来实现一些高级功能,例如纹理过滤、纹理坐标变换、混合和剪裁等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值