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

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 co GL_CLAMP_TO_EDGE , ale w przypadkach, gdy zaciskanie, pobrane dane GL_TEXTURE_BORDER_COLOR są zastępowane kolorem określonym przez GL_TEXTURE_BORDER_COLOR .

  • GL_REPEAT powoduje, że część całkowita współrzędnych tekstury jest ignorowana. Tekstura jest wyłożona kafelkami .

powtórz teksturę

  • 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.

tekstura lustra

  • GL_MIRROR_CLAMP_TO_EDGE powoduje, że współrzędna GL_MIRRORED_REPEAT jest powtarzana jak dla GL_MIRRORED_REPEAT dla jednego powtórzenia tekstury, w którym to punkcie współrzędna ma zostać zaciśnięta jak w GL_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.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow