为什么旋转矩阵不会在文本的中心旋转?

时间:2018-04-07 16:39:29

标签: c opengl matrix rotation

我正在尝试在OpenGL中旋转文本,但问题是文本旋转得像它应该但在错误的点,即它不会在文本的中心旋转(并且我想要我知道怎么做!)。

我之所以感到困惑是因为我对文本的不同字母使用多个纹理让我感到困惑。

如果您想知道我使用的是哪个库,则为CGLM

但是当我编译这段代码时,rotate函数会以错误的旋转点旋转对象。

根据Rabbid76。我做了以下工作来获得整个文本的大小: -

for (int i = 0; i < (signed)strlen(text); i++)
{
    tw += iterator[(int)text[i]].Character_Array.Size[0];
    th += iterator[(int)text[i]].Character_Array.Size[1];
}

我仍然没有得到正确的结果,文字仍然是错误的。

在任何轮换之前(这是它应该旋转的地方): - Before rotation 旋转45度后(它的位置错位): - After rotation

编辑:源文件的整个代码: -

// Std. Includes
#include <stdio.h>
#include <stdlib.h>
// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
// GLM
#include <cglm/cglm.h>
// FreeType
#include <ft2build.h>
#include FT_FREETYPE_H

// Properties
const GLuint WIDTH = 800, HEIGHT = 600;

const GLchar * vertexShaderSource =
    "#version 330 core\n"
    "layout(location = 0) in vec4 vertex;\n"
    "out vec2 TexCoords;\n"
    "uniform mat4 projection;\n"
    "uniform mat4 model;\n"
    "void main()\n"
    "{\n"
    "gl_Position = projection * model * vec4(vertex.xy, 0.0, 1.0);\n"
    "TexCoords = vertex.zw;\n"
    "}\n\0";

const GLchar * fragmentShaderSource =
    "#version 330 core\n"
    "in vec2 TexCoords;\n"
    "out vec4 color;\n"
    "uniform sampler2D text;\n"
    "uniform vec4 textColor;\n"
    "void main()\n"
    "{\n"
    "vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, TexCoords).r);\n"
    "color = textColor * sampled;\n"
    "}\n\0";

/// Holds all state information relevant to a character as loaded using FreeType
typedef struct {
    GLuint TextureID;   // ID handle of the glyph texture
    int Size[2];    // Size of glyph
    int Bearing[2];  // Offset from baseline to left/top of glyph
    GLuint Advance;    // Horizontal offset to advance to next glyph
} Character;

typedef struct
{
    GLchar char_Array;
    Character Character_Array;
} Iterator;

Iterator * iterator;

GLuint VAO, VBO;
// RenderText function.. to render our text...
void RenderText(GLuint program, const char * text, GLfloat originx, GLfloat originy, GLfloat x, GLfloat y, GLfloat scalex, GLfloat scaley, float rotation, float r, float g, float b, float a);

// The MAIN function, from here we start our application and run the Game loop
int main()
{
    iterator = NULL;
    // Init GLFW
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", NULL, NULL); // Windowed
    glfwMakeContextCurrent(window);

    // Initialize GLEW to setup the OpenGL Function pointers
    glewExperimental = GL_TRUE;
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);

    // Set OpenGL options
    glEnable(GL_CULL_FACE);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // Compile and setup the shader
    GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertex_shader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertex_shader);
    GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragment_shader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragment_shader);
    GLuint program = glCreateProgram();
    glAttachShader(program, vertex_shader);
    glAttachShader(program, fragment_shader);
    glLinkProgram(program);
    glDeleteShader(vertex_shader);
    glDeleteShader(fragment_shader);

    mat4 projection;
    glm_ortho(0.0f, (GLfloat)WIDTH, (GLfloat)HEIGHT, 0.0f, -1, 1, projection);
    glUseProgram(program);
    glUniformMatrix4fv(glGetUniformLocation(program, "projection"), 1, GL_FALSE, (GLfloat *)projection);

    // FreeType
    FT_Library ft;
    // All functions return a value different than 0 whenever an error occurred
    if (FT_Init_FreeType(&ft))
        printf("ERROR::FREETYPE: Could not init FreeType Library\n");

    // Load font as face
    FT_Face face;
    if (FT_New_Face(ft, "playfair.ttf", 0, &face))
        printf("ERROR::FREETYPE: Failed to load font\n");

    // Set size to load glyphs as
    FT_Set_Pixel_Sizes(face, 0, 72);

    // Disable byte-alignment restriction
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    int i = 0;
    // Load first 128 characters of ASCII set
    for (GLubyte c = 0; c < 255; c++)
    {
        // Load character glyph 
        if (FT_Load_Char(face, c, FT_LOAD_RENDER))
        {
            printf("ERROR::FREETYTPE: Failed to load Glyph\n");
            continue;
        }
        // Generate texture
        GLuint texture;
        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(
            GL_TEXTURE_2D,
            0,
            GL_RED,
            face->glyph->bitmap.width,
            face->glyph->bitmap.rows,
            0,
            GL_RED,
            GL_UNSIGNED_BYTE,
            face->glyph->bitmap.buffer
        );
        // Set texture options
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        // Now store character for later use
        Character character = {
            texture,
            {(signed)face->glyph->bitmap.width, (signed)face->glyph->bitmap.rows},
            {face->glyph->bitmap_left, face->glyph->bitmap_top},
            (GLuint)face->glyph->advance.x
        };
        iterator = (Iterator*)realloc(iterator, sizeof(Iterator) * (i + 1));
        iterator[i].Character_Array = character;
        iterator[i].char_Array = c;
        i++;
    }
    glBindTexture(GL_TEXTURE_2D, 0);
    // Destroy FreeType once we're finished
    FT_Done_Face(face);
    FT_Done_FreeType(ft);


    // Configure VAO/VBO for texture quads
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    glfwVulkanSupported();
    // Game loop
    float k = 0;
    while (!glfwWindowShouldClose(window))
    {
        // Check and call events
        glfwPollEvents();

        // Clear the colorbuffer
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        if (k > 360)
        {
            k = 0;
        }
        k += 0.1f;
        RenderText(program, "Rotation", 0, 0, 10.0f, 10.0f, 1.0f, 1.0f, k, 0.5f, 0.8f, 0.2f, 1.0f);

        // Swap the buffers
        glfwSwapBuffers(window);
    }

    glfwTerminate();
    return 0;
}

void RenderText(GLuint program, const char * text, GLfloat originx, GLfloat originy, GLfloat x, GLfloat y, GLfloat scalex, GLfloat scaley, float rotation, float r, float g, float b, float a)
{
    // Activate corresponding render state  
    glUseProgram(program);
    glUniform4f(glGetUniformLocation(program, "textColor"), r, g, b, a);
    glActiveTexture(GL_TEXTURE0);
    glBindVertexArray(VAO);

    GLfloat tw = iterator[(int)text[i]].Character_Array.Bearing[0],
        th = 0.0f;
    for (int i = 0; i < (signed)strlen(text); i++)
    {
        Character ch = iterator[(int)text[i]].Character_Array;
        tw += (ch.Advance >> 6);
        th += ch.Size[1];
    }

    GLfloat rx = tw / 2.0f;
    GLfloat ry = th / 2.0f;

    mat4 model;
    glm_mat4_identity(model);

    glm_translate(model, (vec3) { originx, originy, 0.0f });

    glm_translate(model, (vec3) { scalex * rx, scaley * ry, 0.0f });
    glm_rotate(model, glm_rad(rotation), (vec3) { 0.0f, 0.0f, 1.0f });
    glm_translate(model, (vec3) { -scalex * rx, -scaley * ry, 0.0f });

    glm_scale(model, (vec3) { scalex, scaley, 1.0f });

    GLfloat xpos = 0.0;
    GLfloat ypos = 0.0;
    for (int i = 0; i < (signed)strlen(text); i++)
    {
        Character ch = iterator[(int)text[i]].Character_Array;

        mat4 ch_model;
        memcpy(ch_model, model, 16 * sizeof(float));
        glm_translate(ch_model, (vec3) { x, 0.0f, 0.0f });

        // Now advance cursors for next glyph (note that advance is number of 1/64 pixels)
        x += (ch.Advance >> 6); // Bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels))

        glUniformMatrix4fv(glGetUniformLocation(program, "model"), 1, GL_FALSE, (GLfloat *)ch_model);

        // Render glyph texture over quad
        glBindTexture(GL_TEXTURE_2D, ch.TextureID);

        GLfloat w = ch.Size[0] * scalex;
        GLfloat h = ch.Size[1] * scaley;
        GLfloat vertices[6][4] = {
            { 0.0f,  h,     0.0, 1.0 },
            { w,     0.0f,  1.0, 0.0 },
            { 0.0f,  0.0f,  0.0, 0.0 },

            { 0.0f,  h,     0.0, 1.0 },
            { w,     h,     1.0, 01.0 },
            { w,     0.0f,  01.0, 0.0 },
        };

        // Update content of VBO memory
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // Be sure to use glBufferSubData and not glBufferData
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        // Render quad
        glDrawArrays(GL_TRIANGLES, 0, 6);
    }
    glBindVertexArray(0);
    glBindTexture(GL_TEXTURE_2D, 0);
}

1 个答案:

答案 0 :(得分:0)

glm_rotate在原点应用轮播。因此,如果几何体的中心不在原点,则不会围绕对象/几何体的中心进行旋转。

解决方案: X = T(p)Rz(φ)T(-p)其中X是最终变换,p是枢轴

  1. 将原点(或对象中心,而不是对象的位置,它不相同)转换为原点
  2. 应用旋转或缩放
  3. 将枢轴转换回原来的位置
  4. 如果要围绕对象的中心旋转(如果对象的中心不在原点),则必须计算该对象/几何的中心。在应用任何轮换之前。您可以计算边界框然后获得该框的中心。

    cglm还提供了以下功能:glm_vec_center(min, max, pivot); /* pivot = center of object */。现在我们有中心点。让我们围绕这一点轮换:

    /* ... */
    
    glm_vec_center(min, max, pivot); /* center of object */
    glm_vec_inv_to(pivot, pivotInv); /* -pivot */
    
    /* ... */
    
    glm_translate(model, pivot);
    glm_rotate(model, angle, axis);
    glm_translate(model, pivotInv);
    

    上面的代码与 X = T(p)Rz(φ)T(-p)(平移(-pivot),旋转,平移(枢轴))相同。代码在代码中似乎相反,因为glm_translate =转换*转换。如果glm_translate = Translate * Transform,则不会是逆序。 cglm将来也可以提供这种替代的翻译乘法

    编辑:启动v0.4.2版本cglm为此提供了功能:检查glm_rotate_at()glm_quat_rotate_at()glm_rotate_atm和{{ 1}}为枢轴点创建新的旋转,因为glm_quat_rotate_atmglm_rotate_at会旋转现有的变换。要使用这些功能,请确保您拥有最新版本。