我无法理解glOrtho
的用法。能否有人解释一下它的用途?
它是否用于设置x、y和z坐标范围的限制?
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
这意味着x、y和z的范围是从-1到1?
看一下这张图片:图像投影
glOrtho
命令会产生“斜视”(Oblique)投影,这是底部行中显示的。无论顶点在 z 方向上离观察者有多远,它们都不会消失到远处。
我每次需要在 OpenGL 中进行 2D 图形绘制(例如血条、菜单等),都会在窗口被调整大小时使用以下代码:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0f, windowWidth, windowHeight, 0.0f, 0.0f, 1.0f);
这将把OpenGL坐标重新映射为相应的像素值(X从0到窗口宽度,Y从0到窗口高度)。请注意,我已经翻转了Y值,因为OpenGL坐标从窗口左下角开始。因此,通过翻转,我得到了一个更常规的(0,0)起点,它位于窗口左上角。
请注意,Z值被剪辑在0到1之间。因此,在指定顶点位置的Z值时要小心,如果它超出该范围,它将被剪裁。否则,如果它在该范围内,它看起来对位置没有影响,除了Z测试。
z= -2
。如果我使用glOrtho(.., 0.0f, -4.0f);
、..-1.0f, -3.0f)
或..-3.0f, -1.0f)
,则三角形是不可见的。为了可见,远参数必须为正2或更大;似乎近参数无关紧要。任何一个都可以工作:..0.0f, 2.0f)
、..-1.0f, 2.0f)
、..-3.0f, 2.0f)
或..0.0f, 1000.0f
。 - ToolmakerSteveglOrtho
命令会产生您在底部行中看到的“斜”投影,我认为您指的是正交投影,而不是斜投影。斜投影不是正交投影的直接反面吗? - Distjoymain.c
#include <stdlib.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
static int ortho = 0;
static void display(void) {
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
if (ortho) {
} else {
/* This only rotates and translates the world around to look like the camera moved. */
gluLookAt(0.0, 0.0, -3.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}
glColor3f(1.0f, 1.0f, 1.0f);
glutWireCube(2);
glFlush();
}
static void reshape(int w, int h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (ortho) {
glOrtho(-2.0, 2.0, -2.0, 2.0, -1.5, 1.5);
} else {
glFrustum(-1.0, 1.0, -1.0, 1.0, 1.5, 20.0);
}
glMatrixMode(GL_MODELVIEW);
}
int main(int argc, char** argv) {
glutInit(&argc, argv);
if (argc > 1) {
ortho = 1;
}
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
glutCreateWindow(argv[0]);
glClearColor(0.0, 0.0, 0.0, 0.0);
glShadeModel(GL_FLAT);
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMainLoop();
return EXIT_SUCCESS;
}
编译:
gcc -ggdb3 -O0 -o main -std=c99 -Wall -Wextra -pedantic main.c -lGL -lGLU -lglut
使用 glOrtho
运行:
./main 1
使用 glFrustrum
运行:
./main
图片来源。
参数
我们始终从+z向-z看,+y朝上:
glOrtho(left, right, bottom, top, near, far)
left
: 我们看到的最小 x
值right
: 我们看到的最大 x
值bottom
: 我们看到的最小 y
值top
: 我们看到的最大 y
值-near
: 我们看到的最小 z
值。是的,这是 near
的负数。因此,负输入意味着正 z
。-far
: 我们看到的最大 z
值。同样是负数。架构:
图片来源。
底层原理
最终,OpenGL总是“使用”:
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
glOrtho
也不使用glFrustrum
,那么我们得到的就是这样。
glOrtho
和glFrustrum
只是线性变换(又称矩阵乘法),使得:
glOrtho
:将给定的3D矩形转换为默认立方体glFrustrum
:将给定的金字塔部分转换为默认立方体图片来源。
变换后的最后一步很简单:
x
、y
和 z
在 [-1, +1]
内z
分量,只取 x
和 y
,现在可以放入 2D 屏幕中使用 glOrtho
时,z
被忽略,因此您可能会始终使用 0
。
您可能希望使用 z != 0
的原因是使精灵通过深度缓冲隐藏背景。
弃用
glOrtho
从OpenGL 4.5开始已经被弃用:兼容性配置文件12.1中的“FIXED-FUNCTION VERTEX TRANSFORMATIONS”已标记为红色。
因此,不要在生产中使用它。但是,了解它是获得一些OpenGL见解的好方法。
现代OpenGL 4程序在CPU上计算变换矩阵(很小),然后将矩阵和所有要转换的点提供给OpenGL,在并行情况下快速完成数千个点的矩阵乘法。
手写的顶点着色器然后显式地执行乘法,通常使用OpenGL着色语言方便的向量数据类型。
由于您明确编写着色器,因此可以根据需要调整算法。这种灵活性是更现代的GPU的主要特征,与旧GPU不同,旧GPU使用固定算法和一些输入参数,现在可以进行任意计算。另请参见:https://dev59.com/wmMm5IYBdhLWcg3wMs3R#36211337
使用明确的GLfloat transform[]
,它看起来会像这样:
glfw_transform.c
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
static const GLuint WIDTH = 800;
static const GLuint HEIGHT = 600;
/* ourColor is passed on to the fragment shader. */
static const GLchar* vertex_shader_source =
"#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"layout (location = 1) in vec3 color;\n"
"out vec3 ourColor;\n"
"uniform mat4 transform;\n"
"void main() {\n"
" gl_Position = transform * vec4(position, 1.0f);\n"
" ourColor = color;\n"
"}\n";
static const GLchar* fragment_shader_source =
"#version 330 core\n"
"in vec3 ourColor;\n"
"out vec4 color;\n"
"void main() {\n"
" color = vec4(ourColor, 1.0f);\n"
"}\n";
static GLfloat vertices[] = {
/* Positions Colors */
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f
};
/* Build and compile shader program, return its ID. */
GLuint common_get_shader_program(
const char *vertex_shader_source,
const char *fragment_shader_source
) {
GLchar *log = NULL;
GLint log_length, success;
GLuint fragment_shader, program, vertex_shader;
/* Vertex shader */
vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
glCompileShader(vertex_shader);
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
glGetShaderiv(vertex_shader, GL_INFO_LOG_LENGTH, &log_length);
log = malloc(log_length);
if (log_length > 0) {
glGetShaderInfoLog(vertex_shader, log_length, NULL, log);
printf("vertex shader log:\n\n%s\n", log);
}
if (!success) {
printf("vertex shader compile error\n");
exit(EXIT_FAILURE);
}
/* Fragment shader */
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
glCompileShader(fragment_shader);
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
glGetShaderiv(fragment_shader, GL_INFO_LOG_LENGTH, &log_length);
if (log_length > 0) {
log = realloc(log, log_length);
glGetShaderInfoLog(fragment_shader, log_length, NULL, log);
printf("fragment shader log:\n\n%s\n", log);
}
if (!success) {
printf("fragment shader compile error\n");
exit(EXIT_FAILURE);
}
/* Link shaders */
program = glCreateProgram();
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &success);
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
if (log_length > 0) {
log = realloc(log, log_length);
glGetProgramInfoLog(program, log_length, NULL, log);
printf("shader link log:\n\n%s\n", log);
}
if (!success) {
printf("shader link error");
exit(EXIT_FAILURE);
}
/* Cleanup. */
free(log);
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
return program;
}
int main(void) {
GLint shader_program;
GLint transform_location;
GLuint vbo;
GLuint vao;
GLFWwindow* window;
double time;
glfwInit();
window = glfwCreateWindow(WIDTH, HEIGHT, __FILE__, NULL, NULL);
glfwMakeContextCurrent(window);
glewExperimental = GL_TRUE;
glewInit();
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glViewport(0, 0, WIDTH, HEIGHT);
shader_program = common_get_shader_program(vertex_shader_source, fragment_shader_source);
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
/* Position attribute */
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
/* Color attribute */
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
glBindVertexArray(0);
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shader_program);
transform_location = glGetUniformLocation(shader_program, "transform");
/* THIS is just a dummy transform. */
GLfloat transform[] = {
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
};
time = glfwGetTime();
transform[0] = 2.0f * sin(time);
transform[5] = 2.0f * cos(time);
glUniformMatrix4fv(transform_location, 1, GL_FALSE, transform);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
glfwSwapBuffers(window);
}
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glfwTerminate();
return EXIT_SUCCESS;
}
编译和运行:
gcc -ggdb3 -O0 -o glfw_transform.out -std=c99 -Wall -Wextra -pedantic glfw_transform.c -lGL -lGLU -lglut -lGLEW -lglfw -lm
./glfw_transform.out
输出:
glOrtho
的矩阵非常简单,只由缩放和平移组成:
scalex, 0, 0, translatex,
0, scaley, 0, translatey,
0, 0, scalez, translatez,
0, 0, 0, 1
glFrustum
矩阵的手动计算并不太难,但会变得很麻烦。请注意,不能像glOrtho
那样仅用缩放和平移来构建截锥体,更多信息请参见:https://gamedev.stackexchange.com/a/118848/25171
GLM OpenGL C++数学库是计算此类矩阵的流行选择。http://glm.g-truc.net/0.9.2/api/a00245.html记录了ortho
和frustum
操作。common.h:19:23: error: ‘TIME_UTC’ undeclared (first use in this function) timespec_get(&ts, TIME_UTC);
。 - user2188550