OpenGL'de glOrtho () nasıl kullanılır?


88

Kullanımını anlayamıyorum glOrtho. Birisi ne için kullanıldığını açıklayabilir mi?

Xy ve z koordinatlarının sınırını ayarlamak için mi kullanılır?

glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);

Bu x, y ve z aralığının -1 ile 1 arasında olduğu anlamına mı gelir?


1
Bu video bana çok yardımcı oldu.
ViniciusArruda

Yanıtlar:


155

Şu resme bir göz atın: Grafik Projeksiyonlar görüntü açıklamasını buraya girin

glOrthoKomut Eğer alt satırında gördüğünüz bir "Eğik" projeksiyon üretir. Köşeler z yönünde ne kadar uzakta olursa olsun, mesafeye çekilmeyeceklerdir.

OpenGL'de (sağlık çubukları, menüler vb.) 2D grafikler yapmam gerektiğinde, pencere her yeniden boyutlandırıldığında aşağıdaki kodu kullanarak glOrtho kullanıyorum:

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0f, windowWidth, windowHeight, 0.0f, 0.0f, 1.0f);

Bu, OpenGL koordinatlarını eşdeğer piksel değerlerine yeniden eşler (X, 0'dan windowWidth'e ve Y, 0'dan windowHeight'a gider). OpenGL koordinatları pencerenin sol alt köşesinden başladığı için Y değerlerini çevirdim. Yani çevirerek, pencerenin sol üst köşesinden başlayan daha geleneksel bir (0,0) elde ediyorum.

Z değerlerinin 0'dan 1'e kırpıldığına dikkat edin. Bu nedenle, tepe noktanızın konumu için bir Z değeri belirlerken dikkatli olun, bu aralığın dışına çıkarsa kırpılacaktır. Aksi takdirde, bu aralığın içindeyse, Z testleri dışında konum üzerinde hiçbir etkisinin olmadığı görünecektir.


91
aman tanrım seni seviyorum Bu tek satırlık kodu çevrimiçi olarak bulmanın / anlamanın ne kadar süreceği hakkında bir fikriniz var mı? Teşekkürler, bunun için ilk doğan çocuğuma adını vereceğim
karpathy

2
Not: (Android'de) modelde yalnızca negatif z değerleri olsa bile, son (uzak) parametre için pozitif bir değere sahip olmak gerekli görünmektedir. Ben de köşeler ile, (özürlü itlaf ile) basit üçgen test yaptım z= -2. Ben kullanıldığı takdirde üçgen görünmez oldu glOrtho(.., 0.0f, -4.0f);, ..-1.0f, -3.0f)ya ..-3.0f, -1.0f). Görünebilmesi için uzak parametrenin POZİTİF 2 veya daha büyük olması gerekir; yakın parametrenin ne olduğu önemli değilmiş. Bunlardan herhangi çalıştı: ..0.0f, 2.0f), ..-1.0f, 2.0f), ..-3.0f, 2.0f), veya ..0.0f, 1000.0f.
ToolmakerSteve

10
OpenGl'deki kötü eğitimlerin miktarı çok saçma.
basickarl

1
@Kari, Umarım bu bağlantı yardımcı olabilir. > learnopengl.com/#!In-Practice/2D-Game/Rendering-Sprites
huahsin68

1
@mgouin z aralığı, Z-yakın düzleminizin ve Z-uzak düzleminizin nerede olduğunu belirtir. Geometrinizi çizdiğinizde, onun Z değerleri iki Z düzleminin içinde olmalıdır . Z düzlemlerinin dışına düşerlerse, geometriniz işlenmeyecektir. Ayrıca oluşturucunuzun yalnızca belirli bir derinlik çözünürlüğü vardır. Uzak uçağınızı 1000 birim uzağa ayarladıysanız ve birbirinden 0.1 birim uzakta küçük yüzleri olan küçük bir model çizmeyi denerseniz, OpenGL size ihtiyacınız olan derinlik çözünürlüğünü veremez ve Z ile savaşırsınız (titreyen) yüzler arasında.
Mikepote

55

Minimum çalıştırılabilir örnek

glOrtho: 2D oyunlar, yakın ve uzak nesneler aynı boyutta görünür:

görüntü açıklamasını buraya girin

glFrustrum: 3D gibi daha fazla gerçek hayat, aynı nesneler daha uzakta daha küçük görünür:

görüntü açıklamasını buraya girin

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

GitHub yukarı akış .

Derleyin:

gcc -ggdb3 -O0 -o main -std=c99 -Wall -Wextra -pedantic main.c -lGL -lGLU -lglut

Şununla çalıştırın glOrtho:

./main 1

Şununla çalıştırın glFrustrum:

./main

Ubuntu 18.10'da test edilmiştir.

Şema

Ortho: kamera bir düzlem, görünür hacim bir dikdörtgendir:

görüntü açıklamasını buraya girin

Frustrum: kamera bir nokta, görünür hacim bir piramidin bir dilimi:

görüntü açıklamasını buraya girin

Görüntü kaynağı .

Parametreler

Her zaman + z'den -z'ye + y'den yukarı bakıyoruz:

glOrtho(left, right, bottom, top, near, far)
  • left: asgari xgörüyoruz
  • right: xgördüğümüz maksimum
  • bottom: asgari ygörüyoruz
  • top: ygördüğümüz maksimum
  • -near: asgari zgörüyoruz. Evet , bu -1zamanlar near. Yani negatif girdi, pozitif anlamına gelir z.
  • -far: maksimum zgörüyoruz. Ayrıca olumsuz.

Şema:

Görüntü kaynağı .

Kaputun altında nasıl çalışır

Sonunda, OpenGL her zaman "kullanır":

glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);

