[ Previous Section | Next Section | Chapter Index | Main Index ]

Subsections
Setting Material Properties
Color Material

Section 4.2

OpenGL Materials


OpenGL uses the term material to refer to the properties of an object that determine how it interacts with light. In standard OpenGL processing (when it has not been overridden by the GL Shading Language), material properties are assigned to vertices. Lighting calculations are done for vertices only, and the results of those calculations are interpolated to other points of the object. The calculations involved are summarized in a mathematical formula known as the lighting equation, which will be covered in the next section. But you should know that the whole calculation is a rather loose approximation for physical reality, and the use of interpolation is another entire level of approximation that can introduce serious distortion, especially when using large polygons. The system is designed for speed of calculation rather than perfect realism.


4.2.1  Setting Material Properties

Material properties are set using the glMaterial* family of commands, principly glMaterialfv and glMateriali. (There are no versions of this command that take parameters of type double.) These commands are defined in the GL class and are specified by

public void glMateriali(int face, int propertyName, int propertyValue)
public void glMaterialfv(int face, int propertyName, float[] propertyValue, int offset)

The first parameter tells which face of a polygon the command applies to. It must be one of the constants GL.GL_FRONT_AND_BACK, GL.GL_FRONT or GL.GL_BACK. This reflects the fact that different material properties can be assigned to the front and the back faces of polygons, since it sometimes desirable for the front and the back faces to have a different appearance. When the face parameter is GL.GL_FRONT_AND_BACK, the command sets the value of the material property for both sides simultaneously. The front material is also used for point and line primitives. Note that the back material is ignored completely unless two-sided lighting has been turned on by calling

gl.glLightModeli(GL.GL_LIGHT_MODEL_TWO_SIDE, GL.GL_TRUE)

The second parameter for glMaterial is propertyName, which tells which material property is being set. For glMateriali, the only legal property name is GL.GL_SHININESS, and the property value must be an integer in the range from 0 to 128, inclusive. This property determines the size and sharpness of specular highlights. Larger values produce smaller, sharper highlights. The default value is zero, which gives very large highlights that are almost never desirable. Try values close to 10 for a larger, fuzzier highlight and values of 100 or more for a small, sharp highlight. (It's worth noting that specular highlights are one area where polygon size can have a significant impact. Since lighting calculations are only done at vertices, OpenGL will entirely miss any specular highlight that should occur in the middle of a polygon but not at the vertices. And when a highlight does occur at a vertex, the interpolation process can smear out the highlight to points that should not show any specular highlight at all. Using very small polygons will alleviate the problem, but will require more computation.)

For glMaterialfv, the propertyName parameter can be GL.GL_AMBIENT, GL.GL_DIFFUSE, GL.GL_AMBIENT_AND_DIFFUSE, GL.GL_SPECULAR, or GL.GL_EMISSION. The names refer to the four different types of material color supported by OpenGL, as discussed in the previous section. The property name GL.GL_AMBIENT_AND_DIFFUSE allows both the ambient and the diffuse material colors to be set simultaneously to the same value. The third parameter for glMaterialfv is an array of type float[], and the fourth parameter specifies an index in the array. The index is often zero and is absent in the C API. Four numbers in the array, starting at the specified index, specify the red, green, blue, and alpha components of a color. The alpha component is used for blending, or transparency; for the time being, we will set alpha equal to 1.

In the case of the red, blue, and green components of the ambient, diffuse, or specular color, the term "color" really means reflectivity. That is, the red component of a color gives the proportion of red light hitting the surface that is reflected by that surface, and similarly for green and blue. There are three different types of reflective color because there are three different types of light in the environment, and a material can have a different reflectivity for each type of light. (More about that in the next section.)

The red, green, and blue component values are generally between 0 and 1, but they are not clamped to that range. That is, it is perfectly legal to have a negative color component or a color component that is greater than 1. For example, setting the red component to 1.5 would mean that the surface reflects 50% more red light than hits it -- a physical impossibility, but something that you might want to do for effect.

The default material has ambient color (0.2,0.2,0.2,1) and diffuse color (0.8,0.8,0.8,1). Specular and emission color are both black, that is, (0,0,0,1). It's not surprising that materials, by default, do not emit extra color. However, it is a little surprising that materials, by default, have no specular reflection. This means that the objects that you have seen in all our examples so far exhibit ambient and diffuse color only, with no specular highlights. Here is an image to change that:

This image shows eight spheres that differ only in the value of the GL_SHININESS material property. The ambient and diffuse material colors are set to (0.75,0.75,0,1), for a general yellow appearance. The specular color is (0.75,0.75,0.75,1), which adds some little blue to the specular highlight, making it appear whiter as well as brighter than the rest of the sphere. For the sphere on the left, the shininess is 0, which leads to an ugly specular "highlight" that covers an entire hemisphere. Going from left to right, the shininess increases by 16 from one sphere to the next. The material colors for this image were specified using

gl.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, 
                             new float[] { 0.75f, 0.75f, 0.75f, 1 },  0);
gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, 
                             new float[] { 0.75f, 0.75f, 0, 1 },  0);

and the shininess for sphere number i was set using

gl.glMateriali(GL.GL_FRONT, GL.GL_SHININESS, i*16);

4.2.2  Color Material

Materials are used in lighting calculations and are ignored when lighting is not enabled. Similarly, the current color as set by glColor* is ignored, by default, when lighting is enabled. However, as we have seen, calling gl.glEnable(GL.GL_COLOR_MATERIAL) will cause OpenGL to take the current color into account even while lighting is enabled.

More specifically, enabling GL_COLOR_MATERIAL tells OpenGL to substitute the current color for the ambient and for the diffuse material color when doing lighting computations. The specular and emission material colors are not affected. However, you can change this default behavior of color material by using the method

public void glColorMaterial(int face, int propertyName)

to say which material properties should be tied to glColor. The parameters in this method correspond to the first two parameters of glMaterialfv. That is, face can be GL.GL_FRONT_AND_BACK, GL.GL_FRONT, or GL.GL_BACK, and propertyName can be GL_AMBIENT_AND_DIFFUSE, GL_DIFFUSE, GL_AMBIENT, GL_SPECULAR, or GL_EMISSION. The most likely use of this method is to call

gl.glColorMaterial(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE)

so that GL_COLOR_MATERIAL will affect only the diffuse material color and not the ambient color.


In Section 3.4, we saw how to use the glDrawArrays and glDrawElements methods with vertex arrays, normal arrays, and texture coordinate arrays. It is also possible to specify a color array to be used with these methods. (More exactly, in Jogl, the color data must actually be stored in a Java nio Buffer rather than an array.) Use of a color array must be enabled by calling

gl.glEnableClientState(GL.GL_COLOR_ARRAY)

and gl.glColorPointer must be called to specify the location of the data. Exactly how to do this depends on whether a vertex buffer object is used, but the format is exactly parallel to that used with gl.glVertexPointer for specifying the location of vertex data.

Furthermore, it's important to note that using GL_COLOR_ARRAY is equivalent to calling glColor* for each vertex that is generated. If lighting is enabled, these colors are ignored, unless GL_COLOR_MATERIAL has been enabled. That is, if you are using a color array while lighting is enabled, you must call gl.glEnable(GL.GL_COLOR_MATERIAL) before calling gl.glDrawArrays or gl.glDrawElements. Otherwise, the color data will not have any effect. You could also, optionally, call gl.glColorMaterial to say which material property the color data will affect.


[ Previous Section | Next Section | Chapter Index | Main Index ]