Could not show multiple textures

I use the PVRTTextureLoadFromPVR to load two different textures into two different GLuint as following

 

 if(PVRTTextureLoadFromPVR(c_sz_BK_TextureFile, &m_uiBKTex) != PVR_SUCCESS)
 {
  PVRShellSet(prefExitMessage, "ERROR: Cannot load the texturen");
  return false;
 }

 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );


 if(PVRTTextureLoadFromPVR(c_sz_FG_TextureFile, &m_uiFGTex) != PVR_SUCCESS)
 {
  PVRShellSet(prefExitMessage, "ERROR: Cannot load the texturen");
  return false;
 }

 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

 

In the RenderScene function, I did the following

 glActiveTexture(GL_TEXTURE0);
 glBindTexture(GL_TEXTURE_2D, m_uiBKTex);
 glActiveTexture(GL_TEXTURE1);
 glBindTexture(GL_TEXTURE_2D, m_uiFGTex);

 

Then I draw two planes, one big and one small. Each is with the corresponding texture. However, I always see the same texture is mapped on both planes. The second one is not mapped on the 2nd plane as I expected.

 

Can you give me a hint why I could not map the 2nd texture?

 

 

Here is the complete source code.

 


#include <math.h>
#include <stdio.h>

#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include "OGLES2Tools.h" 
#include "PVRShell.h"

// Index to bind the attributes to vertex shaders
#define VERTEX_ARRAY 0
#define TEXCOORD_ARRAY 1
#define NORMAL_ARRAY 2
#define COLOR_ARRAY  3

const char c_sz_BK_TextureFile[]  = "BG-m.pvr";
const char c_sz_FG_TextureFile[]  = "FG-m.pvr";


//two triangles
GLfloat BK_TRIANGLE_VETEX[18] ={
-1.0f, 1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f
};

//normal for each vertex of the two triangles
GLfloat BK_TRIANGLE_NORMAL[18] = {
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f};

GLfloat BK_TRIANGLE_COLOR[24] = {
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f};

GLfloat BK_TRIANGLE_TEXTCOORD[12] = {
        0.0f,1.0f ,   // UVs
        1.0f,1.0f ,
        1.0f,0.0f,
        0.0f,1.0f ,  
        1.0f,0.0f ,
        0.0f,0.0f};

 

//two triangles as popup button
GLfloat FG_TRIANGLE_VETEX[18] ={
0.55f, 0.22f, 1.0f,
1.0f, 0.22f, 1.0f,
1.0f, -1.0f, 1.0f,
0.55f, 0.22f,  1.0f,
1.0f, -1.0f, 1.0f,
0.55f, -1.0f, 1.0f
};

//normal for each vertex of the two triangles as popup button
GLfloat FG_TRIANGLE_NORMAL[18] = {
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f};

GLfloat FG_TRIANGLE_COLOR[24] = {
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f};

GLfloat FG_TRIANGLE_TEXTCOORD[12] = {
        0.0f,1.0f ,   // UVs
        1.0f,1.0f ,
        1.0f,0.0f,
        0.0f,1.0f ,  
        1.0f,0.0f ,
        0.0f,0.0f};


/*!****************************************************************************
 Class implementing the PVRShell functions.
******************************************************************************/
class OGLESBasicTnL : public PVRShell
{
 // The vertex and fragment shader OpenGL handles
 GLuint m_uiVertexShader, m_uiFragShader;

 // The program object containing the 2 shader objects
 GLuint m_uiProgramObject;

