9
$\begingroup$

I use SDL2.

Currently my only shader has a MVP matrix, and transforms the points with it.

I calculate my View and Projection matrix for a camera with GLM:

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

I searched it, but I can only find legacy implementation of multiple viewports.

If I create for example 4 cameras and let's say that they all the same, except every camera has a different quarter of the screen to render. (So I want 4 viewports with the same content)

How can I do this? Sorry if I didn't give enough informations, feel free to ask.

$\endgroup$
  • $\begingroup$ Are you specifically looking to perform the rendering 4 times, or just to render once and display the result in 4 different places? $\endgroup$ – trichoplax Jan 11 '17 at 12:50
  • $\begingroup$ Perform the rendering 4 times. What I wrote is just a simplified example, for example I want the main camera to render a car at the full window, and another camera render the car from the side in a small square in one of the corners. $\endgroup$ – Tudvari Jan 11 '17 at 12:59
  • $\begingroup$ If I understood this correctly so you need to split your window to for example 4 parts and in each part render other parts of the scene like that? $\endgroup$ – narthex Jan 11 '17 at 14:15
  • $\begingroup$ Yes, but not strictly 4 parts. I want it to be flexible. Flexible size and position of viewport rectangle. $\endgroup$ – Tudvari Jan 11 '17 at 14:38
8
$\begingroup$

Rendering to different viewports (parts) of the same screen can be done as follows:

For example splitting screen into four parts and rendering the same scene four times to each corner with different uniforms and different viewports:

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
$\endgroup$
  • $\begingroup$ So with glViewPort() I can define where I want to draw next, and with glBlendFunc I can define how the GPU should blend the overlapping areas (framebuffers) with each other. Am I correct? $\endgroup$ – Tudvari Jan 11 '17 at 15:30
  • 1
    $\begingroup$ Yes, you are free to parametrize that. As parameters of viewport are x, y of left bottom corner of chunk and width, height of chunk. With blending you can experiment. $\endgroup$ – narthex Jan 11 '17 at 15:36
  • 1
    $\begingroup$ why blending? how is it related to the question ? $\endgroup$ – Raxvan Jan 11 '17 at 15:38
  • 3
    $\begingroup$ You don't need blending or multiple framebuffers. Rendering will not write to any pixels outside the current glViewport rectangle, so you can just set up and draw each viewport in turn. BTW you can also use the scissor test to restrict clears to a certain rectangle, in case you want overlapping viewports. $\endgroup$ – Nathan Reed Jan 12 '17 at 1:02
  • 2
    $\begingroup$ Blending has nothing to do with this and makes the answer more confusing. The setting up of the viewports is all that is needed $\endgroup$ – default Jan 12 '17 at 15:34
9
$\begingroup$

If you are writing your own Vertex/Fragment Shader there is another additional possibility to do this. It is much more complicated but might be useful for you and/or others. Additionally it speeds up the whole drawing process since it uses only one call to a draw command. The maximum number of Viewports is defined by GL_MAX_VIEWPORTS and is usually 16.

Since OpenGL 4.1 the method glViewportArrayv exists. It can define an array of Viewports. The Viewports which are created by this method have an index assigned.

This index can be used in the Vertex Shader to set the Viewport to which the scene is rendered. (You have to include the "ARB_shader_viewport_layer_array" extension in the shader code)

In your case I would suggest to do the following:

  • Store the 4 camera matrices in a Shader Storage Buffer (Array of mat4) to have them in the Vertex Shader
  • Use indexed drawing, for example :glDrawElementsInstanced
  • use the build in gl_InstanceID of the Vertex Shader to access the camera matrix array
  • set the build in variable output variable gl_ViewportIndex in the Fragment Shader to the gl_InstanceID. (for details see ARB_fragment_layer_viewport)
$\endgroup$
  • $\begingroup$ "This index can be used in the Vertex Shader to set the Viewport to which the scene is rendered." No, it cannot. Well, not without the ARB_shader_viewport_layer_array extension, or the AMD equivalents. None of those are standard in GL 4.5. You are perhaps thinking of Geometry Shaders. $\endgroup$ – Nicol Bolas Jan 13 '17 at 14:38
  • $\begingroup$ @Nicol, Thanks for this hint! I forgot to mention, that you have to include the extension. I will edit my answer. $\endgroup$ – Christian_B Jan 16 '17 at 7:26
5
$\begingroup$

This is a copy of @narthex's answer, except with only the viewports since that is all you need. I'm not sure why the frame buffer / blend stuff is included in his answer.

//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
$\endgroup$
0
$\begingroup$

These are all good answers, and yet there is another way: (Isn't there always?)

Due to the growing popularity of virtual reality, the folks at OculusVR have developed a trio of "Multiview" extensions called:

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

These extensions allow multiple views of the same scene to be rendered in a single draw call, eliminating the redundancy of rendering the same scene in the same state from the viewpoint of each eye. Although created for the purpose of VR, the developer is not necessarily limited to merely two views. Rather, this extension allows for as many views as MAX_VIEWS_OVR specifies.

Before using these extensions, the developer should check for their support in the user's graphics driver by adding the following code:

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

From there, it is a matter of setting up your framebuffer to utilize this feature:

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

and likewise in the shader:

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

In a CPU-bound application, this extension can dramatically reduce rendering time, especially with more complex scenes:

Relative CPU time between multiview and regular stereo. The smaller the better, with the number of cubes on the x-axis and the relative time on the y-axis.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.