Várias viewports com o OpenGL moderno?

9

Eu uso SDL2 .

Atualmente, meu único shader possui uma matriz MVP e transforma os pontos com ela.

Calculo minha matriz de exibição e projeção para uma câmera com GLM:

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

Eu procurei, mas só consigo encontrar a implementação herdada de várias viewports.

Se eu criar, por exemplo, 4 câmeras e digamos que todas são iguais , exceto que todas as câmeras têm um quarto diferente da tela para renderizar. (Então, eu quero 4 viewports com o mesmo conteúdo)

Como posso fazer isso? Desculpe se eu não dei informações suficientes, não hesite em perguntar.

Tudvari
fonte
Você está olhando especificamente para executar a renderização 4 vezes ou apenas para renderizar uma vez e exibir o resultado em 4 locais diferentes?
precisa saber é o seguinte
Execute a renderização 4 vezes. O que escrevi é apenas um exemplo simplificado, por exemplo, quero que a câmera principal renderize um carro na janela inteira e outra câmera renderize o carro de lado em um pequeno quadrado em um dos cantos.
Tudvari
Se eu entendi isso corretamente, então você precisa dividir sua janela para, por exemplo, 4 partes e em cada parte tornar outras partes da cena como que ?
Narthex
Sim, mas não estritamente 4 partes. Eu quero que seja flexível. Tamanho e posição flexíveis do retângulo da janela de visualização.
Tudvari 11/01

Respostas:

8

A renderização para diferentes viewports (partes) da mesma tela pode ser feita da seguinte maneira:

Por exemplo, dividir a tela em quatro partes e renderizar a mesma cena quatro vezes em cada canto com uniformes e viewports diferentes:

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
fonte
Portanto, com glViewPort () eu posso definir onde quero desenhar a seguir e com glBlendFunc posso definir como a GPU deve misturar as áreas sobrepostas (framebuffers) entre si. Estou correcto?
Tudvari
11
Sim, você é livre para parametrizar isso. Como parâmetros de viewportsão x, y do canto inferior esquerdo do pedaço e largura, altura do pedaço. Com a mistura, você pode experimentar.
Narthex
11
por que misturar? como isso está relacionado à questão?
Raxvan
3
Você não precisa de misturas ou vários buffers de quadros. A renderização não grava em pixels fora do retângulo glViewport atual; portanto, você pode configurar e desenhar cada viewport por vez. BTW, você também pode usar o teste de tesoura para restringir as limpezas a um certo retângulo, caso deseje viewports sobrepostas.
Nathan Reed
2
A mistura não tem nada a ver com isso e torna a resposta mais confusa. A configuração das viewports é tudo o que é necessário
padrão
10

Se você estiver escrevendo seu próprio Vertex / Fragment Shader, há outra possibilidade adicional de fazer isso. É muito mais complicado, mas pode ser útil para você e / ou outras pessoas. Além disso, acelera todo o processo de desenho, pois utiliza apenas uma chamada para um comando de desenho. O número máximo de viewports é definido por GL_MAX_VIEWPORTS e geralmente é 16.

Desde o OpenGL 4.1, o método glViewportArrayv existe. Ele pode definir uma matriz de viewports. As viewports criadas por esse método têm um índice atribuído.

Esse índice pode ser usado no Vertex Shader para definir a Viewport na qual a cena é renderizada. (Você deve incluir a extensão " ARB_shader_viewport_layer_array " no código do shader)

No seu caso, sugiro fazer o seguinte:

  • Armazene as 4 matrizes da câmera em um Shader Storage Buffer (matriz de mat4) para tê-las no Vertex Shader
  • Use desenho indexado , por exemplo: glDrawElementsInstanced
  • use a compilação em gl_InstanceID do Vertex Shader para acessar a matriz da câmera
  • defina a compilação na variável de saída variável gl_ViewportIndex no Fragment Shader como gl_InstanceID. (para detalhes, consulte ARB_fragment_layer_viewport )
Christian_B
fonte
" Este índice pode ser usado no Vertex Shader para definir a Viewport na qual a cena é renderizada. " Não, não pode. Bem, não sem a extensão ARB_shader_viewport_layer_array ou os equivalentes da AMD. Nenhum deles é padrão no GL 4.5. Você talvez esteja pensando em Geometry Shaders.
Nicol Bolas
@ Nicol, Obrigado por esta dica! Esqueci de mencionar que você deve incluir a extensão. Vou editar minha resposta.
Christian
5

Esta é uma cópia da resposta da @ narthex, exceto apenas com as janelas de exibição, pois é tudo o que você precisa. Não sei por que o buffer de quadro / mistura está incluído em sua resposta.

//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
padrão
fonte
0

Todas essas são boas respostas e, no entanto, existe outra maneira: (nem sempre existe?)

Devido à crescente popularidade da realidade virtual, o pessoal do OculusVR desenvolveu um trio de extensões "Multiview" chamadas:

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

Essas extensões permitem que várias visualizações da mesma cena sejam renderizadas em uma única chamada de desenho, eliminando a redundância de renderizar a mesma cena no mesmo estado do ponto de vista de cada olho. Embora criado para o propósito de VR, o desenvolvedor não está necessariamente limitado a apenas duas visualizações. Em vez disso, essa extensão permite quantas visualizações o MAX_VIEWS_OVR especifica.

Antes de usar essas extensões, o desenvolvedor deve verificar seu suporte no driver gráfico do usuário adicionando o seguinte 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 daí, é necessário configurar o buffer de estrutura para utilizar este recurso:

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

e da mesma forma no 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);
}

Em um aplicativo vinculado à CPU, essa extensão pode reduzir drasticamente o tempo de renderização, especialmente em cenas mais complexas :

Tempo relativo da CPU entre multiview e estéreo comum.  Quanto menor, melhor, com o número de cubos no eixo x e o tempo relativo no eixo y.

Jackalope
fonte