Translate
To translate a model, we add an offset to each of its positions. Translation is sometimes expressed as vector addition. Here position \(\mathbf{p}\) is translated by vector \(\textrm{offset}\):
A translation is not achieved by permanently adding the offset to the positions stored in the vertex buffer. Vertex buffers should generally be treated as read-only data. Instead, we apply the offset to the position in the vertex shader.
In GLSL, the offset is declared as a vec3
uniform and applied using the +
operator:
uniform vec3 offset;
in vec3 position;
void main() {
vec3 translatedPosition = position + offset;
gl_Position = vec4(translatedPosition, 1.0);
}
uniform vec3 offset; in vec3 position; void main() { vec3 translatedPosition = position + offset; gl_Position = vec4(translatedPosition, 1.0); }
On the CPU side of our renderer, we set the uniform before drawing. Here the object is shifted a little bit to the right and a bit more up:
shader.setUniform3f('offset', 0.1, 0.3, 0);
shader.setUniform3f('offset', 0.1, 0.3, 0);
Matching Vector Types
Since we are currently confining our geometry to the \(z = 0\) plane, we may be tempted to make the offset
uniform a vec2
and set the uniform with this simpler call:
shader.setUniform2f('offset', 0.1, 0.3);
shader.setUniform2f('offset', 0.1, 0.3);
The GLSL code no longer compiles because we can't add a vec3
and a vec2
. One possible fix is to call the vec3
constructor explicitly and add the components together manually with scalar addition:
// Through 3-component constructor.
vec3 translatedPosition = vec3(
position.x + offset.x,
position.y + offset.y,
position.z
);
// Through 3-component constructor. vec3 translatedPosition = vec3( position.x + offset.x, position.y + offset.y, position.z );
This is not an ideal solution because scalar addition is slow and verbose. Another possible fix is to duplicate the position and mutate the x- and y-components with the +=
operator:
// Through copy-and-mutate.
vec3 translatedPosition = position;
position.x += offset.x;
position.y += offset.y;
// Through copy-and-mutate. vec3 translatedPosition = position; position.x += offset.x; position.y += offset.y;
This is still scalar addition, however. First the x-component is mutated, and then the y-component. The graphics card will perform these additions in parallel if we use vector addition. However, the vectors must be of the same type. Leaving offset
as a vec3
is probably the best solution. If we have no control over the uniform type, we can convert the vec2
to a vec3
with the aid of converting constructor:
// Through converting constructor.
vec3 translatedPosition = position + vec3(offset, 0);
// Through converting constructor. vec3 translatedPosition = position + vec3(offset, 0);
Now the x-, y-, and z-components are all added simultaneously. Favor vector operations over scalar operations as much as possible.