Search code examples
c++opengl2d-games

Layering quads on top of each other in OpenGL


I'm creating a 2D tile based tactical rpg using OpenGL using C++ and I'm having difficulties with layering my tiles/quads. I want to to able to put say a tree textured quad, the image is of the tree with a surrounding transparent alpha layer, on top of an opaque grass textured quad. I need to have the tree appear on top of the grass with the grass showing through the alpha layer of the tree image.

So far I've been messing with glDisable(GL_DEPTH_TEST), glEnable(GL_BLEND), andglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) but with no luck so far. I end up with a tree on a black background, instead of the grass tile. Could someone point me in the right direction?

Below is my render function and initialize function which are probably most relevant.

void View::initialize() {
  updateProjection(window);
  glDisable(GL_DEPTH_TEST);
  glClearColor(0.0, 0.0, 0.0, 1.0);
  camera = new Camera(Vector3(-1, -3, 25), Vector3(-1, -3, 0), Vector3(0, 1, 0));
  loadImages();
  initShaders();

  //needed for transparency?
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

void View::render(World* worldTemp)
{
  Matrix4 mvpMatrix, viewMatrix, modelMatrix;
  Matrix4 XAxisRotationMatrix, YAxisRotationMatrix, ZAxisRotationMatrix;

  input->handleInput(camera, worldTemp->getPlayer());

  worldTemp->timerTick();
  worldTemp->clearFog();

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);            // Clear Screen and Depth Buffer

  XAxisRotationMatrix = Matrix4::IDENTITY;
  YAxisRotationMatrix = Matrix4::IDENTITY;
  ZAxisRotationMatrix = Matrix4::IDENTITY;
  XAxisRotationMatrix.rotate(Vector3(1.0, 0.0, 0.0), XAxisRotationAngle);
  YAxisRotationMatrix.rotate(Vector3(0.0, 1.0, 0.0), YAxisRotationAngle);
  ZAxisRotationMatrix.rotate(Vector3(0.0, 0.0, 1.0), ZAxisRotationAngle);

  viewMatrix = camera->getViewMatrix();

  modelMatrix = translationMatrix(Vector3(-4, 2, 0));
  mvpMatrix = projectionMatrix * viewMatrix * modelMatrix;

  //Spit out the map
  for (int i = 0; i < 100; i++){
      for (int j = 0; j < 100; j++){
          for (int t = 0; t < 5; t++){
              if (worldTemp->getTile(i, j)->isOccupied() == true) {
                  if (worldTemp->getTile(i, j)->getOccupyingEntityIndexed(t)->getFog()){
                    worldTemp->getTile(i, j)->getOccupyingEntityIndexed(t)->getEntityQuad()->render_self(mvpMatrix, true);
                  }
                  else{
                    worldTemp->getTile(i, j)->getOccupyingEntityIndexed(t)->getEntityQuad()->render_self(mvpMatrix);
                  }
              }
          }
      }
  }

  //Place the player
  worldTemp->getPlayer()->getEntityQuad()->render_self(mvpMatrix);
  renderEnemies();

  glutSwapBuffers(); //works with GL_DOUBLE. use glFlush(); instead, if using GL_SINGLE
}

Solution

  • Basically, in 2d games layering done via ordering of render calls. If you want layer A on top of layer B, you should render layer B first and then layer A.

    blend function that you're using should depend on texture format of images. There are two common formats for alpha:

    1. Pre-multiplied alpha

    2. Straight alpha

      More info about this: https://developer.nvidia.com/content/alpha-blending-pre-or-not-pre

    opengl es2 premultiplied vs straight alpha + blending

    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); is for premultiplied alpha, and it should work well if you use colors as is. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); is for straight alpha.