How to skip frames in RenderScene

Hi there


I am trying to make a screen shot. But the setting is very different on this frame (different color compared with previous and next frames) when I make the screenshot. That is why I do not want to display this frame on the screen and there will be confusing content changing on the screen.

 

So, what is the best solution for this special application?

- Is there a switch I can use in the renderscene function so that the current frame will not be displayed?

- Or can I define an internal buffer which will not be used for displaying?

 

Thank you for your suggestion,

 

Renaissance

 

Can anyone give me some help on this issue?


 

Thanks a lot

Hi Renaissance,





I’m not entirely sure what your use case is here. Do you mean the frame that you want to take a screen-shot of has a different effect applied to it compared to your normal render, for example a post-processing effect?





It sounds like render to texture will solve this, as you will be performing an off-screen render (see our RenderToTexture training course for an example use of this). You can perform a render pass for your “screen-shot” effect, then do a glReadPixels() to retrieve the rendered image (capture the screen-shot), then perform your normal render pass that will be rendered to the display.





In our PVRShell, there is a function called PVRShellScreenSave(), which can be used to save a screen shot of the current render. If you are already using the shell, you can use this function as-is. Otherwise, you can read through the code to see how we’ve implemented this feature.





You should be aware that there is a cost associated with glReadPixels() as it will remove the parallelism between the GPU and CPU (stalls will be introduced). For this reason, you should try to use this sparingly and should be aware of the performance hit you will taken when it is used.





Thanks,


Joe

Thank you so much, Joe.

 

I just want to clarify what I need. Actually, I need a off-screen render, which will be captured as an extrenal image file. Here is a simple flow chart.

 

Frame 1 (rendered to screen)


Frame 2 (rendered to screen)

...
Frame i (The content of the frame is changed and it will not be rendered to screen, but will be saved to external image file)


Frame i+1 (The content of the frame is changed back to the status as frame i-1 and it will be rendered to screen)


Frame i+2 (rendered to screen)

..

 

I have already read the code in the OGLES2RenderToTexture.cpp. Based on my understanding, the renderscene function is stil called very time for a specific frame. In order to get the screen shot of frame i, I still have to call this function. How can I do the off-screen render and save it to an external image file?

 

Thank you very much,

 

Renaissance

 

 

Btw, which graphics API are you using? OGLES 1.x or 2.0?





Unless I’ve misunderstood your use case and you have the need to display frame i-1 repeatly (e.g. frames i, i+1, i+2 render exactly the same image as frame i-1), then I think you should do the following











OffScreenRender(render off screen, then capture with glReadPixels()) Frame i (render the normal scene within the RenderScene() block to render it to the screen)





Frame i+1(render to screen)                 





Frame i+2(render to screen)














So, basically you will still call RenderScene to do your normal render, but will also perform an off-screen render at the start of this for the render that you want to save. I’ll give some pseudo code to explain this (based on our OGLES2 render to texture training course - all psuedo code would be implemented in RenderScene() function):







/* Bind FBO for offscreen render /

glBindFramebuffer(GL_FRAMEBUFFER, m_uFBO);



/
Perform the off-screen render that you want to save /



/
Retrieve the rendered image - NOTE: this will stall the CPU as it has to wait for the GPU render to complete*/

glReadPixels();



// We are done with rendering to our FBO so switch back to the back buffer.

glBindFramebuffer(GL_FRAMEBUFFER, m_i32OriginalFbo);



/* Perform normal render that you want to be visible on the screen */



[/CODE]<br /> <br />/* Bind FBO for offscreen render */<br /> <br />glBindFramebuffer(GL_FRAMEBUFFER, m_uFBO);<br /> <br />...<br /> <br />/* Perform the off-screen render that you want to save */<br /> <br />...<br /> <br />/* Retrieve the rendered image - NOTE: this will stall the CPU as it has to wait for the GPU render to complete*/<br /> <br />glReadPixels();<br /> <br /><br /> <br />// We are done with rendering to our FBO so switch back to the back buffer.<br /> <br />glBindFramebuffer(GL_FRAMEBUFFER, m_i32OriginalFbo);<br /> <br />...<br /> <br />/* Perform normal render that you want to be visible on the screen */<br /> <br />...<br /> <br />

