web-dev-qa-db-ja.com

OpenGLでglOrtho()を使用する方法は?

glOrthoの使用法が理解できません。誰かがそれが何に使われているのか説明できますか?

X、y、z座標制限の範囲を設定するために使用されていますか?

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

これは、x、y、zの範囲が-1〜1であることを意味しますか?

77
ufk

この写真を見てください: グラフィカルな投影enter image description here

glOrthoコマンドは、一番下の行に表示される「斜め」投影を生成します。頂点がz方向にどれだけ離れていても、頂点は遠ざかりません。

ウィンドウのサイズが変更されるたびに次のコードを使用して、OpenGLで2Dグラフィックス(ヘルスバー、メニューなど)を実行する必要があるたびにglOrthoを使用します。

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

これにより、OpenGL座標が同等のピクセル値に再マップされます(Xは0からwindowWidthに、Yは0からwindowHeightになります)。 OpenGL座標はウィンドウの左下隅から始まるため、Y値を反転していることに注意してください。そのため、反転することにより、ウィンドウの左上隅から始まる従来の(0,0)が得られます。

Z値は0から1にクリップされることに注意してください。したがって、頂点の位置にZ値を指定する場合、その範囲外にある場合はクリップされることに注意してください。それ以外の場合、その範囲内にある場合、Zテストを除き、位置に影響がないように見えます。

136
Mikepote

最小限の実行可能な例

glOrtho:2Dゲーム、近いオブジェクトと遠いオブジェクトは同じサイズで表示されます:

enter image description here

glFrustrum:3Dのようによりリアルに、遠く離れた同一のオブジェクトはより小さく見えます:

enter image description here

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アップストリーム

コンパイル:

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

glOrthoで実行:

./main 1

glFrustrumで実行:

./main

Ubuntu 18.10。でテスト済み。

スキーマ

オルソ:カメラは平面で、可視ボリュームは長方形です:

enter image description here

錐台:カメラは点であり、目に見えるボリュームはピラミッドのスライスです:

enter image description here

画像ソース

パラメータ

常に+ zから-zに向かって、+ yを上向きに見ていきます。

glOrtho(left, right, bottom, top, near, far)
  • left:最小のx
  • right:最大x参照
  • bottom:最小のy
  • top:最大y参照
  • -near:最小のzが表示されます。 はい、これは-1near。したがって、負の入力は正のzを意味します。
  • -farzの最大値。また否定的。

スキーマ:

画像ソース

フードの下での仕組み

最終的に、OpenGLは常に「使用」します。

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

glOrthoglFrustrumも使用しない場合、それが得られます。

glOrthoglFrustrumは、次のような線形変換(行列乗算)です。

  • glOrtho:指定された3D長方形をデフォルトのキューブに取り込みます
  • glFrustrum:指定されたピラミッドセクションを既定のキューブに取り込みます

この変換は、すべての頂点に適用されます。これは2Dで私が意味するものです:

画像ソース

変換後の最終ステップは簡単です。

  • キューブの外側のポイントを削除(カリング):xy、およびz[-1, +1]
  • zコンポーネントを無視し、xyのみを取得します。これらは2D画面に配置できます

glOrthoの場合、zは無視されるため、常に0

使用したい理由の1つはz != 0は、深度バッファでスプライトが背景を隠すようにすることです。

非推奨

glOrthoは、 OpenGL 4.5 :互換性プロファイル12.1の時点で非推奨です。 「固定機能頂点変換」は赤です。

したがって、実稼働には使用しないでください。いずれにせよ、それを理解することはOpenGLの洞察を得る良い方法です。

最新のOpenGL 4プログラムは、CPUで(小さい)変換マトリックスを計算し、マトリックスとOpenGLに変換するすべてのポイントを提供します。これにより、異なるポイントに対して数千のマトリックス乗算を非常に高速に並列実行できます。

手動で記述された 頂点シェーダー その後、通常OpenGLシェーディング言語の便利なベクターデータタイプを使用して、明示的に乗算を行います。

シェーダーを明示的に記述するため、これにより、必要に応じてアルゴリズムを微調整できます。このような柔軟性は、最新のGPUの主要な機能であり、一部の入力パラメーターで固定アルゴリズムを実行していた古いGPUとは異なり、任意の計算を実行できるようになりました。参照: https://stackoverflow.com/a/36211337/895245

明示的なGLfloat transform[]これは次のようになります。

#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アップストリーム

出力:

enter image description here

glOrthoのマトリックスは本当に簡単で、スケーリングと変換のみで構成されています。

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

OpenGL 2 docs に記載されているとおり。

glFrustum matrix も手作業で計算するのは難しくありませんが、迷惑になり始めます。 glOrthoのようなスケーリングと変換だけでは錐台を構成できないことに注意してください。詳細は次のとおりです。 https://gamedev.stackexchange.com/a/118848/25171

GLM OpenGL C++数学ライブラリは、このような行列を計算するための一般的な選択肢です。 http://glm.g-truc.net/0.9.2/api/a00245.html は、ortho操作とfrustum操作の両方を文書化します。

glOrthoは、parallel投影を生成する変換を記述します。現在の行列(glMatrixModeを参照)にこの行列を乗算し、次の行列を引数としてglMultMatrixが呼び出されたかのように、結果が現在の行列を置き換えます。

OpenGLドキュメント (太字)

番号は、クリッピングプレーンの位置(左、右、下、上、近く、遠く)を定義します。

「通常の」投影法は、奥行きの錯覚を提供する透視投影法です。 Wikipedia 平行投影を次のように定義します:

平行投影には、現実と投影面の両方で平行な投影線があります。

平行投影は、仮想的な視点を持つ透視投影に対応します。たとえば、カメラがオブジェクトから無限の距離にあり、無限の焦点距離、つまり「ズーム」を持っている場合です。

4
ChrisF