Come usare glOrtho () in OpenGL?


89

Non riesco a capire l'utilizzo di glOrtho. Qualcuno può spiegare a cosa serve?

Viene utilizzato per impostare l'intervallo del limite delle coordinate xy e z?

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

Significa che l'intervallo x, yez va da -1 a 1?


1
Questo video mi ha aiutato molto.
ViniciusArruda

Risposte:


156

Dai un'occhiata a questa immagine: Proiezioni grafiche inserisci qui la descrizione dell'immagine

Il glOrthocomando produce una proiezione "Obliqua" che vedete nella riga inferiore. Non importa quanto siano lontani i vertici nella direzione z, non si ritireranno in lontananza.

Uso glOrtho ogni volta che ho bisogno di fare grafica 2D in OpenGL (come barre di salute, menu ecc.) Usando il seguente codice ogni volta che la finestra viene ridimensionata:

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

Questo rimapperà le coordinate OpenGL nei valori pixel equivalenti (X che va da 0 a windowWidth e Y che va da 0 a windowHeight). Nota che ho invertito i valori Y perché le coordinate OpenGL iniziano dall'angolo in basso a sinistra della finestra. Quindi capovolgendo, ottengo un (0,0) più convenzionale a partire dall'angolo in alto a sinistra della finestra piuttosto.

Nota che i valori Z sono troncati da 0 a 1. Quindi fai attenzione quando specifichi un valore Z per la posizione del vertice, verrà troncato se non rientra in tale intervallo. Altrimenti, se è all'interno di tale intervallo, sembrerà non avere alcun effetto sulla posizione tranne che per i test Z.


91
oh mio dio TI AMO. Hai idea di quanto tempo ci vuole per trovare / capire questa singola riga di codice online? Grazie,
chiamerò il

2
Nota: (su Android) anche se il modello ha solo valori z negativi, sembra essere necessario avere un valore positivo per il parametro finale (lontano). Ho fatto un semplice test del triangolo (con l'abbattimento disabilitato), con i vertici in z= -2. Il triangolo era invisibile se ho usato glOrtho(.., 0.0f, -4.0f);, ..-1.0f, -3.0f)o ..-3.0f, -1.0f). Per essere visibile, il parametro far doveva essere POSITIVO 2 o maggiore; non sembrava importare quale fosse il parametro near. Ognuna di queste ha funzionato: ..0.0f, 2.0f), ..-1.0f, 2.0f), ..-3.0f, 2.0f), o ..0.0f, 1000.0f.
ToolmakerSteve

10
È ridicolo la quantità di cattivi tutorial su OpenGl che ci sono.
basickarl

1
@ Kari, spero che questo collegamento possa aiutare. > learnopengl.com/#!In-Practice/2D-Game/Rendering-Sprites
huahsin68

1
@mgouin L'intervallo z specifica dove si trovano il tuo piano Z vicino e il tuo piano Z lontano. Quando disegni la tua geometria, i suoi valori Z devono essere all'interno dei due piani Z. Se cadono al di fuori dei piani Z, la geometria non verrà renderizzata. Inoltre il tuo renderer ha solo una certa risoluzione per la profondità. Se hai il tuo aereo lontano impostato a 1000 unità di distanza e provi a disegnare un modello minuscolo con facce piccole a 0,1 unità di distanza l'una dall'altra, OpenGL non sarà in grado di darti la risoluzione di profondità di cui hai bisogno e otterrai combattimenti Z (sfarfallio) tra le facce.
Mikepote

56

Esempio minimo eseguibile

glOrtho: Giochi 2D, oggetti vicini e lontani appaiono della stessa dimensione:

inserisci qui la descrizione dell'immagine

glFrustrum: più reali come il 3D, oggetti identici più lontani appaiono più piccoli:

inserisci qui la descrizione dell'immagine

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 a monte .

Compilare:

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

Corri con glOrtho:

./main 1

Corri con glFrustrum:

./main

Testato su Ubuntu 18.10.

Schema

Orto: la fotocamera è un piano, il volume visibile un rettangolo:

inserisci qui la descrizione dell'immagine

Frustrum: la fotocamera è un punto, il volume visibile una fetta di piramide:

inserisci qui la descrizione dell'immagine

Fonte dell'immagine .

Parametri

Cerchiamo sempre da + z a -z con + y verso l'alto:

glOrtho(left, right, bottom, top, near, far)
  • left: minimo xvediamo
  • right: massimo xvediamo
  • bottom: minimo yvediamo
  • top: massimo yvediamo
  • -near: minimo zvediamo. , questo è il -1momento near. Quindi un input negativo significa positivo z.
  • -far: massimo zvediamo. Anche negativo.

Schema:

Fonte dell'immagine .

Come funziona sotto il cofano

Alla fine, OpenGL "usa" sempre:

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

Se non usiamo né glOrthoglFrustrum, questo è ciò che otteniamo.

glOrthoe glFrustrumsono solo trasformazioni lineari (moltiplicazione di matrici AKA) tali che:

  • glOrtho: prende un dato rettangolo 3D nel cubo predefinito
  • glFrustrum: prende una data sezione della piramide nel cubo predefinito

Questa trasformazione viene quindi applicata a tutti i vertici. Questo è ciò che intendo in 2D:

Fonte dell'immagine .

Il passaggio finale dopo la trasformazione è semplice:

  • rimuovere tutti i punti al di fuori del cubo (culling): assicurati solo che x, ye zsono dentro[-1, +1]
  • ignora il zcomponente e prendi solo xe y, che ora può essere messo in uno schermo 2D

Con glOrtho, zviene ignorato, quindi potresti anche usare sempre 0.

Uno dei motivi che potresti voler usare z != 0è fare in modo che gli sprite nascondano lo sfondo con il buffer di profondità.

Deprecazione

glOrthoè deprecato a partire da OpenGL 4.5 : il profilo di compatibilità 12.1. "TRASFORMAZIONI VERTICE A FUNZIONE FISSA" è in rosso.

Quindi non usarlo per la produzione. In ogni caso, comprenderlo è un buon modo per ottenere alcune informazioni su OpenGL.

I moderni programmi OpenGL 4 calcolano la matrice di trasformazione (che è piccola) sulla CPU, quindi danno la matrice e tutti i punti da trasformare in OpenGL, che può eseguire migliaia di moltiplicazioni di matrici per diversi punti molto velocemente in parallelo.

Gli shader dei vertici scritti manualmente eseguono quindi la moltiplicazione in modo esplicito, di solito con i comodi tipi di dati vettoriali dell'OpenGL Shading Language.

Poiché scrivi lo shader in modo esplicito, questo ti consente di modificare l'algoritmo in base alle tue esigenze. Tale flessibilità è una delle principali caratteristiche delle GPU più moderne, che a differenza di quelle vecchie che eseguivano un algoritmo fisso con alcuni parametri di input, ora possono eseguire calcoli arbitrari. Vedi anche: https://stackoverflow.com/a/36211337/895245

Con un esplicito GLfloat transform[]sarebbe simile a questo:

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 a monte .

Compila ed esegui:

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

Produzione:

inserisci qui la descrizione dell'immagine

La matrice per glOrthoè davvero semplice, composta solo da ridimensionamento e traslazione:

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

come menzionato nei documenti di OpenGL 2 .

Anche la glFrustummatrice non è troppo difficile da calcolare a mano, ma inizia a diventare fastidiosa. Nota come il frustum non può essere composto solo con ridimensionamento e traduzioni come glOrtho, maggiori informazioni su: https://gamedev.stackexchange.com/a/118848/25171

La libreria matematica GLM OpenGL C ++ è una scelta popolare per il calcolo di tali matrici. http://glm.g-truc.net/0.9.2/api/a00245.html documenti sia una orthoe frustumle operazioni.


1
"cosa dovrebbe essere usato invece?" - costruire le proprie matrici e assegnarle direttamente.
Kromster

Faccio fatica a provare a compilare il tuo ultimo esempio di codice (triangolo di trasformazione), ho clonato il repository ma ricevo solo l'errorecommon.h:19:23: error: ‘TIME_UTC’ undeclared (first use in this function) timespec_get(&ts, TIME_UTC);
Ivanzinho

1
@Ivanzinho Non sono riuscito a riprodurre su Ubuntu 20.04, presumibilmente perché è in C11 che il tuo GCC non implementa ancora. Ma ora ho ridotto a icona l'esempio su questa risposta senza common.h come avrei dovuto fare prima quindi dovrebbe funzionare :-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

4

glOrtho descrive una trasformazione che produce una proiezione parallela . La matrice corrente (vedere glMatrixMode) viene moltiplicata per questa matrice e il risultato sostituisce la matrice corrente, come se glMultMatrix fosse chiamata con la seguente matrice come argomento:

Documentazione OpenGL (il mio grassetto)

I numeri definiscono le posizioni dei piani di ritaglio (sinistro, destro, inferiore, superiore, vicino e lontano).

La proiezione "normale" è una proiezione prospettica che fornisce l'illusione della profondità. Wikipedia definisce una proiezione parallela come:

Le proiezioni parallele hanno linee di proiezione parallele sia nella realtà che nel piano di proiezione.

La proiezione parallela corrisponde a una proiezione prospettica con un punto di vista ipotetico, ad esempio uno in cui la telecamera si trova a una distanza infinita dall'oggetto e ha una lunghezza focale infinita, o "zoom".


Ciao, grazie per l'informazione. non riuscivo a capire bene la differenza tra proiezione parallela e prospettiva. ho cercato un po 'su google
ufk

6
Sfortunatamente le informazioni che hai ottenuto da Answers.com sono praticamente inutili. Una vista isometrica, ad esempio, è molto tridimensionale, ma è una proiezione parallela senza prospettiva. Vedi qui, e ci sono anche collegamenti a molti altri esempi di proiezioni: en.wikipedia.org/wiki/Isometric_projection
Ben Voigt
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.