Ricerca…
Nozioni di base di texturing
Una texture è una forma di archiviazione dei dati che consente un comodo accesso non solo a particolari voci di dati, ma anche di campionare i punti mescolando (interpolando) più voci insieme.
Nelle trame OpenGL possono essere utilizzate per molte cose, ma più comunemente è mappare un'immagine a un poligono (per esempio un triangolo). Per mappare la trama a un triangolo (o un altro poligono) dobbiamo dire a ogni vertice quale parte della trama corrisponde. Assegniamo una coordinata di trama a ciascun vertice di un poligono e verrà quindi interpolata tra tutti i frammenti di quel poligono. Le coordinate della trama in genere vanno da 0 a 1 negli assi xe come mostrato nell'immagine seguente:
Le coordinate della trama di questo triangolo sono simili a questo:
GLfloat texCoords[] = {
0.0f, 0.0f, // Lower-left corner
1.0f, 0.0f, // Lower-right corner
0.5f, 1.0f // Top-center corner
};
Inserisci tali coordinate in VBO (oggetto buffer vertice) e crea un nuovo attributo per lo shader. Dovresti già avere almeno un attributo per le posizioni dei vertici, quindi crearne un altro per le coordinate della trama.
Generazione di texture
La prima cosa da fare sarà generare un oggetto texture a cui farà riferimento un ID che verrà memorizzato in una trama int unsigned:
GLuint texture;
glGenTextures(1, &texture);
Dopodiché deve essere associato in modo tale che tutti i comandi di texture successivi configureranno questa texture:
glBindTexture(GL_TEXTURE_2D, texture);
Caricamento immagine
Per caricare un'immagine è possibile creare il proprio caricatore di immagini oppure utilizzare una libreria di caricamento dell'immagine come SOIL (libreria di immagini OpenGL semplice) in c ++ o PNGDecoder di TWL in java.
Un esempio di caricamento dell'immagine con SOIL potrebbe essere:
int width, height;
unsigned char* image = SOIL_load_image("image.png", &width, &height, 0, SOIL_LOAD_RGB);
Ora puoi assegnare questa immagine all'oggetto texture:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
Dopodiché dovresti sciogliere l'oggetto texture:
glBindTexture(GL_TEXTURE_2D, 0);
Avvolgi il parametro per le coordinate della trama
Come visto sopra, l'angolo in basso a sinistra della trama ha le coordinate UV (st) (0, 0) e l'angolo in alto a destra della trama ha le coordinate (1, 1), ma le coordinate della trama di una mesh possono essere in qualsiasi intervallo. Per gestirlo, è necessario definire in che modo le coordinate della trama vengono avvolte sulla trama.
Il parametro wrap per la coordinata della trama può essere impostato con glTextureParameter usando GL_TEXTURE_WRAP_S
, GL_TEXTURE_WRAP_T
e GL_TEXTURE_WRAP_R
.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
I parametri possibili sono:
GL_CLAMP_TO_EDGE
fa sì che le coordinate della trama vengano bloccate nell'intervallo [1 / 2N, 1 - 1 / 2N] , dove N è la dimensione della trama nella direzione.GL_CLAMP_TO_BORDER
fa lo stesso diGL_CLAMP_TO_EDGE
, ma nei casi in cui il clamping, i dati di texel recuperati vengono sostituiti con il colore specificato daGL_TEXTURE_BORDER_COLOR
.GL_REPEAT
fa in modo che la parte intera della coordinata della trama sia ignorata. La trama è piastrellata .
-
GL_MIRRORED_REPEAT
: Se la parte intera della coordinata della trama è pari, allora viene ignorata. Al contrario, se la parte intera della coordinata della trama è dispari, la coordinata della trama è impostata su 1 - frac (s) . il / i frattale / i è la parte frazionaria della coordinata della trama. Ciò fa sì che la trama venga specchiata ogni 2 volte.
-
GL_MIRROR_CLAMP_TO_EDGE
causa la ripetizione della coordinata di testo come perGL_MIRRORED_REPEAT
per una rettifica della trama, a quel punto la coordinata daGL_CLAMP_TO_EDGE
come inGL_CLAMP_TO_EDGE
.
Notare che il valore predefinito per GL_TEXTURE_WRAP_S
, GL_TEXTURE_WRAP_T
e GL_TEXTURE_WRAP_R
è GL_REPEAT
.
Applicazione di trame
L'ultima cosa da fare è legare la trama prima della chiamata dell'estrazione:
glBindTexture(GL_TEXTURE_2D, texture);
Texture e Framebuffer
È possibile allegare un'immagine in una texture a un framebuffer, in modo da poter eseguire il rendering direttamente su tale texture.
glGenFramebuffers (1, &framebuffer);
glBindFramebuffer (GL_FRAMEBUFFER, framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
texture,
0);
Nota: non è possibile leggere e scrivere dalla stessa trama nello stesso task di rendering, perché chiama un comportamento non definito. Ma puoi usare: glTextureBarrier()
tra le chiamate di rendering per questo.
Leggi i dati della trama
Puoi leggere i dati delle texture con la funzione glGetTexImage
:
char *outBuffer = malloc(buf_size);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glGetTexImage(GL_TEXTURE_2D,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
outBuffer);
Nota: il tipo e il formato della trama sono solo per esempio e possono essere diversi.
Usando PBO
Se si associa un buffer a GL_PIXEL_UNPACK_BUFFER
il parametro data
in glTexImage2D
è un offset in quel buffer.
Ciò significa che glTexImage2D non ha bisogno di attendere che tutti i dati vengano copiati dalla memoria dell'applicazione prima che possa tornare, riducendo il sovraccarico nel thread principale.
glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glBufferData(GL_PIXEL_UNPACK_BUFFER, width*height*3, NULL, GL_STREAM_DRAW);
void* mappedBuffer = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
//write data into the mapped buffer, possibly in another thread.
int width, height;
unsigned char* image = SOIL_load_image("image.png", &width, &height, 0, SOIL_LOAD_RGB);
memcpy(mappedBuffer, image, width*height*3);
SOIL_free_image(image);
// after reading is complete back on the main thread
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
Ma dove i PBO sono davvero brillanti è quando hai bisogno di leggere il risultato di un rendering nella memoria dell'applicazione. Per leggere i dati dei pixel in un buffer, collegarli a GL_PIXEL_PACK_BUFFER
quindi il parametro dei dati di glGetTexImage
sarà un offset in tale buffer:
glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glBufferData(GL_PIXEL_PACK_BUFFER, buf_size, NULL, GL_STREAM_COPY);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glGetTexImage(GL_TEXTURE_2D,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
null);
//ensure we don't try and read data before the transfer is complete
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
// then regularly check for completion
GLint result;
glGetSynciv(sync, GL_SYNC_STATUS, sizeof(result), NULL, &result);
if(result == GL_SIGNALED){
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
void* mappedBuffer = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
//now mapped buffer contains the pixel data
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
}
Usare le trame negli shader GLSL
Il vertex shader accetta solo le coordinate della texture come un attributo vertice e inoltra le coordinate allo shader del frammento. Per impostazione predefinita, garantisce anche che il frammento riceva le coordinate correttamente interpolate in base alla sua posizione in un triangolo:
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoordIn;
out vec2 texCoordOut;
void main()
{
gl_Position = vec4(position, 1.0f);
texCoordOut = texCoordIn;
}
Il framment shader accetta quindi la variabile di output texCoord
come variabile di input. È quindi possibile aggiungere una texture allo shader di frammenti dichiarando un uniform sampler2D
. Per campionare un frammento della trama, utilizziamo una texture
funzione incorporata che ha due parametri. La prima è la trama che vogliamo campionare e la seconda è la coordinata di questa texture:
in vec2 texCoordOut;
out vec4 color;
uniform sampler2D image;
void main()
{
color = texture(image, texCoordOut);
}
Nota che l' image
non è l'ID della trama diretta qui. È l'id dell'unità texture che verrà campionata. A loro volta, le trame non sono legate direttamente ai programmi; sono legati alle unità di tessitura. Ciò si ottiene rendendo l'unità texture attiva con glActiveTexture
, quindi chiamando glBindTexture
avrà effetto su questa particolare unità di texture. Tuttavia, poiché l'unità di trama predefinita è l'unità di trama 0
, i programmi che utilizzano una trama possono essere semplificati omettendo questa chiamata.