Выбрать главу

}

And voilá, this is the shortest Draw routine ever seen, with a great looking effect!

We call the RenderToTexture function. This renders the stretched spring once thanks to our viewport change. The stretched spring is rendered to our texture, and the buffers are cleared.

We then draw the "REAL" spring (the 3D object you see on the screen) by calling ProcessHelix( ).

Finally, we draw some blended quads in front of the spring. The textured quads will be stretched to fit over top of the REAL 3D spring.

void Draw (void) // Draw The Scene

{

 glClearColor(0.0f, 0.0f, 0.0f, 0.5); // Set The Clear Color To Black

 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer

 glLoadIdentity(); // Reset The View

 RenderToTexture(); // Render To A Texture

 ProcessHelix(); // Draw Our Helix

 DrawBlur(25,0.02f); // Draw The Blur Effect

 glFlush (); // Flush The GL Rendering Pipeline

}

I hope you enjoyed this tutorial, it really doesn't teach much other than rendering to a texture, but it's definitely an interesting effect to add to your 3d programs.

If you have any comments suggestions or if you know of a better way to implement this effect contact me rio@spinningkids.org.

You are free to use this code however you want in productions of your own, but before you RIP it, give it a look and try to understand what it does, that's the only way ripping is allowed! Also, if you use this code, please, give me some credit!

I want also leave you all with a list of things to do (homework) :D

1) Modify the DrawBlur routine to get an horizontal blur, vertical blur and some more good effects (Twirl blur!).

2) Play with the DrawBlur parameter (add, remove) to get a good routine to sync with your music.

3) Play around with DrawBlur params and a SMALL texture using GL_LUMINANCE (Funky Shininess!).

4) Try superfake volumetric shadows using dark textures instead of luminance one!

Ok, that should be all for now.

Visit my site and (SK one) for more upcoming tutorials at http://www.spinningkids.org/rio.

Dario Corno (rIo)
Jeff Molofee (NeHe)

* DOWNLOAD Visual C++ Code For This Lesson. 

Lesson 37

Cel-Shading By Sami "MENTAL" Hamlaoui

Seeing as people still e-mail me asking for source code to the article I wrote on GameDev.net a while ago, and seeing as the 2nd version of that article (with source for every API out there) isn't even close to being halfway finished, I've hacked together this tutorial for NeHe (that was actually going to be the original intention of the article) so all of you OpenGL gurus can play around with it. Sorry for the choice of model, but I've been playing Quake 2 extensivly recently… :)

Note: The original article for this code can be found at: http://www.gamedev.net/reference/programming/features/celshading.

This tutorial doesn't actually explain the theory, just the code. WHY it works can be found at the above link. Now for crying out loud STOP E-MAILING ME REQUESTS FOR SOURCE CODE!!!!

Enjoy :).

First of all, we need to include a few extra header files. The first one (math.h) is so we can use the sqrtf (square root) function, and the second (stdio.h) is for file access.

#include <math.h> // Header File For The Math Library

#include <stdio.h> // Header File For The Standard I/O Library

Now we are going to define a few structures to help store our data (saves having hundreds of arrays of floats). The first one is the tagMATRIX structure. If you look closely, you will see that we are storing the matrix as a 1D array of 16 floats as opposed to a 2D 4×4 array. This is down to how OpenGL stores it's matrices. If we used 4×4, the values would come out in the wrong order.

typedef struct tagMATRIX // A Structure To Hold An OpenGL Matrix

{

 float Data[16]; // We Use [16] Due To OpenGL's Matrix Format

} MATRIX;

Second up is the vector class. This simply stores a value for X, Y and Z.

typedef struct tagVECTOR // A Structure To Hold A Single Vector

{

 float X, Y, Z; // The Components Of The Vector

} VECTOR;

Third, we have the vertex structure. Each vertex only needs it's normal and position (no texture co-ordinates). They MUST be stored in this order, or else when it comes to loading the file things will go horribly wrong (I found out the hard way :(. That'll teach me to hack my code to pieces.).

typedef struct tagVERTEX // A Structure To Hold A Single Vertex

{

 VECTOR Nor; // Vertex Normal

 VECTOR Pos; // Vertex Position

} VERTEX;

Finally, the polygon structure. I know this is a stupid way of storing vertexes, but for the sake of simplicity it works perfectly. Usually I would use an array of vertexes, an array of polygons, and contain the Index number of the 3 verts in the polygon structure, but this is easier to show you what's going on.

typedef struct tagPOLYGON // A Structure To Hold A Single Polygon

{

 VERTEX Verts[3]; // Array Of 3 VERTEX Structures

} POLYGON;

Pretty simple stuff here too. Look at the comments for an explaination of each variable.

bool outlineDraw = true; // Flag To Draw The Outline

bool outlineSmooth = false; // Flag To Anti-Alias The Lines

float outlineColor[3] = { 0.0f, 0.0f, 0.0f }; // Color Of The Lines

float outlineWidth = 3.0f; // Width Of The Lines

VECTOR lightAngle; // The Direction Of The Light

bool lightRotate = false; // Flag To See If We Rotate The Light

float modelAngle = 0.0f; // Y-Axis Angle Of The Model

bool modelRotate = false; // Flag To Rotate The Model

POLYGON *polyData = NULL; // Polygon Data

int polyNum = 0; // Number Of Polygons

GLuint shaderTexture[1]; // Storage For One Texture

This is as simple as model file formats get. The first few bytes store the number of polygons in the scene, and the rest of the file is an array of tagPOLYGON structures. Because of this, the data can be read in without any need to sort it into any particular order.

BOOL ReadMesh () // Reads The Contents Of The "model.txt" File

{

 FILE *In = fopen ("Data\\model.txt", "rb"); // Open The File

 if (!In) return FALSE; // Return FALSE If File Not Opened

 fread (&polyNum, sizeof (int), 1, In); // Read The Header (i.e. Number Of Polygons)

 polyData = new POLYGON [polyNum]; // Allocate The Memory

 fread (&polyData[0], sizeof (POLYGON) * polyNum, 1, In);// Read In All Polygon Data

 fclose (In); // Close The File

 return TRUE; // It Worked

}

Some basic math functions now. The DotProduct calculates the angle between 2 vectors or planes, the Magnitude function calculates the length of the vector, and the Normalize function reduces the vector to a unit length of 1.