Minimal lauffähiges Beispiel
glOrtho
: 2D-Spiele, Objekte in der Nähe und in der Ferne haben dieselbe Größe:
glFrustrum
: realistischer wie 3D erscheinen identische Objekte weiter entfernt kleiner:
Haupt 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 stromaufwärts .
Kompilieren:
gcc -ggdb3 -O0 -o main -std=c99 -Wall -Wextra -pedantic main.c -lGL -lGLU -lglut
Laufen Sie mit glOrtho
:
./main 1
Laufen Sie mit glFrustrum
:
./main
Getestet unter Ubuntu 18.10.
Schema
Ortho: Kamera ist eine Ebene, sichtbares Volumen ein Rechteck:
Frustrum: Kamera ist ein Punkt, sichtbares Volumen ein Stück einer Pyramide:
Bildquelle .
Parameter
Wir schauen immer von + z nach -z mit + y nach oben:
glOrtho(left, right, bottom, top, near, far)
left
: Minimum sehen x
wir
right
: Maximum sehen x
wir
bottom
: Minimum sehen y
wir
top
: Maximum sehen y
wir
-near
: Minimum sehen z
wir. Ja , das ist -1
mal near
. Ein negativer Eingang bedeutet also positiv z
.
-far
: Maximum sehen z
wir. Auch negativ.
Schema:
Bildquelle .
Wie es unter der Haube funktioniert
Am Ende "verwendet" OpenGL immer:
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
Wenn wir weder glOrtho
noch verwenden glFrustrum
, bekommen wir das.
glOrtho
und glFrustrum
sind nur lineare Transformationen (AKA-Matrixmultiplikation), so dass:
glOrtho
: Nimmt ein bestimmtes 3D-Rechteck in den Standardwürfel auf
glFrustrum
: nimmt einen bestimmten Pyramidenabschnitt in den Standardwürfel auf
Diese Transformation wird dann auf alle Scheitelpunkte angewendet. Das meine ich in 2D:
Bildquelle .
Der letzte Schritt nach der Transformation ist einfach:
- entfernen Sie alle Punkte außerhalb des Würfels (Culling): Stellen Sie sicher , dass nur
x
, y
und z
sind in[-1, +1]
- ignoriere die
z
Komponente und nimm nur x
und y
, das jetzt in einen 2D-Bildschirm eingefügt werden kann
Mit glOrtho
, z
ignoriert wird, so könnten Sie auch immer verwenden 0
.
Ein Grund, den Sie möglicherweise verwenden möchten, z != 0
besteht darin, dass Sprites den Hintergrund mit dem Tiefenpuffer ausblenden.
Missbilligung
glOrtho
ist ab OpenGL 4.5 veraltet : das Kompatibilitätsprofil 12.1. "VERTEX-TRANSFORMATIONEN MIT FESTFUNKTION" ist rot.
Verwenden Sie es also nicht für die Produktion. In jedem Fall ist das Verstehen ein guter Weg, um OpenGL-Einblicke zu erhalten.
Moderne OpenGL 4-Programme berechnen die Transformationsmatrix (die klein ist) auf der CPU und geben dann die Matrix und alle zu transformierenden Punkte in OpenGL an, wodurch die Tausenden von Matrixmultiplikationen für verschiedene Punkte sehr schnell parallel ausgeführt werden können.
Manuell geschriebene Vertex-Shader führen dann die Multiplikation explizit durch, normalerweise mit den praktischen Vektordatentypen der OpenGL Shading Language.
Da Sie den Shader explizit schreiben, können Sie den Algorithmus an Ihre Bedürfnisse anpassen. Diese Flexibilität ist ein Hauptmerkmal moderner GPUs, die im Gegensatz zu den alten, die einen festen Algorithmus mit einigen Eingabeparametern verwendet haben, jetzt beliebige Berechnungen durchführen können. Siehe auch: https://stackoverflow.com/a/36211337/895245
Mit einem expliziten GLfloat transform[]
würde es ungefähr so aussehen:
#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 stromaufwärts .
Ausgabe:
Die Matrix für glOrtho
ist wirklich einfach und besteht nur aus Skalierung und Übersetzung:
scalex, 0, 0, translatex,
0, scaley, 0, translatey,
0, 0, scalez, translatez,
0, 0, 0, 1
wie in den OpenGL 2-Dokumenten erwähnt .
Die glFrustum
Matrix ist auch nicht allzu schwer von Hand zu berechnen, wird aber nervig. Beachten Sie, dass Kegelstumpf nicht nur mit Skalierung und Übersetzungen wie möglich hergestellt werden glOrtho
kann. Weitere Informationen finden Sie unter: https://gamedev.stackexchange.com/a/118848/25171
Die GLM OpenGL C ++ - Mathematikbibliothek ist eine beliebte Wahl für die Berechnung solcher Matrizen. http://glm.g-truc.net/0.9.2/api/a00245.html dokumentiert sowohl eine ortho
als auch frustum
Operationen.