Once again, Joe, Thank you so much for your reply. Your suggestion is very helpful. Based on your feedback, I made a test in the OGLES2RenderToTexture example. In the following is the source code I used and you may just look at the part in red font. BTW, I am using OpenGL ES 2. The test I did is on PC windows.

 

In general, I made the test based on your pseudo code. At one special frame, I called a different function which does the rendering. Then I bind the frame to a different FBO and dump out the data as external file.  When the program runs to a different frame, only the original renderscene will be called.

 

I found that after I loaded the saved image data (off-screen shot), the content of the image is not what I expected. The 2D image is with a simple fixed value.

 

When I commented out the following line in my code,

glBindFramebuffer(GL_FRAMEBUFFER, m_uFBO[0]);

I did get the correct screenshot I expected. But of course, this image was rendered on the screen at the same time.

 

I wondering if you can give a hint that there must be something else I should add to this program.

 

Thank you very much,

 

Renaissance

 

/******************************************************************************

 @File         OGLES2RenderToTexture.cpp

 @Title        RenderToTexture

 @Version     

 @Copyright    Copyright (C)  Imagination Technologies Limited.

 @Platform     Independent

 @Description  Shows how to use a frame buffer object to render to a texture.

******************************************************************************/
#include <math.h>

#include "PVRShell.h"
#include "OGLES2Tools.h"

/******************************************************************************
 shader attributes
******************************************************************************/
// vertex attributes
enum EVertexAttrib {
 VERTEX_ARRAY, TEXCOORD_ARRAY, eNumAttribs };
const char* g_aszAttribNames[] = {
 "inVertex", "inTexCoord" };

// shader uniforms
enum EUniform {
 eMVPMatrix, eNumUniforms };
const char* g_aszUniformNames[] = {
 "MVPMatrix" };

/******************************************************************************
 Content file names
******************************************************************************/

// Source and binary shaders
const char c_szFragShaderSrcFile[] = "FragShader.fsh";
const char c_szFragShaderBinFile[] = "FragShader.fsc";
const char c_szVertShaderSrcFile[] = "VertShader.vsh";
const char c_szVertShaderBinFile[] = "VertShader.vsc";

/*****************************************************************************
 ** Class: OGLES2RenderToTexture
******************************************************************************/
class OGLES2RenderToTexture : public PVRShell
{
 // Print3D class used to display text
 CPVRTPrint3D m_Print3D;

 // OpenGL handles for shaders, textures, FBOs and VBOs
 GLuint m_uiVertShader;
 GLuint m_uiFragShader;
 GLuint m_auiTexture[2];
 GLuint  m_uiTrunkTex;
 GLuint  m_uiVbo;
 GLuint  m_uFBO[1];
    GLuint m_auiFbo[2];
 int  m_i32CurrentFbo;
 int  m_i32OriginalFbo;
 GLuint m_auiDepthBuffer[2];
 GLubyte *m_pScreenBuf;

 int     m_framenumber;
 // Group shader programs and their uniform locations together
 struct
 {
  GLuint uiId;
  GLuint auiLoc[eNumUniforms];
 }
 m_ShaderProgram;

 int m_i32TexSize;

 float m_fAngle;
 float m_fAngle2;

 unsigned int m_ui32Framenum;
 unsigned int m_ui32Time;

public:
 virtual bool InitApplication();
 virtual bool InitView();
 virtual bool ReleaseView();
 virtual bool QuitApplication();
 virtual bool RenderScene();

 void LoadTextures();
 bool LoadShaders(CPVRTString* pErrorStr);
 void LoadVbos();

 bool DrawScreen();
 bool DrawScreen_S();
 bool RenderFractal();
};

