In diesem Kapitel vollenden wir das Projekt. Die Nachtseite der Erde ist im Moment noch dunkel, bekommt nun aber die Lichter der Städte bei Nacht. Außerdem nutzen wir OpenGLs Blending Funktionen um ein insgesamt realistischeres Bild der Erde zu zeichnen.

Einführung

Im letzten Schritt dieses Kurses wollen wir uns ansehen, wie man mehrere Texturen überblendet. Hierzu dient das sog. Blending. Eine gute Erklärung dazu finden Sie im Artikel “Blending” auf LearnOpenGL. Wichtig ist vor allem zu verstehen was die Funktion glBlendFunc macht und welche Möglichkeiten man damit hat.

Neue Texturen

Auch in diesem Kapitel benötigen wir wieder ein paar neue Texturen. Laden Sie sich die folgenden Dateien herunter und speichern Sie Sie unter dem angegebenen Pfad.

Neue Klasse Planet

Die gesamte Logik für das Zeichnen der Erde werden wir nun in eine eigene Klasse Planet kapseln.

planet.h

#pragma once #include "sphere.h" class Planet : public Sphere { public: Planet(std::shared_ptr<Texture> &texture, std::shared_ptr<Texture> &specularTexture, std::shared_ptr<Texture> &nightTexture); void render() const override; private: void renderQuads(const float *worldMatrix) const; std::shared_ptr<Texture> specularTexture = nullptr; std::shared_ptr<Texture> nightTexture = nullptr; };

planet.cpp

#include "planet.h" Planet::Planet(std::shared_ptr<Texture> &texture, std::shared_ptr<Texture> &specularTexture, std::shared_ptr<Texture> &nightTexture) : Sphere(texture), specularTexture(specularTexture), nightTexture(nightTexture) { // Custom lights float black[3] = {0.0f, 0.0f, 0.0f}; float white[3] = {1.0f, 1.0f, 1.0f}; float white2[3] = {1.05f, 1.05f, 1.05f}; float nightLightBright[3] = {0.3f, 0.3f, 0.3f}; float nightLightDark[3] = {0.1f, 0.1f, 0.1f}; float blue[3] = {0.397f, 0.587f, 1.0f}; // Light 2 is the sun with blue tint glLightfv(GL_LIGHT2, GL_DIFFUSE, blue); glLightfv(GL_LIGHT2, GL_SPECULAR, black); glLightfv(GL_LIGHT2, GL_AMBIENT, black); // Light 3 is diffuse light shining in the direction of the camera float lightPositionF[4] = {0.0f, 0.0f, 50000.0f, 0.0f}; glLightfv(GL_LIGHT3, GL_DIFFUSE, white2); glLightfv(GL_LIGHT3, GL_SPECULAR, black); glLightfv(GL_LIGHT3, GL_AMBIENT, black); glLightfv(GL_LIGHT3, GL_POSITION, lightPositionF); // Light 4 is the night light glLightfv(GL_LIGHT4, GL_DIFFUSE, nightLightBright); glLightfv(GL_LIGHT4, GL_SPECULAR, black); glLightfv(GL_LIGHT4, GL_AMBIENT, nightLightDark); // Light 5 is the sun with only diffuse light glLightfv(GL_LIGHT5, GL_DIFFUSE, white); glLightfv(GL_LIGHT5, GL_SPECULAR, black); glLightfv(GL_LIGHT5, GL_AMBIENT, black); // Light 6 is the sun with only specular light glLightfv(GL_LIGHT6, GL_DIFFUSE, black); glLightfv(GL_LIGHT6, GL_SPECULAR, white); glLightfv(GL_LIGHT6, GL_AMBIENT, black); } void Planet::render() const { Matrix4 worldMatrix = position * rotation * scale; float worldMatrixF[16]; worldMatrix.toColumnMajor(worldMatrixF); float lightPositionSun[4] = {0.0f, 0.0f, 50000.0f, 0.0f}; glLightfv(GL_LIGHT2, GL_POSITION, lightPositionSun); glLightfv(GL_LIGHT5, GL_POSITION, lightPositionSun); glLightfv(GL_LIGHT6, GL_POSITION, lightPositionSun); float lightPositionInverse[4] = {0.0f, 0.0f, -50000.0f, 0.0f}; glLightfv(GL_LIGHT7, GL_POSITION, lightPositionInverse); // Disable default light glDisable(GL_LIGHT1); // render athmosphere glBlendFunc(GL_ONE, GL_ZERO); glEnable(GL_LIGHT2); glBindTexture(GL_TEXTURE_2D, 0); renderQuads(worldMatrixF); glDisable(GL_LIGHT2); // reduce athmosphere to halo glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); glClear(GL_DEPTH_BUFFER_BIT); glEnable(GL_LIGHT3); glBindTexture(GL_TEXTURE_2D, 0); renderQuads(worldMatrixF); glDisable(GL_LIGHT3); // render night texture glBlendFunc(GL_ONE, GL_ONE); glClear(GL_DEPTH_BUFFER_BIT); glEnable(GL_LIGHT4); glBindTexture(GL_TEXTURE_2D, nightTexture->id); renderQuads(worldMatrixF); glDisable(GL_LIGHT4); // render day texture glBlendFunc(GL_ONE, GL_ONE); glClear(GL_DEPTH_BUFFER_BIT); glEnable(GL_LIGHT5); glBindTexture(GL_TEXTURE_2D, texture->id); renderQuads(worldMatrixF); glDisable(GL_LIGHT5); // render specular reflections glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR); glClear(GL_DEPTH_BUFFER_BIT); glEnable(GL_LIGHT6); glBindTexture(GL_TEXTURE_2D, specularTexture->id); renderQuads(worldMatrixF); glDisable(GL_LIGHT6); // Restore all settings to default glBindTexture(GL_TEXTURE_2D, 0); glBlendFunc(GL_ONE, GL_ZERO); glEnable(GL_LIGHT1); } void Planet::renderQuads(const float *worldMatrix) const { glPushMatrix(); glMultMatrixf(worldMatrix); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (float *)&ambient); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, (float *)&diffuse); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (float *)&specular); glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, (float *)&emission); glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess); glBegin(GL_QUADS); for (auto vertex : vertices) { glNormal3fv((float *)&vertex.normal); glTexCoord2fv((float *)&vertex.texcoord); glVertex3fv((float *)&vertex.position); } glEnd(); glPopMatrix(); }