 // Texture handle
 GLuint m_uiBKTex;
 GLuint m_uiFGTex;


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


/*!****************************************************************************
 @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 OGLESBasicTnL::InitApplication()
{
    bool m_bUseVertexProgram = true;

 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 OGLESBasicTnL::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 OGLESBasicTnL::InitView()
{
 // Fragment and vertex shaders code

 char* pszFragShader = "
  uniform sampler2D sampler2d;
  varying mediump float varDot;
  varying mediump vec2 varCoord;
  varying mediump vec4 v_primaryColor;
  void main (void)
  {
   gl_FragColor = texture2D(sampler2d,varCoord) * varDot*v_primaryColor;
  }";
 char* pszVertShader = "
  attribute highp vec4 myVertex;
  attribute mediump vec3 myNormal;
  attribute mediump vec4 myUV;
  attribute mediump vec4  myColor;
  uniform mediump mat4 myPMVMatrix;
  uniform mediump vec4 myTMatrix;
  uniform mediump float myScale;
  uniform mediump float myYScale;
  uniform mediump mat3 myModelViewIT;
  uniform mediump vec3 myLightDirection;
  varying mediump float varDot;
  varying mediump vec2 varCoord;
  varying mediump vec4 v_primaryColor;
  void main(void)
  {
   v_primaryColor = myColor;
   gl_Position = (myPMVMatrix * myVertex);
   gl_Position.x = gl_Position.x*myScale;
   gl_Position.y = gl_Position.y*myScale*myYScale;
   gl_Position.z = gl_Position.z*myScale;
   gl_Position = gl_Position + myTMatrix;
   varCoord = myUV.st;
   mediump vec3 transNormal = myModelViewIT * myNormal;
   varDot = max( dot(transNormal, myLightDirection), 0.0 );
  }";


 // Create the fragment shader object
 m_uiFragShader = glCreateShader(GL_FRAGMENT_SHADER);

 // Load the source code into it
 glShaderSource(m_uiFragShader, 1, (const char**)&pszFragShader, NULL);

 // Compile the source code
 glCompileShader(m_uiFragShader);

 // Check if compilation succeeded
 GLint bShaderCompiled;
    glGetShaderiv(m_uiFragShader, GL_COMPILE_STATUS, &bShaderCompiled);
 if (!bShaderCompiled)
 {
  // An error happened, first retrieve the length of the log message
  int i32InfoLogLength, i32CharsWritten;
  glGetShaderiv(m_uiFragShader, GL_INFO_LOG_LENGTH, &i32InfoLogLength);

  // Allocate enough space for the message and retrieve it
  char* pszInfoLog = new char[i32InfoLogLength];
        glGetShaderInfoLog(m_uiFragShader, i32InfoLogLength, &i32CharsWritten, pszInfoLog);

  /*
   Displays the message in a dialog box when the application quits
   using the shell PVRShellSet function with first parameter prefExitMessage.
  */
  char* pszMsg = new char[i32InfoLogLength+256];
  sprintf(pszMsg, "Failed to compile fragment shader: %s", pszInfoLog);
  PVRShellSet(prefExitMessage, pszMsg);
  delete [] pszMsg;
  delete [] pszInfoLog;
  return false;
 }

 // Loads the vertex shader in the same way
 m_uiVertexShader = glCreateShader(GL_VERTEX_SHADER);
 glShaderSource(m_uiVertexShader, 1, (const char**)&pszVertShader, NULL);
 glCompileShader(m_uiVertexShader);
    glGetShaderiv(m_uiVertexShader, GL_COMPILE_STATUS, &bShaderCompiled);
 if (!bShaderCompiled)
 {
  int i32InfoLogLength, i32CharsWritten;
  glGetShaderiv(m_uiVertexShader, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
  char* pszInfoLog = new char[i32InfoLogLength];
        glGetShaderInfoLog(m_uiVertexShader, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
  char* pszMsg = new char[i32InfoLogLength+256];
  sprintf(pszMsg, "Failed to compile vertex shader: %s", pszInfoLog);
  PVRShellSet(prefExitMessage, pszMsg);
  delete [] pszMsg;
  delete [] pszInfoLog;
  return false;
 }

 // Create the shader program
    m_uiProgramObject = glCreateProgram();

 // Attach the fragment and vertex shaders to it
    glAttachShader(m_uiProgramObject, m_uiFragShader);
    glAttachShader(m_uiProgramObject, m_uiVertexShader);

 // Bind the custom vertex attribute "myVertex" to location VERTEX_ARRAY
    glBindAttribLocation(m_uiProgramObject, VERTEX_ARRAY, "myVertex");
 // Bind the custom vertex attribute "myUV" to location TEXCOORD_ARRAY
    glBindAttribLocation(m_uiProgramObject, TEXCOORD_ARRAY, "myUV");
 // Bind the custom vertex attribute "myNormal" to location NORMAL_ARRAY
    glBindAttribLocation(m_uiProgramObject, NORMAL_ARRAY, "myNormal");
 // Bind the custom vertex attribute "myColor" to location COLOR_ARRAY
    glBindAttribLocation(m_uiProgramObject, COLOR_ARRAY, "myColor");
 // Link the program
    glLinkProgram(m_uiProgramObject);

 // Check if linking succeeded in the same way we checked for compilation success
    GLint bLinked;
    glGetProgramiv(m_uiProgramObject, GL_LINK_STATUS, &bLinked);
 if (!bLinked)
 {
  int i32InfoLogLength, i32CharsWritten;
  glGetProgramiv(m_uiProgramObject, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
  char* pszInfoLog = new char[i32InfoLogLength];
  glGetProgramInfoLog(m_uiProgramObject, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
  char* pszMsg = new char[i32InfoLogLength+256];
  sprintf(pszMsg, "Failed to link program: %s", pszInfoLog);
  PVRShellSet(prefExitMessage, pszMsg);
  delete [] pszMsg;
  delete [] pszInfoLog;
  return false;
 }
 
 if(PVRTTextureLoadFromPVR(c_sz_BK_TextureFile, &m_uiBKTex) != PVR_SUCCESS)
 {
  PVRShellSet(prefExitMessage, "ERROR: Cannot load the texturen");
  return false;
 }

 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

 if(PVRTTextureLoadFromPVR(c_sz_FG_TextureFile, &m_uiFGTex) != PVR_SUCCESS)
 {
  PVRShellSet(prefExitMessage, "ERROR: Cannot load the texturen");
  return false;
 }

 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

 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 OGLESBasicTnL::ReleaseView()
{
 // Frees the texture
 glDeleteTextures(1, &m_uiBKTex);
 glDeleteTextures(1, &m_uiFGTex);

 // Frees the OpenGL handles for the program and the 2 shaders
 glDeleteProgram(m_uiProgramObject);
 glDeleteShader(m_uiVertexShader);
 glDeleteShader(m_uiFragShader);


 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 OGLESBasicTnL::RenderScene()
{
 glViewport(0, 0, 800, 600);
 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

 glUseProgram(m_uiProgramObject);

 glActiveTexture(GL_TEXTURE0);
 glBindTexture(GL_TEXTURE_2D, m_uiBKTex);
 glActiveTexture(GL_TEXTURE1);
 glBindTexture(GL_TEXTURE_2D, m_uiFGTex);

 glEnable(GL_CULL_FACE);
 glCullFace(GL_FRONT);

 // Clears the color and depth buffer
 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 //*************************************************************************
 //Draw the background
 //*************************************************************************

 float alpha = 0;  //-p
 float beta =  0;  //-q
 float theta = 0;           // o


 float aModelViewIT0[] =
 {
  cos(theta)*cos(alpha), cos(alpha)*sin(beta)*sin(theta)-sin(alpha)*cos(beta), cos(alpha)*cos(beta)*sin(theta)+sin(alpha)*sin(beta),
  cos(theta)*sin(alpha), sin(alpha)*sin(beta)*sin(theta)+cos(alpha)*cos(beta), sin(alpha)*cos(beta)*sin(theta)-sin(beta)*cos(alpha),
  -sin(theta),   sin(beta)*cos(theta),         cos(beta)*cos(theta)
 };


 /*
  Bind the projection model view matrix (PMVMatrix) to the
  corresponding uniform variable in the shader.
  This matrix is used in the vertex shader to transform the vertices.
 */
 float aPMVMatrix0[] =
 {
  cos(theta)*cos(alpha), cos(alpha)*sin(beta)*sin(theta)-sin(alpha)*cos(beta), cos(alpha)*cos(beta)*sin(theta)+sin(alpha)*sin(beta), 0,
  cos(theta)*sin(alpha), sin(alpha)*sin(beta)*sin(theta)+cos(alpha)*cos(beta), sin(alpha)*cos(beta)*sin(theta)-sin(beta)*cos(alpha),0,
  -sin(theta),   sin(beta)*cos(theta),         cos(beta)*cos(theta),0,
  0,0,0,1
 };


 int i32Location = glGetUniformLocation(m_uiProgramObject, "myPMVMatrix");
 glUniformMatrix4fv( i32Location, 1, GL_FALSE, aPMVMatrix0);

 i32Location = glGetUniformLocation(m_uiProgramObject, "myModelViewIT");
 glUniformMatrix3fv( i32Location, 1, GL_FALSE, aModelViewIT0);

 float aTMatrix0[] = {0.0f,0.0f,0.0f,0.0f};
 i32Location = glGetUniformLocation(m_uiProgramObject, "myTMatrix");
 glUniform4fv( i32Location,1,aTMatrix0);

 float aScale0 = 1.0;
 i32Location = glGetUniformLocation(m_uiProgramObject, "myScale");
 glUniform1f( i32Location,aScale0);

 float aYScale0 = 1.0;
 i32Location = glGetUniformLocation(m_uiProgramObject, "myYScale");
 glUniform1f( i32Location,aYScale0);

 // Bind the Light Direction vector to the shader
 i32Location = glGetUniformLocation(m_uiProgramObject, "myLightDirection"); 
 glUniform3f( i32Location, 0, 0, 1.5);


 //*************************************************************************
 //Draw the background
 //*************************************************************************
 glEnableVertexAttribArray(VERTEX_ARRAY);
 glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, BK_TRIANGLE_VETEX);
 
 glEnableVertexAttribArray(NORMAL_ARRAY);
 glVertexAttribPointer(NORMAL_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, BK_TRIANGLE_NORMAL);

 glEnableVertexAttribArray(TEXCOORD_ARRAY); 
 glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, 0, BK_TRIANGLE_TEXTCOORD);

 glEnableVertexAttribArray(COLOR_ARRAY); 
 glVertexAttribPointer(COLOR_ARRAY, 4, GL_FLOAT, GL_FALSE, 0, BK_TRIANGLE_COLOR);

 glBindTexture(GL_TEXTURE_2D, m_uiBKTex);
 // Draws a non-indexed triangle array
 glDrawArrays(GL_TRIANGLES, 0,6);


 glUseProgram(m_uiProgramObject);
 //*************************************************************************
 //Draw the foreground
 //*************************************************************************
 glViewport(0, 0, 800, 600);
 i32Location = glGetUniformLocation(m_uiProgramObject, "myPMVMatrix");
 glUniformMatrix4fv( i32Location, 1, GL_FALSE, aPMVMatrix0);

 i32Location = glGetUniformLocation(m_uiProgramObject, "myModelViewIT");
 glUniformMatrix3fv( i32Location, 1, GL_FALSE, aModelViewIT0);

 i32Location = glGetUniformLocation(m_uiProgramObject, "myTMatrix");
 glUniform4fv( i32Location,1,aTMatrix0);

 i32Location = glGetUniformLocation(m_uiProgramObject, "myScale");
 glUniform1f( i32Location,aScale0);

 i32Location = glGetUniformLocation(m_uiProgramObject, "myYScale");
 glUniform1f( i32Location,aYScale0);
 
 i32Location = glGetUniformLocation(m_uiProgramObject, "myLightDirection");
 glUniform3f( i32Location, 0, 0, 1.5);

 glEnableVertexAttribArray(VERTEX_ARRAY);
 glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, FG_TRIANGLE_VETEX);
 
 glEnableVertexAttribArray(NORMAL_ARRAY);
 glVertexAttribPointer(NORMAL_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, FG_TRIANGLE_NORMAL);

 glEnableVertexAttribArray(TEXCOORD_ARRAY);  
 glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, 0, FG_TRIANGLE_TEXTCOORD); 

 glEnableVertexAttribArray(COLOR_ARRAY); 
 glVertexAttribPointer(COLOR_ARRAY, 4, GL_FLOAT, GL_FALSE, 0, FG_TRIANGLE_COLOR);

 glBindTexture(GL_TEXTURE_2D, m_uiFGTex); 
 // Draws a non-indexed triangle array
 glDrawArrays(GL_TRIANGLES, 0,6);


 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 OGLESBasicTnL();
}

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

Hi renaissance,<?: prefix = o ns = "urn:schemas-microsoft-com:office:office" />


The reason you are getting this problem seems to stem from your use of glActiveTexture. GL has up to 8 active texture units, GL_TEXTURE0-7 which can be used for multitexturing.


You've assigned your background to active texture 0 and then the foreground to active texture 1. Calls to glActiveTexture() are used to bind a given texture to your specified texture unit (e.g. GL_TEXTURE0), and this texture unit will remain active until you call it again with a different texture unit. So later in your render loop, when you call glBindTexture again to set the texture, it is binding them to GL_TEXTURE1.


Although it is essentially undefined behaviour, it appears that GL_TEXTURE0 is used by your sampler in the shader. So GL_TEXTURE0 is never changing, whilst GL_TEXTURE1 is changing but is never sampled, which causes you to see the same texture twice.


 


To solve your issue, firstly, you should add glUniform1i(glGetUniformLocation(m_uiProgramObject, "sampler2d"), 0); within InitView to explicitly state that you are sampling GL_TEXTURE0 in your shader. The integer value you pass in to this function refers to the active texture you are using, so 0 means that the sampler will use GL_TEXTURE0.


You also need to remove the following four lines from your RenderScene function


glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_uiBKTex);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, m_uiFGTex);
[/CODE]

But leave in your other glBindTexture calls as these will be needed to change textures.

For a good example of how to use multiple textures in a program I suggest you look at the Bumpmap training course in the SDK. The second texture is actually a normal map but the methods still apply. I'd also suggest taking a look at our performance recommendations on optimising your code (particularly for reducing the number of calls you make to GL), which you can find in the SDK package documentation folder.

Hope this helps :)

[CODE]glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_uiBKTex);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, m_uiFGTex);
[/CODE]

But leave in your other glBindTexture calls as these will be needed to change textures.


For a good example of how to use multiple textures in a program I suggest you look at the Bumpmap training course in the SDK. The second texture is actually a normal map but the methods still apply. I'd also suggest taking a look at our performance recommendations on optimising your code (particularly for reducing the number of calls you make to GL), which you can find in the SDK package documentation folder.


Hope this helps :)

Thank you so much, Tobias.

As you suggested, I did the modification and the method works for the two texture code.

Here is a follow-up question.

 

In my original code, you may see the two planes are with the same z offset. But the final rendering effect is related to the order which plane is rendered first.  If I change the the following offset as


//two triangles as popup button
GLfloat FG_TRIANGLE_VETEX[18] ={
0.55f, 0.22f, 1.0f,
1.0f, 0.22f, 1.0f,
1.0f, -1.0f, 1.0f,
0.55f, 0.22f,  1.0f,
1.0f, -1.0f, 1.0f,
0.55f, -1.0f, 1.0f
};

 

