Informàtica gràfica 5. Visibilitat

En aquest article extendrem la biblioteca d’objectes per a dibuixar objectes poligonals, eliminarem les parts ocultes i les cares de darrere i realçarem les aristes per a donar als objectes aparença de volum.

Primitives poligonals

Fins ara hem dibuixat els objectes usant només un conjunt d’anells. El que anem a fer ara és dibuixar les mateixes primitives però com una superfície formada per polígons.

La idea bàsica és que ara dibuixarem tires de quadrilàters com els de la següent figura:

poligons1

on els vèrtex parells estaran tots a la mateixa altura (en el mateix anell), i els senars a una distinta (en l’anell superior)

Ara utilitzarem cintes en lloc de tires (que utilitzavem per a fer anells), segons el següent codi:

void cinta(float R, float dR, float y, float dy, float N) {
  int i;
  float x, z;

  glBegin(GL_QUAD_STRIP);
  for(i = 0; i <= N; i++) {
    x = dameX(R, N, i);
    z = dameZ(R, N, i);
    glVertex3f(x, y, z);
    x = dameX(R+dR, N, i);
    z = dameZ(R+dR, N, i);
    glVertex3f(x, y+dy, z);
  }
  glEnd();
}

I el codi per a dibuixar una forma sòlida utilitzant tires serà el següent:

void formaSolida(float H, int N, float(*f)(float y)) {
  int i;
  float y, r1, r2;

  for(i = 0; i < N; i++) {
    y = i*H/N-(H/2);
    r1 = f(y);
    r2 = f(y+(H/N));
    cinta(r1, r2-r1, y, H/N, N);
  }
}

En la següent imatge veiem un cilindre sòlid:

cilindrosolido1

Si ens fixem veiem que només hem dibuixat el contorn, necessitem també la tapa superior i la tapa inferior per a que siga un cilindre completament sòlid com aquest:

cilindrosolido2

Per al que hem utilitzat el següent codi:

void tapa(float R, float y, int N) {
	int i;
	float x, z;
	glBegin(GL_POLYGON);
	for(i = 0; i < N; i++) {
		x = dameX(R, N, i);
		z = dameZ(R, N, i);
		glVertex3f(x, y, z);
	}
	glEnd();
}

float fCilindro(float y) {
	return(0.5);
}

void cilindroSolido(int N) {
	formaSolida(1.0f, N, fCilindro);
	tapa(fCilindro(0.5), 0.5, N);
	tapa(fCilindro(0.5), -0.5, N);
}

Eliminació de parts ocultes i cares de darrere

Al dibuixar superfícies és important que les que estiguen més a prop de l'observador oculten les que estan més llunyanes, i que aquesta ocultació siga independent de l'ordre en que es dibuixen les superfícies.

OpenGL implementa un algorisme d'ocultació basat en un buffer de profunditat (z-buffer). Per a utilitzar-lo hem d'aplicar els següents comandaments:

  1. Indicar que l'aplicació OpenGL va a utilitzar el z-buffer:
    glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    
  2. Activar el test de profunditat:
    glEnable(GL_DEPTH_TEST);
    
  3. Cada vegada que anem a dibuixar l'escena, a més d'esborrar el buffer de color, haurem d'esborrar el buffer de profunditat:
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    

I d'altra banda, podem eliminar les cares de darrere ja que aquestes no les veurem i les estem dibuixant; d'aquesta manera estalviarem un temps de càlcul que no es reflexarà en la imatge final. Aquesta tècnica s'anomena back-face culling.

Només hem d'utilitzar els següents comandaments d'OpenGL:

glFrontFace(GL_CW); /* GL_CW indica que són les cares els vèrtex de les quals estan definits en el sentit de les agulles del rellotge; GL_CCW al contrari */
glCullFace(GL_FRONT); /* També GL_BACK i GL_FRONT_AND_BACK */
glEnable(GL_CULL_FACE);

Realçat d'aristes

Per a que els objectes ens donen sensació de profunditat ara que encara no estem utilitzant la iluminació ni les textures, necessitem dibuixar les cares i també les aristes; simplement dibuixarem els nostres objectes dos vegades, en la primera passada com a polígons i en la segona només les aristes de les cares, com per exemple el següent codi:

// Primera passada, dibuixem polígons
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.0, 1.0);
Objecte();
// Segona passada, dibuixem aristes
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDisable(GL_POLYGON_OFFSET_FILL);
Objecte();

El codi que ens quedaria a l'afegir els anteriors comandaments seria el següent: