Es mostren els missatges amb l'etiqueta de comentaris OpenGL. Mostrar tots els missatges
Es mostren els missatges amb l'etiqueta de comentaris OpenGL. Mostrar tots els missatges

dissabte, 5 de febrer del 2011

Suficiència d'OpenGL ES 2.0 Part II (Framebuffers)

Continuant el post anterior, i una mica en la mateixa línea, avordem un tema "important", sobretot si es tracta de fer efectes abançats: Els Framebuffers.

D'acord, primer de tot, necessitem entendre una mica de teoria sobre on dibuixem les coses i quins espais de memòria ens són disponibles.

Aquests espais de memòria als que em refereixo són els buffers de renderització; allà on guardem informació de color, profunditat, etc... En l'exemple més senzill de OpenGL, disposem de 2 buffers: el front buffer i el back buffer. Aquests buffers guarden informació de color (RGB i a vegades un Alpha que indica "quantitat d'opacitat"), el front buffer es mostra per pantalla mentre que el backbuffer és on dibuixem. Un cop acavem de dibuixar un frame, intercanviem el front i el back, així no es veu mai "mentre dibuixem" sinó que veiem sempre dibuixos sencers.

Addicionalment se li poden afegir 2 buffers: el depth buffer i el stencil buffer. Sobre l'stencil no en parlarè gaire, per no dir gens; mai l'he fet servir i no sé ben bé per a què serveix, però el depth buffer és molt útil. Aquest búffer de profunditat (depth) guarda la profunditat on se suposa que cada pixel està pintat. Això ens permet un mètode senzill per renderitzar les coses que estàn més a prop: si anem a dibuixar una cosa més llunyana que la que hi ha dibuixada, no la dibuixem. Si és més propera, la dibuixem i actualitzem el depth.

Molt bé, ara veiem que per renderitzar necessitem un buffer de color i, opcionalment, un de profunditat i/o un de stencil. Per defecte normalment en tenim un de cada (més el front buffer), però a vegades ens interessa dibuixar coses "fora de la pantalla". Per exemple: Imaginem-nos que estem en un joc, en una sala amb càmeres de videovigilància. Aquestes càmeres mostren, en temps real, altres habitacions. Per a acomplir aquest efecte, podem renderitzar aquestes altres habitacions en una textura cada una i, posteriorment, renderitzar la textura a la càmera.

Això és el que es coneix com a off-screen rendering, i consisteix en crear un "Framebuffer object", que és una col·lecció de buffers de distínta índole, i dir-li a l'openGL que renderitzi sobre aquest. Els buffer poden ser de 2 maneres: o bé una textura, o bé un Renderbuffer. La diferència essencial és que no tenim fàcilment accessible la informació dels Renderbuffers, mentre que una textura la tenim sempre a mà.

Codi:

//------------------------------------------------------------

//Primer creem els Renderbuffers, que poden servir com a color,
//profunditat o stencil
GLuint renderbuffers[2];
glGenRenderbuffers(2, &(renderbuffers[0]));

//activem el renderbuffer
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffers[0]);

//definim com és el renderbuffer
glRenderbufferStorage(GL_RENDERBUFFER,
                      format,         //Format intern
                      width, height); //dimensions

// Formats estàndard:
// GL_RGB565, GL_RGBA4, GL_RGB5_A1,         -- Color
// GL_DEPTH_COMPONENT16, GL_STENCIL_INDEX8
//
// Formats amb l'extensió GL_OES_rgb8_rgba8
// GL_RGB8_OES, GL_RGBA8_OES
//
// Formats amb l'extensió GL_OES_depth24 / GL_OES_depth32
// GL_DEPTH_COMPONENT24_OES, GL_DEPTH_COMPONENT32_OES
//
// Formats amb l'extensió GL_OES_stencil1 / GL_OES_stencil4
// GL_STENCIL_INDEX1, GL_STENCIL_INDEX4
//
// Formats amb l'extensió GL_OES_packed_depth_stencil
// GL_DEPTH24_STENCIL8_OES

// Les dimensions cal que siguin iguals o menors a
// GL_MAX_RENDERBUFFER_SIZE
 
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffers[0]);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 800, 600);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffers[1]);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 800, 600);