Ne kullanırsak ne glOrthode kullanırsak glFrustrum, elde ettiğimiz budur.

glOrthove glFrustrumsadece doğrusal dönüşümlerdir (AKA matris çarpımı) öyle ki:

  • glOrtho: belirli bir 3B dikdörtgeni varsayılan küpün içine alır
  • glFrustrum: belirli bir piramit bölümünü varsayılan küpün içine alır

Bu dönüşüm daha sonra tüm tepe noktalarına uygulanır. 2D'de kastettiğim bu:

Görüntü kaynağı .

Dönüşümden sonraki son adım basittir:

  • (itlaf) küpün herhangi noktaları dışında kaldırın: sadece sağlamak x, yve zvardır[-1, +1]
  • zbileşeni görmezden gelin ve yalnızca xve yşimdi 2D ekrana yerleştirilebilen al

With glOrtho, zgöz ardı edilir, bu yüzden her zaman kullanabilirsiniz 0.

Kullanmak isteyebileceğiniz bir neden z != 0, sprite'ların arka planı derinlik tamponuyla gizlemesini sağlamaktır.

Kullanımdan kaldırma

glOrthoOpenGL 4.5 itibariyle kullanımdan kaldırılmıştır : uyumluluk profili 12.1. "SABİT FONKSİYONLU KÖŞE DÖNÜŞÜMLERİ" kırmızı renkte.

Bu yüzden onu üretim için kullanmayın. Her durumda, bunu anlamak OpenGL içgörüsü elde etmenin iyi bir yoludur.

Modern OpenGL 4 programları, CPU üzerindeki dönüşüm matrisini (küçük olan) hesaplar ve ardından matrisi ve dönüştürülecek tüm noktaları, farklı noktalar için binlerce matris çarpımını paralel olarak gerçekten hızlı yapabilen OpenGL'ye verir.

Manuel olarak yazılan köşe gölgelendiricileri daha sonra çarpımı açıkça, genellikle OpenGL Gölgelendirme Dilinin uygun vektör veri türleriyle yapar.

Gölgelendiriciyi açıkça yazdığınız için bu, algoritmayı ihtiyaçlarınıza göre ayarlamanıza olanak tanır. Bu tür bir esneklik, bazı giriş parametreleriyle sabit bir algoritma yapan eski GPU'ların aksine, artık rastgele hesaplamalar yapabilen daha modern GPU'ların önemli bir özelliğidir. Ayrıca bkz .: https://stackoverflow.com/a/36211337/895245

