Вращающийся вокруг поворотной точки с помощью opengl - PullRequest
0 голосов
/ 24 декабря 2018

Я пытаюсь реализовать преобразования в моей программе для 2D-графики, но я сталкиваюсь с загадкой (даже если это тема noob, извините).Мой пример кода пытается перевести квадрат в центр экрана, а затем повернуть на 45 градусов с центром в качестве оси, но что-то идет не так.

Порядок матричной операции был взят из learnopengl.com https://learnopengl.com/code_viewer.php?code=in-practice/breakout/sprite_renderer

import math
type 
    OGLfloat = float32
    OGLuint = uint32
    OGLint = int32

const 
    POSITION_LENGTH = 3.OGLint
    COLOR_LENGTH = 4.OGLint

const
    WINDOW_W = 640
    WINDOW_H = 480

let
    colorDataOffset = POSITION_LENGTH * OGLint(sizeof(OGLfloat))

#[ Then many Opengl constants and functions translation from C, not pasted here. 
Nim users knows it's easy to do.]#

var 
    vertices = @[OGLfloat(-1.0), 1.0, 0, # Position   
                                0, 0, 1, 1, # Color
                          0, 1.0, 0,
                                0, 0, 1, 1,
                          0, 0, 0,
                                0, 0, 1, 1,
                          -1.0, 0.0, 0,
                                0, 0, 1, 1
                          ]

    indices = @[OGLuint(0), 1, 2, 2, 3, 0]

type Mat4x4* = array[16, OGLfloat] # 4 x 4 Matrix

# The operation who will concatenate the translation, rotation and scaling matrices.
proc `*`(a, b:Mat4x4):Mat4x4 =
    result[0] = a[0] * b[0] + a[4] * b[1] + a[8] * b[2] + a[12] * b[3]
    result[1] = a[1] * b[0] + a[5] * b[1] + a[9] * b[2] + a[13] * b[3]   
    result[2] = a[2] * b[0] + a[6] * b[1] + a[10] * b[2] + a[14] * b[3]
    result[3] = a[3] * b[0] + a[7] * b[1] + a[11] * b[2] + a[15] * b[3]

    result[4] = a[0] * b[4] + a[4] * b[5] + a[8] * b[6] + a[12] * b[7]
    result[5] = a[1] * b[4] + a[5] * b[5] + a[9] * b[6] + a[13] * b[7]
    result[6] = a[2] * b[4] + a[6] * b[5] + a[10] * b[6] + a[14] * b[7]
    result[7] = a[3] * b[4] + a[7] * b[5] + a[11] * b[6] + a[15] * b[7] 

    result[8] = a[0] * b[8] + a[4] * b[9] + a[8] * b[10] + a[12] * b[11]
    result[9] = a[1] * b[8] + a[5] * b[9] + a[9] * b[10] + a[13] * b[11]
    result[10] = a[2] * b[8] + a[6] * b[9] + a[10] * b[10] + a[14] * b[11]
    result[11] = a[3] * b[8] + a[7] * b[9] + a[11] * b[10] + a[15] * b[11]

    result[12] = a[0] * b[12] + a[4] * b[13] + a[8] * b[14] + a[12] * b[15]
    result[13] = a[1] * b[12] + a[5] * b[13] + a[9] * b[14] + a[13] * b[15]
    result[14] = a[2] * b[12] + a[6] * b[13] + a[10] * b[14] + a[14] * b[15]
    result[15] = a[3] * b[12] + a[7] * b[13] + a[11] * b[14] + a[15] * b[15]

proc rotation2D(deg:float):Mat4x4 =
    let
        rad = PI * deg / 180 # convert degrees to radians
        s = OGLfloat(sin(rad))
        c = OGLfloat(cos(rad))
    result = [c, s, 0, 0,
            - s, c, 0, 0,
              0, 0, 1, 0,
              0, 0, 0, 1]

proc translation(x,y:float):Mat4x4 = #{.noInit.} =
  result = [OGLfloat(1), 0, 0, 0,
                    0, 1, 0, 0,
                    0, 0, 1, 0,
                    x, y, 0, 1]

proc scaling(x,y:float):Mat4x4 =
    result = [OGLfloat(x), 0, 0, 0, 
                        0, y, 0, 0, 
                        0, 0, 1, 0, 
                        0, 0, 0, 1]

var 
    #[ The order of the operations was taken from "learnopengl.com" 
    but without the help of GLM, thanks to the previous functions.]#

    modelMatrix = translation(1.0, -1.0) * # move to the screen center
                  translation(-0.5, 0.5) * # move to the quad center
                  rotation2D(45.0) * # rotation on Z axis
                  translation(0.5, -0.5) * # re-move to the quad center ???
                  scaling(1.0, 1.0) # change nothing with these values.

# Init the system and pop the window.
var glfwErr = glfwInit()
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3)
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3)
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE)
var winHandle = glfwCreateWindow(WINDOW_W, WINDOW_H)
glfwMakeContextCurrent(winHandle)
var glewErr = glewInit()

# Shaders
var
    shadID:OGLuint
    vertSrc:cstring = """
        #version 330 core
        layout (location = 0) in vec3 aPos;
        layout (location = 1) in vec4 aColor;

        out vec4 vColor;

        uniform mat4 model;

        void main()
        {
            gl_Position = model * vec4(aPos, 1.0f);
            vColor = aColor;
        }
        """
    fragSrc:cstring = """
        #version 330 core
        out vec4 FragColor;    
        in vec4 vColor;

        void main()
        {
            FragColor = vColor;
        }

        """
# opengl stuff for sending the shader text and the vertices.
proc send_src(vert:var cstring, frag:var cstring):OGLuint =
    var success:OGLint
    # vertex
    var vertexShader = glCreateShader(GL_VERTEX_SHADER)
    glShaderSource(vertexShader, 1, addr vert, nil)
    glCompileShader(vertexShader)
    # Check compilation errors.
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, addr success)
    if bool(success) == false:
        echo(" vertex shader compilation failed (send_src)")
    else:
        echo("vertexShader compiled (send_src)")

    # fragment
    var fragmentShader = glCreateShader(GL_FRAGMENT_SHADER)
    glShaderSource(fragmentShader, 1, addr frag, nil)
    glCompileShader(fragmentShader)
    # Check compilation errors.
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, addr success)
    if bool(success) == false:
        echo("fragment shader compilation failed (send_src)")
    else:
        echo("fragmentShader compiled (send_src)")

    # Shader program
    result = glCreateProgram()
    glAttachShader(result, vertexShader)
    glAttachShader(result, fragmentShader)
    glLinkProgram(result)
    # Check for linkage errors.
    glGetProgramiv(result, GL_LINK_STATUS, addr success)
    if success == 0:
        echo("program linking failed (send_src)") 
    else:
        echo("shader linked (send_src)")

    glDeleteShader(vertexShader)
    glDeleteShader(fragmentShader)

glViewport(0, 0, WINDOW_W, WINDOW_H)
shadID = send_src(vertSrc, fragSrc)

var VAO, VBO, EBO:OGLuint
glGenVertexArrays(1, addr VAO)
glGenBuffers(1, addr VBO)
glGenBuffers(1, addr EBO)
glBindVertexArray(VAO)
glBindBuffer(GL_ARRAY_BUFFER, VBO)
glBufferData(GL_ARRAY_BUFFER, vertices.len * sizeof(OGLfloat), 
             addr vertices[0], GL_STATIC_DRAW)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.len * sizeof(OGLuint), 
             addr indices[0], GL_STATIC_DRAW)
# Position layout
glVertexAttribPointer(0, POSITION_LENGTH, GL_FLOAT, GL_FALSE, (POSITION_LENGTH + COLOR_LENGTH) * OGLint(sizeof(OGLfloat)), 
                      nil)
glEnableVertexAttribArray(0)
# Color layout
glVertexAttribPointer(1, COLOR_LENGTH, GL_FLOAT, GL_FALSE, (POSITION_LENGTH + COLOR_LENGTH) * OGLint(sizeof(OGLfloat)), 
                      cast[pointer](colorDataOffset))