//Crear identificadors de frambuffer,
// de forma similar a les textures:
GLuint framebufferID;
glGenFramebuffers(1,                //nombre d'id a generar
                  &framebufferID);

// Activar el framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);

// Aquesta funció ens serveix tant per especificar les propietats
//del framebuffer (punts d'anclatge) com per indicar que volem
//que es renderitzi a aquest. Per tornar a renderitzar als buffer
//"normals" hem de fer:
glBindFramebuffer(GL_FRAMEBUFFER, 0);

// Lligar un renderbuffer al framebuffer
gl_FramebufferRenderbuffer(GL_FRAMEBUFFER,
                           punt_d_anclatge,
                           GL_RENDERBUFFER,
                           renderbufferID); //0 per no anclar-hi res

// Punts d'anclatge:
// GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT
// Com a nota important, saber que en OpenGL estàndard hi ha més
//punts d'anclatge, concretament, els de color 1, 2, 3... Això 
//serveix per poder pintar a més d'un lloc a la vegada, i ho 
//explicarè als fragment shaders.

//Continuant amb l'exemple
gl_FramebufferRenderbuffer(GL_FRAMEBUFFER,
                           GL_COLOR_ATTACHMENT0,
                           GL_RENDERBUFFER,
                           renderbuffers[0]);
gl_FramebufferRenderbuffer(GL_FRAMEBUFFER,
                           GL_DEPTH_ATTACHMENT,
                           GL_RENDERBUFFER,
                           renderbuffers[1]);
gl_FramebufferRenderbuffer(GL_FRAMEBUFFER,
                           GL_STENCIL_ATTACHMENT,
                           GL_RENDERBUFFER,
                           0);

//Comprovar errors
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);

//Result pot ser:
// GL_FRAMEBUFFER_COMPLETE -- OK
// GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT -- Algun error
//                                      -- en algun anclatge.
// GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT -- No hi ha cap
//                                              --anclatge
// GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS -- Anclatges amb
//                                      -- dimensions diferents
// GL_FRAMEBUFFER_INCOMPLETE_FORMATS -- Algun format no suportat
// GL_FRAMEBUFFER_UNSUPPORTED -- Combinació d'anclatges i formats
//                            -- no suportats.


// Com s'ha dit anteriorment, la gràcia de tot plegat és la de
//dibuixar a una textura, per això anclem una textura al 
//framebuffer com ho anclavem un renderbuffer
glFramebufferTexture2D(GL_FRAMEBUFFER,
                       punt_d_anclatge, //Només color o profunditat
                       target,
                       texture_id,
                       0); //nivell mipmap.

// Targets:
// GL_TEXTURE_2D, GL_TEXTURE_CUBE_MAP_POSITIVE_X, ...

//EXTRA: Textures 3D
glFramebufferTexture3DOES(GL_FRAMEBUFFER,
                          GL_COLOR_ATTACHMENT0, //No depth
                          GL_TEXTURE_3D_OES,
                          texture_id,
                          0, //nivell mipmap.
                          z_offset); //profunditat

// Finalment i nogensmenys important:
glDeleteRenderbuffers(2, &(renderbuffers[0])); //La pots liar si 
                           //encara està en ús en un framebuffer
glDeleteFramebuffers(1, &framebufferID);

//------------------------------------------------------------


Per altra banda, i de manera molt ràpida, m'agradaria introduir una altra funcionalitat molt similar, encara que amb un rendiment més baix (tot i que a vegades necessitem fer-la servir en front de framebuffers). Aquesta és simplement copiar el contingut de l'actual framebuffer (el back buffer en cas de no tenir un framebuffer activat) a una textura.


//------------------------------------------------------------
glCopyTexImage2D(target, // GL_TEXTURE_2D, GL_TEXTURE_CUBE...
                 0,      // nivell mipmap
                 texture_format,
                 x, y,   // On de la pantalla comencem a copiar
                 w, h,   // tamany de la textura / àrea a copiar
                 0);     // border

// El format pot ser
// GL_RGBA, GL_RGB, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_ALPHA
// A més, en OpenGL estàndard (MAI en ES) es pot copiar el depth
// buffer si s'especifica el format
// GL_DEPTH_COMPONENT


