• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

GML [SOLVED] Matrix rotation applied after invisible translation?

H

Hieran_Del8

Guest
I'm having trouble with basic matrix transforms. Please understand, I've done my research, I'm coming here as a last resort.

As a simple test, I am trying to render a triangle in rotation about the y-axis. I expect a stationary triangle that rotates, but this is what I'm seeing (in orthographic projection):
renderTest.gif

The program seems to be applying a translation transform before a rotation transform, but I cannot see where this is happening since I am not setting one. This is all of my code:
Main create event:
Code:
m_modelRotation = 0;

m_projectionTransform = matrix_build_projection_ortho(50, 50, 1, 100);

m_viewTransform = matrix_build_lookat(
    0, 0, -10, 
    0, 0, 0, 
    0, 1, 0);

m_worldTransform = matrix_build_identity();
Main step event:
Code:
m_modelRotation += 1;

m_worldTransform = matrix_build(
    0, 0, 0, 
    0, m_modelRotation, 0, 
    1, 1, 1);
Main draw event:
Code:
matrix_set(matrix_projection, m_projectionTransform);
matrix_set(matrix_view, m_viewTransform);

matrix_set(matrix_world, m_worldTransform);

draw_primitive_begin(pr_trianglelist);
    draw_vertex_color(-5, -5, c_red, 1);
    draw_vertex_color(0, 5, c_green, 1);
    draw_vertex_color(5, -5, c_blue, 1);
    draw_primitive_end();
 
It looks to me like the triangle isn't at zero on the z axis.

I don't have GMS2 so I can't test that theory myself.

What depth is the triangle located at, maybe that could affect where it is drawn along the z axis? Does gms2 have the equivalent to d3d_set_depth?
 
H

Hieran_Del8

Guest
Hmm, that's a good point, I've been drawing 2D points. I wrote a shader ("wrote" as in modified the default to fit the needs) that sets the z component of the 2D coordinates to 0, and it exhibited the same behavior of rotating about the y-axis, but around some centroid just off the left side of the screen. (I've verified the shader is working, set the fragment color to white to prove it.)

I also created a "frozen" vertex buffer with 3 components, wrote the 3d shader, and am getting the exact same result. (And I tested with a perspective fov projection transform, same behavior.) Here's the new code:
Main create:
Code:
m_modelRotation = 0;

m_projectionTransform = matrix_build_projection_ortho(50, 50, 1, 100);

m_viewTransform = matrix_build_lookat(
    0, 0, -10,
    0, 0, 0,
    0, 1, 0);

m_worldTransform = matrix_build_identity();

vertex_format_begin();
    vertex_format_add_position_3d();
    vertex_format_add_color();
    m_vertexFormat = vertex_format_end();

m_vertexBuffer = vertex_create_buffer();
vertex_begin(m_vertexBuffer, m_vertexFormat);
    vertex_position_3d(m_vertexBuffer, -5, -5, 0);
    vertex_color(m_vertexBuffer, c_red, 1);
    vertex_position_3d(m_vertexBuffer, 0, 5, 0);
    vertex_color(m_vertexBuffer, c_green, 1);
    vertex_position_3d(m_vertexBuffer, 5, -5, 0);
    vertex_color(m_vertexBuffer, c_blue, 1);
    vertex_end(m_vertexBuffer);
  
vertex_freeze(m_vertexBuffer);
Main step (unchanged):
Code:
m_modelRotation += 1;

m_worldTransform = matrix_build(
    0, 0, 0,
    0, m_modelRotation, 0,
    1, 1, 1);
Main draw:
Code:
shader_set(shd_standard3D);

matrix_set(matrix_projection, m_projectionTransform);
matrix_set(matrix_view, m_viewTransform);

matrix_set(matrix_world, m_worldTransform);

vertex_submit(m_vertexBuffer, pr_trianglelist, -1);

shader_reset();
Main cleanup (new event):
Code:
vertex_delete_buffer(m_vertexBuffer);
vertex_format_delete(m_vertexFormat);
Shader shd_standard3D.vsh (new):
Code:
//
// Simple passthrough vertex shader
//
attribute vec3 in_Position;                  // (x,y,z)
attribute vec4 in_Colour;                    // (r,g,b,a)

varying vec4 v_vColour;

void main()
{
    vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;
  
    v_vColour = in_Colour;
}
Shader shd_standard3D.fsh (new):
Code:
//
// Simple passthrough fragment shader
//
varying vec4 v_vColour;

void main()
{
    gl_FragColor = v_vColour;
}

The translation seems to correlate with the cosine of the y-axis rotation, as in:
Code:
x = projection_width / 2 * cos(yRotation) - projection_width / 2;
But that doesn't make any sense, I'm not setting any translation anywhere, it's like the transforms are being applied in the wrong order.

EDIT:
To clarify, the transforms are not being set in the wrong order, I just can't figure out what's messing with the render.
 
Last edited by a moderator:
H

Hieran_Del8

Guest
Trying to figure out this problem still. I wrote a demo opengl app to ensure my sanity, and it performs exactly as intended:
renderTest2.gif

The code for such is in the following spoiler:
Code:
#include "GL/glew.h"
#include "GL/glut.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"

#pragma comment(lib, "glew32.lib")
//#pragma comment(lib, "glew32s.lib") //<-- what is this even for?

struct ShaderProgramData
{
    const char* vertexShaderSource;
    const char* fragmentShaderSource;
    int vertexShaderID;
    int fragmentShaderID;
    int programID;
    int hWvp;
};

ShaderProgramData shader3D =
{
//the vertex shader
"uniform mat4 u_wvp;\r\n"
"attribute vec3 in_Position;\r\n"
"attribute vec4 in_Color;\r\n"
"varying vec4 v_Color;\r\n"
"\r\n"
"void main()\r\n"
"{\r\n"
"\tvec4 position = vec4(in_Position.x, in_Position.y, in_Position.z, 1.0);\r\n"
"\tgl_Position = u_wvp * position;\r\n"
"\tv_Color = in_Color;\r\n"
"}\r\n"
,

//the fragment shader
"varying vec4 v_Color;\r\n"
"\r\n"
"void main()\r\n"
"{\r\n"
"\tgl_FragColor = v_Color;\r\n"
"}\r\n"
,


    0,
    0,
    0,
    0
};

struct ModelData
{
    unsigned int vertexBufferID;
    int vertexCount;
    const float srcData[];
};

ModelData triangle =
{
    0,
    3,
    {
        -5.0f, -5.0f, 0.0f,
            1.0f, 0.0f, 0.0f, 1.0f,
        0.0f, 5.0f, 0.0f,
            0.0f, 1.0f, 0.0f, 0.5f,
        5.0f, -5.0f, 0.0f,
            0.0f, 0.0f, 1.0f, 1.0f
    },
};

void draw()
{
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    //glLoadIdentity(); //<-- fixed function matrix style

    //set shader
    glUseProgram(shader3D.programID);

    static glm::mat4 sworld;

    glm::mat4 proj = glm::orthoLH(-25.0f, 25.0f, -25.0f, 25.0f, 1.0f, 100.0f);
    //glm::mat4 proj = glm::perspectiveFovLH(glm::pi<float>() / 3.0f, 500.0f, 500.0f, 1.0f, 100.0f);
    glm::mat4 view = glm::lookAtLH(glm::vec3(0.0f, 0.0f, -10.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
    glm::mat4 world = glm::rotate(sworld, .001f, glm::vec3(0.0f, 1.0f, 0.0f));
    sworld = world;

    glm::mat4 transform = proj * view * world;

        //set vertex attributes
        //glVertexAttribFormat(0, 3, GL_FLOAT, GL_FALSE, 0); //not available until 4.5
        //glVertexAttribFormat(1, 4, GL_FLOAT, GL_FALSE, 12);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 28, (const void*)0);
        glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 28, (const void*)12);
        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);

        //set vertex array to render
        glBindBuffer(GL_ARRAY_BUFFER, triangle.vertexBufferID);
        //glBindVertexArray(triangle.vertexArrayID); //not available until 4.3

        //set uniforms
        glUniformMatrix4fv(shader3D.hWvp, 1, false, (const GLfloat*)&transform[0]);

        //draw triangle
        glDrawArrays(GL_TRIANGLES, 0, triangle.vertexCount);

        //reset vertex attributes
        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);

    //reset shader
    glUseProgram(0);

    //glFlush(); //<-- used with GLUT_SINGLE
    glutSwapBuffers();
}

