What values should I send to normalized view matrix so a tilemap scrolling only spans a tile?

This is the code that produces the projection, view and model matrices that get sent to the shader:

    GL.glEnable(GL.GL_BLEND)
    GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA)    

    arguments['texture'].bind()

    arguments['shader'].bind()
    arguments['shader'].uniformi('u_Texture', arguments['texture'].slot)

    proj = glm.ortho(0.0, float(arguments['screenWidth']), 0.0, float(arguments['screenHeight']), -1.0, 1.0)

    arguments['cameraXOffset'] = (float(arguments['cameraXOffset']) / 32) / float(arguments['screenWidth'])
    arguments['cameraYOffset'] = (- float(arguments['cameraYOffset']) / 32) / float(arguments['screenHeight'])

    print('{}, {}'.format(arguments['cameraXOffset'], arguments['cameraYOffset']))

    view = glm.translate(glm.mat4(1.0), glm.vec3(float(arguments['cameraXOffset']), float(arguments['cameraYOffset']), 0.0))

    model = glm.translate(glm.mat4(1.0), glm.vec3(0.0, 0.0, 0.0))

    arguments['shader'].uniform_matrixf('u_Proj', proj)
    arguments['shader'].uniform_matrixf('u_View', view)
    arguments['shader'].uniform_matrixf('u_Model', model)

The projection matrix goes from 0.0 to screen width, and from 0.0 to screen height. That allows me to use the actual width in pixels of the tiles (32x32) when determining the vertex floats. Also, when the user presses the wasd keys, the camera accumulates offsets that span the width or height of a tile (always 32). Unfortunately, to reflect that offset in the view matrix, it seems that I need to normalize it, and I can't figure out how to do it so a single movement in any cardinal direction spans a single tile and nothing more. It constantly accumulates an error, so at the end of the map in any direction it shows a band of background (white in this case, for now).

This is the most important part that determines how much it will scroll with the given camera offsets:

    arguments['cameraXOffset'] = (float(arguments['cameraXOffset']) / 32) / float(arguments['screenWidth'])
    arguments['cameraYOffset'] = (- float(arguments['cameraYOffset']) / 32) / float(arguments['screenHeight'])

Can any of you figure out if that "normalization" for the sake of the view matrix is correct? Or is this a rounding issue? In that case, could I solve it somehow?

Vertex shader:

#version 330 core 

layout(location = 0) in vec3 position;
layout(location = 1) in vec4 color; 
layout(location = 2) in vec2 texCoord;

out vec4 v_Color;
out vec2 v_TexCoord;

uniform mat4 u_Proj;
uniform mat4 u_View;
uniform mat4 u_Model;

void main()
{ 
    gl_Position = u_Model * u_View * u_Proj * vec4(position, 1.0);
    v_TexCoord = texCoord; v_Color = color;
}  

FINAL VERSION: Solved. As mentioned by the commenter, had to change this line in the vertex shader:

gl_Position = u_Model * u_View * u_Proj * vec4(position, 1.0);

to:

gl_Position = u_Proj * u_View * u_Model * vec4(position, 1.0);

The final version of the code, that finally allows the user to scroll exactly one tile over:

arguments['texture'].bind()

arguments['shader'].bind()
arguments['shader'].uniformi('u_Texture', arguments['texture'].slot)

proj = glm.ortho(0.0, float(arguments['screenWidth']), 0.0, float(arguments['screenHeight']), -1.0, 1.0)

arguments['cameraXOffset'] = (float(arguments['cameraXOffset']) / 32) / arguments['screenWidth']
arguments['cameraYOffset'] = (float(-arguments['cameraYOffset']) / 32) / arguments['screenHeight']

view = glm.translate(glm.mat4(1.0), glm.vec3(float(arguments['cameraXOffset']), float(arguments['cameraYOffset']), 0.0))

 model = glm.translate(glm.mat4(1.0), glm.vec3(0.0, 0.0, 0.0))

 arguments['shader'].uniform_matrixf('u_Proj', proj)
 arguments['shader'].uniform_matrixf('u_View', view)
 arguments['shader'].uniform_matrixf('u_Model', model)

Upvotes: 1

Views: 202

Answers (1)

Rabbid76
Rabbid76

Reputation: 210908

You have to flip the order of the matrices when you transform the vertex coordinate to the clip space coordinate:

gl_Position = u_Proj * u_View * u_Model * vec4(position, 1.0);

See GLSL Programming/Vector and Matrix Operations:

Furthermore, the *-operator can be used for matrix-vector products of the corresponding dimension, e.g.:

vec2 v = vec2(10., 20.);
mat2 m = mat2(1., 2.,  3., 4.);
vec2 w = m * v; // = vec2(1. * 10. + 3. * 20., 2. * 10. + 4. * 20.)

Note that the vector has to be multiplied to the matrix from the right.

If a vector is multiplied to a matrix from the left, the result corresponds to to multiplying a column vector to the transposed matrix from the right. This corresponds to multiplying a column vector to the transposed matrix from the right:
Thus, multiplying a vector from the left to a matrix corresponds to multiplying it from the right to the transposed matrix:

vec2 v = vec2(10., 20.);
mat2 m = mat2(1., 2.,  3., 4.);
vec2 w = v * m; // = vec2(1. * 10. + 2. * 20., 3. * 10. + 4. * 20.)

This also applies to the matrix multiplication itself. The first matrix which has to be applied to the vector, has to be the most right matrix and the last matrix the most left, in the row of concatenated matrices.

Upvotes: 1

Related Questions