U ve?ini aplikacija sa 3D grafikom nije dovoljno iscrtati figure koriste?i samo boje (solid colors) ve? se na povr?ine figura postavljaju odre?ene teksture.
Tekstura je slika za koju se defini?e kako se povezuje sa vertex-ima odnosno pikselima rezultuju?e slike. Ukoliko je zadatak postaviti teksturu na kocku onda je potrebno obezbediti sliku teksture i definisati koje ta?ke teksture odgovaraju svakom od verteksa. Na osnovu odre?enih osobina se vrednosti za sve piksele izme?u interpoliraju, sli?no kao ?to se de?ava sa primenom boja u prethodom primeru.
Kako se radi sa teksturama u WebGL ?e biti prikazano u narednom primeru. Rezultat koji se dobija na kraju je prikazan na slici ispod.
Prvo je, kao i do sada, potrebno izvr?iti izmene na shader programima.
<script id="shader-vertex" type="x-shader/x-vertex"> attribute vec2 aVertexTexture; attribute vec3 aVertexPosition; uniform mat4 mvMatrix; uniform mat4 pMatrix; varying vec2 vTexture; void main() { gl_Position = pMatrix * mvMatrix * vec4(aVertexPosition, 1); vTexture = aVertexTexture; } </script> <script id="shader-fragment" type="x-shader/x-fragment"> varying highp vec2 vTexture; uniform sampler2D uSampler; void main() { highp vec4 textureColor = texture2D(uSampler, vTexture); gl_FragColor = texture2D(uSampler, vTexture); } </script>
Vertex shader je sli?an kao u prethodnom primeru s tim da se sada kroz varying promenljivu do fragment shader-a ne prenosi boja ve? podatak o teksturi. U nastavku ?e biti poja?njeno o kakvim se podacima radi.
U fragment shader-u se pored varyng promenljive pojavljue nova uniform promenljiva uSampler tipa sampler2D
.
Ova promenljiva sar?i deo teksture (slike), a pozivom funkcije texture2D
se iz te teksture dobija boja za odgovaraju?i piksel
(definisan sa varyng
promenljivom popunjenom u vertex shader-u).
U ovom radu predstavljaju najjednostavniji slu?а?evi kori??enja i detalji o kori??enju tekstura u 3D grafici izlaze van okvira ovog rada s obzirom na ?irinu te teme.
Nakon definicije shader-a defini?ese bafer koji, sli?no kao u primeru sa bojama, nosi informaciju o tome kako se tekstura povezuje sa vertexima.
textureCoordinates = [ 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0 ]; txBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, txBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates), gl.STATIC_DRAW);
Za svaki vertex svake strane kocke je definisano koja koordinata slike odgovara tom vertex-u. Vidi se, na osnovu vrednosti u baferu, da ?e svaka strana kocke biti iscrtana na isti na?in.
Pre pozivanja funkcije za iscrtavanje potrebno je inicijalizovati teksturu.
function initTextures() { cubeTexture = gl.createTexture(); cubeImage = new Image(); cubeImage.src = "brick4.png"; cubeImage.onload = function () { handleTexture(cubeImage, cubeTexture); } } function handleTexture(image, texture) { gl.bindTexture(gl.TEXTURE_2D, texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.bindTexture(gl.TEXTURE_2D, null); }
Funkcijom gl.createTexture()
se kreira WebGLTexture
objekat.
Funkcija koja dalje inicijalizuje teksturu se mo?e pozvati tek kada se slika koja joj odgovara kompletno u?ita.
Kao ?to se koristi naredba bindBuffer
da bi se ozna?io aktuelni bafer
tako se daje i instrukcija da za trenutnu teksturu postavi novokreirana teksturu pozivom funkcije bindTexture
.
Funkcija texImage2D
je zadu?ena za u?itavanje slike u radnu memoriju.
Parametri koji se u nastavku postavljaju kontroli?u pona?anje teksture u razli?itim situacijama.
Postoje razli?iti parametri, a u primeru se postavljaju parametri (filteri) koji kontroli?u skaliranje slike.
Oni daju instrukciju WebGL-u kako da iscrta teksturu na povr?ini koja je ve?a (gl.TEXTURE_MAG_FILTER
) odnosno manja (gl.TEXTURE_MAG_FILTER
)
od slike koja defini?e teksturu. U primeru se koristi funkcija NEAREST
za definisanje ovog algoritma, ali postoje i druge funkcije.
Na kraju je jo? potrebno obezbediti pristup uniform promenljivi uSampler
kao ?to je bio slu?aj i sa ostalim ovakvim promenljivama.
gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, cubeTexture); var sh_sampler = gl.getUniformLocation(shaderProgram, "uSampler"); gl.uniform1i(sh_sampler, 0);
WebGL mo?e u jednom momentu (jedan poziv funkcije drawElemets
) da radi sa najvi?e 32 teksture.
One su ozna?ene konstantama od TEXTURE0
do TEXTURE31
i funkcijom activeTexture
se postavlja prvu od tih tekstura na pripremljenu.
Ukoliko biste ovako kreirali aplikaciju verovatno ne biste dobili dobar rezultat. Umesto kocke sa teksturom bila bi prikazana crna kocka.
U radu sa teksturama, pogotovo kod po?etnika, se javlja nekoliko tipi?nih gre?aka koje su navedene u nastavku.
WebGL aplikacije sastoje iz JavaScript i GLSL koda. Za debug dela koda koji je pisan u JavaScriptu se mogu koristiti neki od standardnih alata, a jedan od njih je Google Developer Tools dostupan u Chrome browser-u. Ukoliko se pogleda poruka na konzoli ovog alata dobija se slede?a poruka o gre?ci.
WebGLRenderingContext]RENDER WARNING: texture bound to texture unit 0 is not renderable.
It maybe non-power-of-2 and have incompatible texture filtering or is not 'texture complete'.
U poruci su navedena 3 mogu?a razloga za neuspe?no renderovanje. U navedenom primeru je u pitanju tre?i razlog, odnosno tekstura nije kompletna u?itana.
Iz navedenog koda na listingu se mo?e zaklju?iti da se funkcija initTextures()
poziva pre funkcije drawScene()
.
U funkciji initTextures()
se u?itava slika i ukoliko slika nije zavr?ila sa u?itavanjem ne?e se izvr?iti ni handleTexture()
.
Funkcija drawScene
, asinhorno u odnosu na u?itavanje slike, nastavlja sa izvr?avanjem, ali WebGL (u ve?ini slu?ajeva) ne?e imati valdinu teksturu za iscrtavanje.
Re?enje ovog problema mo?e biti takvo da se postavi neka takozvana flag vrednost koja ozna?ava kada je u?itavanje zavr?eno
i da se tek tada pozove funkcija drawScene
.
Drugo re?enje je da se na po?etku u?ita slika, a da se funkcija za iscrtavanje pozove nakon uspe?nog u?itavanja. Ovo re?enje mo?e dovesti do odre?enog odlaganja prikazivanja (delay-a) ali se dobija ispravan rezultat. Naravno, postoje i druga re?enja koja zavise od aplikacije i programera.
function initTextures() { cubeTexture = gl.createTexture(); handleTexture(cubeImage, cubeTexture); } function handleTexture(image, texture) { gl.bindTexture(gl.TEXTURE_2D, texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.bindTexture(gl.TEXTURE_2D, null); } function start() { var canvas = document.getElementById("canvas_s1"); initWebGL(canvas); initShaders(); initBuffers(); initTextures(); initMatrices(); drawScene(); } function prepareAndStart() { cubeImage = new Image(); cubeImage.src = "brick4.png"; cubeImage.onload = function () { start(); } }
Ukoliko slika koja se koristi za teksturu nije sa istog domena gde i aplikacija mo?e do?i do naru?avanja sigurnosti i pojave gre?ke. Zavisno od browsera- se, mo?e se koristiti slika sa drugog domena, ali tako da za ovaj zahtev postoji takozvana CORS (Cross origin resource sharing) dozvola.
Ukoliko browser ima cross-domain restrikciju problem se mo?e javiti i ukoliko se stranica u kojoj je implementirana aplikacija otvara iz fajl sistema, a ne preko servera. U tim situacija u?itavanje slike (iako je mo?da iz istog foldera) ne prolazi cross-domain kontrolu.
U Chrome browser-u se, na primer, u consoli u ovim slu?ajevima mo?e videti ovakva gre?ka:
Uncaught SecurityError: An attempt was made to break through the security policy of the user agent.
Re?enje ovog problema je da se aplikacija postavi na neki server (mo?e i lokalni) i da joj se pristupa preko http protokola.
WebGL ima samo delimi?nu podr?ku za takozvane NPOT teksture. NPOT je skra?eno od Non-power-of-two i odnosi se na teksture ?ije dimenzije ne predstavljaju stepen broja 2. Ukoliko bi se u prethodnom primeru koristila ovakva tekstura u consol-i bi se dobila slede?a poruka:
WebGL: drawElements: texture bound to texture unit 0 is not renderable.
It maybe non-power-of-2 and have incompatible texture filtering or is not 'texture complete'.
Or the texture is Float or Half Float type with linear filtering
while OES_float_linear or OES_half_float_linear extension is not enabled.