CS 381 Fall 2012  >  Lecture Notes for Tuesday, October 30, 2012

CS 381 Fall 2012
Lecture Notes for Tuesday, October 30, 2012

Texture Examples

Summary of Texture Code

To make use of textures in an OpenGL application using GLSL shaders:

See whatever_tex_shaders.zip for more examples of shaders that use textures. These shaders are intended for use with usetextures.cpp.

See minfilters.cpp for an application that generates rather unorthodox mipmaps and uses them to demonstrate the various min filters. The application uses the same shaders as usetextures.cpp.

Using the Texture Transformation

Recall that OpenGL stores a texture transformation. This is handled exactly like the model/view and projection transformations. The matrix mode is GL_TEXTURE.

We send the texture transformation to shaders via a texture channel.

[C++]

glActiveTexture(GL_TEXTURE0);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glRotated(texrotang, 0.,0.,1.);
glMatrixMode(GL_MODELVIEW);  // Always go back to model/view mode

Note that we are dealing with 2-D texture coordinates. Thus, if we put a rotation in the texture transformation, then it should probably be about the z-axis.

Then in a GLSL vertex shader, we get the texture coordinates in gl_MultiTexCoord0. The texture transformation matrix is in gl_TextureMatrix[CHANNEL], where CHANNEL is the number of the appropriate channel (zero?).

[GLSL vertex shader]

vec4 mytexcoord4 = gl_TextureMatrix[0] * gl_MultiTexCoord0;

Remember that, while we usually think in terms of painting the texture image on the surface, what texture coordinates actually tell us is where the vertex goes in the texture image.

Application usetextures.cpp has been modified to alter the texture transformation.

Textures & Lighting

We have used a texture to get the color for a surface. We can easily light a texture-mapped surface by getting the paint color from the texture look-up, and then doing lighting computations as before.

Remember that texture coordinates are attributes; they are only available to the vertex shader. Thus, in our vertex shader, we get the texture coordinates from the application, transform them with the texture matrix, and pass them to the fragment shader in a varying vec2. In the fragment shader, we do the usual lighting computations, but we get our paint color from a texture look-up, instead of gl_Color.

See light_tex_shaders.zip for examples of shaders that do per-fragment lighting of a texture-mapped surface. These shaders are intended for use with usetextures.cpp.

Using Multiple Textures

Once we have a texture-using application, it is not hard to deal with multiple textures. In the application, generate multiple names, and then do the process of creating and sending a texture more than once. Send each texture over a different channel. In the rendering code, send the different channel numbers to different shader sampler variables. In the shaders, have multiple sampler variables, and do a separate look-up for each. This results in multiple colors, which can be combined in any way you like.

Application usetextures.cpp has been modified to create two texture images.

See light_tex_shaders.zip for shaders that make use of two textures.

Billboarding

Introduction

How can we draw a sphere-shaped fuzzy blob? One way to do this is to draw a picture of such a blob, and always face it toward the camera.

Billboarding means to turn a polygon so that it faces the camera. There are two ways we might do this.

Cylindrical billboarding
Turn about a single (vertical?) axis, to face the camera as nearly as possible.
Spherical billboarding
Turn about an arbitrary axis, to face the camera exactly.

In addition, if we do spherical billboarding, we might be interested in rotating the polygon so that its top edge stays on top.

Billboarding has some surprising applications. In particular we can simulate objects that look the same from different directions by texture mapping a picture of the object on a polygon, and then billboarding the polygon.

We will use this idea to draw our fuzzy blob as a translucent, textured, billboarded polygon. We will use spherical billboarding without worrying about which way is up.

The Math

We consider spherical billboarding. Say we have unit vectors \(\mathbf{u}\), \(\mathbf{v}\). To rotate \(\mathbf{u}\) so that it points in the direction of \(\mathbf{v}\), we want to rotate through the angle between the vectors, about an axis perpendicular to both.

The angle between the vectors is the angle with cosine equal to \(\mathbf{u}\cdot\mathbf{v}\). An axis perpendicular to both is \(\mathbf{u}\times\mathbf{v}\).

When we do billboarding, \(\mathbf{u}\) is the normal vector for our polygon, and \(\mathbf{v}\) is the vector that points from the polygon to the camera. If we draw the polygon in the \(x\),\(y\)-plane, then \(\mathbf{u} = \langle 0,0,1 \rangle\).

