Wiele rzutni z nowoczesnym OpenGL?


9

Używam SDL2 .

Obecnie mój jedyny moduł cieniujący ma matrycę MVP i przekształca za jej pomocą punkty.

Obliczam moją macierz widoku i projekcji dla kamery z GLM:

glm::lookAt(pos, pos + forward, up);
glm::perspective(fov, aspectRatio, zNear, zFar);

Przeszukałem go, ale mogę znaleźć tylko starszą implementację wielu rzutni.

Jeśli utworzę na przykład 4 kamery i powiedzmy, że wszystkie są takie same , tyle że każda kamera ma do renderowania inną ćwiartkę ekranu . (Więc chcę 4 rzutnie o tej samej treści)

W jaki sposób mogę to zrobić? Przepraszam, jeśli nie podałem wystarczających informacji, możesz zapytać.


Czy konkretnie chcesz wykonać renderowanie 4 razy, czy tylko renderować raz i wyświetlić wynik w 4 różnych miejscach?
trichoplax

Wykonaj rendering 4 razy. To, co napisałem, jest tylko uproszczonym przykładem, na przykład chcę, aby główna kamera renderowała samochód w pełnym oknie, a inna kamera renderowała samochód z boku w małym kwadracie w jednym z rogów.
Tudvari,

Jeśli dobrze to zrozumiałem, więc musisz podzielić okno na na przykład 4 części i w każdej części renderować inne części sceny w ten sposób ?
narthex,

Tak, ale nie tylko 4 części. Chcę, żeby było elastyczne. Elastyczny rozmiar i położenie prostokąta rzutni.
Tudvari,

Odpowiedzi:


8

Renderowanie do różnych rzutni (części) tego samego ekranu można wykonać w następujący sposób:

Na przykład podzielenie ekranu na cztery części i renderowanie tej samej sceny cztery razy w każdym rogu z różnymi mundurami i różnymi rzutniami:

bindFramebuffer();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
scene->setConstPerFrameUniforms();

//left bottom
glViewport(0, 0, WindowWidth*0.5, WindowHeight*0.5);
scene->setVarPerFrameUniforms(1);
scene->draw();

//right bottom
glViewport(WindowWidth*0.5, 0, WindowWidth*0.5, WindowHeight*0.5);
scene->setVarPerFrameUniforms(2);
scene->draw();

//left top
glViewport(0, WindowHeight*0.5, WindowWidth*0.5, WindowHeight*0.5);
scene->setVarPerFrameUniforms(3);
scene->draw();

//right top
glViewport(WindowWidth*0.5, WindowHeight*0.5, WindowWidth*0.5, WindowHeight*0.5);
scene->setVarPerFrameUniforms(4);
scene->draw();

glViewport(0, 0, WindowWidth, WindowHeight); //restore default

Zatem za pomocą glViewPort () mogę zdefiniować, gdzie chcę narysować dalej, a za pomocą glBlendFunc mogę zdefiniować, w jaki sposób GPU ma łączyć nakładające się obszary (bufory ramek) ze sobą. Mam rację?
Tudvari,

1
Tak, możesz to sparametryzować. Jako parametry viewportsą x, y lewego dolnego rogu porcji i szerokość, wysokość porcji. Dzięki mieszaniu możesz eksperymentować.
narthex

1
po co mieszać? jak to się ma do pytania?
Raxvan

3
Nie potrzebujesz mieszania ani wielu buforów ramki. Renderowanie nie zapisuje żadnych pikseli poza bieżącym prostokątem glViewport, więc możesz po prostu skonfigurować i narysować każdą rzutnię po kolei. BTW możesz również użyć testu nożycowego, aby ograniczyć usuwanie do określonego prostokąta, na wypadek, gdybyś chciał nakładać się na siebie rzutnie.
Nathan Reed,

2
Mieszanie nie ma z tym nic wspólnego i sprawia, że ​​odpowiedź jest bardziej myląca. Konfiguracja rzutni jest wszystkim, czego potrzeba
domyślnie

11

Jeśli piszesz własny Shader Vertex / Fragment, istnieje jeszcze jedna dodatkowa możliwość. Jest to znacznie bardziej skomplikowane, ale może być przydatne dla Ciebie i / lub innych osób. Dodatkowo przyspiesza cały proces rysowania, ponieważ używa tylko jednego wywołania polecenia rysowania. Maksymalna liczba rzutni jest zdefiniowana przez GL_MAX_VIEWPORTS i zwykle wynosi 16.

Od OpenGL 4.1 istnieje metoda glViewportArrayv . Może definiować tablicę rzutni. Rzutnie utworzone tą metodą mają przypisany indeks.

