io7m | single-page | multi-page | archive (zip, signature)
4. Ripple2D GPU displacement mapping6. Lists
PreviousUpNext

Caustics
Examples
A simple extension of the Ripple program with multitexturing and blending, results in the Caustics program. The program works almost identically to Ripple, but takes an extra texture as input. One of the textures is modified through displacement mapping as before and then layered over the top with trivial alpha compositing. When fed the image of ceramic tiles, a transparent "caustics" texture, and a cloudlike displacement map, the program gives a believable approximation of the effect of sunlight shining through water onto the bottom of a swimming pool.
Program
Most of the program's source code is the same as before. The program loads all necessary textures and shaders, and creates a framebuffer. The only significant differences are the extra texture and the use of the multitexturing shader that simply blends and applies two incoming textures.
Caustics(
  final String underlay,
  final String overlay,
  final String displace)
  throws IOException
{
  this.texture_underlay = Utilities.loadTexture(underlay);
  this.texture_overlay = Utilities.loadTexture(overlay);
  this.texture_displacement_map = Utilities.loadTexture(displace);

  this.framebuffer_texture =
    Utilities.createEmptyTexture(
      Caustics.TEXTURE_WIDTH,
      Caustics.TEXTURE_HEIGHT);
  this.framebuffer = Utilities.createFramebuffer(this.framebuffer_texture);

  this.shader_uv =
    Utilities.createShader("dist/shader_uv.v", "dist/shader_multi_uv.f");
  this.shader_displace =
    Utilities.createShader("dist/shader_uv.v", "dist/shader_displace.f");
}
Rendering to a texture happens exactly as before:
private void renderToTexture()
{
  GL11.glMatrixMode(GL11.GL_PROJECTION);
  GL11.glLoadIdentity();
  GL11.glOrtho(0, 1, 0, 1, 1, 100);

  GL11.glMatrixMode(GL11.GL_MODELVIEW);
  GL11.glLoadIdentity();
  GL11.glTranslated(0, 0, -1);

  GL11.glViewport(0, 0, Caustics.TEXTURE_WIDTH, Caustics.TEXTURE_HEIGHT);
  GL11.glClearColor(0.25f, 0.25f, 0.25f, 1.0f);
  GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);

  GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, this.framebuffer);
  {
    GL13.glActiveTexture(GL13.GL_TEXTURE0);
    GL11.glBindTexture(GL11.GL_TEXTURE_2D, this.texture_overlay);
    GL13.glActiveTexture(GL13.GL_TEXTURE1);
    GL11.glBindTexture(GL11.GL_TEXTURE_2D, this.texture_displacement_map);

    GL20.glUseProgram(this.shader_displace);
    {
      final int ut =
        GL20.glGetUniformLocation(this.shader_displace, "texture");
      final int udm =
        GL20.glGetUniformLocation(this.shader_displace, "displace_map");
      final int umax =
        GL20.glGetUniformLocation(this.shader_displace, "maximum");
      final int utime =
        GL20.glGetUniformLocation(this.shader_displace, "time");

      GL20.glUniform1i(ut, 0);
      GL20.glUniform1i(udm, 1);
      GL20.glUniform1f(umax, 0.1f);
      GL20.glUniform1f(utime, this.time);
      Utilities.checkGL();

      GL11.glBegin(GL11.GL_QUADS);
      {
        GL11.glTexCoord2f(0, 1);
        GL11.glVertex3d(0, 1, 0);
        GL11.glTexCoord2f(0, 0);
        GL11.glVertex3d(0, 0, 0);
        GL11.glTexCoord2f(1, 0);
        GL11.glVertex3d(1, 0, 0);
        GL11.glTexCoord2f(1, 1);
        GL11.glVertex3d(1, 1, 0);
      }
      GL11.glEnd();
    }
    GL20.glUseProgram(0);

    GL13.glActiveTexture(GL13.GL_TEXTURE0);
    GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
    GL13.glActiveTexture(GL13.GL_TEXTURE1);
    GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
  }
  GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
  Utilities.checkGL();
}
Now, framebuffer_texture contains a transparent displaced texture. Rendering the scene now only differs in that the texturing shader applies two textures to polygons instead of just the one. The shader takes extra parameters controlling how the textures are to be scaled, and a parameter that controls the degree of alpha blending. In effect, the "tile" texture scaled by 50% and applied to the textured quad, and then the transparent "caustics" texture is applied over the top at 40% opacity.
private void renderScene()
{
  GL11.glMatrixMode(GL11.GL_PROJECTION);
  GL11.glLoadIdentity();
  GL11.glFrustum(-1, 1, -1, 1, 1, 100);

  GL11.glMatrixMode(GL11.GL_MODELVIEW);
  GL11.glLoadIdentity();

  GL11.glTranslated(0, 0, -1.25);
  GL11.glRotated(30, 0, 0, 1);

  GL11.glViewport(0, 0, Caustics.SCREEN_WIDTH, Caustics.SCREEN_HEIGHT);
  GL11.glClearColor(0.25f, 0.25f, 0.25f, 1.0f);
  GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);

  GL20.glUseProgram(this.shader_uv);
  {
    GL13.glActiveTexture(GL13.GL_TEXTURE0);
    GL11.glBindTexture(GL11.GL_TEXTURE_2D, this.texture_underlay);
    GL13.glActiveTexture(GL13.GL_TEXTURE1);
    GL11.glBindTexture(GL11.GL_TEXTURE_2D, this.framebuffer_texture);
    Utilities.checkGL();

    final int ut0 = GL20.glGetUniformLocation(this.shader_uv, "texture0");
    GL20.glUniform1i(ut0, 0);
    final int ut1 = GL20.glGetUniformLocation(this.shader_uv, "texture1");
    GL20.glUniform1i(ut1, 1);
    final int um = GL20.glGetUniformLocation(this.shader_uv, "mix");
    GL20.glUniform1f(um, 0.5f);
    final int us0 = GL20.glGetUniformLocation(this.shader_uv, "scale0");
    GL20.glUniform1f(us0, 2.0f);
    final int us1 = GL20.glGetUniformLocation(this.shader_uv, "scale1");
    GL20.glUniform1f(us1, 0.8f);
    Utilities.checkGL();

    GL11.glBegin(GL11.GL_QUADS);
    {
      GL11.glTexCoord2f(0, 0);
      GL11.glVertex3d(-0.75, 0.75, 0);
      GL11.glTexCoord2f(0, 1);
      GL11.glVertex3d(-0.75, -0.75, 0);
      GL11.glTexCoord2f(1, 1);
      GL11.glVertex3d(0.75, -0.75, 0);
      GL11.glTexCoord2f(1, 0);
      GL11.glVertex3d(0.75, 0.75, 0);
    }
    GL11.glEnd();
  }
  GL20.glUseProgram(0);
  Utilities.checkGL();
}

PreviousUpNext
4. Ripple2D GPU displacement mapping6. Lists