int main(int argc, char **argv)
{
    //initialize GLUT
    glutInit(&argc, argv);

    //glutInitDisplayMode(GLUT_SINGLE);
    glutInitDisplayMode(GLUT_DOUBLE);
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("Hello World, openGL!");

    //initialize GLEW, must be _after_ an openGL context is created (ie. glut window creation)
    glewInit();

        //set up shader program, error checking ignored
        shader3D.vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
        shader3D.fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);

        glShaderSource(shader3D.vertexShaderID, 1, &shader3D.vertexShaderSource, 0);
        glShaderSource(shader3D.fragmentShaderID, 1, &shader3D.fragmentShaderSource, 0);

        glCompileShader(shader3D.vertexShaderID);
        glCompileShader(shader3D.fragmentShaderID);

        shader3D.programID = glCreateProgram();
        glAttachShader(shader3D.programID, shader3D.vertexShaderID);
        glAttachShader(shader3D.programID, shader3D.fragmentShaderID);
        glBindAttribLocation(shader3D.programID, 0, "in_Position"); //these must be set _before_ the shader program is linked
        glBindAttribLocation(shader3D.programID, 1, "in_Color"); //alternatively, could just let program decide index, and call glGetAttribLocation

        glLinkProgram(shader3D.programID);

        shader3D.hWvp = glGetUniformLocation(shader3D.programID, "u_wvp");

    //set up triangle vertex array
    glGenBuffers(1, &triangle.vertexBufferID);
    glBindBuffer(GL_ARRAY_BUFFER, triangle.vertexBufferID);
    glBufferData(GL_ARRAY_BUFFER, triangle.vertexCount * 28, triangle.srcData, GL_STATIC_DRAW);

    //set up rendering options, not needed atm
    //glEnable(GL_DEPTH | GL_BLEND);
    //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    //set rendering function
    glutDisplayFunc(draw);
    glutIdleFunc(draw);

    //run main loop, glut holds the program in here until the "game" is finished
    glutMainLoop();

    //remove vertex array
    glDeleteBuffers(1, &triangle.vertexBufferID);

    //remove shader resources
    glDetachShader(shader3D.programID, shader3D.vertexShaderID); //is this necessary since the program will be deleted?
    glDetachShader(shader3D.programID, shader3D.fragmentShaderID);
    glDeleteProgram(shader3D.programID);
    glDeleteShader(shader3D.vertexShaderID);
    glDeleteShader(shader3D.fragmentShaderID);

    return 0;
}

Is rendering with arbitrary matrix transforms broken? Or am I doing something wrong? I've written graphics apps for over a decade with directx, and can evidently write opengl, so there must be something I'm not seeing with the simple GMS2 demos above. Please, any help is appreciated!
 
H

Hieran_Del8

Guest
Oh my gosh, I found it. After rendering the image through the shader to the application surface, the application surface is drawn to the backbuffer, but with the current transforms! It is fixed by setting the projection, view, and world matricies to identity. I should be happy on figuring this out. Should be. After all that time lost though...
 
Top