// Per copiar a una subregió d'una textura
glCopyTexSubImage2D(target, 0, texture_format,
                    xoffset, // coordenades on començar a copiar
                    yoffset, // a la textura
                    x, y, w, h, 0);

// Les 3D només poden fer servir la "SubImage"
glCopyTexSubImage3DOES(target, 0, texture_format,
                       xoffset, yoffset,
                       zoffset, // coordenada z a on es copia
                       x, y, w, h, 0);
//------------------------------------------------------------

 I fins aqui tots els temes relacionats amb renderitzar múltiples vegades un frame. Amb això es poden aconseguir efectes xulos, ja sigui:
Glow, Blur, HDR, Ombres (Shadowmap)...

divendres, 28 de gener del 2011

Guia ràpida d'OpenGL (ES)

Molts bons el que sigui.

He estat llegint un llibre d'OpenGL ES 2.0, la versió per sistemes integrats d'OpenGL. La gràcia (explicat ràpidament) de la versió ES és que és essencialment el mateix que la versió "mare" però simplificada, o sigui, amb totes les funcions "redundants" eliminades. Un programa fet doncs, en OpenGL ES és pràcticament un 100% portable a OpenGL normal (excepte que les extensions són diferens, petits detallets).

Què vui fer jo ara? Escriure'm un resumet del llibre, ja que l'he de tornar a la biblioteca eventualment.

Així que, què, saveu com va OpenGL, més o menys? El de veritat, el dels Shaders, eh!

Essencialment, el que un ha de fer perque es dibuixi alguna cosa és especificar un seguit de geometria (Triangles, línees o "punts"), que recorre un seguit de transformacions fins que són pintades. Quines transformacions?