=>

 


//two triangles as popup button
GLfloat FG_TRIANGLE_VETEX[18] ={
0.55f, 0.22f, -1.0f,
1.0f, 0.22f, -1.0f,
1.0f, -1.0f, -1.0f,
0.55f, 0.22f,  -1.0f,
1.0f, -1.0f, -1.0f,
0.55f, -1.0f, -1.0f
};

 

The small plane is not blocked by the big one as I expected. However, if I choose to render this plane before the big one, we can see it is blocked. Looks to me the rendering result is only related to the order which plane is rendered first in this example. I am just wondering why the depth information is not taking effect in the example.

 

Thank you very much,

 

Renaissance

You might have to enable the depth test with glEnable(GL_DEPTH_TEST);

Thank you, Martin.

 

First of all, I tried what you suggested by enabling GL_DEPTH_TEST. However, I can only have on plane rendered.

 

Do I also need to create a depthRenderbuffer object when enabling GL_DEPTH_TEST?  I tried the following approach but it is still not working. I still can only get on plane in the output window...

 

GLuint depthRenderbuffer

...

glGenRenderbuffers(1, &depthRenderbuffer);

....

glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);

No, I don’t think you need renderbuffers for depth tests. Make sure that the z coordinates returned by the vertex shader are in the visible range (between -w coordinate and +w coordinate). Your vertex shader is somewhat strange, in particular these lines:

   gl_Position.x = gl_Position.xmyScale;
   gl_Position.y = gl_Position.y
myScalemyYScale;
   gl_Position.z = gl_Position.z
myScale;
   gl_Position = gl_Position + myTMatrix;

It is hard to understand what your are doing if you use this kind of nonstandard transformations. You should probably use the viewport transformation (glViewport) for whatever you are trying to achieve with these lines.

Hi Martin:

Thank you again for your response.

 

The Z coordinate range I am using is -1.0 to 1.0 for x, y, z. For the shader, since my output screen is 800 by 600 and it is not square, I have to squeeze my target a littlle bit. But for the two planes, you can see these scaling factors are all 1.0. In other word, it will not affact the rendering of the two planes.  If you run my very original code, you will see the shader works fine.

 

As to my question in the previous post, the z location for the two planes are 1.0 and -1.0 and they are in the valid range.

 

Thanks,

 

Renaissance

Can anyone give me some help on this question?

 

Thank you,

 

Renaissance

Could you post your current code again?

Thank you, Martin

 


#include <math.h>
#include <stdio.h>

#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include "OGLES2Tools.h" 
#include "PVRShell.h"

// Index to bind the attributes to vertex shaders
#define VERTEX_ARRAY 0
#define TEXCOORD_ARRAY 1
#define NORMAL_ARRAY 2
#define COLOR_ARRAY  3

const char c_sz_BK_TextureFile[]  = "BG-m.pvr";
const char c_sz_FG_TextureFile[]  = "FG-m.pvr";


//two triangles
GLfloat BK_TRIANGLE_VETEX[18] ={
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, 1.0f
};

//normal for each vertex of the two triangles
GLfloat BK_TRIANGLE_NORMAL[18] = {
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f};

GLfloat BK_TRIANGLE_COLOR[24] = {
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f};

GLfloat BK_TRIANGLE_TEXTCOORD[12] = {
                          0.0f,1.0f ,   // UVs
        1.0f,1.0f ,
        1.0f,0.0f,
        0.0f,1.0f ,  
        1.0f,0.0f ,
        0.0f,0.0f};

 

//two triangles as popup button
GLfloat FG_TRIANGLE_VETEX[18] ={
0.55f, 0.22f, -1.0f,
1.0f, 0.22f, -1.0f,
1.0f, -1.0f, -1.0f,
0.55f, 0.22f,  -1.0f,
1.0f, -1.0f, -1.0f,
0.55f, -1.0f, -1.0f
};

//normal for each vertex of the two triangles as popup button
GLfloat FG_TRIANGLE_NORMAL[18] = {
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f,
 0.0f,0.0f,1.0f};

GLfloat FG_TRIANGLE_COLOR[24] = {
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f,
 1.0f,1.0f,1.0f,1.0f};

GLfloat FG_TRIANGLE_TEXTCOORD[12] = {
                          0.0f,1.0f ,   // UVs
        1.0f,1.0f ,
        1.0f,0.0f,
        0.0f,1.0f ,  
        1.0f,0.0f ,
        0.0f,0.0f};


/*!****************************************************************************
 Class implementing the PVRShell functions.
******************************************************************************/
class OGLESBasicTnL : public PVRShell
{
 // The vertex and fragment shader OpenGL handles
 GLuint m_uiVertexShader, m_uiFragShader;

 // The program object containing the 2 shader objects
 GLuint m_uiProgramObject;

