Computer graphics -- 2008-2009 -- info.uvt.ro/Laboratory 3

From Wikiversity

Quick links: front; laboratories agenda, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, JOGL template.


(J)OGL BASICS (PART 2)[edit]

NOTE: Grab the application template from the following link: Computer graphics -- 2008-2009 -- info.uvt.ro/JOGL-Template

Antialising[edit]

Aliasing effects are induced due to discretization of images or to effects that cause different continuous signals to become indistinguishable.

Several antialiasing techniques ca be used in (J)OGL:

  • Color blending -- means that two colors can be mixed together using various criteria. The next example shows how we can use it to obtain an antialiasing effect:
	[...]

	public void init(GLAutoDrawable canvas)
	{

		[...]

		// Activate the GL_LINE_SMOOTH state variable. Other options include 
		// GL_POINT_SMOOTH and GL_POLYGON_SMOOTH.
		gl.glEnable(GL.GL_LINE_SMOOTH);

		// Activate the GL_BLEND state variable. Means activating blending.
		gl.glEnable(GL.GL_BLEND);

		// Set the blend function. For antialiasing it is set to GL_SRC_ALPHA for the source
		// and GL_ONE_MINUS_SRC_ALPHA for the destination pixel.
		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);

		// Control GL_LINE_SMOOTH_HINT by applying the GL_DONT_CARE behavior. 
		// Other behaviours include GL_FASTEST or GL_NICEST.
		gl.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_DONT_CARE);
		// Uncomment the following two lines in case of polygon antialiasing
		//gl.glEnable(GL.GL_POLYGON_SMOOTH);
		//glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);

	}

	public void display(GLAutoDrawable canvas) {
		GL2 gl = canvas.getGL().getGL2();

		[...]
		
		gl.glLineWidth(1.5f);
	
		gl.glColor3f(1.f, 0.f, 0.f);
		gl.glBegin(GL2.GL_LINES);
			gl.glVertex2f(0.2f, 0.2f);
			gl.glVertex2f(0.9f, 0.9f);			
		gl.glEnd();
		
		gl.glColor3f(0.f, 1.f, 0.f);
		gl.glBegin(GL2.GL_LINES);
			gl.glVertex2f(0.9f, 0.2f);
			gl.glVertex2f(0.2f, 0.9f);
		gl.glEnd();
		[...]
	}

	[...]

We could play with the previous code by disabling GL_BLEND and/or GL_LINE_SMOOTH and notice the difference.

  • Handling the accumulation buffer -- is the most simple OGL buffer and is handled by only one function (method) glAccum. It is an extended-range color buffer but instead of rendering images in it they are added to the contents of the accumulation buffer after rendering. It is used for creating antialiasing effects, motion blur, depth of field etc. This approach will be detailed in a later laboratory when will deal with buffers in more detail.
  • multisampling -- more efficient than the first technique (done by the fragment shader) but cannot be restricted to just anti-alias a subset of polygons in the scene (optimization!?)

Links:

Polygons[edit]

Are made of a single line loop between several points.

Validity[edit]

Only convex polygons are valid in (J)OGL. If we want to draw concave polygons we should draw them by using lines with GL_LINE_LOOP. Alternatively we could divide our concave polygon into smaller convex ones by triangulation or tessallation (See bellow).

Link:

The building blocks of polyons[edit]

Polygons are made of smaller polygons. These building blocks can be either other triangles (triangulation) or other smaller polygons (tessellation).

The simplest polygon is a triangle.

Dimensionality[edit]

Polygons may lie in the same plane or not depending on whether all their vertices lie in the same plane or not.

Primitives[edit]

In (J)OGL we can create polygons by interpreting vertices in a broad way:

  • polygons -- by using GL.GL_POLYGON
  • quads -- by using GL.GL_QUADS
    • quad strip -- by using GL.GL_QUAD_STRIP
  • triangles -- by using GL.GL_TRIANGLES
    • triangle strip -- by using GL.GL_TRIANGLE_STRIP
    • triangle fan -- by using GL.GL_TRIANGLE_FAN

