This is the Polish translation of Advanced-OpenGL/Advanced-Data article of learnopengl.com tutorial series.
Od dłuższego czasu używamy buforów w OpenGL do przechowywania danych. Istnieją bardziej interesujące sposoby manipulowania buforami, a także inne interesujące metody przekazywania dużych ilości danych do shaderów za pośrednictwem tekstur. W tym samouczku omówimy bardziej interesujące funkcje operujące na buforach i sposób wykorzystania obiektów tekstur do przechowywania dużych ilości danych (część samouczka dot. tekstur nie została jeszcze napisana).
Bufor OpenGL jest tylko obiektem, który zarządza częścią pamięci i niczym więcej. Ustawiamy znaczenie bufora po powiązaniu go z określonym
Do tej pory wypełniamy pamięć zarządzaną przez obiekty bufora, wywołując NULL
jako argument danych, funkcja przydzieliłaby tylko pamięć i jej nie wypełniała. Jest to użyteczne, jeśli najpierw chcemy zarezerwować określoną ilość pamięci, a następnie powrócić do tego bufora, aby wypełnić go kawałek po kawałku.
Zamiast wypełniać cały bufor jednym wywołaniem funkcji, możemy również wypełnić określone obszary bufora, wywołując
glBufferSubData(GL_ARRAY_BUFFER, 24, sizeof(data), &data); // Zakres: [24, 24 + sizeof(data)]
Jeszcze inną metodą zapisywania danych do bufora jest pobranie wskaźnika do pamięci bufora i bezpośrednie skopiowanie danych do bufora. wywołanie
float data[] = {
0.5f, 1.0f, -0.35f
...
};
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// pobierz wskaźnik
void *ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
// teraz skopiuj dane do pamięci
memcpy(ptr, data, sizeof(data));
// pamiętaj, aby powiedzieć OpenGL, że skończyliśmy z operacjami na wskaźniku
glUnmapBuffer(GL_ARRAY_BUFFER);
Mówiąc OpenGL, skończyliśmy operację na wskaźniku za pomocą
Korzystanie z
Grupowanie atrybutów wierzchołków
Używając
Moglibyśmy również zgrupować wszystkie dane wektora w duże porcje danych według typu atrybutu zamiast ich przeplatania. Zamiast przeplatanego układu 123123123123
stosujemy podejście grupowania 111122223333
.
Podczas ładowania danych wierzchołków z pliku zazwyczaj pobierasz tablicę pozycji, wektorów normalnych i/lub tablicę współrzędnych tekstury. Może to kosztować trochę wysiłku, aby połączyć te tablice w jedną dużą tablicę przeplatanych danych. Podejście grupowania jest wtedy prostszym rozwiązaniem, które możemy łatwo wdrożyć za pomocą
float positions[] = { ... };
float normals[] = { ... };
float tex[] = { ... };
// wypełnij bufor
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(positions), &positions);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(positions), sizeof(normals), &normals);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(positions) + sizeof(normals), sizeof(tex), &tex);
W ten sposób możemy bezpośrednio przenieść tablice atrybutów wierzchołków jako całość do bufora bez konieczności ich wcześniejszego przetwarzania. Mogliśmy również połączyć je w jedną dużą tablicę i natychmiast wypełnić bufor za pomocą
Będziemy również musieli zaktualizować wskaźniki atrybutów wierzchołków, aby odzwierciedlić te zmiany:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(sizeof(positions)));
glVertexAttribPointer(
2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)(sizeof(positions) + sizeof(normals)));
Zauważ, że parametr stride
jest równy rozmiarowi atrybutu wierzchołka, ponieważ następny wektor atrybutów wierzchołka można znaleźć bezpośrednio po jego 3 (lub 2) komponencie.
To daje nam jeszcze inne podejście do ustawiania i określania atrybutów wierzchołków. Korzystanie z obu metod nie przynosi natychmiastowej korzyści OpenGL, jest to w większości bardziej zorganizowany sposób ustawiania atrybutów wierzchołków. Podejście, które wybierzesz, jest oparte wyłącznie na twoich preferencjach i rodzaju aplikacji.
Kopiowanie buforów
Po wypełnieniu buforów danymi możesz chcieć udostępnić te dane innym buforom lub skopiować zawartość bufora do innego bufora. Funkcja
void glCopyBufferSubData(GLenum readtarget, GLenum writetarget, GLintptr readoffset,
GLintptr writeoffset, GLsizeiptr size);
Parametry readtarget
i writetarget
przewidują podanie przeznaczenia buforów, obu buforów uczestniczących w wymianie danych. Możemy na przykład skopiować z bufora VERTEX_ARRAY_BUFFER do bufora VERTEX_ELEMENT_ARRAY_BUFFER, określając odpowiednio te przeznaczenia buforów jako bufory do odczytu (readtarget) i do zapisu (writetarget). Bufory obecnie powiązane z tymi typami przeznaczenia zostaną użyte do operacji kopiowania.
Ale co by było, gdybyśmy chcieli odczytywać i zapisywać dane do dwóch różnych buforów, które są jednocześnie buforami tablicy wierzchołków? Nie możemy powiązać dwóch buforów w tym samym czasie z tym samym przeznaczeniem bufora. Z tego powodu, OpenGL daje nam dwa dodatkowe przeznaczenia buforów o nazwach GL_COPY_READ_BUFFER i GL_COPY_WRITE_BUFFER. Następnie wiążemy wybrane przez nas bufory do nowych typów przeznaczenia i ustawiamy je jako argumenty readtarget
i writetarget
.
Funkcja size
z danego przesunięcia readoffset
i zapisuje je w buforze writetarget
rozpoczynając od pozycji writeoffset
. Przykład kopiowania zawartości dwóch buforów tablic wierzchołków pokazano poniżej:
float vertexData[] = { ... };
glBindBuffer(GL_COPY_READ_BUFFER, vbo1);
glBindBuffer(GL_COPY_WRITE_BUFFER, vbo2);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, sizeof(vertexData));
Mogliśmy to również zrobić, poprzez powiązanie tylko bufora writetarget
z jednym z nowych typów przeznaczenia buforów:
float vertexData[] = { ... };
glBindBuffer(GL_ARRAY_BUFFER, vbo1);
glBindBuffer(GL_COPY_WRITE_BUFFER, vbo2);
glCopyBufferSubData(GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, sizeof(vertexData));
Dzięki dodatkowej wiedzy na temat sposobu manipulowania danymi buforów możemy już z nich korzystać w bardziej interesujący sposób. Im dalej wejdziesz w OpenGL, tym bardziej przydatne stają się te nowe metody zarządzania danymi. W następnym samouczku omówimy