0
\$\begingroup\$

I've been working on creating the GUI system for a 3d game I've been working on, and have hit a bit of an obstacle in trying to rotate GUI elements (Google searching did not yield what I was looking for). This is how the screen looks without any rotation. As of now, rotating each element distorts the GUI elements, like this, whereas I want an effect closer to this, without any stretch or skewing.

My code, using glm, looks like this:

void GUI::NewGUIBase::rebuildMatrix()
{
    glm::vec3 newModel = glm::vec3(absoluteTopLeft.x, absoluteTopLeft.y, 1.0f);

    glm::mat4 rotate = glm::rotate(glm::mat4(1.0f), glm::radians(this->rotation), glm::vec3(0, 0, 1));
    glm::mat4 trans = glm::translate(glm::mat4(1.0f), newModel);
    this->setModel(trans * rotate);
}

I believe that the issue is due to the fact that my game window is not a square given that OpenGL uses window coordinates from -1 to 1. However, I can't figure out how I would take this into account to achieve the desired effect of rotation without any stretch.

In case it helps, a GUI element knows its own top left corner and its own dimensions (in OpenGL coordinates), and the game window's width and height in pixels.

\$\endgroup\$
3
  • \$\begingroup\$ Where are you setting glViewport and/or your projection matrix? \$\endgroup\$
    – user1430
    Commented Aug 11, 2016 at 20:25
  • \$\begingroup\$ @JoshPetrie glViewport is set in the main loop whenever the window is resized, with glViewport(0, 0, newWidth, newHeight). The projection matrix for drawing GUI is just an identity matrix (a different projection matrix is used for the 3d stuff, but they are fine) \$\endgroup\$
    – Brenn_
    Commented Aug 11, 2016 at 21:12
  • \$\begingroup\$ You probably want to use a projection that includes your window aspect ratio, such as a basic ortho projection. \$\endgroup\$
    – user1430
    Commented Aug 11, 2016 at 21:28

1 Answer 1

1
\$\begingroup\$

I sloved this by changing the GUI system to pass real position instead of normailzed position, just an example conecpt here:

float vertexPosition[] = 
{
  //  x         y
  -1.0f/2.0f,  1.0f/2.0f, // top left
  -1.0f/2.0f, -1.0f/2.0f, // bottom left
   1.0f/2.0f,  1.0f/2.0f, // top right
   1.0f/2.0f, -1.0f/2.0f, // bottom right
}

And for each vertexPosition, I multiply it by texture's Width and Height.

for (int i = 0; i < length; i++)
{
    if (i % 2 == 0)
        vertexPosition[i] *= (float)width;
    else
        vertexPosition[i] *= (float)height;
}

Then we got the real position, and you should use glBufferData to update your vbo (note: you should bind the vao properly before), and in the shader do the normailize in the final output position, like this:

Start Edit(1):

#version 330 core
// All positions should be half around the origin in ndc.
in vec2 position;
uniform vec2 translate;
uniform mat4 scale;
uniform mat4 rotate;
uniform vec2 screenSize;

out vec2 TexCoords;

void main()
{
    vec4 realPosition = rotate * scale * vec4(position.x, position.y, 0.0f, 1.0f);
    float normX = realPosition.x / (screenSize.x / 2.0f) + translate.x / screenSize.x;
    float normY = realPosition.y / (screenSize.y / 2.0f) + translate.y /screenSize.y;
    gl_Position = vec4(normX, normY, 0.0f, 1.0f);
}

You need to pass translate seperately, because we use real postion, otherwise, you might need to compress translation outside and make a matrix.

End Edit

You maybe have a final question, how could get the TexCoord in shader ? there is two option to get it. One option, pass a Texture Coord that you defined as a Vertex Attribtue in every loop, or if it is constant you should pass it out of loop. The other option is calculate in your shader.

In conclusion, the step of doing this is:

  1. Pass the real position instead of normalized position.
  2. Do your transformation in shader as usual.
  3. Normalized your final position before you give it to gl_Position.

Edit(2)(3):

The texCoord part, I did it and share here :)

//.... just like above
out vec2 TexCoords;

void main()
{
    //.... just like above
    float texX = position.x / texSize.x + 0.5f;
    float texY = position.y / texSize.y + 0.5f; 
    // if you need to flip image, use texY = 1.0 - texY; here
    TexCoords = vec2(texX, texY);
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .