With this installment we will finally make our object look like it is made of glass. Luckily we have already been introduced to shaders in a previous post so all that remains to be done is to describe how we can incorporate our new environment/cube map into our shader and use it to fake reflection and refraction.

The first thing we are going to need for our calculations is the normal and position associated for each pixel. These can be calculated in the Vertex shader, as can the usual set of calculations needed for basic operations. As such our Vertex shader is quite simple as shown in Figure 1.

varying vec3 normal;
varying vec3 view;
void main()
{
// the basics that have to be done
gl_FrontColor = gl_Color;
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = ftransform();
// calculate the view vector in eye-space using the ModelView and Projection matrix
view = (gl_ModelViewProjectionMatrix * gl_Vertex).xyz;
// calculate the normal vector in eye-space using the ModelView and Projection matrix
normal = normalize(gl_NormalMatrix * gl_Normal);
}
// Figure 1

Using the same code as in the previous post the cube map texture should automatically be available to us in the GLSL shader as a texture. The only difference is that the type of the texture is *samplerCube* instead of *sampler2D*. Thus we begin our fragment shader like this:

// our cube map texture
uniform samplerCube tex;
//variables passed in from our vertex shader
varying vec3 view;
varying vec3 normal;
void main()
{

When we want to access our cube map we do so through the use of a vector which expresses the direction we wish to sample the ‘cube’ that surrounds our object. For example, by calculating the reflection vector of the view around the normal and sampling this we can see what would be ‘reflected’ if our object was a perfect mirror.

// create the reflection vector
vec3 reflect_v = normalize(reflect(view,-normal));
// sample the cube map to find the reflected color
vec3 colReflect = textureCube(tex,reflect_v).rgb;

GLSL provides a very useful function which we use here for performing the reflection of *‘view’* around *‘-normal’* so we don’t even have to think about it.

We can do exactly the same thing to find the colour that would come from refracting through the surface. This refraction would of course depend on the refractive indices of the two materials. Because we are trying to model glass this means that we are going from a refractive index of 1.0 (air) to 1.2 (glass).

const float eta = 1.00 / 1.20;
// create the reflection vector
vec3 refract_v = normalize(refract(view,-normal,eta));
// sample the cube map to find the reflected color
vec3 colReflect = textureCube(tex,refract_v).rgb;

There is one major limitation to this of course. A real simulation would model the effect of coming out the other side of the glass, and perhaps even entering another material if there are other objects in the scene / concave objects. This is pretty serious but for our purposes it works ok because we limit ourselves to only one object and it kind of looks ‘good enough’.

Finally we composite the reflected and refracted components together, along with a bit of specular lighting from the phong model discussed previously.

// calculate the specular lighting
float light = pow(max(dot(normal, gl_LightSource[0].halfVector.xyz),0.0),1000.0);
// coefficients for each of the calculated components (reflection, refraction, lighting). These DON'T have to add to 1.0
const vec3 coeff = vec3( 0.3, 0.6, 0.5 );
gl_FragColor = vec4(coeff.x * colReflect + coeff.y * colRefract + coeff.z * vec3(light,light,light) + gl_Color.rgb,1.0);
}

This gives us a final result that looks something like this:

Glass