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 entkoppeln wir die Wolken von der Erdoberfläche, so dass sich diese bewegen können.

Inhalt


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.


Kontinuierliche Animationen

Im letzten Kapitel hatten Sie die Aufgabe die Animation von Erde und Satellit ohne die unschönen “Ruckler” umzusetzen. Hier ist eine universelle Möglichkeit, dieses Problem zu lösen.

Wenn Sie selber eine andere Lösung gefunden haben dann ist das sehr gut. Machen Sie sich trotzdem auch mit dieser Lösung vertraut damit Sie erklären können was die Funktion timePeriod() macht.

cgmath.h

float timePeriod(unsigned long period, unsigned long offset);

cgmath.c

#include <math.h>
#include <stdlib.h>
#include <sys/time.h>
#include "cgmath.h"

cgmath.c

float timePeriod(unsigned long period, unsigned long offset)
{
    struct timeval t;
    gettimeofday(&t, NULL);
    unsigned long seconds = ((unsigned long)(t.tv_sec) + offset) % period;
    double microseconds = (unsigned long)(t.tv_usec) / 1000000.0;
    double time = seconds + microseconds;
    return (float)(time / period);
}

scene.c

static matrix calculateEarthRotation(float multiplier)
{
    float earthRotation = timePeriod(86400 * multiplier, 0) * 2.f * M_PI;

    float earthEcliptic = cosf(timePeriod(31557600, 864000) * 2.f * M_PI) * deg2rad(-23.4f);
    
    return matrixMultiply(matrixRotateX(earthEcliptic), matrixRotateY(earthRotation));
}

scene.c

static matrix calculateSatellitePosition()
{
    float scale = 25.0 / 6370.0;

    int orbitTime = 5400;
    float orbitRadius = 6770.0 / 6370.0;

    matrix orbit = matrixRotateY(timePeriod(orbitTime, 0) * 2.f * M_PI);

    matrix translation = matrixMultiply(matrixTranslate(orbitRadius, 0, 0), matrixScale(scale));

    static float tu = 0.1f;
    tu += 0.1f;
    matrix tumbling = matrixMultiply(matrixRotateY(deg2rad(tu)), matrixRotateZ(deg2rad(tu)));

    return matrixMultiply(orbit, matrixMultiply(translation, tumbling));
}

scene.c

void renderScene()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    loadCameraViewMatrix(0);
    glDisable(GL_LIGHT1);
    glEnable(GL_LIGHT2);
    renderMesh(cubeMap, matrixScale(1), cubeMapTexture);
    glClear(GL_DEPTH_BUFFER_BIT);

    loadCameraViewMatrix(1);

    vector4 lightPosition = {0, 0, 50000, 0};
    glLightfv(GL_LIGHT1, GL_POSITION, &lightPosition);

    glDisable(GL_LIGHT2);
    glEnable(GL_LIGHT1);
    renderMesh(earthMesh, calculateEarthRotation(1), earthTexture);

    glDisable(GL_LIGHT1);
    glEnable(GL_LIGHT2);
    renderMesh(satelliteMesh, calculateSatellitePosition(), satelliteTexture);
}

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.

  • Wolken
    ➔ Computergrafik/cgb_10/res/cloud_combined_8192.jpg
  • Erde ohne Wolken
    ➔ Computergrafik/cgb_10/res/land_ocean_ice_8192.jpg
  • Erde bei Nacht
    ➔ Computergrafik/cgb_10/res/lights_8192.jpg

Texturen laden und einbinden

Dieser Abschnitt sollte selbsterklärend sein. Wir laden die neuen Texturen damit wir Sie verwenden können.

scene.c

static GLuint earthTexture;
static GLuint earthNightTexture;
static GLuint earthCloudTexture;
static GLuint satelliteTexture;
static GLuint cubeMapTexture;

scene.c

void loadScene(GLFWwindow* window)
{
    setViewportSize(window);

    glClearColor(0, 0, 0, 0);

    earthMesh = createSphereMesh(white);
    satelliteMesh = createCubeMesh(white);
    cubeMap = createCubeMap(white);

    earthTexture = loadTexture("res/land_ocean_ice_8192.jpg");
    earthNightTexture = loadTexture("res/lights_8192.jpg");
    earthCloudTexture = loadTexture("res/cloud_combined_8192.jpg");
    satelliteTexture = loadTexture("res/thm2k.png");
    cubeMapTexture = loadTexture("res/cubemap8k.jpg");

    glLightfv(GL_LIGHT1, GL_DIFFUSE, &sunLight);
    glLightfv(GL_LIGHT1, GL_AMBIENT, &ambientLight);
    glLightfv(GL_LIGHT1, GL_SPECULAR, &white);

    glLightfv(GL_LIGHT2, GL_DIFFUSE, &noLight);
    glLightfv(GL_LIGHT2, GL_AMBIENT, &sunLight);

    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, &noLight);
}

