Tôi không thể hiểu cách sử dụng glOrtho
. Ai đó có thể giải thích nó được sử dụng để làm gì?
Nó được sử dụng để thiết lập phạm vi giới hạn tọa độ xy và z?
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
Có nghĩa là phạm vi x, y và z từ -1 đến 1?
Tôi không thể hiểu cách sử dụng glOrtho
. Ai đó có thể giải thích nó được sử dụng để làm gì?
Nó được sử dụng để thiết lập phạm vi giới hạn tọa độ xy và z?
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
Có nghĩa là phạm vi x, y và z từ -1 đến 1?
Câu trả lời:
Hãy xem hình ảnh này: Phép chiếu đồ họa
Các glOrtho
lệnh tạo ra một "xiên" chiếu mà bạn nhìn thấy ở hàng dưới cùng. Không có vấn đề bao xa các đỉnh theo hướng z, chúng sẽ không lùi vào khoảng cách.
Tôi sử dụng glOrtho mỗi khi tôi cần làm đồ họa 2D trong OpenGL (chẳng hạn như thanh sức khỏe, menu, v.v.) bằng cách sử dụng mã sau mỗi khi cửa sổ được thay đổi kích thước:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0f, windowWidth, windowHeight, 0.0f, 0.0f, 1.0f);
Điều này sẽ ánh xạ lại các tọa độ OpenGL thành các giá trị pixel tương đương (X đi từ 0 đến windowWidth và Y từ 0 đến windowHeight). Lưu ý rằng tôi đã lật các giá trị Y vì tọa độ OpenGL bắt đầu từ góc dưới cùng bên trái của cửa sổ. Vì vậy, bằng cách lật, tôi nhận được một (0,0) thông thường hơn bắt đầu ở góc trên cùng bên trái của cửa sổ.
Lưu ý rằng các giá trị Z được cắt bớt từ 0 đến 1. Vì vậy, hãy cẩn thận khi bạn chỉ định giá trị Z cho vị trí đỉnh của bạn, nó sẽ bị cắt bớt nếu nằm ngoài phạm vi đó. Nếu không, nếu nó nằm trong phạm vi đó, nó sẽ không ảnh hưởng đến vị trí ngoại trừ các bài kiểm tra Z.
z= -2
. Tam giác là vô hình nếu tôi sử dụng glOrtho(.., 0.0f, -4.0f);
, ..-1.0f, -3.0f)
hoặc ..-3.0f, -1.0f)
. Để hiển thị, thông số xa phải là TÍCH CỰC 2 hoặc lớn hơn; nó dường như không quan trọng thông số gần là gì. Bất kỳ trong số này làm việc: ..0.0f, 2.0f)
, ..-1.0f, 2.0f)
, ..-3.0f, 2.0f)
, hoặc ..0.0f, 1000.0f
.
Ví dụ tối thiểu có thể chạy được
glOrtho
: Trò chơi 2D, các đối tượng ở gần và xa xuất hiện cùng kích thước:
glFrustrum
: cuộc sống thực hơn như 3D, các đối tượng giống hệt nhau ở xa hơn trông nhỏ hơn:
C chính
#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;
}
Biên dịch:
gcc -ggdb3 -O0 -o main -std=c99 -Wall -Wextra -pedantic main.c -lGL -lGLU -lglut
Chạy với glOrtho
:
./main 1
Chạy với glFrustrum
:
./main
Đã thử nghiệm trên Ubuntu 18.10.
Lược đồ
Ortho: camera là một mặt phẳng, thể tích hiển thị là một hình chữ nhật:
Frustrum: camera là một điểm, thể tích có thể nhìn thấy được là một phần của kim tự tháp:
Thông số
Chúng tôi luôn tìm kiếm từ + z đến -z với + y trở lên:
glOrtho(left, right, bottom, top, near, far)
left
: tối thiểu x
chúng tôi thấyright
: tối đa x
chúng tôi thấybottom
: tối thiểu y
chúng tôi thấytop
: tối đa y
chúng tôi thấy-near
: tối thiểu z
chúng ta thấy. Vâng , đây là -1
thời gian near
. Vì vậy, một đầu vào tiêu cực có nghĩa là tích cực z
.-far
: tối đa z
chúng tôi thấy. Cũng tiêu cực.Lược đồ:
Cách nó hoạt động dưới mui xe
Cuối cùng, OpenGL luôn "sử dụng":
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
Nếu chúng tôi không sử dụng glOrtho
cũng không glFrustrum
, đó là những gì chúng tôi nhận được.
glOrtho
và glFrustrum
chỉ là các phép biến đổi tuyến tính (phép nhân ma trận AKA) sao cho:
glOrtho
: lấy một hình chữ nhật 3D nhất định thành hình khối mặc địnhglFrustrum
: đưa một phần kim tự tháp đã cho vào khối lập phương mặc địnhBiến đổi này sau đó được áp dụng cho tất cả các đỉnh. Đây là những gì tôi muốn nói trong 2D:
Bước cuối cùng sau khi chuyển đổi rất đơn giản:
x
, y
và z
ở trong[-1, +1]
z
thành phần và chỉ lấy x
và y
, bây giờ có thể được đưa vào màn hình 2DVới glOrtho
, z
được bỏ qua, vì vậy bạn cũng có thể sử dụng 0
.
Một lý do bạn có thể muốn sử dụng z != 0
là làm cho các sprites ẩn nền bằng bộ đệm độ sâu.
Không dùng nữa
glOrtho
không được dùng nữa kể từ OpenGL 4.5 : cấu hình tương thích 12.1. "CHUYỂN ĐỔI VERTEX CHỨC NĂNG CỐ ĐỊNH" có màu đỏ.
Vì vậy, không sử dụng nó cho sản xuất. Trong mọi trường hợp, hiểu nó là một cách tốt để có được một số thông tin chi tiết về OpenGL.
Các chương trình OpenGL 4 hiện đại tính toán ma trận biến đổi (nhỏ) trên CPU, sau đó đưa ma trận và tất cả các điểm cần chuyển đổi sang OpenGL, có thể thực hiện song song hàng nghìn phép nhân ma trận cho các điểm khác nhau.
Các trình tô bóng đỉnh được viết thủ công sau đó thực hiện phép nhân một cách rõ ràng, thường là với các kiểu dữ liệu vectơ thuận tiện của Ngôn ngữ tô bóng OpenGL.
Vì bạn viết shader một cách rõ ràng, điều này cho phép bạn điều chỉnh thuật toán theo nhu cầu của mình. Tính linh hoạt như vậy là một tính năng chính của các GPU hiện đại hơn, không giống như các GPU cũ sử dụng thuật toán cố định với một số tham số đầu vào, giờ đây có thể thực hiện các phép tính tùy ý. Xem thêm: https://stackoverflow.com/a/36211337/895245
Với một cách rõ ràng, GLfloat transform[]
nó sẽ trông giống như sau:
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "common.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
};
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;
}
Đầu ra:
Ma trận cho glOrtho
thực sự đơn giản, chỉ bao gồm chia tỷ lệ và dịch:
scalex, 0, 0, translatex,
0, scaley, 0, translatey,
0, 0, scalez, translatez,
0, 0, 0, 1
như đã đề cập trong tài liệu OpenGL 2 .
Các glFrustum
ma trận không phải là quá khó để tính toán bằng tay hoặc, nhưng bắt đầu nhận được gây phiền nhiễu. Lưu ý rằng không thể tạo ra sự thất vọng khi chỉ mở rộng quy mô và các bản dịch như glOrtho
, thông tin thêm tại: https://gamedev.stackexchange.com/a/118848/25171
Thư viện toán GLM OpenGL C ++ là một lựa chọn phổ biến để tính toán các ma trận như vậy. http://glm.g-truc.net/0.9.2/api/a00245.html tài liệu cả an ortho
và frustum
phép toán.
glOrtho mô tả một phép biến đổi tạo ra một phép chiếu song song . Ma trận hiện tại (xem glMatrixMode) được nhân với ma trận này và kết quả thay thế ma trận hiện tại, như thể glMultMatrix được gọi với ma trận sau làm đối số của nó:
Tài liệu OpenGL (chữ in đậm của tôi)
Các con số xác định vị trí của các mặt phẳng cắt (trái, phải, dưới cùng, trên cùng, gần và xa).
Phép chiếu "bình thường" là phép chiếu phối cảnh cung cấp ảo giác về chiều sâu. Wikipedia định nghĩa một phép chiếu song song là:
Hình chiếu song song có các đường chiếu song song cả trong thực tế và trong mặt phẳng hình chiếu.
Phép chiếu song song tương ứng với phép chiếu phối cảnh có góc nhìn giả định — ví dụ: hình chiếu mà máy ảnh nằm cách vật thể một khoảng vô hạn và có tiêu cự vô hạn, hay còn gọi là "thu phóng".