WebGL per HTML5

[Lezione 3] - Colori

14/02/2017


Nel WebGL il colore viene gestito attraverso la combinazione dei tre canali RED, GREEN e BLUE (RGB). A questi viene aggiunto il canale ALPHA che gestisce la trasparenza del colore (il modello RGB diventa quindi RGBA). Il range di valori che possono essere assegnati ad ogni parametro R, G, B o A variano tra 0.0 e 1.0. Per il canale ALPHA il valore 0.0 rappresenta la totale trasparenza (opacità 0%) mentre il valore 1.0 la elimina totalmente (opacità 100%). Tutti i valori intermeti la regoleranno di conseguenza.
Prendiamo per un attimo in considerazione l'istruzione che assegna un colore di sfondo a tutta la scena gl.clearColor(). Come abbiamo visto nell'esempio riportato nella lezione precedente il colore di sfondo assegnato è il colore nero, infatti i parametri passati sono i seguenti gl.clearColor(0.0, 0.0, 0.0, 1.0) dove i quattro valori rappresentano i tre colori RGB ed il canale Alpha. I tre colori impostati a 0.0 daranno come risultato proprio il colore NERO.

Oltre ad impostare la Scena, il colore viene utilizzato per gli oggetti 3D (Objects) e nella gestione dell'illuminazione (Light).

Di seguito riportiamo il codice della lezione precedente con le dovute modifiche per far si che i due triangoli presenti nella scena siano colorati. Per colorare un triangolo bisognerà assegnare un colore ad ogni vertice. Assegnando sempre lo stesso colore avremo l'intero triangolo colorato in maniega omogenea se invece assegnamo colori differenti vedremo che i colori ai vertici saranno quelli da noi assegnati e per interpolazione il triangolo assumenrà delle sfumature molto interessanti.

Ogni riga di codice aggiunta o commentata per la gestione del colore verrà identificata con "//color".

<!doctype html>
<html>
    <head>
        <title>webgl</title>
        <style>
            body{ background-color: gray; }
            canvas{ background-color: black; }
            .parent{ width:100%; }
            .child{ width: 50%; margin: 0 auto; }
        </style>
        <script id="shader-vs" type="x-shader/x-vertex">
                attribute vec3 aVertexPosition; // posizione (x,y,z) del vertice
                attribute vec3 aVertexColor; //color

                varying highp vec4 vColor; //color
                void main(void) {
                    gl_Position = vec4(aVertexPosition, 1.0);
                    vColor = vec4(aVertexColor, 1.0); //color
                }
        </script>
        <script id="shader-fs" type="x-shader/x-fragment">
            varying highp vec4 vColor; //color
            void main(void) {
                //gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); //color
                gl_FragColor = vColor; //color
            }
        </script>
        <script>
            var gl = null,
                canvas = null,
                glProgram = null,
                fragmentShader = null,
                vertexShader = null;
        
            var vertexPositionAttribute = null,
                trianglesVerticeBuffer = null;

            var vertexColorAttribute = null, //color
                trianglesColorBuffer = null; //color

            function initWebGL()
            {
                canvas = document.getElementById("my-canvas");
                try{
                    gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
                } catch(e) {
                }
			
                if(gl)
                {
		    initShaders();
		    setupBuffers();
		    //(function animLoop(){
                        setupWebGL();
                        drawScene();
                    //})();
                }else{
		    alert("ERRORE: Il tuo Browser non supporta WebGL");
	        }
            }

            function initShaders()
            {
                var fs_source = document.getElementById('shader-fs').innerHTML,
                    vs_source = document.getElementById('shader-vs').innerHTML;
                
                vertexShader = makeShader(vs_source, gl.VERTEX_SHADER);
                fragmentShader = makeShader(fs_source, gl.FRAGMENT_SHADER);

                glProgram = gl.createProgram();

                gl.attachShader(glProgram, vertexShader);
                gl.attachShader(glProgram, fragmentShader);

                gl.linkProgram(glProgram);

                if (!gl.getProgramParameter(glProgram, gl.LINK_STATUS)) {
                    alert("Impossibile inizializzare lo shader program.");
                }

                gl.useProgram(glProgram);
            }

            function makeShader(src, type)
            {
                 //compile the vertex shader
                 var shader = gl.createShader(type);
                 gl.shaderSource(shader, src);
                 gl.compileShader(shader);
                 if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
                     alert("Errore di compilazione dello shader: " + gl.getShaderInfoLog(shader));
                 }
                 return shader;
            }

            function setupBuffers()
            {
                //VERTICI
                var triangleVertices = [
                    //triangolo sinistro
                    -0.5, 0.5, 0.0, //vertice 0
                     0.0, 0.0, 0.0, //vertice 1
                    -0.5, -0.5, 0.0, //vertice 2
                    //triangolo destro
                    0.5, 0.5, 0.0, //vertice 3
                    0.0, 0.5, 0.0, //vertice 4
                    0.5, -0.2, 0.0 //vertice 5
                ];
                trianglesVerticeBuffer = gl.createBuffer();
                gl.bindBuffer(gl.ARRAY_BUFFER, trianglesVerticeBuffer);
                gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleVertices), gl.STATIC_DRAW);

                //color [INIZIO]
		//COLORI
		var triangleVerticesColors = [
                    //triangolo sinistro
                    1.0, 0.0, 0.0, // RGB vertice 0 - colore rosso
                    1.0, 1.0, 1.0, // RGB vertice 1 - colore bianco
                    1.0, 0.0, 0.0, // RGB vertice 2 - colore rosso
                    //triangolo destro
                    0.0, 0.0, 1.0, // RGB vertice 3 - colore blu
                    1.0, 1.0, 1.0, // RGB vertice 4 - colore bianco
                    0.0, 1.0, 0.0  // RGB vertice 5 - colore verde
                ];
                trianglesColorBuffer = gl.createBuffer();
                gl.bindBuffer(gl.ARRAY_BUFFER, trianglesColorBuffer);
                gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleVerticesColors), gl.STATIC_DRAW);
		//color [FINE]
            }

            function setupWebGL()
            {
                gl.clearColor(0.0, 0.0, 0.0, 1.0);
                gl.clear(gl.COLOR_BUFFER_BIT);
            }

            function drawScene()
            {
                //VERTICI
                vertexPositionAttribute = gl.getAttribLocation(glProgram, "aVertexPosition");
                gl.bindBuffer(gl.ARRAY_BUFFER, trianglesVerticeBuffer);
                gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
                gl.enableVertexAttribArray(vertexPositionAttribute);

                //color [INIZIO]
		//COLORI
		vertexColorAttribute = gl.getAttribLocation(glProgram, "aVertexColor");
                gl.bindBuffer(gl.ARRAY_BUFFER, trianglesColorBuffer);
                gl.vertexAttribPointer(vertexColorAttribute, 3, gl.FLOAT, false, 0, 0);
                gl.enableVertexAttribArray(vertexColorAttribute);
		//color [FINE]

                gl.drawArrays(gl.TRIANGLES, 0, 6); //visualizza tutti e due i triangoli
            }
        </script>
    </head>
    <body onLoad="initWebGL()" >
        <div class="parent">
            <div class="child">
                <canvas id="my-canvas" width="600" height="400">
                    Il tuo browser non supporta l'elemento canvas di HTML5
                </canvas>
            </div>
        </div>
    </body>