Blending aktivieren

Nun müssen wir die Blending Funktion aktivieren. Dies tun wir einmalig beim Laden der Szene. Ebenfalls legen wir hier mit glBlendFunc() die Standard Blend Funktion fest.

scene.c

void loadScene(GLFWwindow* window)
{
    setViewportSize(window);

    glClearColor(0, 0, 0, 0);

    earthMesh = createSphereMesh(white);
    satelliteMesh = createCubeMesh(white);
    cubeMap = createCubeMap(white);

    earthTexture = loadTexture("res/land_ocean_ice_8192.jpg");
    earthNightTexture = loadTexture("res/lights_8192.jpg");
    earthCloudTexture = loadTexture("res/cloud_combined_8192.jpg");
    satelliteTexture = loadTexture("res/thm2k.png");
    cubeMapTexture = loadTexture("res/cubemap8k.jpg");

    glLightfv(GL_LIGHT1, GL_DIFFUSE, &sunLight);
    glLightfv(GL_LIGHT1, GL_AMBIENT, &ambientLight);

    glLightfv(GL_LIGHT2, GL_DIFFUSE, &noLight);
    glLightfv(GL_LIGHT2, GL_AMBIENT, &sunLight);

    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, &noLight);

    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ZERO);
}

Wenn Sie die Anwendung mit dieser Änderung starten, sollte alles genauso aussehen wie ohne Blending, denn die Blendfunktion (GL_ONE, GL_ZERO) entspricht den Standardverhalten von OpenGL ohne aktiviertem Blending.


Blendfunktionen anwenden

Im folgenden machen wir Gebrauch von der neuen Möglichkeit des Blending indem wir die Erde insgesamt fünfmal rendern. Bei jedem Renderdurchlauf setzen wir aber unterschiedliche Texturen, unterschiedliche Blendfunktionen und löschen regelmäßig den Tiefenpuffer. Betrachten Sie das Ergebnis und versuchen Sie durch geschicktes auskommentieren die einzelnen Schritte zu verstehen. Überprüfen Sie auch mit welcher Farbe Sie Ihr Earth Mesh geladen haben und checken Sie ob das einen Unterschied macht.

scene.c

void renderScene()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    loadCameraViewMatrix(0);
    glDisable(GL_LIGHT1);
    glEnable(GL_LIGHT2);
    glBlendFunc(GL_ONE, GL_ZERO);
    renderMesh(cubeMap, matrixScale(1), cubeMapTexture);
    glClear(GL_DEPTH_BUFFER_BIT);

    loadCameraViewMatrix(1);

    vector4 lightPosition = {0, 0, 50000, 0};
    glLightfv(GL_LIGHT1, GL_POSITION, &lightPosition);

    glDisable(GL_LIGHT1);
    glEnable(GL_LIGHT2);
    renderMesh(earthMesh, calculateEarthRotation(1), earthNightTexture);
    glClear(GL_DEPTH_BUFFER_BIT);

    glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
    renderMesh(earthMesh, calculateEarthRotation(0.08), earthCloudTexture);
    glClear(GL_DEPTH_BUFFER_BIT);

    glDisable(GL_LIGHT2);
    glEnable(GL_LIGHT1);
    glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
    renderMesh(earthMesh, calculateEarthRotation(1), 0);
    glClear(GL_DEPTH_BUFFER_BIT);

    glBlendFunc(GL_ONE, GL_ONE);
    renderMesh(earthMesh, calculateEarthRotation(1), earthTexture);
    glClear(GL_DEPTH_BUFFER_BIT);
    
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
    renderMesh(earthMesh, calculateEarthRotation(0.08), earthCloudTexture);

    glDisable(GL_LIGHT1);
    glEnable(GL_LIGHT2);
    glBlendFunc(GL_ONE, GL_ZERO);
    renderMesh(satelliteMesh, calculateSatellitePosition(), satelliteTexture);
}

Die Athmosphäre

In unserer Darstellung der Erde fehlt aktuell noch die Athmosphäre. Hier ein Beispiel, was damit gemeint ist. Haben Sie eine Idee wie man das mit unseren Mitteln umsetzen kann? Wir werden in der Veranstaltung darüber diskutieren.

Prüfungsvorbereitung

  • Können Sie erklären aus welchen einzelnen Ebenen sich unsere finale Szene zusammensetzt und welche Blendfunktionen dafür verwendet wurden?
  • Wissen Sie auch welche mathematische Formel für die jeweilige Überblendung verwendet wird?
  • Können Sie beschrieben, welche Formel (oder welches mathematische Prinzip) für die Beleuchtung verwendet wird?
  • Machen Sie einen Verschlag wie man die Athmosphäre darstellen könnte.