 // Texture handle
 GLuint m_uiBKTex;
 GLuint m_uiFGTex;
 GLuint depthRenderbuffer;

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


/*!****************************************************************************
 @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 OGLESBasicTnL::InitApplication()
{
    bool m_bUseVertexProgram = true;

 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 OGLESBasicTnL::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 OGLESBasicTnL::InitView()
{
 // Fragment and vertex shaders code

 char* pszFragShader = "
  uniform sampler2D sampler2d;
  varying mediump float varDot;
  varying mediump vec2 varCoord;
  varying mediump vec4 v_primaryColor;
  void main (void)
  {
   gl_FragColor = texture2D(sampler2d,varCoord) * varDot*v_primaryColor;
  }";
 char* pszVertShader = "
  attribute highp vec4 myVertex;
  attribute mediump vec3 myNormal;
  attribute mediump vec4 myUV;
  attribute mediump vec4  myColor;
  uniform mediump mat4 myPMVMatrix;
  uniform mediump vec4 myTMatrix;
  uniform mediump float myScale;
  uniform mediump float myYScale;
  uniform mediump mat3 myModelViewIT;
  uniform mediump vec3 myLightDirection;
  varying mediump float varDot;
  varying mediump vec2 varCoord;
  varying mediump vec4 v_primaryColor;
  void main(void)
  {
   v_primaryColor = myColor;
   gl_Position = (myPMVMatrix * myVertex);
   gl_Position.x = gl_Position.x*myScale;
   gl_Position.y = gl_Position.y*myScale*myYScale;
   gl_Position.z = gl_Position.z*myScale;
   gl_Position = gl_Position + myTMatrix;
   varCoord = myUV.st;
   mediump vec3 transNormal = myModelViewIT * myNormal;
   varDot = max( dot(transNormal, myLightDirection), 0.0 );
  }";


 // Create the fragment shader object
 m_uiFragShader = glCreateShader(GL_FRAGMENT_SHADER);

 // Load the source code into it
 glShaderSource(m_uiFragShader, 1, (const char**)&pszFragShader, NULL);

 // Compile the source code
 glCompileShader(m_uiFragShader);

 // Check if compilation succeeded
 GLint bShaderCompiled;
    glGetShaderiv(m_uiFragShader, GL_COMPILE_STATUS, &bShaderCompiled);
 if (!bShaderCompiled)
 {
  // An error happened, first retrieve the length of the log message
  int i32InfoLogLength, i32CharsWritten;
  glGetShaderiv(m_uiFragShader, GL_INFO_LOG_LENGTH, &i32InfoLogLength);

  // Allocate enough space for the message and retrieve it
  char* pszInfoLog = new char[i32InfoLogLength];
        glGetShaderInfoLog(m_uiFragShader, i32InfoLogLength, &i32CharsWritten, pszInfoLog);

  /*
   Displays the message in a dialog box when the application quits
   using the shell PVRShellSet function with first parameter prefExitMessage.
  */
  char* pszMsg = new char[i32InfoLogLength+256];
  sprintf(pszMsg, "Failed to compile fragment shader: %s", pszInfoLog);
  PVRShellSet(prefExitMessage, pszMsg);
  delete [] pszMsg;
  delete [] pszInfoLog;
  return false;
 }

 // Loads the vertex shader in the same way
 m_uiVertexShader = glCreateShader(GL_VERTEX_SHADER);
 glShaderSource(m_uiVertexShader, 1, (const char**)&pszVertShader, NULL);
 glCompileShader(m_uiVertexShader);
    glGetShaderiv(m_uiVertexShader, GL_COMPILE_STATUS, &bShaderCompiled);
 if (!bShaderCompiled)
 {
  int i32InfoLogLength, i32CharsWritten;
  glGetShaderiv(m_uiVertexShader, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
  char* pszInfoLog = new char[i32InfoLogLength];
        glGetShaderInfoLog(m_uiVertexShader, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
  char* pszMsg = new char[i32InfoLogLength+256];
  sprintf(pszMsg, "Failed to compile vertex shader: %s", pszInfoLog);
  PVRShellSet(prefExitMessage, pszMsg);
  delete [] pszMsg;
  delete [] pszInfoLog;
  return false;
 }

 // Create the shader program
    m_uiProgramObject = glCreateProgram();

 // Attach the fragment and vertex shaders to it
    glAttachShader(m_uiProgramObject, m_uiFragShader);
    glAttachShader(m_uiProgramObject, m_uiVertexShader);

 // Bind the custom vertex attribute "myVertex" to location VERTEX_ARRAY
    glBindAttribLocation(m_uiProgramObject, VERTEX_ARRAY, "myVertex");
 // Bind the custom vertex attribute "myUV" to location TEXCOORD_ARRAY
    glBindAttribLocation(m_uiProgramObject, TEXCOORD_ARRAY, "myUV");
 // Bind the custom vertex attribute "myNormal" to location NORMAL_ARRAY
    glBindAttribLocation(m_uiProgramObject, NORMAL_ARRAY, "myNormal");
 // Bind the custom vertex attribute "myColor" to location COLOR_ARRAY
    glBindAttribLocation(m_uiProgramObject, COLOR_ARRAY, "myColor");
 // Link the program
    glLinkProgram(m_uiProgramObject);

 // Check if linking succeeded in the same way we checked for compilation success
    GLint bLinked;
    glGetProgramiv(m_uiProgramObject, GL_LINK_STATUS, &bLinked);
 if (!bLinked)
 {
  int i32InfoLogLength, i32CharsWritten;
  glGetProgramiv(m_uiProgramObject, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
  char* pszInfoLog = new char[i32InfoLogLength];
  glGetProgramInfoLog(m_uiProgramObject, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
  char* pszMsg = new char[i32InfoLogLength+256];
  sprintf(pszMsg, "Failed to link program: %s", pszInfoLog);
  PVRShellSet(prefExitMessage, pszMsg);
  delete [] pszMsg;
  delete [] pszInfoLog;
  return false;
 }
 
 if(PVRTTextureLoadFromPVR(c_sz_BK_TextureFile, &m_uiBKTex) != PVR_SUCCESS)
 {
  PVRShellSet(prefExitMessage, "ERROR: Cannot load the texturen");
  return false;
 }

 //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
 //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

 if(PVRTTextureLoadFromPVR(c_sz_FG_TextureFile, &m_uiFGTex) != PVR_SUCCESS)
 {
  PVRShellSet(prefExitMessage, "ERROR: Cannot load the texturen");
  return false;
 }

 //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
 //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

 //glUniform1i(glGetUniformLocation(m_uiProgramObject, "sampler2d"), 0);


 //glGenRenderbuffers(1, &depthRenderbuffer);

 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 OGLESBasicTnL::ReleaseView()
{
 // Frees the texture
 glDeleteTextures(1, &m_uiBKTex);
 glDeleteTextures(1, &m_uiFGTex);

 // Frees the OpenGL handles for the program and the 2 shaders
 glDeleteProgram(m_uiProgramObject);
 glDeleteShader(m_uiVertexShader);
 glDeleteShader(m_uiFragShader);


 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 OGLESBasicTnL::RenderScene()
{
 glViewport(0, 0, 800, 600);
 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

 glUseProgram(m_uiProgramObject);

 glEnable(GL_CULL_FACE);
 glCullFace(GL_FRONT);

 //glEnable(GL_DEPTH_TEST); 


 // Clears the color and depth buffer
 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 glEnable(GL_TEXTURE_2D);


 //*************************************************************************
 //Draw the background
 //*************************************************************************

 float alpha = 0;  //-p
 float beta =  0;  //-q
 float theta = 0;           // o


 float aModelViewIT0[] =
 {
  cos(theta)*cos(alpha), cos(alpha)*sin(beta)*sin(theta)-sin(alpha)*cos(beta), cos(alpha)*cos(beta)*sin(theta)+sin(alpha)*sin(beta),
  cos(theta)*sin(alpha), sin(alpha)*sin(beta)*sin(theta)+cos(alpha)*cos(beta), sin(alpha)*cos(beta)*sin(theta)-sin(beta)*cos(alpha),
  -sin(theta),   sin(beta)*cos(theta),         cos(beta)*cos(theta)
 };


 /*
  Bind the projection model view matrix (PMVMatrix) to the
  corresponding uniform variable in the shader.
  This matrix is used in the vertex shader to transform the vertices.
 */
 float aPMVMatrix0[] =
 {
  cos(theta)*cos(alpha), cos(alpha)*sin(beta)*sin(theta)-sin(alpha)*cos(beta), cos(alpha)*cos(beta)*sin(theta)+sin(alpha)*sin(beta), 0,
  cos(theta)*sin(alpha), sin(alpha)*sin(beta)*sin(theta)+cos(alpha)*cos(beta), sin(alpha)*cos(beta)*sin(theta)-sin(beta)*cos(alpha),0,
  -sin(theta),   sin(beta)*cos(theta),         cos(beta)*cos(theta),0,
  0,0,0,1
 };


 int i32Location = glGetUniformLocation(m_uiProgramObject, "myPMVMatrix");
 glUniformMatrix4fv( i32Location, 1, GL_FALSE, aPMVMatrix0);

 i32Location = glGetUniformLocation(m_uiProgramObject, "myModelViewIT");
 glUniformMatrix3fv( i32Location, 1, GL_FALSE, aModelViewIT0);

 float aTMatrix0[] = {0.0f,0.0f,0.0f,0.0f};
 i32Location = glGetUniformLocation(m_uiProgramObject, "myTMatrix");
 glUniform4fv( i32Location,1,aTMatrix0);

 float aScale0 = 1.0;
 i32Location = glGetUniformLocation(m_uiProgramObject, "myScale");
 glUniform1f( i32Location,aScale0);

 float aYScale0 = 1.0;
 i32Location = glGetUniformLocation(m_uiProgramObject, "myYScale");
 glUniform1f( i32Location,aYScale0);

 // Bind the Light Direction vector to the shader
 i32Location = glGetUniformLocation(m_uiProgramObject, "myLightDirection"); 
 glUniform3f( i32Location, 0, 0, 1.5);


 //*************************************************************************
 //Draw the background
 //*************************************************************************
 glEnableVertexAttribArray(VERTEX_ARRAY);
 glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, BK_TRIANGLE_VETEX);
 
 glEnableVertexAttribArray(NORMAL_ARRAY);
 glVertexAttribPointer(NORMAL_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, BK_TRIANGLE_NORMAL);

 glEnableVertexAttribArray(TEXCOORD_ARRAY); 
 glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, 0, BK_TRIANGLE_TEXTCOORD);

 glEnableVertexAttribArray(COLOR_ARRAY); 
 glVertexAttribPointer(COLOR_ARRAY, 4, GL_FLOAT, GL_FALSE, 0, BK_TRIANGLE_COLOR);


 glBindTexture(GL_TEXTURE_2D, m_uiBKTex);
 glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);

 // Draws a non-indexed triangle array
 glDrawArrays(GL_TRIANGLES, 0,6);

 


 glUseProgram(m_uiProgramObject);
 //*************************************************************************
 //Draw the foreground
 //*************************************************************************
 glViewport(0, 0, 800, 600);


 i32Location = glGetUniformLocation(m_uiProgramObject, "myPMVMatrix");
 glUniformMatrix4fv( i32Location, 1, GL_FALSE, aPMVMatrix0);

 i32Location = glGetUniformLocation(m_uiProgramObject, "myModelViewIT");
 glUniformMatrix3fv( i32Location, 1, GL_FALSE, aModelViewIT0);

 i32Location = glGetUniformLocation(m_uiProgramObject, "myTMatrix");
 glUniform4fv( i32Location,1,aTMatrix0);

 i32Location = glGetUniformLocation(m_uiProgramObject, "myScale");
 glUniform1f( i32Location,aScale0);

 i32Location = glGetUniformLocation(m_uiProgramObject, "myYScale");
 glUniform1f( i32Location,aYScale0);
 
 i32Location = glGetUniformLocation(m_uiProgramObject, "myLightDirection");
 glUniform3f( i32Location, 0, 0, 1.5);

 glEnableVertexAttribArray(VERTEX_ARRAY);
 glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, FG_TRIANGLE_VETEX);

 glEnableVertexAttribArray(NORMAL_ARRAY);
 glVertexAttribPointer(NORMAL_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, FG_TRIANGLE_NORMAL);

 glEnableVertexAttribArray(TEXCOORD_ARRAY);  
 glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, 0, FG_TRIANGLE_TEXTCOORD); 

 glEnableVertexAttribArray(COLOR_ARRAY); 
 glVertexAttribPointer(COLOR_ARRAY, 4, GL_FLOAT, GL_FALSE, 0, FG_TRIANGLE_COLOR);

glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
 glBindTexture(GL_TEXTURE_2D, m_uiFGTex); 
 // Draws a non-indexed triangle array
 glDrawArrays(GL_TRIANGLES, 0,6);

 


 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 OGLESBasicTnL();
}

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

Some notes:

- I suspect your projection matrix is not helping as drawing outside a very small z range results in nothing appearing on the screen.
- You really don't need a renderbuffer for this stuff at all

I've taken the liberty of editing your code. I would suggest comparing what's below with what you hav in an automated diff, if you have one.

I stripped out a lot of redundant calls and manipulated the vertex positions a bit. Also, I have added a little interactivity - on windows, press 1 to enable and disable the depth test; press 2 to reverse the render order of the quads. The keys will be different on other platforms - they are the standard PVRShell Action1 and Action2 keys. Switching the render order should make no difference to the output until the depth test is disabled.

I hope this helps - if you have further questions please post.

Code:

#include <math.h>
#include <stdio.h>
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include "OGLES2Tools.h"
#include "PVRShell.h"
// Index to bind the attributes to vertex shaders
#define VERTEX_ARRAY 0
#define TEXCOORD_ARRAY 1
#define NORMAL_ARRAY 2
#define COLOR_ARRAY  3
const char c_sz_BK_TextureFile[]  = "BG-m.pvr";
const char c_sz_FG_TextureFile[]  = "FG-m.pvr";

