Múltiples ventanas gráficas con OpenGL moderno?

9

Yo uso SDL2 .

Actualmente, mi único sombreador tiene una matriz MVP y transforma los puntos con ella.

Calculo mi matriz de Vista y Proyección para una cámara con GLM:

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

Lo busqué, pero solo puedo encontrar la implementación heredada de múltiples ventanas gráficas.

Si creo, por ejemplo, 4 cámaras y digamos que todas son iguales , excepto que cada cámara tiene un cuarto diferente de la pantalla para renderizar. (Entonces quiero 4 viewports con el mismo contenido)

¿Cómo puedo hacer esto? Lo siento si no di suficiente información, no dude en preguntar.

Tudvari
fuente
¿Está buscando específicamente realizar el renderizado 4 veces, o simplemente renderizar una vez y mostrar el resultado en 4 lugares diferentes?
trichoplax
Realizar el renderizado 4 veces. Lo que escribí es solo un ejemplo simplificado, por ejemplo, quiero que la cámara principal renderice un automóvil en la ventana completa, y otra cámara renderice el automóvil desde el costado en un pequeño cuadrado en una de las esquinas.
Tudvari
Si entendí esto correctamente, ¿necesita dividir su ventana a, por ejemplo, 4 partes y en cada parte representar otras partes de la escena como esa ?
narthex
Sí, pero no estrictamente 4 partes. Quiero que sea flexible. Tamaño flexible y posición del rectángulo de la vista.
Tudvari
Relacionado: stackoverflow.com/questions/726379/multiple-viewports-in-opengl/…
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功

Respuestas:

8

El renderizado a diferentes viewports (partes) de la misma pantalla se puede hacer de la siguiente manera:

Por ejemplo, dividir la pantalla en cuatro partes y renderizar la misma escena cuatro veces en cada esquina con diferentes uniformes y diferentes vistas:

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
narthex
fuente
Entonces, con glViewPort () puedo definir dónde quiero dibujar a continuación, y con glBlendFunc puedo definir cómo la GPU debe combinar las áreas superpuestas (framebuffers) entre sí. ¿Estoy en lo correcto?
Tudvari
1
Sí, eres libre de parametrizar eso. Como parámetros de viewportson x, y de la esquina inferior izquierda del fragmento y ancho, altura del fragmento. Con la mezcla puedes experimentar.
narthex
1
¿Por qué mezclar? ¿Cómo se relaciona con la pregunta?
Raxvan
3
No necesita mezclar o múltiples framebuffers. El renderizado no se escribirá en ningún píxel fuera del rectángulo glViewport actual, por lo que puede configurar y dibujar cada ventana por turno. Por cierto, también puede usar la prueba de tijera para restringir los claros a un cierto rectángulo, en caso de que desee superponer las vistas.
Nathan Reed
2
La combinación no tiene nada que ver con esto y hace que la respuesta sea más confusa. La configuración de las ventanas gráficas es todo lo que se necesita
predeterminado el
10

Si está escribiendo su propio Vertex / Fragment Shader, existe otra posibilidad adicional para hacerlo. Es mucho más complicado pero puede ser útil para usted y para otros. Además, acelera todo el proceso de dibujo, ya que solo utiliza una llamada a un comando de dibujo. El número máximo de Viewports está definido por GL_MAX_VIEWPORTS y generalmente es 16.

Desde OpenGL 4.1 existe el método glViewportArrayv . Puede definir una matriz de ventanas gráficas. Las ventanas gráficas creadas por este método tienen un índice asignado.

Este índice se puede usar en el sombreador de vértices para establecer la ventana gráfica en la que se representa la escena. ( Debe incluir la extensión " ARB_shader_viewport_layer_array " en el código del sombreador)

En su caso, sugeriría hacer lo siguiente:

  • Almacene las 4 matrices de la cámara en un Shader Storage Buffer (Matriz de mat4) para tenerlas en el Vertex Shader
  • Utilice el dibujo indexado , por ejemplo: glDrawElementsInstanced
  • use la compilación en gl_InstanceID del Vertex Shader para acceder a la matriz de matriz de la cámara
  • establezca la compilación en la variable de salida variable gl_ViewportIndex en el Fragment Shader al gl_InstanceID. (para más detalles ver ARB_fragment_layer_viewport )
Christian_B
fuente
" Este índice puede usarse en el sombreador de vértices para establecer la ventana gráfica en la que se representa la escena " . No, no puede. Bueno, no sin la extensión ARB_shader_viewport_layer_array, o los equivalentes AMD. Ninguno de ellos es estándar en GL 4.5. Quizás estés pensando en Geometry Shaders.
Nicol Bolas
@ Nicolas, ¡Gracias por esta pista! Olvidé mencionar que tienes que incluir la extensión. Editaré mi respuesta.
Christian_B
5

Esta es una copia de la respuesta de @ narthex, excepto con solo las ventanas gráficas, ya que eso es todo lo que necesita. No estoy seguro de por qué el material del búfer / mezcla de cuadros está incluido en su respuesta.

//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
defecto
fuente
0

Estas son todas buenas respuestas y, sin embargo, hay otra manera: (¿No hay siempre?)

Debido a la creciente popularidad de la realidad virtual, la gente de OculusVR ha desarrollado un trío de extensiones "Multiview" llamadas:

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

Estas extensiones permiten que se visualicen múltiples vistas de la misma escena en una sola llamada de dibujo, eliminando la redundancia de renderizar la misma escena en el mismo estado desde el punto de vista de cada ojo. Aunque creado con el propósito de VR, el desarrollador no se limita necesariamente a solo dos vistas. Más bien, esta extensión permite tantas vistas como MAX_VIEWS_OVR especifica.

Antes de usar estas extensiones, el desarrollador debe verificar su compatibilidad en el controlador de gráficos del usuario agregando el siguiente código:

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 );
}

A partir de ahí, se trata de configurar su framebuffer para utilizar esta función:

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

y del mismo modo en el sombreador:

#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);
}

En una aplicación vinculada a la CPU, esta extensión puede reducir drásticamente el tiempo de renderizado, especialmente con escenas más complejas :

Tiempo relativo de CPU entre multiview y estéreo regular.  Cuanto más pequeño, mejor, con el número de cubos en el eje xy el tiempo relativo en el eje y.

Jackalope
fuente