/*!****************************************************************************
 @Function  LoadTextures
 @Description Loads the textures required for this training course
******************************************************************************/
void OGLES2RenderToTexture::LoadTextures()
{
    /*
  Initialise the textures
 */

 // Allocates one texture handle for the trunk texture
 glGenTextures(1, &m_uiTrunkTex);

 // Binds this texture handle so we can load the data into it
 glBindTexture(GL_TEXTURE_2D, m_uiTrunkTex);

 // Creates the data as a 32bits integer array (8bits per component)
 GLuint* pTexData = new GLuint[32*32];

 // Create texture pattern
 for (int i=0; i<32; i++)
 {
  for (int j=0; j<32; j++)
  {
   pTexData[i*32+j] = 0xFF000000 + ((255 - (j-16)*(j-15)) << 8) + ((j * 8) & 0xFF);
  }
 }

 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, pTexData);

    /* Destroy the array as it is no longer needed*/
    delete [] pTexData;

 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

 // Allocate two textures
 glGenTextures(2, m_auiTexture);
 for (int i = 0; i < 2; ++i)
 {
  // Binds this texture handle so we can load the data into it
  glBindTexture(GL_TEXTURE_2D, m_auiTexture);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_i32TexSize, m_i32TexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);

  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 }
}

/*!****************************************************************************
 @Function  LoadShaders
 @Output  pErrorStr  A string describing the error on failure
 @Return  bool   true if no error occured
 @Description Loads and compiles the shaders and links the shader programs
    required for this training course
******************************************************************************/
bool OGLES2RenderToTexture::LoadShaders(CPVRTString* pErrorStr)
{
 /*
  Load and compile the shaders from files.
  Binary shaders are tried first, source shaders
  are used as fallback.
 */
 if (PVRTShaderLoadFromFile(
   c_szVertShaderBinFile, c_szVertShaderSrcFile, GL_VERTEX_SHADER, GL_SGX_BINARY_IMG, &m_uiVertShader, pErrorStr) != PVR_SUCCESS)
 {
  return false;
 }

 if (PVRTShaderLoadFromFile(
   c_szFragShaderBinFile, c_szFragShaderSrcFile, GL_FRAGMENT_SHADER, GL_SGX_BINARY_IMG, &m_uiFragShader, pErrorStr) != PVR_SUCCESS)
 {
  return false;
 }

 /*
  Set up and link the shader program
 */

 if (PVRTCreateProgram(&m_ShaderProgram.uiId, m_uiVertShader, m_uiFragShader, g_aszAttribNames, eNumAttribs, pErrorStr) != PVR_SUCCESS)
 {
  PVRShellSet(prefExitMessage, pErrorStr->c_str());
  return false;
 }

 // Store the location of uniforms for later use
 for (int i = 0; i < eNumUniforms; ++i)
 {
  m_ShaderProgram.auiLoc = glGetUniformLocation(m_ShaderProgram.uiId, g_aszUniformNames);
 }

 return true;
}

/*!****************************************************************************
 @Function  LoadVbos
 @Description Loads the vertex data required for this training course into a
    vertex buffer object
******************************************************************************/
void OGLES2RenderToTexture::LoadVbos()
{
 float afVertexData[] = {
  // trunk
  -0.1f, -1.0f, 0.5f,  0.0f, 1.0f,
   0.1f, -1.0f, 0.5f,  1.0f, 1.0f,
  -0.08f, -0.4f, 0.5f,  0.0f, 0.2f,
   0.08f, -0.4f, 0.5f,  1.0f, 0.2f,
   0.0f, -0.3f, 0.5f,  0.5f, 0.0f,

   // feedback quad
   -0.65f, 0.0f, 0.65f,  0.0f, 0.0f,
   -0.65f, 1.3f, 0.65f,  0.0f, 1.0f,
    0.65f, 0.0f, 0.65f,  1.0f, 0.0f,
    0.65f, 1.3f, 0.65f,  1.0f, 1.0f,
 };

 glGenBuffers(1, &m_uiVbo);
 glBindBuffer(GL_ARRAY_BUFFER, m_uiVbo);
 glBufferData(GL_ARRAY_BUFFER, sizeof(afVertexData), afVertexData, GL_STATIC_DRAW);
 glBindBuffer(GL_ARRAY_BUFFER, 0);
}