Pipeline:
  1. Vertex Shader: Agafa paràmetres que li dónes directament i les passes a la següent fase. El paràmetre important a passar són les coordenades de "clip" (coordenades de retallada), que essencialment són les coordenades dels vertexos normalitzades.
  2. Rasterització: Es divideix en diferents fases, l'objectiu és trobar els fragments que faràn el "Fragment Shader.
    1. Divisió de perspectiva: S'agafen les coordenades de clip (xc, yc, zc, wc) i es transformen a coordenades de Device (xd, yd, zd) = (xc/wc, yc/wc, zc/wc). Aquelles coordenades fóra del rang [-1, 1] són descartades.
    2. Transformació de Viewport: Transforma les anteriors coordenades a coordenades de pantalla (les x i y) i també transforma proporcionalment les z a un rang que tu mateix has especificat.
    3. Rasterització: Un cop tens cada vèrtex situat en un pixel, "s'envien" tots els pixels intermitjos al Fragment Shader. Prèviament es fa el culling (Eliminar els triangles que estiguin mirant en contra la càmera.
  3. Fragment Shader: Agafa paràmetres interpolats del Vertex Shader i els utilitza per definir el color del fragment. A més disposa de les coordenades del fragment en pantalla, les coordenades dels point-sprites, i si la cara mira o no a càmera.
  4. Operacions per fragment: També n'hi ha vàries.
    1. Scissor Testing: Fa que només es pintin pixels definits en un rectangle definit.
    2. Stencil Test: No ho he entès mai, ni mai he vist cap exemple de ningú fent-ho servir.
    3. Depth Test: Decideix si un fragment es dibuixa o no segons la seva profunditat.
    4. Blending: Barreja el color amb el color anterior.
    5. Dithering: Tampoc ho he fet servir mai.
Coses Ràpides que cal saber de shaders:

Els Shaders tenen 3 tipus de "variables globals":
  • Uniform: Són paràmetres que comparteix tota la geometria, on la geometria és allò que dibuixes d'un cop, un model, vamos. Exemples serien les matrius de transformació, les llums, etc...
  • Attribute: Són paràmetres per vèrtex. Per exemple Coordenades-Objecte, Normals, coordenades de textura i altres.
  • Varying: Aquestes s'escriuen al Vertex Shader i es llegeixen al Fragment Shader. És la manera de passar informació de l'una a l'altre.
Cada shader es defineix amb una funció "main" de tota la vida.

Com es creen els shaders? "Fàcil"!

Primer cal crear ambdós shaders:


//---------------------------------------------------------
GLenum type; //GL_VERTEX_SHADER | GL_FRAGMENT_SHADER

GLuint shader_object = glCreateShader(type);

glShaderSource( shader_object, 
                count,   // número d'strings que passes
                strings, // array de char*, amb el codi
                lenghts);// array de int, amb la llargada de cada
                         // string. Si és NULL se suposa que
                         // acaven en '\0'. Si un element és
                         // negatiu se suposa que aquell acava
                         // en '\0'.

glCompileShader( shader_object );
glGetShaderiv( shader_object,
               param,    // GL_COMPILE_STATUS
                         // GL_DELETE_STATUS
                         // GL_INFO_LOG_LENGTH
                         // GL_SHADER_SOURCE_STATUS
                         // GL_SHADER_TYPE
               &result);

//Comprovar que el compile status sigui true, sino agafar el log
if(!result) {
  int len;
  glGetShaderiv( shader_object, GL_INFO_LOG_LENGTH, len);

  char* infoLog = new char[len];
  glGetShaderInfoLog(shader_object,
                     len,
                     NULL, //llargada, com que ja la savem, null
                     infoLog);

  printf("Log: %s", infoLog);
}

//Eventualment
glDeleteShader(shader_object);
//---------------------------------------------------------

Un cop creats els shaders (Vertex i Fragment), cal lligar-los:

//---------------------------------------------------------
GLuint vertex_shader, fragment_shader;

GLuint shader_program = glCreateProgram();

glAttachShader(shader_program, vertex_shader);
glAttachShader(shader_program, fragment_shader);

//glDetachShader( ... )
glLinkProgram(shader_program); //Fer el link de les "coses"

//glValidateProgram(shader_program) serveix per validar i agafar
//info per el log, però és lent i només s'ha d'usar per "debugar"
//i buscar errors.

glProgramiv( ... ); 
//similar al de shader objects, amb paràmetres
//GL_ACTIVE_ATTRIBUTES             --nombre d'atributs
//GL_ACTIVE_ATTRIBUTES_MAX_LENGTH  --len de l'atribut més llarg

//GL_ACTIVE_UNIFORMS               -- "
//GL_ACTIVE_UNIFORMS_MAX_LENGTH    -- "
//GL_ATTACHED_SHADERS              -- nombre de shaders afegits
//GL_DELETE_STATUS                 -- si es vol eliminar
//GL_INFO_LOG_LENGHT               --
//GL_LINK_STATUS                   -- si s'ha linkad bé
//GL_VALIDATE_STATUS               -- si ha validat

glGetProgramInfoLog( ... ); // com el dels objectes.

//Eventualment
glDeleteProgram( shader_program );
//---------------------------------------------------------

Un cop tenim els shaders compilats i tot, cal agafar les localitzacions dels "uniforms". Amb les localitzacions serem capaços d'escriure aquests valors. "Passar-los" al shader.

Hi ha una funció, glGetActiveUniform, que serveix per saber quina "uniform" hi ha en cada posició i quin tipus té, però aquesta no és la manera normal de buscar-ho (ja sabem quines "uniform" hi ha en el shader, l'hem escrit nosaltres!).

GLint uniformLocation = glGetUniformLocation( shader_program, "nom_uniform");

Així tenim la localització, que podem fer servir així:

void glUniform1f(GLint location, GLfloat v0);

Coses a tenir en compte: Per fer-ho servir el shader ha d'estar "actiu" (més, després) i tmb que n'hi ha moltes, de funcions, i es troben a [1].

Els atributs s'agafen de manera similar.

GLint attributeLocation = glGetAttributeLocation( shader_program, "nom_attribute");

Com preparar la "Geometria"

No m'estaré de métodes arcaics per definir geometria i aniré directament al métode més "eficient".

Conceptualment es tracta de pujar a memória VRAM, memòria de la targeta gràfica, totes les dades necessàries per dibuixar la geometria, i posteriorment cridar-la quan calgui. L'algoritme vé a ser el següent:
  1. En una fase de "init", definir i omplir els buffers.
  2. En una fase de "render":
    1. Activar el Shader que s'utilitza ( glUseProgram(program_id); ).
    2. Definir totes les Uniforms que fa servir el Shader
    3. Fer la crida de "Pinta la geometria".
  3. En una fase de "cleanup" eliminar els buffers.
Què són i com funcionen els buffers? Imaginem-nos que volem renderitzar el següent cuadrat:

[0] ----- [1]
 |         |
 |         |
 |         |
[2] ----- [3]

Aquest té 4 vertexos, amb coordenades [0 => (0, 5, 0)], [1 => (5, 5, 0)], [2 => (0, 0, 0)], [3 => (5, 0, 0)], i amb coordenades de textura [0 => (0, 1)], [1 => (1, 1)], [2 => (0, 0)], [3 => (1, 0)]. Podríem fer un array de la següent manera:

float vertex_data[] = {0, 5, 0, 0, 1, //primer vertex
                       5, 5, 0, 1, 1,
                       0, 0, 0, 0, 0,
                       5, 0, 0, 1, 0  //últim vertex
                      };

Per indicar com s'ha de dibuixar, es pot fer indexat. Primer de tot, cal dividir la geometria en triangles, podem fer 2 triangles en aquest cas: el {0, 2, 1} i el {1, 2, 3}. Així li podem indicar a OpenGL que dibuixi la geometria {0, 2, 1, 1, 2, 3}.

uint16 index_data[] = {0, 2, 1, 1, 2, 3};

Abdós buffers es poden guardar a VRAM i posteriorment activar i utilitzar.

Creant Buffers

Aqui expliquem com crear els buffers, o sigui, la part de "init"


//----------------------------------------------------------
GLuint bufferIds[2];

glGenBufferData(2, bufferIds); //Generem 2 buffers

glBindBuffer(GL_ARRAY_BUFFER, bufferIds[0]); //activem el buffer
                                             //com a buffer de
                                             //dades
glBufferData(GL_ARRAY_BUFFER,   
             numVert * sizeVert, //Tamany que tindran les dades
             vertex_data,        //Dades
             GL_STATIC_DRAW);    //Mode. Li dóna pistes a la gràfica
             //GL_STATIC_DRAW  -- escrit 1, usat molt
             //GL_DYNAMIC_DRAW -- escrit molt, usat molt
             //GL_STREAM_DRAW  -- escrit 1, usat poc

           //GL_ELEMENT... serveix per indexos
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferIds[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, index_data, GL_STATIC_DRAW);

//----------------------------------------------------------
Com a nota adicional, sempre es pot re-crear les dades amb "glBufferData" o canviar una part amb "glBufferSubData" (tot i que si l'has creat amb "STATIC_DRAW" puteges al driver).

Emprant Buffers

Aqui expliquem com renderitzar usant buffers, o sigui la crida a "pintar geometria".


//-------------------------------------------------------------

  //Activem el buffer de vertexos que farem servir
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferId);

  //Activem el buffer d'indexos que farem servir
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vertexBufferId);


  //activem els atributs del shader
glEnableVertexAttribArray(position_attrib); 
glEnableVertexAttribArray(texcoord_attrib); 

glVertexAttribPointer(position_attrib,  // atribut que usem
                      3,         //nº de dades a l'atribut
                      GL_FLOAT,  //tipus de dades
                      GL_FALSE,  //normalització
                      vtxStride, //distancia entre un vtx
                                 //  i el següent
                      0);        //distancia entre l'inici del
                                 //  vtx i les dades en qüestió.     

     /**********************************************************\
     *   Sobre la normalització, serveix per quan usem un tipus *
     * de dades diferent a float i volem que al passar-les a    *
     * float OpenGL les transformi al rang [-1, 1] o no.        *
     \**********************************************************/

     /**********************************************************\
     *   Sobre els tipus de dades, poden ser GL_BYTE,           *
     * GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FIXED, *
     * GL_FLOAT, GL_HALF_FLOAT_OES/GL_HALF_FLOAT_ARB            *
     \**********************************************************/

glVertexAttribPointer(texcoord_attrib,

                      2,        
                      GL_FLOAT, 

                      GL_FALSE, 

                      vtxStride,
                      sizeof(float)*3);

glDrawElements(GL_TRIANGLES,      //Tipus de primitiva
               6,                 //nº de indexos
               GL_UNSIGNED_SHORT, //tipus de dades
               0);                //distància entre l'inici del 
                                  //  buffer i el primer index. 

// Tipus de primitives:
//GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP,
//GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN

// Tipus de dades als indexos
//GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT
//La darrera necessita de l'extensió OES_element_index_uint
       
glDisableVertexAttribArray(position_attrib); 
glDisableVertexAttribArray(texcoord_attrib); 
//-------------------------------------------------------------

Textures:

Les textures en OpenGL sempre han estat un petit percal. No m'extendré massa, però coses "importants":


//----------------------------------------------
glGenTextures(num, &id); //genera textures
glDeleteTextures(num, &id); //ejem

  //Activar textura
glBindTexture(tipus, //GL_TEXTURE_2D, GL_TEXTURE_CUBE_MAP
              id);

  //Definir la informació
glTexImage(capa,
           nivell,         //Nivell de mipmap. Deixeu-lo a 0
           internalFormat, //format intern
           width,         
           height,
           border,         //en OpenGL ES, 0.
           externalFormat, //cal que sigui el mateix que l'intern.
           dataType,       //tipus de dades
           data);          //dades, per "files"

// Capa:
// Per textures 2D, GL_TEXTURE_2D
// Per Cubemap s'han de definir totes 6 cares:
// GL_TEXTURE_CUBE_MAP_POSITIVE_X, ...NEGATIVE_X, ...

// Formats:
//GL_RGBA, GL_RGB, GL_LUMINANCE_ALPHA, GL_LUMINANCE, GL_ALPHA
//Pensar en la luminance com a "escala de grisos"

// Tipus de dades:
//GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_4_4_4_4,
//GL_UNSIGNED_SHORT_5_5_5_1, GL_UNSIGNED_SHORT_5_6_5

  //Definir com es filtra la imatge quan es fa més gran.
glTexParameteri( tipus,
                 GL_TEXTURE_MAG_FILTER,
                 param); //GL_NEAREST / GL_LINEAR

  //Definir com es filtra la imatge quan es fa més petita.
glTexParameteri( tipus,
                 GL_TEXTURE_MIN_FILTER,
                 param); 
              //GL_NEAREST, GL_LINEAR
              //GL_NEAREST_MIPMAP_NEAREST x4 combinacions
//Definir què fa una textura quan té una coordenada fora de [0,1]
glTexParameteri( tipus,
                 GL_TEXTURE_WRAP_S, //O t
                 GL_REPEAT); //GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT



glGenerateMipmap(tipus); //Genera automàticament un mipmap per la 
                         //textura activa

//Finalment parlem de com agafar una textura des del shader.
//Per tal de fer-ho, necessitem una "uniform" especial, anomenada
//"sampler" ("sampler2D" o "samplerCube")

//Activem la textura en un la unitat 0:
glActiveTexture(GL_TEXTURE0); //GL_TEXTURE1, ...
glBindTexture(tipus, textureId);

//Fem que el sampler apunti a la unitat 0
glUniform1i(sampler_uniform, 0);

//----------------------------------------------
També hi ha un altre tipus de textura, la textura3D, que és estandard a OpenGL i a OpenGL ES necessita de l'extenció "GL_OES_texture_3D". Funciona pràcticament igual amb una funció extra per carregar-la "glTexImage3D"[OES] amb un paràmetre adicional indicant la profunditat.

Adicionalment podem crear textures de "profunditat" si l'extenció "GL_OES_depth_texture" està disponible. Això serveix essencialment amb el tema dels pBuffers (vist més endavant) i es creen amb un format "GL_DEPTH_COMPONENT" i tipus "GL_UNSIGNED_SHORT" o "GL_UNSIGNED_INT" si estàn disponibles.


Continuaré en un altre moment. Falta per fer:

  • pBuffers
  • Vertex Shaders
  • Fragment Shaders
  • Operacions post-shader
  • Exemples

[1] http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml