Làm thế nào để sử dụng glOrtho () trong OpenGL?


87

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?


1
Video này đã giúp tôi rất nhiều.
ViniciusArruda

Câu trả lời:


153

Hãy xem hình ảnh này: Phép chiếu đồ họa nhập mô tả hình ảnh ở đây

Các glOrtholệ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.


90
trời ơi, TÔI YÊU BẠN. Bạn có biết mất bao lâu để tìm / tìm ra dòng mã này trực tuyến không? Cảm ơn bạn, tôi sẽ đặt tên cho đứa trẻ sinh ra đầu tiên của tôi sau khi bạn cho điều này
karpathy

2
Lưu ý: (trên Android) ngay cả khi mô hình chỉ có giá trị z âm, thì có vẻ như cần phải có giá trị dương cho tham số cuối cùng (xa). Tôi đã thực hiện một bài kiểm tra tam giác đơn giản (với tính năng chọn bị vô hiệu hóa), với các đỉnh tại 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.
ToolmakerSteve

9
Thật nực cười khi có số lượng hướng dẫn tồi về OpenGl.
basickarl

1
@Kari, Hy vọng liên kết này có thể giúp ích. > Learnopengl.com/#!In-Practice/2D-Game/Rendering-Sprites
huahsin68

1
@mgouin Phạm vi z chỉ định vị trí của mặt phẳng gần Z và mặt phẳng xa Z của bạn. Khi bạn vẽ hình học của mình, các giá trị Z phải nằm bên trong hai mặt phẳng Z. Nếu chúng rơi ra ngoài mặt phẳng Z, hình học của bạn sẽ không được hiển thị. Ngoài ra trình kết xuất của bạn chỉ có một độ phân giải nhất định cho độ sâu. Nếu bạn đặt máy bay xa của mình thành 1000 đơn vị và bạn thử vẽ một mô hình nhỏ với các mặt nhỏ cách nhau 0,1 đơn vị, OpenGL sẽ không thể cung cấp cho bạn độ phân giải độ sâu bạn cần và bạn sẽ nhận được Z-Fighting (nhấp nháy) giữa các mặt.
Mikepote

54

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:

nhập mô tả hình ảnh ở đây

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:

nhập mô tả hình ảnh ở đây

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;
}

GitHub ngược dòng .

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:

nhập mô tả hình ảnh ở đây

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:

nhập mô tả hình ảnh ở đây

Nguồn ảnh .

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 xchúng tôi thấy
  • right: tối đa xchúng tôi thấy
  • bottom: tối thiểu ychúng tôi thấy
  • top: tối đa ychúng tôi thấy
  • -near: tối thiểu zchúng ta thấy. Vâng , đây là -1thờ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 zchúng tôi thấy. Cũng tiêu cực.

Lược đồ:

Nguồn ảnh .

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 glOrthocũng không glFrustrum, đó là những gì chúng tôi nhận được.

glOrthoglFrustrumchỉ 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 định
  • glFrustrum: đưa một phần kim tự tháp đã cho vào khối lập phương mặc định

Biế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:

Nguồn ảnh .

Bước cuối cùng sau khi chuyển đổi rất đơn giản:

  • loại bỏ bất kỳ điểm nào bên ngoài khối lập phương (chọn lọc): chỉ cần đảm bảo rằng x, yzở trong[-1, +1]
  • bỏ qua zthành phần và chỉ lấy xy, bây giờ có thể được đưa vào màn hình 2D

Vớ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 != 0là làm cho các sprites ẩn nền bằng bộ đệm độ sâu.

Không dùng nữa

glOrthokhô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;
}

GitHub ngược dòng .

Đầu ra:

nhập mô tả hình ảnh ở đây

Ma trận cho glOrthothự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 glFrustumma 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 orthofrustumphép toán.


1
"những gì nên được sử dụng thay thế?" - xây dựng ma trận của riêng bạn và gán chúng trực tiếp.
Kromster

4

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".


Xin chào, cảm ơn về thông tin. Tôi không thể hiểu được sự khác biệt giữa phép chiếu song song và phép chiếu phối cảnh. tôi đã truy cập một chút và tìm thấy câu trả lời trên wiki.answers.com/Q/…
ufk

6
Thật không may, thông tin bạn nhận được từ answer.com là khá vô giá trị. Ví dụ, một hình chiếu đẳng hướng rất 3-D, nhưng nó là một hình chiếu song song không có phối cảnh. Xem ở đây và cũng có các liên kết đến nhiều ví dụ khác về phép chiếu: en.wikipedia.org/wiki/Isometric_projection
Ben Voigt
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.