opengl
Teksturowanie
Szukaj…
Podstawy teksturowania
Tekstura jest formą przechowywania danych, która umożliwia wygodny dostęp nie tylko do poszczególnych wpisów danych, ale także do punktów próbnych mieszających (interpolujących) wiele wpisów razem.
W OpenGL tekstur można używać do wielu rzeczy, ale najczęściej jest to mapowanie obrazu na wielokąt (na przykład trójkąt). Aby zmapować teksturę na trójkąt (lub inny wielokąt), musimy powiedzieć każdemu wierzchołkowi, której części tekstury odpowiada. Przypisujemy współrzędną tekstury do każdego wierzchołka wielokąta, a następnie interpolowana jest między wszystkimi fragmentami tego wielokąta. Współrzędne tekstury zwykle wynoszą od 0 do 1 na osi xiy, jak pokazano na poniższym obrazku:
Współrzędne tekstury tego trójkąta wyglądałyby następująco:
GLfloat texCoords[] = {
0.0f, 0.0f, // Lower-left corner
1.0f, 0.0f, // Lower-right corner
0.5f, 1.0f // Top-center corner
};
Umieść te współrzędne w VBO (obiekcie bufora wierzchołków) i utwórz nowy atrybut modułu cieniującego. Powinieneś już mieć co najmniej jeden atrybut dla pozycji wierzchołków, więc utwórz kolejny dla współrzędnych tekstury.
Generowanie tekstury
Pierwszą rzeczą do zrobienia będzie wygenerowanie obiektu tekstury, do którego będzie odwoływał się identyfikator, który będzie przechowywany w teksturze bez znaku int:
GLuint texture;
glGenTextures(1, &texture);
Następnie należy go związać, aby wszystkie kolejne polecenia tekstury skonfigurowały tę teksturę:
glBindTexture(GL_TEXTURE_2D, texture);
Ładowanie obrazu
Aby załadować obraz, możesz utworzyć własny moduł ładujący obraz lub użyć biblioteki ładującej obraz, takiej jak SOIL (prosta biblioteka obrazów OpenGL) w c ++ lub PNGDecoder TWL w Javie.
Przykładem ładowania obrazu za pomocą SOIL byłoby:
int width, height;
unsigned char* image = SOIL_load_image("image.png", &width, &height, 0, SOIL_LOAD_RGB);
Teraz możesz przypisać ten obraz do obiektu tekstury:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
Następnie powinieneś rozpiąć obiekt tekstury:
glBindTexture(GL_TEXTURE_2D, 0);
Zawiń parametr dla współrzędnych tekstury
Jak widać powyżej, lewy dolny róg tekstury ma współrzędne UV (st) (0, 0), a prawy górny róg tekstury ma współrzędne (1, 1), ale współrzędne tekstury siatki mogą być w dowolny zakres. Aby sobie z tym poradzić, należy zdefiniować sposób zawijania współrzędnych tekstury do tekstury.
Parametr zawijania dla współrzędnych tekstury można ustawić za pomocą GL_TEXTURE_WRAP_S
glTextureParameter za pomocą GL_TEXTURE_WRAP_S
, GL_TEXTURE_WRAP_T
i 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);
Możliwe parametry to:
GL_CLAMP_TO_EDGE
powoduje zaciśnięcie współrzędnych tekstury do zakresu [1 / 2N, 1 - 1 / 2N] , gdzie N jest rozmiarem tekstury w kierunku.GL_CLAMP_TO_BORDER
robi to samo coGL_CLAMP_TO_EDGE
, ale w przypadkach, gdy zaciskanie, pobrane daneGL_TEXTURE_BORDER_COLOR
są zastępowane kolorem określonym przezGL_TEXTURE_BORDER_COLOR
.GL_REPEAT
powoduje, że część całkowita współrzędnych tekstury jest ignorowana. Tekstura jest wyłożona kafelkami .
-
GL_MIRRORED_REPEAT
: Jeśli liczba całkowita współrzędnej tekstury jest parzysta, to jest ona ignorowana. W przeciwieństwie do tego, jeśli liczba całkowita współrzędnej tekstury jest nieparzysta, wówczas współrzędna tekstury jest ustawiona na 1 - frac (s) . frakt (s) jest ułamkową częścią współrzędnej tekstury. To powoduje, że tekstura jest odbijana co drugi raz.
-
GL_MIRROR_CLAMP_TO_EDGE
powoduje, że współrzędnaGL_MIRRORED_REPEAT
jest powtarzana jak dlaGL_MIRRORED_REPEAT
dla jednego powtórzenia tekstury, w którym to punkcie współrzędna ma zostać zaciśnięta jak wGL_CLAMP_TO_EDGE
.
Zwróć uwagę, że wartość domyślna dla GL_TEXTURE_WRAP_S
, GL_TEXTURE_WRAP_T
i GL_TEXTURE_WRAP_R
to GL_REPEAT
.
Stosowanie tekstur
Ostatnią rzeczą do zrobienia jest związanie tekstury przed wywołaniem losowania:
glBindTexture(GL_TEXTURE_2D, texture);
Tekstura i bufor ramki
Możesz dołączyć obraz z teksturą do bufora ramki, abyś mógł renderować bezpośrednio do tej tekstury.
glGenFramebuffers (1, &framebuffer);
glBindFramebuffer (GL_FRAMEBUFFER, framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
texture,
0);
Uwaga: nie można czytać i pisać z tej samej tekstury w tym samym zadaniu renderowania, ponieważ wywołuje zachowanie niezdefiniowane. Możesz jednak użyć: glTextureBarrier()
między wywołaniami renderowania.
Czytaj dane tekstury
Dane tekstur można odczytać za pomocą funkcji 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);
Uwaga: typ i format tekstury są tylko na przykład i mogą być różne.
Korzystanie z PBO
Jeśli GL_PIXEL_UNPACK_BUFFER
bufor z GL_PIXEL_UNPACK_BUFFER
to parametr data
w glTexImage2D
jest przesunięciem do tego bufora.
Oznacza to, że glTexImage2D nie musi czekać na skopiowanie wszystkich danych z pamięci aplikacji, zanim będzie mogła wrócić, co zmniejsza obciążenie w głównym wątku.
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);
Ale tam, gdzie naprawdę świecą PBO, trzeba odczytać wynik renderowania z powrotem do pamięci aplikacji. Aby odczytać dane pikseli w buforze, GL_PIXEL_PACK_BUFFER
je z GL_PIXEL_PACK_BUFFER
a następnie parametr danych glGetTexImage
będzie przesunięciem do tego bufora:
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);
}
Używanie tekstur w modułach cieniujących GLSL
Moduł cieniujący wierzchołki akceptuje tylko współrzędne tekstury jako atrybut wierzchołka i przesyła współrzędne do modułu cieniującego fragmenty. Domyślnie zagwarantuje również, że fragment otrzyma odpowiednio interpolowaną współrzędną na podstawie swojego położenia w trójkącie:
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;
}
Moduł cieniujący fragmenty przyjmuje następnie zmienną wyjściową texCoord
jako zmienną wejściową. Następnie możesz dodać teksturę do modułu cieniującego fragmenty, deklarując uniform sampler2D
. Do próbkowania fragmentu tekstury używamy wbudowanej funkcji texture
która ma dwa parametry. Pierwsza to tekstura, z której chcemy próbkować, a druga to współrzędna tej tekstury:
in vec2 texCoordOut;
out vec4 color;
uniform sampler2D image;
void main()
{
color = texture(image, texCoordOut);
}
Pamiętaj, że image
nie jest tutaj bezpośrednim identyfikatorem tekstury. To identyfikator jednostki tekstury, która będzie próbkowana. Z kolei tekstury nie są bezpośrednio powiązane z programami; są związane z jednostkami tekstury. Uzyskuje się to najpierw poprzez uaktywnienie jednostki tekstury za pomocą glActiveTexture
, a następnie wywołanie glBindTexture
wpłynie na tę konkretną jednostkę tekstury. Ponieważ jednak domyślną jednostką tekstury jest jednostka tekstury 0
, programy korzystające z jednej tekstury można uprościć, pomijając to wywołanie.