//two triangles
GLfloat BK_TRIANGLE_VETEX[18] ={
    -1.0f, 1.0f, 0.6f,
    1.0f, 1.0f, 0.6f,
    1.0f, -1.0f, 0.6f,
    -1.0f, 1.0f, 0.6f,
    1.0f, -1.0f, 0.6f,
    -1.0f, -1.0f, 0.6f
};
//normal for each vertex of the two triangles
GLfloat BK_TRIANGLE_NORMAL[18] = {
    0.0f,0.0f,1.0f,
    0.0f,0.0f,1.0f,
    0.0f,0.0f,1.0f,
    0.0f,0.0f,1.0f,
    0.0f,0.0f,1.0f,
    0.0f,0.0f,1.0f
};
GLfloat BK_TRIANGLE_COLOR[24] = {
    1.0f,1.0f,1.0f,1.0f,
    1.0f,1.0f,1.0f,1.0f,
    1.0f,1.0f,1.0f,1.0f,
    1.0f,1.0f,1.0f,1.0f,
    1.0f,1.0f,1.0f,1.0f,
    1.0f,1.0f,1.0f,1.0f
};
GLfloat BK_TRIANGLE_TEXTCOORD[12] = {
    0.0f,1.0f ,   // UVs
    1.0f,1.0f ,
    1.0f,0.0f,
    0.0f,1.0f , 
    1.0f,0.0f ,
    0.0f,0.0f
};

GLfloat FG_TRIANGLE_VETEX[18] ={
    -1.0f, 0.5f, 0.5f,
    0.5f, 0.5f, 0.5f,
    0.5f, -1.0f, 0.5f,
    -1.0f, 0.5f, 0.5f,
    0.5f, -1.0f, 0.5f,
    -1.0f, -1.0f, 0.5f
};

//normal for each vertex of the two triangles as popup button
GLfloat FG_TRIANGLE_NORMAL[18] = {
    0.0f,0.0f,1.0f,
    0.0f,0.0f,1.0f,
    0.0f,0.0f,1.0f,
    0.0f,0.0f,1.0f,
    0.0f,0.0f,1.0f,
    0.0f,0.0f,1.0f
};
GLfloat FG_TRIANGLE_COLOR[24] = {
    1.0f,1.0f,1.0f,1.0f,
    1.0f,1.0f,1.0f,1.0f,
    1.0f,1.0f,1.0f,1.0f,
    1.0f,1.0f,1.0f,1.0f,
    1.0f,1.0f,1.0f,1.0f,
    1.0f,1.0f,1.0f,1.0f
};
GLfloat FG_TRIANGLE_TEXTCOORD[12] = {
    0.0f,1.0f ,   // UVs
    1.0f,1.0f ,
    1.0f,0.0f,
    0.0f,1.0f , 
    1.0f,0.0f ,
    0.0f,0.0f
};

/*!****************************************************************************
Class implementing the PVRShell functions.
******************************************************************************/
class OGLESBasicTnL : public PVRShell
{
    // The vertex and fragment shader OpenGL handles
    GLuint m_uiVertexShader, m_uiFragShader;
    // The program object containing the 2 shader objects
    GLuint m_uiProgramObject;
    // Texture handle
    GLuint m_uiBKTex;
    GLuint m_uiFGTex;

    bool m_bDepthTest, m_bRenderOrder;
public:
    virtual bool InitApplication();
    virtual bool InitView();
    virtual bool ReleaseView();
    virtual bool QuitApplication();
    virtual bool RenderScene();
};

/*!****************************************************************************
@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 OGLESBasicTnL::InitApplication()
{
    PVRShellSet(prefWidth,800);
    PVRShellSet(prefHeight,600);

    m_bDepthTest = m_bRenderOrder = true;
    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 OGLESBasicTnL::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 OGLESBasicTnL::InitView()
{
    // Fragment and vertex shaders code
    char* pszFragShader = "
                          uniform sampler2D sampler2d;
                          varying mediump float varDot;
                          varying mediump vec2 varCoord;
                          varying mediump vec4 v_primaryColor;
                          void main (void)
                          {
                          gl_FragColor = texture2D(sampler2d,varCoord) * varDot*v_primaryColor;
                          }";
    char* pszVertShader = "
                          attribute highp vec4 myVertex;
                          attribute mediump vec3 myNormal;
                          attribute mediump vec4 myUV;
                          attribute mediump vec4  myColor;
                          uniform mediump mat4 myPMVMatrix;
                          uniform mediump vec4 myTMatrix;
                          uniform mediump float myScale;
                          uniform mediump float myYScale;
                          uniform mediump mat3 myModelViewIT;
                          uniform mediump vec3 myLightDirection;
                          varying mediump float varDot;
                          varying mediump vec2 varCoord;
                          varying mediump vec4 v_primaryColor;
                          void main(void)
                          {
                          v_primaryColor = myColor;
                          gl_Position = (myPMVMatrix * myVertex);
                          gl_Position.x = gl_Position.x*myScale;
                          gl_Position.y = gl_Position.y*myScale*myYScale;
                          gl_Position.z = gl_Position.z*myScale;
                          gl_Position = gl_Position + myTMatrix;
                          varCoord = myUV.st;
                          mediump vec3 transNormal = myModelViewIT * myNormal;
                          varDot = max( dot(transNormal, myLightDirection), 0.0 );
                          }";

    // Create the fragment shader object
    m_uiFragShader = glCreateShader(GL_FRAGMENT_SHADER);
    // Load the source code into it
    glShaderSource(m_uiFragShader, 1, (const char**)&pszFragShader, NULL);
    // Compile the source code
    glCompileShader(m_uiFragShader);
    // Check if compilation succeeded
    GLint bShaderCompiled;
    glGetShaderiv(m_uiFragShader, GL_COMPILE_STATUS, &bShaderCompiled);
    if (!bShaderCompiled)
    {
        // An error happened, first retrieve the length of the log message
        int i32InfoLogLength, i32CharsWritten;
        glGetShaderiv(m_uiFragShader, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
        // Allocate enough space for the message and retrieve it
        char* pszInfoLog = new char[i32InfoLogLength];
        glGetShaderInfoLog(m_uiFragShader, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
        /*
        Displays the message in a dialog box when the application quits
        using the shell PVRShellSet function with first parameter prefExitMessage.
        */
        char* pszMsg = new char[i32InfoLogLength+256];
        sprintf(pszMsg, "Failed to compile fragment shader: %s", pszInfoLog);
        PVRShellSet(prefExitMessage, pszMsg);
        delete [] pszMsg;
        delete [] pszInfoLog;
        return false;
    }
    // Loads the vertex shader in the same way
    m_uiVertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(m_uiVertexShader, 1, (const char**)&pszVertShader, NULL);
    glCompileShader(m_uiVertexShader);
    glGetShaderiv(m_uiVertexShader, GL_COMPILE_STATUS, &bShaderCompiled);
    if (!bShaderCompiled)
    {
        int i32InfoLogLength, i32CharsWritten;
        glGetShaderiv(m_uiVertexShader, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
        char* pszInfoLog = new char[i32InfoLogLength];
        glGetShaderInfoLog(m_uiVertexShader, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
        char* pszMsg = new char[i32InfoLogLength+256];
        sprintf(pszMsg, "Failed to compile vertex shader: %s", pszInfoLog);
        PVRShellSet(prefExitMessage, pszMsg);
        delete [] pszMsg;
        delete [] pszInfoLog;
        return false;
    }
    // Create the shader program
    m_uiProgramObject = glCreateProgram();
    // Attach the fragment and vertex shaders to it
    glAttachShader(m_uiProgramObject, m_uiFragShader);
    glAttachShader(m_uiProgramObject, m_uiVertexShader);
    // Bind the custom vertex attribute "myVertex" to location VERTEX_ARRAY
    glBindAttribLocation(m_uiProgramObject, VERTEX_ARRAY, "myVertex");
    // Bind the custom vertex attribute "myUV" to location TEXCOORD_ARRAY
    glBindAttribLocation(m_uiProgramObject, TEXCOORD_ARRAY, "myUV");
    // Bind the custom vertex attribute "myNormal" to location NORMAL_ARRAY
    glBindAttribLocation(m_uiProgramObject, NORMAL_ARRAY, "myNormal");
    // Bind the custom vertex attribute "myColor" to location COLOR_ARRAY
    glBindAttribLocation(m_uiProgramObject, COLOR_ARRAY, "myColor");
    // Link the program
    glLinkProgram(m_uiProgramObject);
    // Check if linking succeeded in the same way we checked for compilation success
    GLint bLinked;
    glGetProgramiv(m_uiProgramObject, GL_LINK_STATUS, &bLinked);
    if (!bLinked)
    {
        int i32InfoLogLength, i32CharsWritten;
        glGetProgramiv(m_uiProgramObject, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
        char* pszInfoLog = new char[i32InfoLogLength];
        glGetProgramInfoLog(m_uiProgramObject, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
        char* pszMsg = new char[i32InfoLogLength+256];
        sprintf(pszMsg, "Failed to link program: %s", pszInfoLog);
        PVRShellSet(prefExitMessage, pszMsg);
        delete [] pszMsg;
        delete [] pszInfoLog;
        return false;
    }

    if(PVRTTextureLoadFromPVR(c_sz_BK_TextureFile, &m_uiBKTex) != PVR_SUCCESS)
    {
        PVRShellSet(prefExitMessage, "ERROR: Cannot load the texturen");
        return false;
    }
    //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
    //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
    if(PVRTTextureLoadFromPVR(c_sz_FG_TextureFile, &m_uiFGTex) != PVR_SUCCESS)
    {
        PVRShellSet(prefExitMessage, "ERROR: Cannot load the texturen");
        return false;
    }
    //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
    //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
    //glUniform1i(glGetUniformLocation(m_uiProgramObject, "sampler2d"), 0);

    //glGenRenderbuffers(1, &depthRenderbuffer);
    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 OGLESBasicTnL::ReleaseView()
{
    // Frees the texture
    glDeleteTextures(1, &m_uiBKTex);
    glDeleteTextures(1, &m_uiFGTex);
    // Frees the OpenGL handles for the program and the 2 shaders
    glDeleteProgram(m_uiProgramObject);
    glDeleteShader(m_uiVertexShader);
    glDeleteShader(m_uiFragShader);

    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 OGLESBasicTnL::RenderScene()
{

    if(PVRShellIsKeyPressed(PVRShellKeyNameACTION1))
        m_bDepthTest = !m_bDepthTest;
    if(PVRShellIsKeyPressed(PVRShellKeyNameACTION2))
        m_bRenderOrder = !m_bRenderOrder;

    glViewport(0, 0, 800, 600);
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_FRONT);
    if(m_bDepthTest)
    {
        glEnable(GL_DEPTH_TEST);
    }
    else
    {
        glDisable(GL_DEPTH_TEST);
    }

    // Clears the color and depth buffer
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glEnable(GL_TEXTURE_2D);

    //*************************************************************************
    //Draw the background
    //*************************************************************************
    float alpha = 0;  //-p
    float beta =  0;  //-q
    float theta = 0;           // o

    float aModelViewIT0[] =
    {
        cos(theta)*cos(alpha), cos(alpha)*sin(beta)*sin(theta)-sin(alpha)*cos(beta), cos(alpha)*cos(beta)*sin(theta)+sin(alpha)*sin(beta),
        cos(theta)*sin(alpha), sin(alpha)*sin(beta)*sin(theta)+cos(alpha)*cos(beta), sin(alpha)*cos(beta)*sin(theta)-sin(beta)*cos(alpha),
        -sin(theta),   sin(beta)*cos(theta),         cos(beta)*cos(theta)
    };

    /*
    Bind the projection model view matrix (PMVMatrix) to the
    corresponding uniform variable in the shader.
    This matrix is used in the vertex shader to transform the vertices.
    */
    float aPMVMatrix0[] =
    {
        cos(theta)*cos(alpha), cos(alpha)*sin(beta)*sin(theta)-sin(alpha)*cos(beta), cos(alpha)*cos(beta)*sin(theta)+sin(alpha)*sin(beta), 0,
        cos(theta)*sin(alpha), sin(alpha)*sin(beta)*sin(theta)+cos(alpha)*cos(beta), sin(alpha)*cos(beta)*sin(theta)-sin(beta)*cos(alpha),0,
        -sin(theta),   sin(beta)*cos(theta),         cos(beta)*cos(theta),0,
        0,0,0,1
    };

    // same shader for both quads so only need to make these calls once
    glUseProgram(m_uiProgramObject);   

    int i32Location = glGetUniformLocation(m_uiProgramObject, "myPMVMatrix");
    glUniformMatrix4fv( i32Location, 1, GL_FALSE, aPMVMatrix0);
    i32Location = glGetUniformLocation(m_uiProgramObject, "myModelViewIT");
    glUniformMatrix3fv( i32Location, 1, GL_FALSE, aModelViewIT0);
    float aTMatrix0[] = {0.0f,0.0f,0.0f,0.0f};
    i32Location = glGetUniformLocation(m_uiProgramObject, "myTMatrix");
    glUniform4fv( i32Location,1,aTMatrix0);
    float aScale0 = 1.0;
    i32Location = glGetUniformLocation(m_uiProgramObject, "myScale");
    glUniform1f( i32Location,aScale0);
    float aYScale0 = 1.0;
    i32Location = glGetUniformLocation(m_uiProgramObject, "myYScale");
    glUniform1f( i32Location,aYScale0);
    // Bind the Light Direction vector to the shader
    i32Location = glGetUniformLocation(m_uiProgramObject, "myLightDirection");
    glUniform3f( i32Location, 0, 0, 1.5);


    // these are the same for both quads as well so only make the calls once
    glEnableVertexAttribArray(NORMAL_ARRAY);
    glVertexAttribPointer(NORMAL_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, BK_TRIANGLE_NORMAL);
    glEnableVertexAttribArray(TEXCOORD_ARRAY);
    glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, 0, BK_TRIANGLE_TEXTCOORD);
    glEnableVertexAttribArray(COLOR_ARRAY);
    glVertexAttribPointer(COLOR_ARRAY, 4, GL_FLOAT, GL_FALSE, 0, BK_TRIANGLE_COLOR);


    // draw

    if(m_bRenderOrder)
    {    // draw one way
        //*************************************************************************
        //Draw the background
        //*************************************************************************
        glEnableVertexAttribArray(VERTEX_ARRAY);
        glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, BK_TRIANGLE_VETEX);

        glBindTexture(GL_TEXTURE_2D, m_uiBKTex);
        glDrawArrays(GL_TRIANGLES, 0,6);


        //*************************************************************************
        //Draw the foreground
        //*************************************************************************

        // different vertex positions
        glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, FG_TRIANGLE_VETEX);

        // different texture
        glBindTexture(GL_TEXTURE_2D, m_uiFGTex);
        glDrawArrays(GL_TRIANGLES, 0,6);
    }
    else
    {    // draw in the other order
        //*************************************************************************
        //Draw the foreground
        //*************************************************************************

        // different vertex positions
        glEnableVertexAttribArray(VERTEX_ARRAY);
        glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, FG_TRIANGLE_VETEX);

        // different texture
        glBindTexture(GL_TEXTURE_2D, m_uiFGTex);
        glDrawArrays(GL_TRIANGLES, 0,6);

        //*************************************************************************
        //Draw the background
        //*************************************************************************
        glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, BK_TRIANGLE_VETEX);

        glBindTexture(GL_TEXTURE_2D, m_uiBKTex);
        glDrawArrays(GL_TRIANGLES, 0,6);

    }



    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 OGLESBasicTnL();
}
/******************************************************************************
End of file (OGLESBasicTnL.cpp)
******************************************************************************/


Gordon2010-11-18 19:04:44

Thank you so much, Gordon.  I do appreciate it that you trimmed my code and made it running.

 

I played with the program and I found out that after setting a new z locations for BK_TRIANGLE_VETEX and FG_TRIANGLE_VETEX, such as

 

GLfloat BK_TRIANGLE_VETEX[18] ={<?: prefix = o ns = "urn:schemas-microsoft-com:office:office" />


-1.0f, 1.0f, 1.0f,


1.0f, 1.0f, 1.0f,


1.0f, -1.0f, 1.0f,


-1.0f, 1.0f, 1.0f,


1.0f, -1.0f, 1.0f,


-1.0f, -1.0f, 1.0f


};

 

Your code will fail too and it generated a black background when depth_test is enabled. If I choose to set as following,

 

GLfloat BK_TRIANGLE_VETEX[18] ={


-1.0f, 1.0f, 0.995f,


1.0f, 1.0f, 0.995f,


1.0f, -1.0f, 0.995f,


-1.0f, 1.0f, 0.995f,


1.0f, -1.0f, 0.995f,


-1.0f, -1.0f, 0.995f


};

 


Your code runs well and background is visualized correctly when depth_test is enabled.

 

For the Foreground, your program works even I choose the following setting

GLfloat FG_TRIANGLE_VETEX[18] ={


    -1.0f, 0.5f, -1.0f,


    0.5f, 0.5f, -1.0f,


    0.5f, -1.0f,-1.0f,


    -1.0f, 0.5f, -1.0f,


    0.5f, -1.0f, -1.0f,


    -1.0f, -1.0f, -1.0f


};

 


I run my original code with the above tests, I got the same visualization results as yours. So, I think the real problem is I should have not use the 1.0 setting for BK.

 

So from the above test, can I say in the PVRShell, the range of z is actually

-1.0<= z < 1.0

Is this correct?

 

Thank you very much.

 

Renaissance.

 

 

Another related question is that if we define the location close to the view point with small value and the location far away with large value, how about the normal definition?

 

In the above code, the normal is defined as  0.0f,0.0f,1.0f, which is not towards the view point. But why we still can see the light reflect from this plane? Can you help me explain this? I have a feeling I do not have a correct understanding of the coordinate system in the OpenGL ES.

 

Thank u very much,

 

Renaissance

About z < 1.0 or z <= 1.0 (actually I think it should be: z < w or z <= w): as a rule of thumb you should never rely on the outcome of an equality comparison of floating-point numbers. The reason is that usually you cannot be sure which values can be represented accurately on your machine. Consider the value 1.1: it uses 2 digits in the decimal system but requires infinitely many digits in the binary system and is therefore usually not represented with infinite precision on a computer.

About the coordinate system: the standard modelview and projection matrices in OpenGL assume a right-handed coordinate system with the camera looking in the negative(!) z direction. (Thus, (0,0,1) is towards the camera for an object in front of the camera.)

Martin Kraus2010-11-19 10:46:23

Thank you, Martin.

 

As you said, the camera (view point) is looking in the negative direction. So, the near plane should have a larger z value and the far plane should have a smaller z value. This is just opposite of what we used in the above code, where FG (near plane) is at -1.0 and BK (far plane) is at 0.995. 

renaissance,

