| CS 381 Fall 2012 > Lecture Notes for Tuesday, October 30, 2012 |
To make use of textures in an OpenGL application using GLSL shaders:
glGenTextures).glActiveTexture),
bind the name to the target (glBindTexture),
get the color data in an array somehow,
and send the texture to OpenGL
(glTexImage2D or gluBuild2DMipmaps).glTexParameter*).GLint to a uniform sampler2D).glTexCoord* inside
glBegin-glEnd,
similar functionality in a vertex array).GL_TEXTURE).attribute vec4 gl_MultiTexCoord0.uniform mat4 gl_TextureMatrix[CHANNEL].
Use it to transform the texture coordinates.varying vec2.
uniform sampler2D.texture2D(SAMPLER,
COORDS),
where COORDS is a vec2.
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.
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.
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.
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.
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.
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.
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!!!
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.
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.
ggchappell@alaska.edu