Tego indeksu można użyć w module cieniującym wierzchołków, aby ustawić rzutnię, do której renderowana jest scena. (Musisz dodać rozszerzenie „ ARB_shader_viewport_layer_array ” do kodu modułu cieniującego)

W twoim przypadku proponuję wykonać następujące czynności:

  • Przechowuj 4 matryce kamer w buforze pamięci modułu cieniującego (macierz Mat4), aby mieć je w module cieniującym wierzchołków
  • Użyj rysunku indeksowanego , na przykład: glDrawElementsInstanced
  • użyj kompilacji w gl_InstanceID modułu Vertex Shader, aby uzyskać dostęp do matrycy kamer
  • ustaw wbudowaną zmienną wyjściową zmienną gl_ViewportIndex w Fragment Shader na wartość gl_InstanceID. (szczegóły patrz ARB_fragment_layer_viewport )

Tego indeksu można użyć w module Vertex Shader do ustawienia rzutni, w której renderowana jest scena. ” Nie, nie można. Cóż, nie bez rozszerzenia ARB_shader_viewport_layer_array lub odpowiedników AMD. Żaden z nich nie jest standardem w GL 4.5. Być może myślisz o Geometry Shaders.
Nicol Bolas,

@Nicol, dzięki za podpowiedź! Zapomniałem wspomnieć, że musisz dołączyć rozszerzenie. Zmienię swoją odpowiedź.
Christian_B

5

To jest kopia odpowiedzi @ narthex, z wyjątkiem tylko rzutni, ponieważ to wszystko, czego potrzebujesz. Nie jestem pewien, dlaczego bufor / mieszanie ramek zostało uwzględnione w jego odpowiedzi.

//left bottom
glViewport(0, 0, WindowWidth*0.5, WindowHeight*0.5);
scene->draw();

//right bottom
glViewport(WindowWidth*0.5, 0, WindowWidth*0.5, WindowHeight*0.5);
scene->draw();

//left top
glViewport(0, WindowHeight*0.5, WindowWidth*0.5, WindowHeight*0.5);
scene->draw();

//right top
glViewport(WindowWidth*0.5, WindowHeight*0.5, WindowWidth*0.5, WindowHeight*0.5);
scene->draw();

glViewport(0, 0, WindowWidth, WindowHeight); //restore default

1

To są wszystkie dobre odpowiedzi, a jednak istnieje inny sposób: (czy nie zawsze?)

Ze względu na rosnącą popularność rzeczywistości wirtualnej ludzie z OculusVR opracowali trio rozszerzeń „Multiview” o nazwie:

  1. OVR_multiview ,
  2. OVR_multiview2 , i
  3. OVR_multiview_multisampled_render_to_texture

Rozszerzenia te umożliwiają renderowanie wielu widoków tej samej sceny w jednym wywołaniu losowania, eliminując nadmiarowość renderowania tej samej sceny w tym samym stanie z punktu widzenia każdego oka. Choć stworzony na potrzeby VR, deweloper niekoniecznie ogranicza się do zaledwie dwóch widoków. To rozszerzenie pozwala raczej na wyświetlenie tylu widoków, ile określa MAX_VIEWS_OVR .

Przed użyciem tych rozszerzeń programista powinien sprawdzić ich obsługę w sterowniku graficznym użytkownika, dodając następujący kod:

const GLubyte* extensions = GL_CHECK( glGetString( GL_EXTENSIONS ) );
char * found_extension = strstr( (const char*)extensions, "GL_OVR_multiview" );
if (NULL == found_extension)
{
     exit( EXIT_FAILURE );
}

Następnie należy skonfigurować bufor ramki do korzystania z tej funkcji:

glFramebufferTextureMultisampledMultiviewOVR = PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEDMULTIVIEWOVR(eglGetProcAddress("glFramebufferTextureMultisampleMultiviewOVR"));
glFramebufferTextureMultisampledMultiviewOVR (GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textureID, 0, 0, 2);

i podobnie w module cieniującym:

#version 300 es
#extension GL_OVR_multiview : enable

layout(num_views = 2) in;
in vec3 vertexPosition;
uniform mat4 MVP[2];

void main(){
    gl_Position = MVP[gl_ViewID_OVR] * vec4(vertexPosition, 1.0f);
}

W aplikacji związanej z procesorem to rozszerzenie może znacznie skrócić czas renderowania, szczególnie w przypadku bardziej złożonych scen :

Względny czas pracy procesora między widokiem z wielu kamer a zwykłym stereo.  Im mniejszy, tym lepiej, z liczbą kostek na osi x i względnym czasem na osi y.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.