sorry, my comment about the standard viewing direction doesn’t apply to your code since you are using a non-standard modelview and projection matrix. What happens with the gl_Position computed by the vertex shader (which I will call (x,y,z,w) ) is:

1) The hardware clips the primitives such that -w < x < w, -w < y < w, -w < z < w.
2) Then all coordinates are divided by w.
3) Then z/w is transformed according to the parameters given to glDepthRangef. By default, the depth of a fragment is computed as 0.5 * z/w + 0.5
4) The default depth test is GL_LESS, i.e. a fragment passes the depth test, if its depth is less than the depth stored in the depth buffer.

Thus, with an identity projection matrix and w=1, it should actually be the fragments with the smallest z coordinate that survive the depth test (which is not what you would get if you used the “standard” projection and modelview matrices that would emulate fixed-function OpenGL behaviour).

With vertex shaders you have to set up appropriate transformation matrices yourself; thus, basically you should either use some standard matrices (which you find in many books and courses about OpenGL) or you really have to know what you are doing.

Hi Martin, Thank you for your response.

 

If the standard modelview and projection matrix you talked about should be defined as following, actually they are exactly the same as I used in my code (just need a little bit calculation). Maybe the shader does look complicated, but actually, based on the input parameters, there should be no special effect on the visualization.

 

 float aModelViewIT[] =
 {1,0,0,

   0,1,0,

   0,0,1}

 float aPMVMatrix[] =
 { 1,0,0,0,

    0,1,0,0,


    0,0,1,0,


    0,0,0,1

}

 

 


    float alpha = 0;  
    float beta =  0;  
    float theta = 0;  
    float aModelViewIT0[] =
    {
        cos(theta)*cos(alpha), cos(alpha)*sin(beta)*sin(theta)-sin(alpha)*cos(beta), cos(alpha)*cos(beta)*sin(theta)+sin(alpha)*sin(beta),
        cos(theta)*sin(alpha), sin(alpha)*sin(beta)*sin(theta)+cos(alpha)*cos(beta), sin(alpha)*cos(beta)*sin(theta)-sin(beta)*cos(alpha),
        -sin(theta),   sin(beta)*cos(theta),         cos(beta)*cos(theta)
    };



    float aPMVMatrix0[] =
    {
        cos(theta)*cos(alpha), cos(alpha)*sin(beta)*sin(theta)-sin(alpha)*cos(beta), cos(alpha)*cos(beta)*sin(theta)+sin(alpha)*sin(beta), 0,
        cos(theta)*sin(alpha), sin(alpha)*sin(beta)*sin(theta)+cos(alpha)*cos(beta), sin(alpha)*cos(beta)*sin(theta)-sin(beta)*cos(alpha),0,
        -sin(theta),   sin(beta)*cos(theta),         cos(beta)*cos(theta),0,
        0,0,0,1
    };<?: prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 


Sorry, I wasn't clear enough: when I refer to "standard" projection and modelview matrices I'm refering to the matrices that are typically used in the fixed-function pipeline. See for example Appendix G of the "OpenGL Programming Guide": http://www.opengl.org/documentation/red_book/ for the "standard" projection matrices. The PowerVR SDK comes also with some utility function to compute these matrices. And here is some code I recently wrote to compute the "standard" matrices (and other vector/matrix operations):

#include <assert.h>
// data types

typedef struct
{
GLfloat m[4][4];
} matrix4x4;

// functions for vectors

///
// Returns the length of a 3D vector.
//
GLfloat length3f(GLfloat x, GLfloat y, GLfloat z)
{
return (GLfloat)sqrtf( x * x + y * y + z * z );
}

///
// Normalizes a 3D vector.
//
void normalize3f(GLfloat *result, GLfloat x, GLfloat y, GLfloat z)
{
GLfloat length;

assert( NULL != result );

length = length3f( x, y, z );
if ((GLfloat)0.0f == length)
{
// we panic, but at least we can initialize the result to a unit vector
result[0] = (GLfloat)1.0f;
result[1] = (GLfloat)0.0f;
result[2] = (GLfloat)0.0f;
}
else
{
result[0] = x / length;
result[1] = y / length;
result[2] = z / length;
}
return;
}

///
// Returns the dot product of two 2D vectors.
//
GLfloat dot3f(GLfloat ax, GLfloat ay, GLfloat az, GLfloat bx, GLfloat by, GLfloat bz)
{
return ax * bx + ay * by + az * bz;
}

///
// Computes the cross product of two 3D vectors
//
void cross3f(GLfloat *result, GLfloat ax, GLfloat ay, GLfloat az, GLfloat bx, GLfloat by, GLfloat bz)
{
assert( NULL != result );

result[0] = ay * bz - az * by;
result[1] = az * bx - ax * bz;
result[2] = ax * by - ay * bx;
return;
}

// functions for 4x4 matrices

///
// Sets a 4x4 matrix. (The indices of the arguments are in traditional row-major order from 1 to 4.)
//
void matrixSet(matrix4x4 *result,
GLfloat m11, GLfloat m12, GLfloat m13, GLfloat m14,
GLfloat m21, GLfloat m22, GLfloat m23, GLfloat m24,
GLfloat m31, GLfloat m32, GLfloat m33, GLfloat m34,
GLfloat m41, GLfloat m42, GLfloat m43, GLfloat m44)
{
assert( NULL != result );

result->m[0][0] = m11;
result->m[0][1] = m21;
result->m[0][2] = m31;
result->m[0][3] = m41;

result->m[1][0] = m12;
result->m[1][1] = m22;
result->m[1][2] = m32;
result->m[1][3] = m42;

result->m[2][0] = m13;
result->m[2][1] = m23;
result->m[2][2] = m33;
result->m[2][3] = m43;

result->m[3][0] = m14;
result->m[3][1] = m24;
result->m[3][2] = m34;
result->m[3][3] = m44;

return;
}

///
// Multiplies a 4x4 matrix with a 4D vector
//
void matrixMultiply4f(GLfloat *result, matrix4x4 *matrix, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
{
assert( NULL != result && NULL != matrix);

result[0] = matrix->m[0][0] * x + matrix->m[1][0] * y +
matrix->m[2][0] * z + matrix->m[3][0] * w;
result[1] = matrix->m[0][1] * x + matrix->m[1][1] * y +
matrix->m[2][1] * z + matrix->m[3][1] * w;
result[2] = matrix->m[0][2] * x + matrix->m[1][2] * y +
matrix->m[2][2] * z + matrix->m[3][2] * w;
result[3] = matrix->m[0][3] * x + matrix->m[1][3] * y +
matrix->m[2][3] * z + matrix->m[3][3] * w;
return;
}

///
// Multiplies two 4x4 matrices.
//
void matrixMultiply(matrix4x4 *result, matrix4x4 *a, matrix4x4 *b)
{
int i, j;
matrix4x4 tmp;

assert( NULL != result && NULL != a && NULL != b );

for (i = 0; i < 4; i++)
{
for (j = 0; j < 4; j++)
{
tmp.m[j] =
a->m[0] * b->m[j][0] +
a->m[1] * b->m[j][1] +
a->m[2] * b->m[j][2] +
a->m[3] * b->m[j][3];
}
}
*result = tmp;
return;
}

///
// Computes the determinant of the upper, left 3x3 submatrix
//
GLfloat matrixDeterminant(matrix4x4 *matrix)
{
assert( NULL != matrix );

return matrix->m[0][0] * matrix->m[1][1] * matrix->m[2][2] +
matrix->m[1][0] * matrix->m[2][1] * matrix->m[0][2] +
matrix->m[2][0] * matrix->m[0][1] * matrix->m[1][2] -
matrix->m[2][0] * matrix->m[1][1] * matrix->m[0][2] -
matrix->m[0][0] * matrix->m[2][1] * matrix->m[1][2] -
matrix->m[1][0] * matrix->m[0][1] * matrix->m[2][2];
}

///
// Computes the transpose of the upper left 3x3 matrix and adjusts the last column
// such that a rigid-body transformation is inverted.
//
void matrixTranspose(matrix4x4 *result, matrix4x4 *matrix)
{
matrix4x4 tmp;

assert( NULL != result && NULL != matrix );

tmp.m[0][0] = matrix->m[0][0];
tmp.m[0][1] = matrix->m[1][0];
tmp.m[0][2] = matrix->m[2][0];
tmp.m[0][3] = matrix->m[0][3];

tmp.m[1][0] = matrix->m[0][1];
tmp.m[1][1] = matrix->m[1][1];
tmp.m[1][2] = matrix->m[2][1];
tmp.m[1][3] = matrix->m[1][3];

tmp.m[2][0] = matrix->m[0][2];
tmp.m[2][1] = matrix->m[1][2];
tmp.m[2][2] = matrix->m[2][2];
tmp.m[2][3] = matrix->m[2][3];

tmp.m[3][0] = -matrix->m[0][0] * matrix->m[3][0] -
matrix->m[0][1] * matrix->m[3][1] - matrix->m[0][2] * matrix->m[3][2];
tmp.m[3][1] = -matrix->m[1][0] * matrix->m[3][0] -
matrix->m[1][1] * matrix->m[3][1] - matrix->m[1][2] * matrix->m[3][2];
tmp.m[3][2] = -matrix->m[2][0] * matrix->m[3][0] -
matrix->m[2][1] * matrix->m[3][1] - matrix->m[2][2] * matrix->m[3][2];
tmp.m[3][3] = matrix->m[3][3];

*result = tmp;
return;
}

///
// Computes the inverse of a 4x4 matrix if the bottom row is (0,0,0,1)
// and the determinant of the upper left 3x3 matrix is not 0.
//
void matrixInverse(matrix4x4 *result, matrix4x4 *matrix)
{
GLfloat determinant;
GLfloat determinant_inv;
matrix4x4 tmp;

assert( NULL != result && NULL != matrix &&
matrix->m[0][3] == 0.0f && matrix->m[1][3] == 0.0f &&
matrix->m[2][3] == 0.0f && matrix->m[3][3] == 1.0f );

determinant = matrixDeterminant( matrix );
if (0.0f == determinant)
{
// matrix is not invertible: we panic, but at least we can initialize the result to the transpose
matrixTranspose(result, matrix);
return;
}
determinant_inv = (GLfloat)( 1.0f / determinant );

// compute inverse of upper left 3x3 matrix
tmp.m[0][0] = determinant_inv * (matrix->m[1][1] * matrix->m[2][2] - matrix->m[2][1] * matrix->m[1][2]);
tmp.m[0][1] = determinant_inv * (matrix->m[2][1] * matrix->m[0][2] - matrix->m[0][1] * matrix->m[2][2]);
tmp.m[0][2] = determinant_inv * (matrix->m[0][1] * matrix->m[1][2] - matrix->m[1][1] * matrix->m[0][2]);
tmp.m[0][3] = (GLfloat)0.0f;

tmp.m[1][0] = determinant_inv * (matrix->m[2][0] * matrix->m[1][2] - matrix->m[1][0] * matrix->m[2][2]);
tmp.m[1][1] = determinant_inv * (matrix->m[0][0] * matrix->m[2][2] - matrix->m[2][0] * matrix->m[0][2]);
tmp.m[1][2] = determinant_inv * (matrix->m[1][0] * matrix->m[0][2] - matrix->m[0][0] * matrix->m[1][2]);
tmp.m[1][3] = (GLfloat)0.0f;

tmp.m[2][0] = determinant_inv * (matrix->m[1][0] * matrix->m[2][1] - matrix->m[2][0] * matrix->m[1][1]);
tmp.m[2][1] = determinant_inv * (matrix->m[2][0] * matrix->m[0][1] - matrix->m[0][0] * matrix->m[2][1]);
tmp.m[2][2] = determinant_inv * (matrix->m[0][0] * matrix->m[1][1] - matrix->m[1][0] * matrix->m[0][1]);
tmp.m[2][3] = (GLfloat)0.0f;

// compute new translation vector as minus inverse of upper left 3x3 matrix times old translation vector
tmp.m[3][0] = -tmp.m[0][0] * matrix->m[3][0] -
tmp.m[1][0] * matrix->m[3][1] - tmp.m[2][0] * matrix->m[3][2];
tmp.m[3][1] = -tmp.m[0][1] * matrix->m[3][0] -
tmp.m[1][1] * matrix->m[3][1] - tmp.m[2][1] * matrix->m[3][2];
tmp.m[3][2] = -tmp.m[0][2] * matrix->m[3][0] -
tmp.m[1][2] * matrix->m[3][1] - tmp.m[2][2] * matrix->m[3][2];
tmp.m[3][3] = (GLfloat)1.0f;

*result = tmp;
return;
}

///
// Computes an identity transformation.
//
void matrixIdentity(matrix4x4 *result)
{
matrixSet(result,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
return;
}

///
// Computes a scaling.
//
void matrixScaling(matrix4x4 *result, GLfloat sx, GLfloat sy, GLfloat sz)
{
matrixSet(result,
sx, 0.0f, 0.0f, 0.0f,
0.0f, sy, 0.0f, 0.0f,
0.0f, 0.0f, sz, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
return;
}

///
// Computes a translation.
//
void matrixTranslation(matrix4x4 *result, GLfloat tx, GLfloat ty, GLfloat tz)
{
matrixSet(result,
1.0f, 0.0f, 0.0f, tx,
0.0f, 1.0f, 0.0f, ty,
0.0f, 0.0f, 1.0f, tz,
0.0f, 0.0f, 0.0f, 1.0f
);
return;
}

///
// Computes a rotation about the axis (x,y,z) by angle (in degrees).
//
void matrixRotation(matrix4x4 *result, GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
{
GLfloat sinAngle, cosAngle;
GLfloat mag = sqrtf(x * x + y * y + z * z);
GLfloat xx, yy, zz, xy, yz, zx, xs, ys, zs;
GLfloat oneMinusCos;

assert( NULL != result );

if (mag <= 0.0f)
{
// no rotation axis: we panic, but we initialize the result with the identity
matrixIdentity(result);
return;
}

x /= mag;
y /= mag;
z /= mag;

sinAngle = sinf ( angle * M_PI / 180.0f );
cosAngle = cosf ( angle * M_PI / 180.0f );

xx = x * x;
yy = y * y;
zz = z * z;
xy = x * y;
yz = y * z;
zx = z * x;
xs = x * sinAngle;
ys = y * sinAngle;
zs = z * sinAngle;
oneMinusCos = 1.0f - cosAngle;

result->m[0][0] = (oneMinusCos * xx) + cosAngle;
result->m[0][1] = (oneMinusCos * xy) - zs;
result->m[0][2] = (oneMinusCos * zx) + ys;
result->m[0][3] = 0.0f;

result->m[1][0] = (oneMinusCos * xy) + zs;
result->m[1][1] = (oneMinusCos * yy) + cosAngle;
result->m[1][2] = (oneMinusCos * yz) - xs;
result->m[1][3] = 0.0f;

result->m[2][0] = (oneMinusCos * zx) - ys;
result->m[2][1] = (oneMinusCos * yz) + xs;
result->m[2][2] = (oneMinusCos * zz) + cosAngle;
result->m[2][3] = 0.0f;

result->m[3][0] = 0.0f;
result->m[3][1] = 0.0f;
result->m[3][2] = 0.0f;
result->m[3][3] = 1.0f;

return;
}

///
// Computes a viewing transformation.
//
void matrixLookAt(matrix4x4 *result, GLfloat eye_x, GLfloat eye_y, GLfloat eye_z,
GLfloat lookat_x, GLfloat lookat_y, GLfloat lookat_z,
GLfloat worldup_x, GLfloat worldup_y, GLfloat worldup_z)
{
GLfloat dir[3];
GLfloat side[3];
GLfloat up[3];
matrix4x4 view_to_model;

assert( NULL != result );

// compute normalized view direction (will be negative z axis)
normalize3f(dir, lookat_x - eye_x, lookat_y - eye_y, lookat_z - eye_z);

// compute normalized side vector (will be positive x axis)
cross3f(side, dir[0], dir[1], dir[2], worldup_x, worldup_y, worldup_z);
normalize3f(side, side[0], side[1], side[2]);

// compute (normalized) up vector (will be positive y axis)
cross3f(up, side[0], side[1], side[2], dir[0], dir[1], dir[2]);

// set view to model transformation
matrixSet(&view_to_model,
side[0], up[0], -dir[0], eye_x,
side[1], up[1], -dir[1], eye_y,
side[2], up[2], -dir[2], eye_z,
0.0f, 0.0f, 0.0f, 1.0f
);

// invert matrix (by transposing)
matrixTranspose( result, &view_to_model );
return;
}

///
// Computes an oblique perspective projection matrix.
//
void matrixFrustum(matrix4x4 *result, GLfloat left, GLfloat right,
GLfloat bottom, GLfloat top, GLfloat nearZ, GLfloat farZ)
{
GLfloat deltaX = right - left;
GLfloat deltaY = top - bottom;
GLfloat deltaZ = farZ - nearZ;

assert( NULL != result && (nearZ > 0.0f) && (farZ > 0.0f) &&
(deltaX > 0.0f) && (deltaY > 0.0f) && (deltaZ > 0.0f) );

matrixSet(result,
2.0f * nearZ / deltaX, 0.0f, (right + left) / deltaX, 0.0f,
0.0f, 2.0f * nearZ / deltaY, (top + bottom) / deltaY, 0.0f,
0.0f, 0.0f, -(nearZ + farZ) / deltaZ, -2.0f * nearZ * farZ / deltaZ,
0.0f, 0.0f, -1.0f, 0.0f
);
return;
}

///
// Computes a perspective projection matrix.
//
void matrixPerspective(matrix4x4 *result, GLfloat fovy, GLfloat aspect, GLfloat nearZ, GLfloat farZ)
{
GLfloat frustumW, frustumH;

frustumH = tanf( fovy / 360.0f * M_PI ) * nearZ;
frustumW = frustumH * aspect;

matrixFrustum( result, -frustumW, frustumW, -frustumH, frustumH, nearZ, farZ );
}

///
// Computes an orthographic projection matrix.
//
void matrixOrtho(matrix4x4 *result, GLfloat left, GLfloat right,
GLfloat bottom, GLfloat top, GLfloat nearZ, GLfloat farZ)
{
GLfloat deltaX = right - left;
GLfloat deltaY = top - bottom;
GLfloat deltaZ = farZ - nearZ;

assert( NULL != result && (deltaX != 0.0f) && (deltaY != 0.0f) && (deltaZ != 0.0f) );

matrixSet(result,
2.0f / deltaX, 0.0f, 0.0f, -(right + left) / deltaX,
0.0f, 2.0f / deltaY, 0.0f, -(top + bottom) / deltaY,
0.0f, 0.0f, -2.0f / deltaZ, -(nearZ + farZ) / deltaZ,
0.0f, 0.0f, 0.0f, 1.0f
);
return;
}



Martin Kraus2010-11-19 23:32:08