我尝试在2D图形程序中实现变换,但遇到一个谜(即使这是一个菜鸟话题,对不起)。 我的示例代码尝试将四边形平移到屏幕的中心,然后以中心为轴旋转45度,但是出了点问题。
矩阵运算的顺序取自learningopengl.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()
这是结果。
祝您假期快乐。
答案 0 :(得分:1)
由于视口为矩形,因此必须考虑视口的纵横比。
计算视口的宽高比(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 Programming/Vector and Matrix Operations。
预览:
请注意,您也可以向着色器添加单独的(正交的)投影矩阵:
#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)