Açıkça ifade GLfloat transform[]etmek gerekirse şuna benzer:

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

GitHub yukarı akış .

Derleyin ve çalıştırın:

gcc -ggdb3 -O0 -o glfw_transform.out -std=c99 -Wall -Wextra -pedantic glfw_transform.c -lGL -lGLU -lglut -lGLEW -lglfw -lm
./glfw_transform.out

Çıktı:

görüntü açıklamasını buraya girin

Matrisi glOrthogerçekten basittir, yalnızca ölçeklendirme ve çeviriden oluşur:

scalex, 0,      0,      translatex,
0,      scaley, 0,      translatey,
0,      0,      scalez, translatez,
0,      0,      0,      1

OpenGL 2 belgelerinde belirtildiği gibi .

glFrustumMatris ya elle olarak hesaplanması çok zor değil, ama can sıkıcı oluyor başlar. Sadece ölçeklendirme ve çevirilerle nasıl hayal kırıklığı yaratılamayacağına dikkat edinglOrtho , daha fazla bilgi için: https://gamedev.stackexchange.com/a/118848/25171

GLM OpenGL C ++ matematik kitaplığı, bu tür matrisleri hesaplamak için popüler bir seçimdir. http://glm.g-truc.net/0.9.2/api/a00245.html belgeler ikisi de bir orthove frustumoperasyonları.


1
"onun yerine ne kullanılmalı?" - kendi matrislerinizi oluşturun ve bunları doğrudan atayın.
Kromster

Son kod örneğinizi (üçgen dönüşümü) derlemeye çalışırken zorlanıyorum, depoyu klonladım ama sadece hatayı alıyorumcommon.h:19:23: error: ‘TIME_UTC’ undeclared (first use in this function) timespec_get(&ts, TIME_UTC);
Ivanzinho

1
@Ivanzinho Ubuntu 20.04'te yeniden üretemedim, muhtemelen bu, GCC'nizin henüz gerçekleştirmediği C11'de olduğu için oluyor. Ama şimdi bu yanıttaki örneği ortak olmadan küçülttüm. H daha önce yapmam gerektiği gibi çalışmalı :-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

4

glOrtho, paralel bir projeksiyon üreten bir dönüşümü tanımlar . Geçerli matris (bkz. GlMatrixMode) bu matrisle çarpılır ve sonuç, sanki glMultMatrix, argümanı olarak aşağıdaki matris ile çağrılmış gibi geçerli matrisin yerini alır:

OpenGL belgeleri (kalın harfim)

Sayılar, kırpma düzlemlerinin (sol, sağ, alt, üst, yakın ve uzak) konumlarını tanımlar.

"Normal" projeksiyon, derinlik yanılsaması sağlayan perspektif bir projeksiyondur. Wikipedia , paralel bir projeksiyonu şu şekilde tanımlar:

Paralel projeksiyonlar, hem gerçekte hem de projeksiyon düzleminde paralel olan projeksiyon hatlarına sahiptir.

Paralel projeksiyon, varsayımsal bir bakış açısına sahip bir perspektif projeksiyona karşılık gelir - örneğin, kameranın nesneden sonsuz bir mesafede uzandığı ve sonsuz bir odak uzunluğuna veya "yakınlaştırmaya" sahip olduğu bir bakış açısı.


Merhaba, bilgi için teşekkürler. paralel ve perspektif projeksiyon arasındaki farkı tam olarak anlayamadım. biraz googledim
ufk

6
Maalesef Answers.com'dan aldığınız bilgiler oldukça değersiz. Örneğin bir izometrik görünüm çok 3 boyutludur, ancak perspektifsiz paralel bir izdüşümdür. Buraya bakın ve diğer birçok projeksiyon örneğine bağlantılar da vardır: en.wikipedia.org/wiki/Isometric_projection
Ben Voigt
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.