A particular polygon which has its own function (method) is the rectangle (by using glRect).

public class MainFrame
{
	[...]

	public void display(GLAutoDrawable canvas) {
		GL2 gl = canvas.getGL().getGL2();

		[...]

		gl.glBegin(GL2.GL_POLYGON);
			gl.glColor3f(1.f, 0.f, 0.f);
			gl.glVertex2f(0.2f, 0.2f);
			gl.glColor3f(0.f, 1.f, 0.f);
			gl.glVertex2f(0.2f, 0.4f);
			gl.glColor3f(0.f, 0.f, 1.f);
			gl.glVertex2f(0.4f, 0.4f);
			gl.glColor3f(1.f, 1.f, 1.f);
			gl.glVertex2f(0.4f, 0.2f);
		gl.glEnd();

		[...]
	}

	[...]
}

Links:

Polygon filling and orientation[edit]

(J)OGL polygons can also be filled with a given color or just draw their contours or display the points (vertices) they are made of. The function (method) which enables you this operation is glPolygonMode(face, mode), where face could take the GL_FRONT, GL_BACK, GL_FRONT_AND_BACK values and mode can take the GL_POINT, GL_LINE or GL_FILL values. Polygons could be rendered differently depending on which side is facing the viewer.

An important feature when drawing polygons is the decision to draw only front or/and back faced ones. This action is specified by the glCullFace(face) where face is specified by GL_FRONT, GL_BACK, GL_FRONT_AND_BACK. This feature is called culling and is used for optimization reasons mostly.

Polygons having their vertices specified in clockwise order are back-faced, while polygons having vertices defined in trigonometric order are front-faced. You could change this default behavior by using the glFrontFace function (method). It receives two parameters: GL_CW (ClockWise) and GL_CCW (CounterClockWise - Trigonometric).

For example the following code will display the polygon:

public class MainFrame
{
	[...]

	public void display(GLAutoDrawable canvas) {
		GL2 gl = canvas.getGL().getGL2();

		[...]

		// Do not render front-faced polygons.
		gl.glCullFace(GL.GL_FRONT);
		// Culling must be enabled in order to work.
		gl.glEnable(GL.GL_CULL_FACE);		

		gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL);

		// Define vertices in clockwise order (back-faced)
		gl.glBegin(GL2.GL_POLYGON);
			gl.glColor3f(1.f, 0.f, 0.f);
			gl.glVertex2f(0.2f, 0.2f);
			gl.glColor3f(0.f, 1.f, 0.f);
			gl.glVertex2f(0.2f, 0.4f);
			gl.glColor3f(0.f, 0.f, 1.f);
			gl.glVertex2f(0.4f, 0.4f);
			gl.glColor3f(1.f, 1.f, 1.f);
			gl.glVertex2f(0.4f, 0.2f);
		gl.glEnd();

		[...]
	}

	[...]
}

while the next one will not:

public class MainFrame
{
	[...]

	public void display(GLAutoDrawable canvas) {
		GL2 gl = canvas.getGL().getGL2();

		[...]

		// Do not render back-faced polygons.
		gl.glCullFace(GL.GL_BACK);
		// Culling must be enabled in order to work.
		gl.glEnable(GL.GL_CULL_FACE);		

		gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL);

		// Define vertices in clockwise order (back-faced).
		gl.glBegin(GL2.GL_POLYGON);
			gl.glColor3f(1.f, 0.f, 0.f);
			gl.glVertex2f(0.2f, 0.2f);
			gl.glColor3f(0.f, 1.f, 0.f);
			gl.glVertex2f(0.2f, 0.4f);
			gl.glColor3f(0.f, 0.f, 1.f);
			gl.glVertex2f(0.4f, 0.4f);
			gl.glColor3f(1.f, 1.f, 1.f);
			gl.glVertex2f(0.4f, 0.2f);
		gl.glEnd();

		[...]
	}

	[...]
}

Polygon stipple[edit]

