Sviluppare un gioco per Android – Lezione 13: Texture Mapping con OpenGL ES pt.1

Creato il 04 aprile 2014 da Paolo Dolci @androidblogit

Sviluppare un gioco per Android – Lezione 13: Texture Mapping con OpenGL ES pt.1
Android Blog Italia.

Nei precedenti due articoli (qui e qui), abbiamo dato uno sguardo introduttivo ad OpenGL ES che, come argomento, avrete capito che è molto ampio. L’ultima tecnica che tratteremo, nella nostra breve panoramica, è il texture mapping, il quale richiederà ben due lezioni. Giunti a questo punto della nostra trattazione, infatti, ci chiediamo come disegnare un quadrato (anziché un triangolo) e come applicare una texture su di esso. Quest’ultima, altro non è che un’immagine bitmap. Visualizzare le immagini con OpenGL, utilizzando questa tecnica, risulterà molto veloce, sopratutto se il nostro intento è creare un gioco 2D.

Tuttavia, la domanda è: come disegnare un quadrato? È possibile ottenere questo oggetto da due triangoli, come mostrato nell’immagine qui di seguito:

Come noterete, i vertici sono nell’ordine ABDC anziché ABCD. Il motivo è molto semplice e deriva da come OpenGL connette più triangoli. Quella che vedete, infatti, viene chiamata triangle strip e, per la precisione, ecco l’ordine dei vertici che segue OpenGL per disegnare la triangle strip mostrata sopra:

Triangolo 1: V1 -> V2 -> V3

Triangolo 2: V3 -> V2 -> V4

L’immagine qui di seguito vi chiarirà le idee:

Viene disegnato il primo triangolo prendendo i vertici nell’ordine giusto, dopodiché viene preso l’ultimo vertice e l’ultimo lato del triangolo disegnato, il quale viene utilizzato come base per il nuovo triangolo. Uno dei vantaggi più importanti di quest’approccio riguarda la memoria, dalla quale vengono eliminati, così facendo, tutti i dati ridondanti.

A questo punto, possiamo modificare il progetto visto nelle precedenti lezioni affinché venga disegnato il quadrato. Il primo passo da effettuare riguarda la creazione della classe Square.java che, come potete vedere, ha una sola differenza rispetto alla classe Triangle.java:

public class Square {

	private FloatBuffer vertexBuffer;	

	private float vertices[] = {
			-1.0f, -1.0f,  0.0f,		// V1 
			-1.0f,  1.0f,  0.0f,		// V2 
			 1.0f, -1.0f,  0.0f,		// V3 
			 1.0f,  1.0f,  0.0f			// V4 
	};

	public Square() {
		ByteBuffer vertexByteBuffer = ByteBuffer.allocateDirect(vertices.length * 4);
		vertexByteBuffer.order(ByteOrder.nativeOrder());

		vertexBuffer = vertexByteBuffer.asFloatBuffer();

		vertexBuffer.put(vertices);

		vertexBuffer.position(0);
	}

	public void draw(GL10 gl) {
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

		gl.glColor4f(0.0f, 1.0f, 0.0f, 0.5f);

		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);

		gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);

		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
	}
}

La differenza risiede nell’aggiunta di un vertice all’array vertices[] (il resto del codice è praticamente identico). Inoltre, dobbiamo effettuare alcune modifiche anche a GlRenderer, in modo che utilizzi Square.java per disegnare il quadrato:

private Square square;	// il quadrato da disegnare

	public GlRenderer() {
		this.square = new Square();
	}

	@Override
	public void onDrawFrame(GL10 gl) {
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

		// resetta la Modelview Matrix
		gl.glLoadIdentity();

		// Disegno
		gl.glTranslatef(0.0f, 0.0f, -5.0f);		// muove di 5 unità nello schermo

//		gl.glScalef(0.5f, 0.5f, 0.5f);			// scala il triangolo del 50% 
								// nel caso sia troppo grande
		square.draw(gl);				// disegna il quadrato

	}

Le piccole modifiche apportate le potete vedere voi stessi nel codice sopra (il restante codice non cambia). Lanciando l’applicazione adesso, dovreste ritrovarvi dinanzi un risultato simile al seguente:

Diamo un’occhiata, procedendo per passi, al metodo draw() di Square.java, rimasto invariato rispetto a quello di Triangle.java:

  • gl.glEnableClientState(GL10.GL_VERTEX_ARRAY)in primo luogo abilitiamo OpenGL per utilizzare il nostro array contenente i vertici;
  • gl.glColor4f(0.0f, 1.0f, 0.0f, 0.5f)il secondo passo, molto semplice, consiste nel colorare il quadrato utilizzando RGBA;
  • gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer): ci serve per indicare ad OpenGL da dove prendere i vertici e di che tipo sono, il primo parametro indica quante coordinate vengono utilizzate per ciascun vertice, il secondo specifica il tipo dei parametri, il terzo contiene l’offset dell’array utilizzato e, infine, specifichiamo qual è il buffer contenente i vertici;
  • gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3): specifica il tipo di primitiva che OpenGL deve disegnare, in questo caso TRIANGLE_STRIP, prendendo i vertici dal nostro array per disegnare seguendo le regole imposte dalla primitiva, il secondo parametro indica la posizione del primo vertice da prendere nell’array, mentre l’ultimo specifica il numero di vertici per disegnare il poligono.
  • gl.glDisableClientState(GL10.GL_VERTEX_ARRAY): disattiviamo lo stato di rendering.

È necessario, prima di proseguire, capire pienamente il processo di OpenGL, in quanto questa fase di rendering è una delle più importanti dell’applicazione.

A questo punto dobbiamo creare la texture e specificare come applicarla al quadrato, ma vedremo tutto ciò con precisione nella seconda parte della lezione. Lavoreremo molto con la classe Square.java per raggiungere il nostro scopo, quindi assicuratevi di aver capito pienamente il rendering OpenGL e, inoltre, disporre le immagini con Android non deve essere nulla di nuovo per voi.

Ecco, brevemente, cosa faremo nella prossima lezione: caricare l’immagine, avvisare il renderer OpenGL che vogliamo utilizzarla come texture e specificare l’esatto punto in cui applicarla. Insomma, come si suol dire in questi casi, più facile a dirsi che a farsi. Per ogni dubbio, non esitate a commentare l’articolo, alla prossima!

Sviluppare un gioco per Android – Lezione 13: Texture Mapping con OpenGL ES pt.1
Android Blog Italia.


Potrebbero interessarti anche :

Possono interessarti anche questi articoli :