0
\$\begingroup\$

What is the best way to handle a moving camera inside many shader programs?

I believe most games need to have one common projection matrix inside all shader programs to use a moving camera. Currently my approach will be to make one global camera object, with which I update every shader program.

I would imagine this gets a bit tedious and possibly heavy once I actually have many programs which to update every movement. So I'd like to construct the program well right away and avoid problems down the road.

What have you done about this, if anything? Do you try to share shader programs between objects?

\$\endgroup\$
  • \$\begingroup\$ Are you using OpenGL, DirectX, Vulkan, Metal...? Generally analogous concepts will apply, but the syntax & terminology may differ, so including this information can help you get more directly applicable answers. \$\endgroup\$ – DMGregory Feb 3 '18 at 15:08
  • \$\begingroup\$ @DMGregory I'm using OpenGL, from what I have now gathered Uniform Buffer Objects may be the answer. \$\endgroup\$ – Felix Feb 3 '18 at 15:42
  • 1
    \$\begingroup\$ I'm not an expert in OpenGL, but that sounds right to me from what I know. If you get it working, please feel free to post an answer to help other users wondering the same thing. :) \$\endgroup\$ – DMGregory Feb 3 '18 at 15:45
  • \$\begingroup\$ @DMGregory Yap. \$\endgroup\$ – Felix Feb 3 '18 at 15:46
1
\$\begingroup\$

A nice way is using Uniform Buffer Objects (OpenGL) which buffer uniform variable information which can be shared across multiple programs. Inside every shader one will declare an uniform block, which has the layout of the uniform buffer.

For example:

// Vertex shader:
#version 150 core
in vec3 position;

uniform mat4 Model;

layout (std140) uniform Camera{
    mat4 View;
    mat4 Proj;
} Cam;

void main(){
    gl_Position = Cam.Proj * Cam.View * Model * vec4(position, 1.0);
}

Then use a struct to fill a layout (C/C++):

typedef struct Something{
    glm::mat4 View;
    glm::mat4 Proj;
} Layout;

and to create the buffer the usual way:

GLuint ID;
Layout layout{glm::mat4(1.0), glm::mat4(1.0)};
glGenBuffers(1, &ID);
glBindBuffer(GL_UNIFORM_BUFFER, ID);
glBufferData(GL_UNIFORM_BUFFER, sizeof layout, &layout, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, ID);

How to set and update the data:

void *data = glMapBuffer(GL_UNIFORM_BUFFER, GL_READ_WRITE);
memcpy(data, &layout, sizeof layout);
glUnmapBuffer(GL_UNIFORM_BUFFER);

To every shader program you must bind the location:

GLuint camloc = glGetUniformBlockIndex(programID, "Camera");
glUniformBlockBinding(programID, camloc, 0);

See the documentation for additional information about the functions and parameters.

\$\endgroup\$
  • \$\begingroup\$ This code is very poor overall. It relies on OpenGL stuff from a decade ago. There's no use of layout(binding for setting the UBO location, you're using glMapBuffer instead of glMapBufferRange. Also, it ignores all buffer object streaming techniques, so you're pretty much guaranteed to kill your performance. \$\endgroup\$ – Nicol Bolas Feb 4 '18 at 16:22
  • \$\begingroup\$ @NicolBolas Thank you for the info, I'm very much a beginner, so I was just glad this worked. Now I can move on to optimise. Does MapBufferRange perform better even when updating the whole buffer? What is used from a decade ago besides the layout binding? \$\endgroup\$ – Felix Feb 4 '18 at 17:48

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.