As for lines polygons can be filled with a stipple pattern by using the glPolygonStipple function (method):

public class MainFrame
{

	[...]
	byte mask[] = {
        	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	        0x03, (byte)0x80, 0x01, (byte)0xC0, 0x06, (byte)0xC0, 0x03, 0x60, 

	        0x04, 0x60, 0x06, 0x20, 0x04, 0x30, 0x0C, 0x20, 
	        0x04, 0x18, 0x18, 0x20, 0x04, 0x0C, 0x30, 0x20,
	        0x04, 0x06, 0x60, 0x20, 0x44, 0x03, (byte)0xC0, 0x22, 
	        0x44, 0x01, (byte)0x80, 0x22, 0x44, 0x01, (byte)0x80, 0x22, 
	        0x44, 0x01, (byte)0x80, 0x22, 0x44, 0x01, (byte)0x80, 0x22,
	        0x44, 0x01, (byte)0x80, 0x22, 0x44, 0x01, (byte)0x80, 0x22, 
	        0x66, 0x01, (byte)0x80, 0x66, 0x33, 0x01, (byte)0x80, (byte)0xCC, 

	        0x19, (byte)0x81, (byte)0x81, (byte)0x98, 0x0C, (byte)0xC1, (byte)0x83, 0x30,
	        0x07, (byte)0xe1, (byte)0x87, (byte)0xe0, 0x03, 0x3f, (byte)0xfc, (byte)0xc0, 
	        0x03, 0x31, (byte)0x8c, (byte)0xc0, 0x03, 0x33, (byte)0xcc, (byte)0xc0, 
	        0x06, 0x64, 0x26, 0x60, 0x0c, (byte)0xcc, 0x33, 0x30,
	        0x18, (byte)0xcc, 0x33, 0x18, 0x10, (byte)0xc4, 0x23, 0x08, 
	        0x10, 0x63, (byte)0xC6, 0x08, 0x10, 0x30, 0x0c, 0x08, 
	        0x10, 0x18, 0x18, 0x08, 0x10, 0x00, 0x00, 0x08};

	public void display(GLAutoDrawable canvas) {
		GL2 gl = canvas.getGL().getGL2();

		gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_FILL);

		// Set the polygon mask.
		gl.glPolygonStipple (mask, 0);
		// Enable polygon stipple.
		gl.glEnable (GL2.GL_POLYGON_STIPPLE);

		// Define vertices in clockwise order (back-faced).
		gl.glBegin(GL2.GL_POLYGON);
			gl.glColor3f(1.f, 0.f, 0.f);
			gl.glVertex2f(0.2f, 0.2f);
			gl.glColor3f(0.f, 1.f, 0.f);
			gl.glVertex2f(0.2f, 0.4f);
			gl.glColor3f(0.f, 0.f, 1.f);
			gl.glVertex2f(0.4f, 0.4f);
			gl.glColor3f(1.f, 1.f, 1.f);
			gl.glVertex2f(0.4f, 0.2f);
		gl.glEnd();

		// Disable polygon stipple.
		gl.glDisable (GL2.GL_POLYGON_STIPPLE);

		[...]
	}

	[...]
}

Polygon normals[edit]

Normals are perpendicular lines on surfaces. They are important when dealing with lights as their effects are computed with regard to the angle between the light direction and the each vertex normal. More on them will be discussed when we will address lighting issues.

Normals can be specified by using the glNormal*() family functions (methods). The values of the arguments should be in the [-1,1] range.

public class MainFrame
{
	[...]

	public void display(GLAutoDrawable canvas) {
		GL2 gl = canvas.getGL().getGL2();

		[...]

		// Do not render front-faced polygons.
		gl.glCullFace(GL.GL_FRONT);
		// Culling must be enabled in order to work.
		gl.glEnable(GL.GL_CULL_FACE);		

		gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL);

		// Define vertices in clockwise order (back-faced).
		gl.glBegin(GL2.GL_POLYGON);
			// Define normal for vertex 1
			gl.glNormal3f(0.f, 0.f, 1.f);
			gl.glColor3f(1.f, 0.f, 0.f);
			gl.glVertex2f(0.2f, 0.2f);

