// Sh: A GPU metaprogramming language.
//
// Copyright (c) 2003 University of Waterloo Computer Graphics Laboratory
// Project administrator: Michael D. McCool
// Authors: Zheng Qin, Stefanus Du Toit, Kevin Moule, Tiberiu S. Popa,
//          Michael D. McCool
// 
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// 
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 
// 1. The origin of this software must not be misrepresented; you must
// not claim that you wrote the original software. If you use this
// software in a product, an acknowledgment in the product documentation
// would be appreciated but is not required.
// 
// 2. Altered source versions must be plainly marked as such, and must
// not be misrepresented as being the original software.
// 
// 3. This notice may not be removed or altered from any source
// distribution.
//////////////////////////////////////////////////////////////////////////////
#include 
#include 
#include 
#include "Shader.hpp"
#include "Globals.hpp"
using namespace SH;
using namespace ShUtil;
#include "util.hpp"
class GlassShader : public Shader {
public:
  ShProgram vsh, fsh;
  GlassShader();
  ~GlassShader();
  bool init();
  ShProgram vertex() { return vsh;}
  ShProgram fragment() { return fsh;}
};
GlassShader::GlassShader()
  : Shader("Refraction: Glass")
{
}
GlassShader::~GlassShader()
{
}
bool GlassShader::init()
{
  std::cerr << "Initializing " << name() << std::endl;
  std::string imageNames[6] = {"left", "right", "top", "bottom", "back", "front"};
  ShImage test_image;
  test_image.loadPng(std::string(SHMEDIA_DIR "/envmaps/aniroom/") + imageNames[0] + ".png");
  ShTextureCube cubemap(test_image.width(), test_image.height());
  {
    for (int i = 0; i < 6; i++) {
      ShImage image;
      image.loadPng(std::string(SHMEDIA_DIR "/envmaps/aniroom/") + imageNames[i] + ".png");
      cubemap.memory(image.memory(), static_cast(i));
    }
  }
  ShAttrib1f theta = ShAttrib1f(1.3f);
  theta.name("relative indices of refraction");
  theta.range(0.0f,2.0f);
  vsh = SH_BEGIN_PROGRAM("gpu:vertex") {
    ShInputPosition4f ipos;
    ShInputNormal3f inorm;
    ShOutputPosition4f opos; // Position in NDC
    ShOutputNormal3f onorm;  // view-space normal
    ShOutputVector3f reflv; // Compute reflection vector
    ShOutputVector3f refrv; // Compute refraction vector
    ShOutputAttrib1f fres; // Compute fresnel term
    opos = Globals::mvp | ipos; // Compute NDC position
    onorm = Globals::mv | inorm; // Compute view-space normal
    onorm = normalize(onorm);
    ShPoint3f posv = (Globals::mv | ipos)(0,1,2); // Compute view-space position
    ShVector3f viewv = -normalize(posv); // Compute view vector
    reflv = reflect(viewv,onorm); // Compute reflection vector
    refrv = refract(viewv,onorm,theta); // Compute refraction vector
    fres = fresnel(viewv,onorm,theta); // Compute fresnel term
    // actually do reflection and refraction lookup in model space
    reflv = Globals::mv_inverse | reflv;
    refrv = Globals::mv_inverse | refrv;
  } SH_END;
  fsh = SH_BEGIN_PROGRAM("gpu:fragment") {
    ShInputPosition4f posh;
    ShInputNormal3f n;  // normal
    ShInputVector3f reflv; // Compute reflection vector
    ShInputVector3f refrv; // Compute refraction vector
    ShInputAttrib1f fres; // Compute fresnel term
    ShOutputColor3f result;
    result = fres*cubemap(reflv)(0,1,2) + (1.0f-fres)*cubemap(refrv)(0,1,2); 
  } SH_END;
  return true;
}
GlassShader the_glass_shader;

About This Glass Shader

This page contains the source for the GlassShader class, a Sh shader that renders a refractive glass material. It loads a six-face cube map environment (the aniroom environment map) and uses it for both reflection and refraction lookups, blending the two results with a Fresnel term.

Vertex Program

The vertex program transforms the input position and normal into view space, computes the view vector, and derives the reflection vector, refraction vector, and Fresnel term. The reflection and refraction vectors are then transformed back into model space so they can be used to sample the cube map.

Fragment Program

The fragment program samples the cube map along the interpolated reflection and refraction vectors and combines the two colors using the interpolated Fresnel term, producing the final glass color.

Shader Parameters

The shader exposes a single named attribute, theta, described as the relative indices of refraction, with a range of 0.0 to 2.0.

Frequently Asked Questions

What does the GlassShader do?

It renders a refractive glass material by combining a reflection lookup and a refraction lookup into a cube map environment, weighted by a Fresnel term.

What environment map does the shader use?

It loads six PNG images named left, right, top, bottom, back, and front from the envmaps/aniroom/ directory under SHMEDIA_DIR and assembles them into a cube map.

What is the theta attribute for?

theta is a named attribute representing the relative indices of refraction. It is used when computing the refraction vector and the Fresnel term, and its range is set from 0.0 to 2.0.

How are reflection and refraction combined?

The fragment program computes fres * cubemap(reflv) plus (1.0 - fres) * cubemap(refrv), so the Fresnel term controls how much of the reflection versus the refraction contributes to the final color.

Why are the reflection and refraction vectors transformed by the inverse model-view matrix?

The vectors are computed in view space, then transformed back into model space so the cube map lookups are performed in model space, as noted in the source comments.