glEnableVertexAttribArray(1)
glBindBuffer(GL_ARRAY_BUFFER, 0)
glBindVertexArray(0)
glUseProgram(shadID)
# The render loop.
while bool(glfwWindowShouldClose(winHandle)) == false:
    glClearColor(0.2, 0.3, 0.3, 1.0)
    glClear(GL_COLOR_BUFFER_BIT)
    glBindVertexArray(VAO)
    glUniformMatrix4fv(glGetUniformLocation(shadID, "model"), 1, char(false), addr modelMatrix[0])
    glDrawElements(GL_TRIANGLES, OGLint(indices.len), GL_UNSIGNED_INT, nil)
    glfwSwapBuffers(winHandle)
    glfwPollEvents()

glDeleteVertexArrays(1, addr VAO)
glDeleteBuffers(1, addr VBO)
glDeleteBuffers(1, addr EBO)
glfwDestroyWindow(winHandle)
glfwTerminate()

И вот результат.

enter image description here

Я желаю вам счастливого праздничного сезона.

1 Ответ

0 голосов
/ 25 декабря 2018

Поскольку окно просмотра имеет прямоугольную форму, необходимо учитывать соотношение сторон окна просмотра.

Рассчитать соотношение сторон (aspect) области просмотра, которая является шириной области просмотра, деленной на ее высоту.

Примените матрицу простого вида к преобразованию.Матрица вида представляет собой простое масштабирование оси x в соответствии с обратным соотношением сторон (1.0/aspect).

Достаточно сначала переместить квадрат на вход экрана и повернуть его:

modelMatrix = scaling(1.0/aspect, 1.0) * # aspect ratio
              rotation2D(45.0) *         # rotation on Z axis
              translation(0.5, -0.5) *   # move to the center of the screen
              scaling(1.0, 1.0)          # change nothing with these values.

Обратите внимание, что согласно оператору инициализации и умножения матрицы преобразование, сопровождаемое вращением, должно быть преобразовано следующим образом:

model = rotate * translate(-pivot_x, -pivot_y)  

См. Программирование GLSL / Векторные и матричные операции.

Предварительный просмотр:


Обратите внимание, что в качестве альтернативы вы можете добавить отдельную (ортогональную) матрицу проекции кшейдер:

#version 330 core

layout (location = 0) in vec3 aPos;
layout (location = 1) in vec4 aColor;

out vec4 vColor;

uniform mat4 project;
uniform mat4 model;

void main()
{
    gl_Position = project * model * vec4(aPos, 1.0f);
    vColor = aColor;
}

projMatrix = scaling(1.0/aspect, 1.0)
modelMatrix = rotation2D(45.0) *         # rotation on Z axis
              translation(0.5, -0.5) *   # move to the center of the screen
              scaling(1.0, 1.0)          # change nothing with these values.

glUniformMatrix4fv(glGetUniformLocation(shadID, "project"), 1, char(false), addr projMatrix[0])
glUniformMatrix4fv(glGetUniformLocation(shadID, "model"), 1, char(false), addr modelMatrix[0])

Если вы хотите вращаться вокруг оси (pivot_x, pivot_y),тогда вам нужно

model = translate(pivot_x, pivot_y) * rotate * translate(-pivot_x, -pivot_y) 

например, поворот (-0,5, 0,5)

modelMatrix = translation(-0.5, 0.5) *   # pivot
              rotation2D(45.0) *         # rotation on Z axis
              translation(0.5, -0.5) *   # inverted pivot
              scaling(1.0, 1.0)         

Предварительный просмотр:


Если вы, наконец, захотите переместить ось квадрацикла в центр экрана, вам необходимо:

model = 
    translate(widht/2 - pivot_x, height/2 - pivot_y) *
    translate(pivot_x, pivot_y) * rotate * translate(-pivot_x, -pivot_y) 

например,

modelMatrix = translation(float(WINDOW_W/2)-100, float(WINDOW_H/2)-100) * 
              translation(100, 100) *
              rotation2D(45.0) *
              translation(-100, -100) *
              scaling(1.0, 1.0)

Какойсовпадает с:

model = translate(widht/2, height/2) * rotate * translate(-pivot_x, -pivot_y) 

modelMatrix = translation(float(WINDOW_W/2), float(WINDOW_H/2)) * 
              rotation2D(45.0) *
              translation(-100, -100) *
              scaling(1.0, 1.0)
...