Now suppose \(\mathbf{u}\) is as above, and \(\mathbf{v} = \langle a, b, c \rangle.\) Then \(\mathbf{u}\cdot\mathbf{v} = c\), and \(\mathbf{u}\times\mathbf{v} = \langle -b, a, 0 \rangle \).

Note that vector \(\mathbf{v}\) is probably not normalized at first. Normalization is easy in GLSL, but may be slightly painful in C++. But we do not need to normalize the cross product; it is perpendicular to both \(\mathbf{u}\), \(\mathbf{v}\) regardless. Also, we do not really need a normalized version of \(\mathbf{v}\); we only need to know the third coordinate of this normalized vector: \(\frac{c}{\sqrt{a^2+b^2+c^2}}\).

Putting all this together: the rotation that takes \(\langle 0,0,1 \rangle\) and makes it point in the direction of \(\langle a,b,c \rangle\) (which might not be normalized) is the rotation through the angle with measure \(\arccos\frac{c}{\sqrt{a^2+b^2+c^2}}\) about the axis \(\langle -b, a, 0 \rangle\). We will not be doing cylindrical billboarding, but it is easy to figure out now. Suppose we want to rotate about the \(y\)-axis. Just do the above, but first set \(b = 0\).

Note: the above does not handle the case when \(\mathbf{v} = \langle 0,0,-1 \rangle\). Need to say something about this!!!

Blending

We can draw a fuzzy blob as an image that is a solid color, but fades from opaque to transparent as we go from the center to the edge of the image.

Translucency can be encoded using alpha, the fourth component of a color. By convention, an alpha of \(1.0\) represents opaque, while \(0.0\) represents transparent.

If a scene contains translucent objects, then, to draw it correctly, we render the polygons in back-to-front order (recall our discussion of HSR methods) and we use blending: combining the fragment color and the existing color in the framebuffer.

Blending occurs at the end of the Fragment Processing section of the pipeline; in particular, it is done after (and thus outside) the fragment shader. We enable blending with glEnable. Disable it again with a similar call to glDisable.

[C++]

glEnable(GL_BLEND);

We control how the fragment and framebuffer colors are combined using glBlendFunc. This takes two parameters, both of which should be prededined constants. The first parameter is the fraction of the fragment color (the source color) that we use; the second is the fraction of the framebuffer color (the destination color). Then the two are added to obtain the final color, which is stored in the framebuffer.

The ordinary way to blend is to use the alpha component of the source color as a linear interpolation parameter between the two colors:

[C++]

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

We can create our blob as a texture in which every texel has the same R, G, B values, and alpha ranges from \(1.0\) at the center to \(0.0\) at the edges I will use a Gaussian blur: set each texel’s alpha component to \(e^-{kd^2}\), where \(d\) is the distance of the texel from the center of the texture, and \(k\) is some appropriate constant.

The Code

In this particular application, it does not matter so much whether the polygons are drawn back-to-front; We will not worry about drawing order; we do need to disable the depth test, however. The result is slightly incorrect, but not noticeably so.

To billboarding, we wrote a function sphericalBillboard, which takes two positions, and does a glRotate* command that turns the vector \(\langle 0,0,1 \rangle\) so that it points in the direction from one position to the other. Thus, we can pass the center of our polygon and the camera position (determined using whereAmI).

[C++]

void sphericalBillboard(const vector<GLdouble> & campos,
                        const vector<GLdouble> & objpos)
{
    const double pi = 3.1415926535898;
    vector<GLdouble> v(3);  // Vector from object to camera
    for (int i = 0; i < 3; ++i)
        v[i] = campos[i] - objpos[i];
    double dot = v[2] / sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
    double ang = acos(dot) * 180./pi;
    glRotated(ang, -v[1],v[0],0.);
}

Then we can call function sphericalBillboard at the place where we would typically do a glRotate*.

The texture is easily generated, but it does need a shader to use it. Our basic-texture-mapping shaders already does this.

See blast.cpp for an application that demonstrates billboarding and blending. The application uses the same shaders as usetextures.cpp; however, the basic_tex shaders are recommended.


CS 381 Fall 2012: Lecture Notes for Tuesday, October 30, 2012 / Updated: 30 Oct 2012 / Glenn G. Chappell / ggchappell@alaska.edu