/*!****************************************************************************
 @Function  InitApplication
 @Return  bool  true if no error occured
 @Description Code in InitApplication() will be called by PVRShell once per
    run, before the rendering context is created.
    Used to initialize variables that are not dependant on it
    (e.g. external modules, loading meshes, etc.)
    If the rendering context is lost, InitApplication() will
    not be called again.
******************************************************************************/
bool OGLES2RenderToTexture::InitApplication()
{
 // Get and set the read path for content files
 CPVRTResourceFile::SetReadPath((char*)PVRShellGet(prefReadPath));
 
 return true;
}

/*!****************************************************************************
 @Function  QuitApplication
 @Return  bool  true if no error  occured
 @Description Code in QuitApplication() will be called by PVRShell once per
    run, just before exiting the program.
    If the rendering context is lost, QuitApplication() will
    not be called.
******************************************************************************/
bool OGLES2RenderToTexture::QuitApplication()
{
    return true;
}

/*!****************************************************************************
 @Function  InitView
 @Return  bool  true if no  error occured
 @Description Code in InitView() will be called by PVRShell upon
    initialization or after a change in the rendering context.
    Used to initialize variables that are dependant on the rendering
    context (e.g. textures, vertex buffers, etc.)
******************************************************************************/
bool OGLES2RenderToTexture::InitView()
{
 // Find the largest square power of two texture that fits into the viewport
 m_i32TexSize = 1;
 int iSize = PVRT_MIN(PVRShellGet(prefWidth), PVRShellGet(prefHeight));
 while (m_i32TexSize * 2 < iSize) m_i32TexSize *= 2;

 srand(PVRShellGetTime());
 m_ui32Framenum = rand() % 5000;

 m_framenumber = 0;

 // Get the initial time
 m_ui32Time = PVRShellGetTime();

 /*
  Initialize VBO data and load textures
 */
 LoadVbos();
 LoadTextures();

 /*
  Load and compile the shaders & link programs
 */
 CPVRTString ErrorStr;
 if (!LoadShaders(&ErrorStr))
 {
  PVRShellSet(prefExitMessage, ErrorStr.c_str());
  return false;
 }

 // Set the sampler2D uniforms to corresponding texture units
 glUniform1i(glGetUniformLocation(m_ShaderProgram.uiId, "sTexture"), 0);

 /*
  Initialize Print3D
 */

 // Is the screen rotated?
 bool bRotate = PVRShellGet(prefIsRotated) && PVRShellGet(prefFullScreen);

 if(m_Print3D.SetTextures(0,PVRShellGet(prefWidth),PVRShellGet(prefHeight), bRotate) != PVR_SUCCESS)
 {
  PVRShellSet(prefExitMessage, "ERROR: Cannot initialise Print3Dn");
  return false;
 }

 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
 

 glGenFramebuffers(1, m_uFBO);


 /*
  Create two handles for a frame buffer object.
 */
 glGenFramebuffers(2, m_auiFbo);
 m_i32CurrentFbo = 1;

 /*
  Get the currently bound frame buffer object. On most platforms this just gives 0.
  */
 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_i32OriginalFbo);

 /*
  Attach the renderable objects (e.g. textures) to the frame buffer object now as
  they will stay attached to the frame buffer object even when it is not bound.
 */

 // We have two FBOs so we're doing the same for each
 for(int i = 0; i < 2; ++i)
 {
  /*
   Firstly, to do anything with a frame buffer object we need to bind it. In the case
   below we are binding our frame buffer object to the frame buffer.
  */
  glBindFramebuffer(GL_FRAMEBUFFER, m_auiFbo);

  /*
   To render to a texture we need to attach it texture to the frame buffer object.
   GL_COLOR_ATTACHMENT0 tells it to attach the texture to the colour buffer, the 0 on the
   end refers to the colour buffer we want to attach it to as a frame buffer object can
   have more than one colour buffer.
  */
  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_auiTexture, 0);

  // Clear the color buffer for this FBO
  glClear(GL_COLOR_BUFFER_BIT);

  /*
   Create and bind a depth buffer to the frame buffer object.

   A depth buffer isn't needed for this training course but will likely be
   required for most uses of frame buffer objects so its attachment is being
   demonstrated here.
  */

  // Generate and bind the handle for the render buffer (which will become our depth buffer)
  glGenRenderbuffers(1, &m_auiDepthBuffer);
  glBindRenderbuffer(GL_RENDERBUFFER, m_auiDepthBuffer);

  /*
   Currently it is unknown to GL that we want our new render buffer to be a depth buffer.
   glRenderbufferStorage will fix this and in this case will allocate a depth buffer of
   m_i32TexSize by m_i32TexSize.
  */
  glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, m_i32TexSize, m_i32TexSize);

  // Now we have our depth buffer attach it to our frame buffer object.
  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_auiDepthBuffer);
 }

 /*
  Unbind the frame buffer object so rendering returns back to the backbuffer.
 */
 glBindFramebuffer(GL_FRAMEBUFFER, m_i32OriginalFbo);

 // Use a nice bright blue as clear colour
 glClearColor(0.6f, 0.8f, 1.0f, 1.0f);

 return true;
}

