2
$\begingroup$

so I'm working on a game engine. It's partially python and c++.

One part that is currently a pain point is setting the uniforms for shaders. Currently I have to define a behavior for all 50+ methods used for setting the uniforms glUniform{1,2,3,4}{uifd}{v}

Is there a single method that I could just call, give it some memory, and tell it, this is a <inset type here> at this location and it repeats this many times?

My motivation:

These uniforms usually come from the "mesh object", (containing single values, bones, textures, etc.) or they come from "shader object" where they're set per shader, (like light position, camera orientation etc.)

So setting all of it programmatically, instead of having to deal with multiple different methods is preferable.

$\endgroup$

2 Answers 2

4
$\begingroup$

You should use uniform buffer objects instead of individual glUniform calls. Then the uniforms are all stored in one block of memory which you tell OpenGL about. The shader and the code that fills the buffer with data have to agree on what the memory layout is, but the code that selects the UBO while drawing doesn't need to care.

You will likely also be able to have one UBO per “mesh object” and one per “shader object”, so that each combination can be activated just with two bindings.

$\endgroup$
1
  • $\begingroup$ woah i didn't knew you could do this!!! that saves me so much work!! $\endgroup$ Commented Jul 7, 2023 at 15:06
2
$\begingroup$

Yes it is. I use template to solve the problem. Here is my solution.

Since the template code in C++ is overly verbose and quite similar, I'm only pasting snippets as examples.
These template codes primarily utilize variable-length template parameters and constexpr if. When calling these wrappers, the C++ compiler can automatically deduce the template type based on your parameters, without you having to explicitly declare any template type parameters.
Of course, if you're dealing with bulk transfer of uniform data to OpenGL, the UBO (Uniform Buffer Object) solution mentioned in the previous answer is definitely better. My scheme is simply an experimental encapsulation.

 template <typename T, typename... Args> constexpr bool all_same_type_v = (std::is_same_v<T, Args> && ...); //wrapper for glUniform number type template<typename... Args> void glUniform(GLint location, Args... values) { constexpr GLuint numOfArgs = sizeof...(values); static_assert(numOfArgs < 5, "Size of arguments must less than 5"); if constexpr (numOfArgs == 1) { if constexpr (all_same_type_v<float, Args...>) { glUniform1f(location, std::forward<Args>(values)...); } else if constexpr (all_same_type_v<GLint, Args...>) { glUniform1i(location, std::forward<Args>(values)...); } //..... //Wrapper for glUniformVec template<GLuint UVarLength, typename T> void glUniformVec(GLint location, GLsizei uniformVarCount, T* data) { assert(uniformVarCount < 5); if constexpr (UVarLength == 1) { if constexpr (std::is_same_v<T, float>) { glUniform1fv(location, uniformVarCount, data); } else if constexpr (std::is_same_v<T, GLint>) { glUniform1iv(location, uniformVarCount, data); } else if constexpr (std::is_same_v<T, GLuint>) { glUniform1uiv(location, uniformVarCount, data); } } else if constexpr (UVarLength == 2) //..... //wrapper for uniform matirx, value is a glm matrix type template<GLuint Cols, GLuint Rows,glm::qualifier q> void glUniformMatrix(GLint location, const glm::mat<Cols, Rows,glm::f32, q>& value) { if constexpr (Cols == 2) { if constexpr (Rows == 2) { glCall(glUniformMatrix2fv,location, 1, GL_FALSE, glm::value_ptr(value)); } else if constexpr (Rows == 3) { glCall(glUniformMatrix2x3fv,location, 1, GL_FALSE, glm::value_ptr(value)); } else if constexpr (Rows == 4) { glCall(glUniformMatrix2x4fv,location, 1, GL_FALSE, glm::value_ptr(value)); } else { static_assert(false, "Unsupport matrix type"); } } else if constexpr (Cols == 3) { if constexpr (Rows == 2) { glCall(glUniformMatrix3x2fv,location, 1, GL_FALSE, glm::value_ptr(value)); } //...... 

However, the problem what annoyed me at present is sometimes I mismatch the type in shaders and glUniform family methods. I'm finding some solution to check this error.

$\endgroup$
2
  • $\begingroup$ Please copy the relevant code to your answer. The link might expire in the future. $\endgroup$ Commented Aug 20, 2024 at 14:16
  • $\begingroup$ From what I've learned since writing my question, is that uniform buffers and shader storage buffers are the best way to go. And also many engines do checks if shader uniforms and code uniforms match either at runtime or in built-in editor. It seems that many people find the easy to make type errors annoying :D $\endgroup$ Commented Sep 1, 2024 at 14:18

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.