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


89

の使い方がわかりませんglOrtho。誰かがそれが何のために使われるのか説明できますか?

xy座標とz座標の範囲の制限を設定するために使用されますか?

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

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


1
このビデオは私を大いに助けました。
ViniciusArruda 2017

回答:


156

この写真を見てください:グラフィカルな投影 ここに画像の説明を入力してください

この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検定を除いて位置に影響を与えないように見えます。


91
なんてこった、私はあなたを愛している。この1行のコードをオンラインで見つけて理解するのにどれくらいの時間がかかるかわかりますか?ありがとう、私はこれのためにあなたにちなんで私の最初の生まれた子供に名前を付けます
karpathy 2010

2
注:(Androidの場合)モデルに負のz値しかない場合でも、最後の(遠い)パラメーターには正の値が必要なようです。頂点をに設定して、単純な三角形テスト(カリングを無効にした状態)を実行しましたz= -2。私が使用している場合、三角形が見えなかったglOrtho(.., 0.0f, -4.0f);..-1.0f, -3.0f)または..-3.0f, -1.0f)。表示するには、farパラメーターがPOSITIVE2以上である必要がありました。ニアパラメータが何であるかは問題ではないようでした。これらのいずれかが働いた: ..0.0f, 2.0f)..-1.0f, 2.0f)..-3.0f, 2.0f)、または..0.0f, 1000.0f
toolmakerSteve 2014

10
OpenGlに悪いチュートリアルがたくさんあるのはばかげています。
basickarl 2014年

1
@Kari、このリンクがお役に立てば幸いです。> learnopengl.com/#!In-Practice/2D-Game/Rendering-Sprites
huahsin68

1
@mgouin z範囲は、Z-near平面とZ-far平面がどこにあるかを指定します。ジオメトリを描画するときは、Z値が2つのZ平面の内側にある必要あります。それらがZ平面の外側にある場合、ジオメトリはレンダリングされません。また、レンダラーの深度には特定の解像度しかありません。遠方の平面を1000単位に設定していて、小さな面が互いに0.1単位離れている小さなモデルを描画しようとすると、OpenGLは必要な深度解像度を提供できず、Zファイティング(ちらつき)が発生します。顔の間。
Mikepote 2017年

56

実行可能な最小限の例

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

ここに画像の説明を入力してください

glFrustrum:3Dのようなより現実的な、遠くにある同一のオブジェクトは小さく見えます:

ここに画像の説明を入力してください

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

Ubuntu18.10でテスト済み。

スキーマ

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

ここに画像の説明を入力してください

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

ここに画像の説明を入力してください

画像ソース

パラメーター

私たちは常に+ zから-zまで+ yを上向きに見ています:

glOrtho(left, right, bottom, top, near, far)
  • left:最小x我々が見ます
  • rightx私たちが見る最大
  • bottom:最小y我々が見ます
  • topy私たちが見る最大
  • -near:最小値zが表示されます。はい、これは-1時間nearです。したがって、負の入力は正を意味しzます。
  • -farz私たちが見る最大。また、否定的です。

スキーマ:

画像ソース

内部でどのように機能するか

結局、OpenGLは常に以下を「使用」します。

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

私たちはどちらも使用している場合glOrthoもをglFrustrum、それは我々が得るものです。

glOrthoそしてglFrustrum、次のような線形変換(別名行列乗算)です。

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

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

画像ソース

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

  • ちょうどことを確認:外部キューブ(間引き)の任意の点を除去しxyそしてz内にあります[-1, +1]
  • 無視zコンポーネントをのみ取るxy、今の2D画面に入れることができました

ではglOrthozあなたにも、常に使用することがありますので、無視されます0

使用する理由の1つz != 0は、スプライトに深度バッファーで背景を非表示にすることです。

非推奨

glOrthoOpenGL 4.5で非推奨になりました:互換性プロファイル12.1。「FIXED-FUNCTIONVERTEXTRANSFORMATIONS」は赤で表示されます。

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

最新のOpenGL4プログラムは、CPU上で変換行列(小さい)を計算し、行列とすべての点をOpenGLに変換します。これにより、さまざまな点に対して数千の行列乗算を非常に高速に並行して実行できます。

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

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

明示的GLfloat transform[]に使用すると、次のようになります。

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

コンパイルして実行します。

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

出力:

ここに画像の説明を入力してください

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

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

OpenGL2のドキュメントに記載されているように。

glFrustum行列は、どちらかの手で計算するにはあまりにもハードではありませんが、迷惑な取得を開始します。錐台のみのスケーリングなどの翻訳をアップにすることはできませんどのように注glOrtho:で、より多くの情報、https://gamedev.stackexchange.com/a/118848/25171

GLM OpenGL C ++数学ライブラリは、このような行列を計算するための一般的な選択肢です。http://glm.g-truc.net/0.9.2/api/a00245.htmlには、orthofrustum操作の両方が記載されています。


1
「代わりに何を使うべきですか?」-独自のマトリックスを作成し、それらを直接割り当てます。
クロムスター2016年

最後のコード例(三角形の変換)をコンパイルするのに苦労しています。リポジトリのクローンを作成しましたが、エラーが発生しますcommon.h:19:23: error: ‘TIME_UTC’ undeclared (first use in this function) timespec_get(&ts, TIME_UTC);
Ivanzinho 2010

1
@Ivanzinho Ubuntu 20.04で再現できませんでした。おそらく、GCCがまだ実装していないC11にあるためです。しかし今、私はこの答えの例をcommon.hなしで最小化しました。以前にやるべきだったので、うまくいくはずです:-)
CiroSantilli郝海东冠状病六四天法轮功Dec9

4

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

OpenGLドキュメント(私の太字)

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

「通常の」投影は、奥行きの錯覚を提供する透視投影です。ウィキペディアでは、平行投影を次のように定義しています。

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

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


こんにちは情報をありがとう。平行射影と透視投影の違いがよくわかりませんでした。私は少しグーグルで
ufk

6
残念ながら、answers.comから取得した情報はかなり価値がありません。たとえば、アイソメビューは非常に3Dですが、遠近法のない平行投影です。ここを参照してください。また、他の多くの投影例へのリンクもあります。en.wikipedia.org
Ben Voigt
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.