/*!****************************************************************************
 @Function  ReleaseView
 @Return  bool  true if no error occured
 @Description Code in ReleaseView() will be called by PVRShell when the
    application quits or before a change in the rendering context.
******************************************************************************/
bool OGLES2RenderToTexture::ReleaseView()
{
 // Delete textures
 glDeleteTextures(2, m_auiTexture);
 glDeleteTextures(1, &m_uiTrunkTex);

 // Delete program and shader objects
 glDeleteProgram(m_ShaderProgram.uiId);

 glDeleteShader(m_uiVertShader);
 glDeleteShader(m_uiFragShader);

 // Delete buffer objects
 glDeleteBuffers(1, &m_uiVbo);
 glDeleteFramebuffers(2, m_auiFbo);

 glDeleteFramebuffers(1, m_uFBO);

 // Delete our depth buffer render buffers
 glDeleteRenderbuffers(2, m_auiDepthBuffer);

 // Release Print3D Textures
 m_Print3D.ReleaseTextures();

 return true;
}

/*!****************************************************************************
 @Function  RenderScene
 @Return  bool  true if no error occured
 @Description Main rendering loop function of the program. The shell will
    call this function every frame.
    eglSwapBuffers() will be performed by PVRShell automatically.
    PVRShell will also manage important OS events.
    Will also manage relevent OS events. The user has access to
    these events through an abstraction layer provided by PVRShell.
******************************************************************************/
bool OGLES2RenderToTexture::RenderScene()
{
 /* vary the branch angles on the fractal sinusoidally */
 m_fAngle = (float)(sin(0.25*PVRT_PIf*(float)(m_ui32Framenum)/256.0f))* 70.0f;

 /* largeish prime number in the angular frequency here, so the motion's not obviously periodic */
 m_fAngle2 = (float)(sin((79.0f/256.0f)*2.0*PVRT_PIf*(float)(m_ui32Framenum)/256.0f))*100.0f + 30.0f;

 /* Convert the angles to radians. */
 m_fAngle  *= 0.017453f;
 m_fAngle2 *= 0.017453f;

 /* Increase the frame count */
 if(PVRShellGetTime() - m_ui32Time > 10)
 {
  m_ui32Time = PVRShellGetTime();
  m_ui32Framenum += 2;

  if(m_ui32Framenum > 20000)
   m_ui32Framenum = 0;
 }

 m_framenumber ++;

 

 // Disable depth test and culling as we don't need it
 glDisable(GL_DEPTH_TEST);
 glDisable(GL_CULL_FACE);


 if (m_framenumber == 5)
 {
  if(!DrawScreen_S())
   return false;
 }

 // Draw the RenderToTexture
 if(!DrawScreen())
  return false;

 


 // Displays the demo name using the tools. For a detailed explanation, see the training course IntroducingPVRTools
 m_Print3D.DisplayDefaultTitle("Render to Texture", "Using FBO", ePVRTPrint3DLogoIMG);
 m_Print3D.Flush();

 return true;
}

bool OGLES2RenderToTexture::DrawScreen()
{
 /*
  We're going to do the following steps to create the effect. Texture 1 refers to the texture
  attached to the first FBO. Texture 2 refers to the texture attached to the second FBO.

  Frame 0

   1. We bind the second frame buffer object so we can do things to it.
   2. We draw two quads with Texture 1 applied.
   3. We draw the trunk.
   4. We make the back buffer current.
   5. We draw 6 quads with Texture 2 applied.

  Frame 1

   6. We bind the first frame buffer object so we can do things to it.
   7. We draw two quads with Texture 2 applied. Texture 2 still contains
    the image from the last frame.
   8. We draw the trunk.
   9. We make the back buffer current.
   10. We draw 6 quads with Texture 1 applied.

  Frame 2

   11. We bind the second frame buffer object so we can do things to it.
   12. We draw two quads with Texture 1 applied. Texture 1 still contains
    the image from the last frame.
   13. We draw the trunk.
   14. We make the back buffer current.
   15. We draw 6 quads with Texture 2 applied.

   16. We repeat steps 6 through to 16 for consecutive frames.
 */

 /* Use the program created with the fragment and vertex shaders. */
 glUseProgram(m_ShaderProgram.uiId);
 glBindBuffer(GL_ARRAY_BUFFER, m_uiVbo);
 glDisable(GL_CULL_FACE);

 glEnableVertexAttribArray(VERTEX_ARRAY);
 glEnableVertexAttribArray(TEXCOORD_ARRAY);
 glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), 0);
 glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));

 

 /*
  Draw the fractal onto the current m_ui32Texture
 */
 if(!RenderFractal())
  return false;

 PVRTMat4 fMatrix;
 fMatrix = PVRTMat4::Identity();

 /*
  Bind the projection model view matrix (PMVMatrix) to
  the associated uniform variable in the shader
 */
 glUniformMatrix4fv(m_ShaderProgram.auiLoc[eMVPMatrix], 1, GL_FALSE, fMatrix.ptr());

 // Clear the color and depth buffer
 glClearColor(0.6f, 0.8f, 1.0f, 1.0f);
 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 /*
  Set the viewport ot fill the screen.
 */
 glViewport(0, 0, PVRShellGet(prefWidth), PVRShellGet(prefHeight));

 /* Set up the matrix we are going to use to rotate and scale the 6 quads. */
 PVRTMat4 fRotZ;
 fMatrix = PVRTMat4::Scale(0.8f *(float)PVRShellGet(prefHeight) / (float)PVRShellGet(prefWidth), 0.8f, 0.8f);
 fRotZ = PVRTMat4::RotationZ(1.047f);

// glEnable(GL_BLEND);
 glBlendFunc(GL_DST_COLOR, GL_ONE);


 /* Bind the texture that we have rendered too.*/
 glBindTexture(GL_TEXTURE_2D, m_auiTexture[m_i32CurrentFbo]);

 /* Draws 6 rotated quads */
 for(int i = 0; i < 6; ++i)
 {
  // Set the transformationh matrix
  glUniformMatrix4fv(m_ShaderProgram.auiLoc[eMVPMatrix], 1, GL_FALSE, fMatrix.ptr());

  // Draw the quad
  glDrawArrays(GL_TRIANGLE_STRIP, 5, 4);

  // Rotate the object by another 60 degrees.
  fMatrix = fMatrix * fRotZ;
 }
 

 // Swap the FBOs
 m_i32CurrentFbo = 1 - m_i32CurrentFbo;

 


 glBindBuffer(GL_ARRAY_BUFFER, 0);
 glDisableVertexAttribArray(VERTEX_ARRAY);
 glDisableVertexAttribArray(TEXCOORD_ARRAY);

 return true;
}

bool OGLES2RenderToTexture::DrawScreen_S()
{
 /* Use the program created with the fragment and vertex shaders. */
 glUseProgram(m_ShaderProgram.uiId);
 glBindBuffer(GL_ARRAY_BUFFER, m_uiVbo);
 glDisable(GL_CULL_FACE);

 glEnableVertexAttribArray(VERTEX_ARRAY);
 glEnableVertexAttribArray(TEXCOORD_ARRAY);
 glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), 0);
 glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));

 

 /*
  Draw the fractal onto the current m_ui32Texture
 */
 if(!RenderFractal())
  return false;

 PVRTMat4 fMatrix;
 fMatrix = PVRTMat4::Identity();

 /*
  Bind the projection model view matrix (PMVMatrix) to
  the associated uniform variable in the shader
 */
 glUniformMatrix4fv(m_ShaderProgram.auiLoc[eMVPMatrix], 1, GL_FALSE, fMatrix.ptr());

 // Clear the color and depth buffer
 glClearColor(0.6f, 0.8f, 1.0f, 1.0f);
 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 /*
  Set the viewport ot fill the screen.
 */
 glViewport(0, 0, PVRShellGet(prefWidth), PVRShellGet(prefHeight));

 /* Set up the matrix we are going to use to rotate and scale the 6 quads. */
 PVRTMat4 fRotZ;
 fMatrix = PVRTMat4::Scale(0.8f *(float)PVRShellGet(prefHeight) / (float)PVRShellGet(prefWidth), 0.8f, 0.8f);
 fRotZ = PVRTMat4::RotationZ(1.047f);

// glEnable(GL_BLEND);
 glBlendFunc(GL_DST_COLOR, GL_ONE);


 /* Bind the texture that we have rendered too.*/
 glBindTexture(GL_TEXTURE_2D, m_auiTexture[m_i32CurrentFbo]);

 /* Draws 6 rotated quads */
 for(int i = 0; i < 6; ++i)
 {
  // Set the transformationh matrix
  glUniformMatrix4fv(m_ShaderProgram.auiLoc[eMVPMatrix], 1, GL_FALSE, fMatrix.ptr());

  // Draw the quad
  glDrawArrays(GL_TRIANGLE_STRIP, 5, 4);

  // Rotate the object by another 60 degrees.
  fMatrix = fMatrix * fRotZ;
 }

 m_pScreenBuf = (GLubyte*) malloc(800 * 600 * 3);

 glBindFramebuffer(GL_FRAMEBUFFER, m_uFBO[0]);
 glReadPixels(0, 0, 800, 600,GL_RGB,GL_UNSIGNED_BYTE,m_pScreenBuf);

 FILE *fp = fopen("ScreenShot.raw","w");

 for(int j=599; j>=0; j--)
 {
  for(int i=799; i>=0; i--)
  {
    fprintf(fp,"%d ",m_pScreenBuf[(j*800+i)*3+2]);
    fprintf(fp,"%d ",m_pScreenBuf[(j*800+i)*3+1]);
    fprintf(fp,"%d ",m_pScreenBuf[(j*800+i)*3+0]);
  }
 }

 fclose(fp);
 free (m_pScreenBuf);
     
  
 // Swap the FBOs
 m_i32CurrentFbo = 1 - m_i32CurrentFbo;


 glBindBuffer(GL_ARRAY_BUFFER, 0);
 glDisableVertexAttribArray(VERTEX_ARRAY);
 glDisableVertexAttribArray(TEXCOORD_ARRAY);

 return true;
}

/*******************************************************************************
 * Function Name  : RenderFractal
 * Description    : Draws the RenderToTexture
 *******************************************************************************/
