Passing Vector Data to OpenGL Shaders Using glUniform3f and glUniform3fv
In OpenGL, uniform variables serve as global inputs for shaders that remain constant for all vertices or fragments processed during a single draw call. To update a three-component vector (vec3) in GLSL, developers typically use either glUniform3f or glUniform3fv. This article demonstrates how to implement both approaches within a shader wrapper class.
Shader Implementation
The following vertex shader simply processes position data, while the fragment shader utilizes a uniform vec3 to determine the final pixel color.
Vertex Shader (standard.vert)
#version 330 core
layout (location = 0) in vec3 inPosition;
layout (location = 1) in vec3 inColor;
out vec3 vertexColor;
void main()
{
gl_Position = vec4(inPosition, 1.0);
vertexColor = inColor;
}
Fragment Shader (standard.frag)
#version 330 core
out vec4 FragColor;
uniform vec3 activeTint;
void main()
{
FragColor = vec4(activeTint, 1.0);
}
Updating Uniforms via Discrete Floats (glUniform3f)
The glUniform3f function is useful when you have the color or vector components stored as separate floating-point variables. It explicitly takes three arguments representing the X, Y, and Z values.
Shader Class Method:
void ShaderProgram::setUniform3f(const std::string& uniformName, float v0, float v1, float v2) {
// Retrieve the location of the uniform variable
GLint location = glGetUniformLocation(this->programID, uniformName.c_str());
// Update the uniform value using discrete floats
glUniform3f(location, v0, v1, v2);
}
Application Usage:
void onDraw() {
glClear(GL_COLOR_BUFFER_BIT);
myShader.use();
// Passing color components individually
myShader.setUniform3f("activeTint", 0.2f, 0.5f, 0.8f);
glBindVertexArray(vaoID);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
}
Updating Uniforms via Arrays (glUniform3fv)
The glUniform3fv function is more flexible, particulalry when working with math libraries (like GLM) or raw arrays. The 'v' suffix indicates it accepts a pointer to a vector (array).
Shader Class Method:
void ShaderProgram::setUniform3fv(const std::string& uniformName, const float* dataPtr) {
GLint location = glGetUniformLocation(this->programID, uniformName.c_str());
// Parameters: location, count (number of vec3 objects), pointer to data
glUniform3fv(location, 1, dataPtr);
}
Application Usage:
void onDraw() {
glClear(GL_COLOR_BUFFER_BIT);
myShader.use();
float rgbArray[] = { 0.75f, 0.1f, 0.2f };
// Passing the array pointer directly
myShader.setUniform3fv("activeTint", rgbArray);
glBindVertexArray(vaoID);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
}
Key Differences and Selection
Both functions accomplish the same goal of populating a vec3 in the GPU's uniform memory, but they differ in how they interface with the CPU-side data:
- glUniform3f: Best for scenarios where values are calculated on the fly or stored as individual scalars.
- glUniform3fv: Preferred when the data is already packed into a structure, a
std::vector, or a GLMvec3object. It also allows updating an array of uniforms by changing thecountparameter from1to the size of the uniform array defined in the shader.