			// Define normal for vertex 2
			gl.glNormal3f(0.f, 0.f, 1.f);
			gl.glColor3f(0.f, 1.f, 0.f);
			gl.glVertex2f(0.2f, 0.4f);

			// Define normal for vertex 3
			gl.glNormal3f(0.f, 0.f, 1.f);
			gl.glColor3f(0.f, 0.f, 1.f);
			gl.glVertex2f(0.4f, 0.4f);

			// Define normal for vertex 4
			gl.glNormal3f(0.f, 0.f, 1.f);
			gl.glColor3f(1.f, 1.f, 1.f);
			gl.glVertex2f(0.4f, 0.2f);
		gl.glEnd();

		[...]
	}

	[...]
}

Normals are mostly used for determining the light influence over a vertex.

Bump mapping primer[edit]

Bump mapping take full advantage of this technique. It is used to create the illusion of 3D surfaces on flat textures.

Two kind of maps are usually used for bump mapping: bump and normal maps. Both texture-types are used to generate fairly realistic shadows on the surface. To achieve this, bump maps store the height of each point on the surface, while normal maps store the surface normals. You typically get smoother results with a normal map than with a bump map.

The idea behind bump mapping is to perturb the normals so that vertices don't lit uniformly. The perturbation depends on either the value of the bump pixel (bump map) or the value of the normal (normal map).

Links:

Building Display Lists[edit]

Display lists can be seen (at first glance) as a C function which once defined can be used as many times we want in our code. Simply put they store batches of OGL commands which can then be used repeatedly in our code. They are also used for their performance.

OGL commands within them are precompiled and, if possible, stored in the graphics card memory. Therefore, in general the execution of a Display List is faster than the execution of the commands contained in it.

Display Lists IDs are generated by using the glGenLists function (method). All the OGL commands that we want to store in a Display List must be placed inside a function (method) block starting with glNewList(int id, int mode) (where mode can be GL_COMPILE - creates the list, or GL_COMPILE_AND_EXECUTE - creates the list and executes the commands contained inside of it) and ending with glEndList.

public class MainFrame
{
	[...]

	int aCircle;

	public void init(GLAutoDrawable canvas)
	{

		[...]

		// Generate a unique ID for our list.
		aCircle = gl.glGenLists(1);

		// Generate the Display List
		gl.glNewList(aCircle, GL2.GL_COMPILE);
			drawCircle(gl, 0.5f, 0.5f, 0.4f);
		gl.glEndList();

	}

	int aCircle;

	public void display(GLAutoDrawable canvas) {
		GL2 gl = canvas.getGL().getGL2();

		[...]

		gl.glColor3f(1.0f, 1.0f, 1.0f);
		// Call the Display List i.e. call the commands stored in it.
		gl.glCallList(aCircle);

	}

	// Here we define the function for building a circle from line segments.
	private void drawCircle(GL gl, float xCenter, float yCenter, float radius) {

		double x,y, angle;

		gl.glBegin(GL2.GL_LINE_LOOP);
		for (int i=0; i<360; i++) {
			angle = Math.toRadians(i);
			x = radius * Math.cos(angle);
			y = radius * Math.sin(angle);
			gl.glVertex2d(xCenter + x, yCenter + y);
		}
		gl.glEnd();

	}
	[...]
}

Links:

API[edit]

Exercises[edit]

  • Draw a house by using GL_TRIANGLES. The Sun should be a filled yellow circle and should move through the scene from left to right. Fill each section of the house with a different color. Use display lists
  • Draw the Mandelbrot set (fractal). You can find the algorithm here http://en.wikipedia.org/wiki/Mandelbrot_set#For_programmers
  • Draw a chess board made out of polygons with squares of white and black. Squares should alternate with one being black and one white. It is prohibited to draw a polygon using the black colour. Instead you should draw them as back-faced and enable culling for them.