Renderer anpassen

Im Konstruktor der Renderer Klasse aktivieren wir Blending. Um die übergebenen Parameter zu verstehen hilft die ganz oben verlinkte Dokumentation.

renderer.cpp

Renderer::Renderer(const std::string &title, uint32_t width, uint32_t height) { ... glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ZERO); ... }

In der Renderer::start() Funktion laden wir die zusätzlichen Texturen und ersetzen die Sphere die wir bisher verwendet haben durch die Klasse Planet.

renderer.cpp

void Renderer::start() { ... auto earthTexture = std::make_shared<Texture>("textures/earth_diffuse.jpg"); auto earthNightTexture = std::make_shared<Texture>("textures/earth_emission.jpg"); auto earthSpecularTexture = std::make_shared<Texture>("textures/earth_specular.jpg"); ... auto earth = std::make_shared<Planet>(earthTexture, earthSpecularTexture, earthNightTexture); ... }

Selbststudium

Ihnen ist sicher aufgefallen, dass die Erklärung zur Planet Klasse sehr spartanisch ist. Das ist Absicht. Was genau dort passiert, lässt sich aber sehr gut nachvollziehen indem man die einzelnen Zeichenebenen einzeln rendert und anschaut.

Das Verständnis der Planet Klasse und ihrer Render Funktion ist sehr wichtig. Suchen Sie sich selber die passenden Quellen und erarbeiten Sie das nötige Wissen. Alle offenen Fragen versuchen wir gemeinsam im nächsten Plenum zu erarbeiten.

Hausaufgabe

  • Bereiten Sie dieses Kapitel eigenständig bis zum nächsten Termin vor.
  • Ziehen Sie weitere Quellen zu Rate um wirklich zu verstehen was hier passiert und wozu das nötig ist.
  • Bereiten Sie sich auf das nächste Plenum vor.

Praxisphase oder Bachelorarbeit

Wer noch tiefer in die Materie einsteigen will, dem kann ich anbieten die Praxisphase oder die Bachelorarbeit im Bereich Computergrafik bei mir zu absolvieren. Ich arbeite ja gerade selber an meinem ersten kommerziellen Spiel und die Faszination für das Thema und die Technologie hat bei mir auch im Studium an der THM begonnen. Vielleicht haben Sie ja eine tolle Idee für ein neues Feature?!

Wenn Sie Interesse haben, schreiben Sie mir gerne eine E-Mail.

Computergrafik im Master

Falls Sie noch überlegen ob ein Masterstudium das richtige für Sie ist: Im Master sind die Gruppen meist kleiner und die Inhalte häufiger Projekte und seltener Klausuren. Außerdem können Sie durch den höheren Anteil an Wahlpflichtmodulen sehr stark selber steuern welche Themen Sie wirklich interessieren.

Wenn Ihnen meine Veranstaltung im Bachelor gefallen hat, dann kann ich Ihnen auch meine beiden Masterveranstaltungen Computergrafik und Effiziente Algorithmen in der Computergrafik empfehlen.

Dort vertiefen wir die Theorie hinter der Computergrafik und wir entwickeln eine universelle 3D Engine, mit der man beliebige Modelle und Szenen laden kann. Die ideale Mischung aus C++, Grafik und Gaming.