bool OGLES2RenderToTexture::RenderFractal()
{
    /*
         To do anything with a frame buffer object we need to bind it. In the case
   below we are binding our frame buffer object to the frame buffer.
    */
 glBindFramebuffer(GL_FRAMEBUFFER, m_auiFbo[m_i32CurrentFbo]);

    /*
         If everything went ok then we can render to the texture.
    */
 if(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
 {
  PVRTMat4 fMatrix, fTrans, fRot;

        // Setup the Viewport to the dimensions of the texture
  glViewport(0, 0, m_i32TexSize, m_i32TexSize);

        // Clear the screen by this colour
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  // Bind the texture used for rendering to for the previous frame
  glBindTexture(GL_TEXTURE_2D, m_auiTexture[1 - m_i32CurrentFbo]);

  // Enable additive blend
  glEnable(GL_BLEND);
  glBlendFunc(GL_ONE, GL_ONE);

  /*
   Initialise the translation array that we are going to use to translate both of the
   the quads that we are going to render to.
  */
  fTrans = PVRTMat4::Translation(0.0f, -0.4f, 0.0f);

  /*
   Set up the rotation matrix that we are going to use to rotate the two quads
   that have the previous texture created for the previous frame bound
   to them.
  */
  fRot = PVRTMat4::RotationZ(m_fAngle + m_fAngle2);
  fMatrix = fTrans * fRot;
  /*
   Set the transformation matrix in the shader.
  */
  glUniformMatrix4fv(m_ShaderProgram.auiLoc[eMVPMatrix], 1, GL_FALSE, fMatrix.ptr());
  glDrawArrays(GL_TRIANGLE_STRIP, 5, 4);

  /*
   Rotate the second quad the other way.
  */
  fRot = PVRTMat4::RotationZ(m_fAngle - m_fAngle2);
  fMatrix = fTrans * fRot;
  glUniformMatrix4fv(m_ShaderProgram.auiLoc[eMVPMatrix], 1, GL_FALSE, fMatrix.ptr());
  glDrawArrays(GL_TRIANGLE_STRIP, 5, 4);

  /*
   Now draw the trunk.
     */
  // Bind the trunk texture
  glBindTexture(GL_TEXTURE_2D, m_uiTrunkTex);

  fMatrix = PVRTMat4::Identity();
  glUniformMatrix4fv(m_ShaderProgram.auiLoc[eMVPMatrix], 1, GL_FALSE, fMatrix.ptr());

  // Draw the trunk
  glDrawArrays(GL_TRIANGLE_STRIP, 0, 5);

 }

 /*
  Unbind the frame buffer object so rendering returns back to the backbuffer.
 */
 glBindFramebuffer(GL_FRAMEBUFFER, m_i32OriginalFbo);
 return true;
}

/*!****************************************************************************
 @Function  NewDemo
 @Return  PVRShell*  The demo supplied by the user
 @Description This function must be implemented by the user of the shell.
    The user should return its PVRShell object defining the
    behaviour of the application.
******************************************************************************/
PVRShell* NewDemo()
{
 return new OGLES2RenderToTexture();
}

/******************************************************************************
 End of file (OGLES2RenderToTexture.cpp)
******************************************************************************/

 

Maybe I know the reason now. I should attach a Renderbuffer, GL_DEPTH_ATTACHMENT and just like what the original code does to the original buffer… Correct me if I am wrong.

 

Actually, I found out even without using a separate framebuffer, I still can achieve my goal. I found that when I called glReadPixels, the current frame will not be displayed and I can do whatever I want.  At least, this is true based on my test today. Is this what the opengel ES 2.0 will do when glReadPixels is called? 

 

Thank you very much,

 

Renaissance

 

 

No, glReadPixels just transfers the pixels from the currently bound FB on the GPU to the CPU/RAM. In order not to show the result, you need to render to an FBO, not the FB, i.e. 0.

Based on the above method, I can successfully make off-screen rendering and image saving in the frame work of one training course. The code is running well on the PC version. Then I re-compile the code and re-run it on OMAP system, I still can get the off-screen image saved. But the image content is all-zero. In other word, the image is all black.


The codes I used for these two cases are the same. Can anyone give me a hint why the OMAP version failed?
 

Thank you very much,

 

Renaissance

I just figure out that the reason is I called the glReadPixels() function wrong.

 

I should use the following method

glReadPixels(0, 0, 800, 600,GL_RGBA,GL_UNSIGNED_BYTE,m_pScreenBuf);

 

Not the one I used in my previous version

glReadPixels(0, 0, 800, 600,GL_RGB,GL_UNSIGNED_BYTE,m_pScreenBuf);


 

Even though, when I move on to my code with this update, where I want to change the background color only for the frameshot (I do not want to see that on the screen), I noticed that during the pause when the data is being written to external file, on the display from the OMAP, I can see this modified frame.  Since this is an off-screen rendering, why I still can see this on the screen?

 

The same code works well on the PC (windows version) and I do not see this frame on the screen while the modified frame is saved to external file.

What caused this different effects?

 

Thank you very much,

 

Renaissance