</html>
Diamo ora una breve spiegazione di quanto scritto nel codice precedente.
La prima operazione che facciamo è quella di attribuire i colori ad ogni singolo vertice (x,y,z) attraverso le righe di codice aggiunte nella funzione setupBuffers(). La corrispondenza tra assegnazione del vertice all'interno del buffer trianglesVerticeBuffer ed assegnazione del colore in trianglesColorBuffer dovrà essere precisa.
Attraverso la funzione drawScene(), ogni vertice ed ogni colore corrispondente verrà passato al vertex shader, nel quale dovrà essere quindi definita la struttura per ospitare tali colori (aVertexColor) come precedentemente fatto per i vertici. Questi valori dovranno essere infine passati al fragment shader attraverso la variabile vColor. Come detto in precedenza i colori vengono gestiti da quest'ultimo shader attraverso la valorizzazione di gl_FragColor.

Proviamo ora a modificare il codice precedente per creare un effetto di cambio colore con un intervallo da noi settato a 100ms. Questo servirà a capire anche come iniziare a creare un'animazione all'interno del nostro mondo 3D anche se la funzione che utilizzeremo per la parte di codice che cicla continuamente (loop) non sarà quella utilizzata in questo esempio.

Per realizzare tale esempio modificheremo poche funzioni ed inseriremo una variabile che chiameremo delta_color da incrementare ad ogni ciclo per modificare i valori dei colori assegnati ad alcuni vertici.
Di seguito riportiamo solamente le funzioni modificate.

            var delta_color = 0.0; //color animation

            function initWebGL()
            {
                canvas = document.getElementById("my-canvas");
                try{
                    gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
                } catch(e) {
                }
			
                if(gl)
                {
		    	initShaders();
	                setupBuffers(); //color animation
		        (function animLoop(){ //color animation
				setTimeout(animLoop, 100); //ritardo di 100 ms (1000 = 1 secondo)
				setupDynamicBuffers(); //color animation
                                setupWebGL();
                                drawScene();
                    })(); //color animation
                }else{
		    		alert("ERRORE: Il tuo Browser non supporta WebGL");
	        	}
            }
            function setupBuffers()
            {
                //VERTICI
                var triangleVertices = [
                    //triangolo sinistro
                    -0.5, 0.5, 0.0, //vertice 0
                     0.0, 0.0, 0.0, //vertice 1
                    -0.5, -0.5, 0.0, //vertice 2
                    //triangolo destro
                    0.5, 0.5, 0.0, //vertice 3
                    0.0, 0.5, 0.0, //vertice 4
                    0.5, -0.2, 0.0 //vertice 5
                ];
                trianglesVerticeBuffer = gl.createBuffer();
                gl.bindBuffer(gl.ARRAY_BUFFER, trianglesVerticeBuffer);
                gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleVertices), gl.STATIC_DRAW);
            }

            function setupDynamicBuffers()
            {
		//color [INIZIO]
		if(delta_color > 1.0) delta_color = 0.0;
		//COLORI
		var triangleVerticesColors = [
                    //triangolo sinistro
                    delta_color, delta_color, 0.0, 
                    delta_color, delta_color, delta_color,
                    delta_color, 0.0, 0.0,
                    //triangolo destro
                    delta_color, delta_color, delta_color,
                    delta_color, delta_color, delta_color,
                    0.0, delta_color, delta_color
                ];
		delta_color +=  0.1;
				
                trianglesColorBuffer = gl.createBuffer();
                gl.bindBuffer(gl.ARRAY_BUFFER, trianglesColorBuffer);
                gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleVerticesColors), gl.STATIC_DRAW);
		//color [FINE]
            }


< lezione precedente