In the previous lesson, you've learned how draw primitives in HOpenGL. You may have noticed that each primitive drawn had an unique color (we call this flat coloring). Although it is fast, this drawing method is also very "ugly", and it does not show the HOpenGL potential.
In this lesson, you'll learn how to draw primitives in a different way, called smooth coloring. This method works in the following way: each vertex, when created, has an extra information, concerning about its color. Hence, a primitive, which is built from a group of vertices, may have a lot of vertices with different colors. The color of the primitive will be a mix of the color of its vertices, as shown in the following picture:
As you may have observed, the upper vertex of the triangle is blue, the left one is red and the right one is green. The color of regions that are far away from the vertices is a mix of these three colors. Please notice that the above picture doesn't have an excellent quality, beacuse it is a .JPG (with a .GIF, the quality would be even poorer).
The main purpose of this lesson is to develop the code to draw the above triangle. At first, suppose that you would like to draw the same triangle with an unique color, red. If the bottom left corner of the screen is the point
triangle :: IO () triangle = do color (Color3 1.0 0.0 0.0 :: Color3 GLfloat) beginEnd Triangles $ mapM_ vertex [ Vertex3 0.20 0.20 0.0, Vertex3 0.80 0.20 0.0, Vertex3 0.50 0.80 (0.0 :: GLfloat)]
Now let me introduce you something new. The above code is equivalent to the following one:
triangle :: IO () triangle = do color (Color3 1.0 0.0 0.0 :: Color3 GLfloat) beginEnd Triangles $ do vertex (Vertex3 0.20 0.20 (0.0 :: GLfloat)) vertex (Vertex3 0.80 0.20 (0.0 :: GLfloat)) vertex (Vertex3 0.50 0.80 (0.0 :: GLfloat))
The difference is that, in the first code, we "mapped" the function vertex
to a list of vertices. In the second code, we used this function vertex by vertex (no lists were used). The main advantage of this is that we can introduce one or more commands between the vertices declarations. The command we'd like to introduce, in this example, is one that changes the current color. Hence, we'll reach the following code:
triangle :: IO () triangle = do beginEnd Triangles $ do color (Color3 1.0 0.0 0.0 :: Color3 GLfloat) vertex (Vertex3 0.20 0.20 (0.0 :: GLfloat)) color (Color3 0.0 1.0 0.0 :: Color3 GLfloat) vertex (Vertex3 0.80 0.20 (0.0 :: GLfloat)) color (Color3 0.0 0.0 1.0 :: Color3 GLfloat) vertex (Vertex3 0.50 0.80 (0.0 :: GLfloat))
Notice that each vertex has a different color. Now there's only one thing left to be done: say to our program that we would like to work with smooth coloring. In order words, we'd like our program to remember the color of each vertex when it draws the primitive. This is done by the shadeModel
function. It has one parameters, whose values can be:
GL.Flat
- Use this if you whish to work with flat coloring. The color of a primitive will be the color of its last vertex.
GL.Smooth
- Use this if you whish to work with smooth coloring. The color of a primitive will be a mix of the colors of its vertices.
As I said before, the first method is the ugliest one, but also the fastest. The second method requires more processing time, but its results are really incomparable.
If we were switching between both flat and smooth methods, the best place to call the shadeModel
function would be before you draw something that uses a method different from the current method. In our example, we're always working with smooth coloring, so the only thing we need to do is to include the following code in the myInit
function:
shadeModel GL.Smooth
Keep in mind that the shadeModel
function is defined in module GL_Colors, which is automatically imported if your program already imports module GL.
Finally, adding a call to triangle
in the display
function, we'll get the following code:
import GLUT import GL myInit :: IO () myInit = do clearColor (Color4 0.0 0.0 0.0 0.0) matrixMode Projection loadIdentity ortho 0.0 1.0 0.0 1.0 (-1.0) 1.0 shadeModel GL.Smooth triangle :: IO () triangle = do beginEnd Triangles $ do color (Color3 1.0 0.0 0.0 :: Color3 GLfloat) vertex (Vertex3 0.20 0.20 (0.0 :: GLfloat)) color (Color3 0.0 1.0 0.0 :: Color3 GLfloat) vertex (Vertex3 0.80 0.20 (0.0 :: GLfloat)) color (Color3 0.0 0.0 1.0 :: Color3 GLfloat) vertex (Vertex3 0.50 0.80 (0.0 :: GLfloat)) display :: DisplayAction display = do clear [ColorBufferBit] triangle flush main :: IO () main = do GLUT.init Nothing createWindow "Smooth" (return ()) [ Single, GLUT.Rgb ] (Just (WindowPosition 100 100)) (Just (WindowSize 300 250)) myInit displayFunc (display) mainLoop
If you change GL.Smooth
by GL.Flat
, in myInit
, you'll get a program that opens the following window:
shadeModel GL.Flat
to improve your program speed.