Neural Graphics and Inverse Rendering - Exam.pdf

Neural Graphics and Inverse Rendering - Exam
Neural Graphics and Inverse Rendering - Exam Aufgabe 1) Ein Bildklassifizierungsproblem soll durch ein Convolutional Neural Network (CNN), und ein Sprachverarbeitungsproblem soll durch ein Recurrent Neural Network (RNN) gelöst werden. Das Bildklassifizierungsproblem umfasst die Identifikation von Handgeschriebenen Ziffern (MNIST-Datensatz), während das Sprachverarbeitungsproblem die Vorhersage des...

© StudySmarter 2024, all rights reserved.

Neural Graphics and Inverse Rendering - Exam

Aufgabe 1)

Ein Bildklassifizierungsproblem soll durch ein Convolutional Neural Network (CNN), und ein Sprachverarbeitungsproblem soll durch ein Recurrent Neural Network (RNN) gelöst werden. Das Bildklassifizierungsproblem umfasst die Identifikation von Handgeschriebenen Ziffern (MNIST-Datensatz), während das Sprachverarbeitungsproblem die Vorhersage des nächsten Wortes in einer Satzfolge umfasst.

a)

  • (a) Entwerfe die Architektur eines CNNs für die Klassifikation von handgeschriebenen Ziffern aus dem MNIST-Datensatz. Beschreibe die genauen Schichten (Convolutional Layer, Pooling Layer, Fully Connected Layer), die Aktivierungsfunktionen und begründe deine Wahl.

Lösung:

Um ein CNN (Convolutional Neural Network) für die Klassifikation von handgeschriebenen Ziffern aus dem MNIST-Datensatz zu entwerfen, sollten wir eine Architektur wählen, die eine gute Balance zwischen Komplexität und Leistungsfähigkeit bietet. Der MNIST-Datensatz besteht aus 28x28 Pixel großen Graustufenbildern von Ziffern (0-9).

  • Architektur des CNNs:
  • Input Layer: Die Eingabeschicht nimmt 28x28 Graustufenbilder auf (Einzelkanal-Bilder).
  • 1. Convolutional Layer: Wir verwenden 32 Filter mit einer Größe von 3x3 und einem Stride von 1. Diese Schicht extrahiert lokale Merkmale aus dem Bild.Aktivierungsfunktion: ReLU (Rectified Linear Unit). ReLU-Funktion führt zu schnellerem Training und verhindert das Vanishing Gradient Problem.
  • 2. Pooling Layer: Max-Pooling mit einer Filtergröße von 2x2 und einem Stride von 2. Diese Schicht reduziert die räumliche Dimension des Bildes und behält die wichtigsten Merkmale bei.
  • 3. Convolutional Layer: 64 Filter mit einer Größe von 3x3 und einem Stride von 1. Diese Schicht extrahiert noch komplexere Merkmale aus dem Bild.Aktivierungsfunktion: ReLU.
  • 4. Pooling Layer: Max-Pooling mit einer Filtergröße von 2x2 und einem Stride von 2.
  • 5. Fully Connected Layer: Diese Schicht transformiert die 2D-Feature-Karten in einen 1D-Feature-Vektor. Hier verwenden wir eine Neuronenschicht mit 128 Einheiten.Aktivierungsfunktion: ReLU.
  • Output Layer: Schließlich haben wir eine Fully Connected Layer mit 10 Neuronen, eins für jede Ziffer (0-9).Aktivierungsfunktion: Softmax, damit erhalten wir Wahrscheinlichkeitswerte für jede Klasse.

Begründung für die Wahl der Schichten und Aktivierungsfunktionen:

  • Convolutional Layers: Diese Schichten helfen bei der Extraktion von immer komplexeren Merkmalen aus dem Bild, indem sie lokal patterneristische und hierarchische Merkmale erkennen.
  • ReLU: Die ReLU-Aktivierungsfunktion wird verwendet, weil sie das Vanishing Gradient Problem verhindert und das Training beschleunigt.
  • Max-Pooling: Diese Schichten reduzieren die räumliche Auflösung der Feature-Karten und helfen, die wichtigsten Merkmale beizubehalten, während die Komplexität des Netzes reduziert wird.
  • Fully Connected Layers: Diese Schichten helfen bei der Klassifikation, indem sie die extrahierten Merkmale in spezifische Klassen transformieren.
  • Softmax: Die Softmax-Aktivierungsfunktion im Output Layer hilft, Wahrscheinlichkeiten für jede Klasse zu berechnen, sodass wir eine eindeutige Klassifikation des Bildes vornehmen können.

c)

  • (c) Entwerfe die Architektur eines RNNs (bevorzugt LSTM oder GRU) zur Vorhersage des nächsten Wortes in einer Satzfolge. Erkläre die Wahl deiner RNN-Typen und die Funktionsweise der Gedächtnismechanismen, die hier verwendet werden.

Lösung:

Um die Architektur eines RNNs zur Vorhersage des nächsten Wortes in einer Satzfolge zu entwerfen, wähle ich ein Long Short-Term Memory (LSTM)-Netzwerk oder ein Gated Recurrent Unit (GRU)-Netzwerk. Beide Arten von Netzwerken sind besonders geeignet für Aufgaben der Sequenzvorhersage, da sie Gedächtnismechanismen besitzen, die helfen, langfristige Abhängigkeiten zu lernen und das Vanishing Gradient Problem zu vermeiden.

Architektur eines RNNs:

  • Eingabeschicht: Die Eingabeschicht nimmt eine Sequenz von Wörtern auf, die als Vektoren (z.B. Word Embeddings) repräsentiert sind. Jeder Vektor hat eine feste Dimension, etwa 100.
  • Embedding Layer: Falls die Eingabewörter nicht bereits als Vektoren vorliegen, verwenden wir eine Embedding-Schicht, um Wörter in niedrigdimensionale dichte Vektoren zu transformieren.
  • LSTM/GRU Layer: Diese Schicht besteht aus mehreren LSTM- oder GRU-Zellen, die die Sequenz von Vektoren verarbeiten. Die Architektur könnte wie folgt aussehen:
    • Input size: Dimension des Wörterbuchs
    • Embedding size: 100
    • Hidden size: 128 (Anzahl der LSTM/GRU-Einheiten)
    • Number of layers: 2 (Zwei LSTM/GRU-Schichten übereinander gestapelt)
  • Fully Connected Layer: Die letzte LSTM/GRU-Ausgabe wird an eine Fully Connected Layer weitergegeben, um die wahrscheinlichste nächste Wortvorhersage zu berechnen. Diese Schicht hat die gleiche Größe wie das Wörterbuch.
  • Softmax Layer: Eine Softmax-Schicht konvertiert die Ausgaben der Fully Connected Layer in Wahrscheinlichkeiten für jedes mögliche nächste Wort.

Wahl des RNN-Typs:

  • LSTM (Long Short-Term Memory): LSTM ist eine besondere Art von RNN, die in der Lage ist, Informationen über längere Sequenzen hinweg zu behalten. Es nutzt spezielle Einheiten, die als Speicherzellen bezeichnet werden, mit drei Hauptfunktionen: Eingangs-, Ausgangs- und Vergessens-Tor. Diese Tore bestimmen, welche Informationen in die Zelle aufgenommen, ausgelesen und vergessen werden sollen.Das Eingangs-Tor (input gate) entscheidet, wie viel des neuen Inputs in den Zellzustand übernommen wird:
     i_t = \sigma(W_i \bm{x}_t + U_i \bm{h}_{t-1} + b_i) 
    Das Vergessens-Tor (forget gate) steuert, wie viel des alten Zellzustands behalten werden soll:
     f_t = \sigma(W_f \bm{x}_t + U_f \bm{h}_{t-1} + b_f) 
    Das Ausgangs-Tor (output gate) bestimmt, wie viel des Zellzustands in die Ausgabe gelangen soll:
     o_t = \sigma(W_o \bm{x}_t + U_o \bm{h}_{t-1} + b_o) 
    Der Zellzustand wird aktualisiert, indem geeignete Anteile des alten Zustands und des neuen Inputs kombiniert werden:
     c_t = f_t \cdot c_{t-1} + i_t \cdot \tilde{c}_t 
    wobei
     \tilde{c}_t = \tanh(W_c \bm{x}_t + U_c \bm{h}_{t-1} + b_c) 
    Die endgültige Ausgabe des LSTM:
     \bm{h}_t = o_t \cdot \tanh(c_t) 
  • GRU (Gated Recurrent Unit): Das GRU ist eine vereinfachte Version des LSTM mit weniger Parametern. Es kombiniert das Eingangs- und Vergessens-Tor zu einem Update-Tor (update gate) und hat ein Reset-Tor (reset gate), um zu steuern, wie viel von der bisherigen Information verworfen werden soll.Das Update-Tor entscheidet, wie viel vom neuen Zustand in den gesamten Zustand aufgenommen wird:
     z_t = \sigma(W_z \bm{x}_t + U_z \bm{h}_{t-1} + b_z) 
    Das Reset-Tor kontrolliert, wie viel von der alten Information in die Berechnung des neuen Zustands einfließen soll:
     r_t = \sigma(W_r \bm{x}_t + U_r \bm{h}_{t-1} + b_r) 
    Der neue Zustand wird berechnet als:
     \bm{h}_t = z_t \cdot \bm{h}_{t-1} + (1 - z_t) \cdot \tilde{h}_t 
    wobei
     \tilde{h}_t = \tanh(W_h \bm{x}_t + U_h (r_t \cdot \bm{h}_{t-1}) + b_h) 

Funktionsweise der Gedächtnismechanismen:

  • Die Gedächtnismechanismen in LSTM und GRU ermöglichen es dem Netzwerk, Informationen über längere Sequenzen hinweg zu behalten und relevante Informationen für die Vorhersage des nächsten Wortes zu bewahren.
  • Die Eingangs-, Ausgangs- und Vergessens-Tore in LSTMs sowie die Update- und Reset-Tore in GRUs steuern die Informationsmenge, die von früheren Zeitpunkten in die aktuelle Berechnung einfließt, was entscheidend für die Modellierung von Kontext in natürlichen Sprachsequenzen ist.

Durch diesen Ansatz kann das RNN effektiv das nächste Wort in einer Satzfolge vorhersagen, indem es die Abhängigkeiten zwischen den Wörtern in der Sequenz lernt und nutzt.

Aufgabe 2)

Du trainierst ein neuronales Netzwerk zur Bildklassifikation und bemerkst, dass das Modell auf dem Trainingsdatensatz sehr gut abschneidet, während es auf den Validierungsdaten deutlich schlechter performt. Um Overfitting zu vermeiden, möchtest Du Techniken wie Dropout und L2-Regularisierung anwenden.

a)

Beschreibe, wie Dropout genau funktioniert und welche Vorteile es bietet. Welche Auswirkung hat Dropout auf die Trainingszeit und die finale Architektur des Netzwerks?

Lösung:

  • Dropout:
Funktionsweise:Dropout ist eine Technik, die darauf abzielt, das Overfitting zu reduzieren, indem es während des Trainingsprozesses zufällig einige der Neuronen in einem neuronalen Netzwerk ausschaltet. Das bedeutet, dass bei jedem Trainingsepoch ein Teil der Neuronen nicht aktiviert wird. Dies zwingt das Modell, robuster und weniger abhängig von einzelnen Neuronen zu werden.Vorteile von Dropout:
  • Dropout reduziert die Überanpassung des Modells an die Trainingsdaten, da verschiedene neuronale Subnetze während des Trainings entstehen.
  • Es verbessert die Generalisierungsfähigkeit des Modells, was zu besseren Leistungen auf den Validierungs- und Testdatensätzen führt.
  • Dropout kann dazu beitragen, dass das Modell sich nicht zu stark auf bestimmte Merkmale der Trainingsdaten verlässt, sodass es robuster gegenüber Rauschen und Variationen wird.
Auswirkungen auf die Trainingszeit und die finale Architektur des Netzwerks:
  • Dropout erhöht die Trainingszeit, da bei jedem Trainingsepoch nur ein Teil der Neuronen aktiviert wird. Daher benötigt das Modell mehr Zeit, um zu konvergieren.
  • Da Dropout während der Vorhersage (Inference-Phase) deaktiviert wird, bleibt die finale Architektur des Netzwerks unverändert. Das Netzwerk verwendet alle Neuronen bei der Vorhersage, aber es wurde durch den Dropout-Mechanismus robuster trainiert.

b)

Implementiere eine Python-Funktion, die die L2-Regularisierung zu einer gegebenen Verlustfunktion hinzufügt. Nutze die gegebene mathematische Formel für den Regularisierungsterm. Diskutiere die Rolle des Regularisierungsparameters (lambda) und wie dessen Wert das Training beeinflussen kann.

'pythondef add_l2_regularization(loss, weights, lambda_param):    regularization_term = lambda_param * sum(w**2 for w in weights)    return loss / 2 + regularization_term / 2 
'

Lösung:

  • Implementierung der L2-Regularisierung:
Hier ist eine Python-Funktion, die die L2-Regularisierung zu einer gegebenen Verlustfunktion hinzufügt:
 def add_l2_regularization(loss, weights, lambda_param):    regularization_term = lambda_param * sum(w ** 2 for w in weights)    return loss / 2 + regularization_term / 2  
Diskussion der Rolle des Regularisierungsparameters (lambda):Der Regularisierungsparameter (\( \lambda \)) spielt eine entscheidende Rolle bei der L2-Regularisierung:
  • Kontrolle der Stärke der Regularisierung: Der Parameter \( \lambda \) bestimmt, wie stark die Regularisierung auf das Modell angewendet wird. Ein höherer Wert von \( \lambda \) führt zu einer stärkeren Regularisierung, wodurch die Gewichte kleiner und das Modell weniger komplex werden.
  • Balance zwischen Bias und Varianz: Die Wahl des Regularisierungsparameters beeinflusst das Bias-Varianz-Dilemma. Ein zu hoher Wert von \( \lambda \) kann zu einem Modell mit hohem Bias führen (Underfitting), während ein zu niedriger Wert zu einem Modell mit hoher Varianz führen kann (Overfitting).
  • Anpassung während der Hyperparameter-Optimierung: Der optimale Wert des Regulaisierungsparameters sollte durch Verfahren wie Cross-Validation oder hyperparameter-Optimierungsstrategien bestimmt werden.
Einfluss auf das Training:
  • Reduzierung von Overfitting: L2-Regularisierung hilft dabei, das Modell vor Overfitting zu schützen, indem es die Größe der Gewichte beschränkt.
  • Stabilisierung des Trainings: Der Regularisierungsparameter kann dazu beitragen, dass das Training des Modells stabiler und weniger anfällig für Schwankungen wird.
  • Längere Trainingszeit: Das Hinzufügen von Regularisierung kann die Trainingszeit erhöhen, da das Modell lernen muss, mit der zusätzlichen Strafe auf die Gewichte umzugehen.

Aufgabe 3)

Raytracing und Rasterisierung sind zwei unterschiedliche Techniken zur Darstellung von 3D-Szenen auf einem 2D-Bildschirm. Raytracing simuliert den realistischen Lichtweg durch die Szenenpixel und erzeugt realistische Schatten, Reflexionen und Brechungen. Rasterisierung hingegen projiziert 3D-Objekte schnell und effizient auf den 2D-Bildschirm und verwendet oft einen Z-Puffer, um Tiefenwerte zu verwalten. Raytracing ist in der Regel rechenintensiver, bietet jedoch einen höheren Realismus, während Rasterisierung besser für Echtzeit-Rendering, wie in Computerspielen, geeignet ist. Die Beleuchtungsformel im Raytracing wird durch \[ L_o(p, \theta_o, \theta_i) = L_e(p, \theta_o) + \frac{1}{\text{PDF}}\times (\int_{\theta_i} f_r(p, \theta_i, \theta_o) \times L_i(p, \theta_i, \theta_o) \times (\theta_i \bullet n)\text{d}\theta_i) \] beschrieben.

b)

Die Beleuchtungsformel beim Raytracing lautet \[ L_o(p, \theta_o, \theta_i) = L_e(p, \theta_o) + \frac{1}{\text{PDF}}\times (\int_{\theta_i} f_r(p, \theta_i, \theta_o) \times L_i(p, \theta_i, \theta_o) \times (\theta_i \bullet n)\text{d}\theta_i) \] . Erkläre die Bedeutung der einzelnen Terme in dieser Formel. Warum ist diese Formel besonders relevant für Raytracing?

Lösung:

Die Beleuchtungsformel beim Raytracing

Die Beleuchtungsformel beim Raytracing lautet:

\[ L_o(p, \theta_o, \theta_i) = L_e(p, \theta_o) + \frac{1}{\text{PDF}} \times \left( \int_{\theta_i} f_r(p, \theta_i, \theta_o) \times L_i(p, \theta_i, \theta_o) \times (\theta_i \cdot n) \text{d}\theta_i \right) \]

Hierbei stehen die einzelnen Terme für:

  • Lo(p, \thetao, \thetai): Dies ist die ausgehende Lichtintensität in Richtung \(\thetao\) vom Punkt \(p\) aus. Es ist der Wert, den wir schlussendlich für den Pixel des Bildes berechnen möchten.
  • Le(p, \thetao): Dies ist die vom Punkt \(p\) in Richtung \(\thetao\) emittierte Lichtintensität. Beispielsweise gibt eine Lichtquelle an diesem Punkt Licht ab.
  • \text{PDF}: Dies ist die Wahrscheinlichkeitsdichtefunktion, die bei der Berechnung des Monte-Carlo-Integrals verwendet wird. Sie spielt eine Rolle bei der Gewichtung der zufällig ausgewählten Strahlen.
  • \int_{\thetai...: Dies steht für das Integral über alle möglichen Richtungen \(\thetai\), aus denen Licht auf den Punkt \(p\) einfällt.
  • fr(p, \thetai, \thetao): Dies ist die Bidirektionale Reflexionsverteilungsfunktion (BRDF), die beschreibt, wie Licht von Richtung \(\thetai\) nach \(\thetao\) reflektiert wird.
  • Li(p, \thetai, \thetao): Dies ist die eingehende Lichtintensität in Richtung \(\thetai\), die den Punkt \(p\) trifft. Dabei handelt es sich um den Beitrag des Lichts, das von anderen Oberflächen reflektiert oder direkt von Lichtquellen ausgestrahlt wird.
  • (\thetai \cdot n): Dies ist das Skalarprodukt zwischen der Richtung des einfallenden Lichtes \(\thetai\) und der Normalen \(n\) an der Oberfläche am Punkt \(p\). Es stellt den Winkel zwischen diesen beiden Vektoren dar und beeinflusst die Intensität des einfallenden Lichtes.

Relevanz der Formel für Raytracing

Diese Formel ist besonders relevant für Raytracing, weil sie:

  • Die komplexen Interaktionen zwischen Licht und Oberflächen realistisch beschreibt. Sie berücksichtigt direkte Beleuchtung, Reflexionen und die Art und Weise, wie Licht von Oberflächen gestreut wird.
  • Ermöglicht die Berechnung realistischer Schattierungen und Beleuchtungseffekte, die in der realen Welt auftreten.
  • Die Flexibilität bietet, verschiedene Materialien und ihre optischen Eigenschaften realistisch darzustellen.
  • Es durch die Integration über alle möglichen Einfallsrichtungen ermöglicht, globale Beleuchtungseffekte wie indirekte Beleuchtung und Farblichtinterferenzen zu berücksichtigen.

Insgesamt ermöglicht die Beleuchtungsformel im Raytracing die Erzeugung extrem realistischer Bilder, was insbesondere bei Szenen, in denen die Licht- und Schatteneffekte entscheidend sind, von großer Bedeutung ist.

c)

Implementiere in Python ein einfaches Raytracing-Modell, welches den Lichtstrahl in einer Szene verfolgt und dabei die Grundzüge der obigen Beleuchtungsformel berücksichtigt. Kommentiere Deinen Code ausführlich und erkläre die Funktionsweise Deines Modells. Führe wichtige Zwischenschritte und Berechnungen mathematisch sauber aus.

Lösung:

Implementierung eines einfachen Raytracing-Modells in Python

In diesem Beispiel implementieren wir ein einfaches Raytracing-Modell in Python. Wir berechnen den Lichtstrahl in einer Szene unter Berücksichtigung der Grundzüge der oben beschriebenen Beleuchtungsformel. Das Modell wird in mehreren Schritten erklärt und kommentiert, um die Funktionsweise zu verdeutlichen.

Schritt 1: Grundlegende Definitionen und Import

import numpy as npclass Vector:    def __init__(self, x, y, z):        self.x = x        self.y = y        self.z = z    def __sub__(self, other):        return Vector(self.x - other.x, self.y - other.y, self.z - other.z)    def __add__(self, other):        return Vector(self.x + other.x, self.y + other.y, self.z + other.z)    def __mul__(self, other):        if isinstance(other, Vector):            return Vector(self.x * other.x, self.y * other.y, self.z * other.z)        else:            return Vector(self.x * other, self.y * other, self.z * other)    def dot(self, other):        return self.x * other.x + self.y * other.y + self.z * other.z    def length(self):        return np.sqrt(self.dot(self))    def normalize(self):        len = self.length()        return self * (1.0 / len)class Ray:    def __init__(self, origin, direction):        self.origin = origin        self.direction = direction.normalize()

Hier definieren wir eine Vector-Klasse und eine Ray-Klasse. Die Vector-Klasse unterstützt grundlegende Vektoroperationen wie Addition, Subtraktion, Skalierung, das Skalarprodukt und Normalisierung. Die Ray-Klasse repräsentiert einen Lichtstrahl, der durch einen Ursprung und eine normalisierte Richtung definiert wird.

Schritt 2: Darstellung von Szenenobjekten

Die Szene besteht aus Objekten wie Kugeln, die Licht reflektieren können. Wir implementieren eine Sphere-Klasse, um Kugeln in der Szene zu repräsentieren.

class Sphere:    def __init__(self, center, radius, color):        self.center = center        self.radius = radius        self.color = color    def intersect(self, ray):        oc = ray.origin - self.center        a = ray.direction.dot(ray.direction)        b = 2.0 * oc.dot(ray.direction)        c = oc.dot(oc) - self.radius * self.radius        discriminant = b * b - 4 * a * c        if discriminant < 0:            return None        else:            t1 = (-b - np.sqrt(discriminant)) / (2.0 * a)            t2 = (-b + np.sqrt(discriminant)) / (2.0 * a)            if t1 > 0:                return t1            elif t2 > 0:                return t2            else:                return None    def normal_at(self, point):        return (point - self.center).normalize()

Die Sphere-Klasse enthält eine Methode intersect, die prüft, ob ein Lichtstrahl mit der Kugel kollidiert, und die Distanz zum Kollisionspunkt berechnet. Die Methode normal_at berechnet die Normalen am Kollisionspunkt.

Schritt 3: Implementierung der Raytracing-Funktion

def trace_ray(ray, spheres, light_pos, ambient_color):    closest_t = float('inf')    closest_sphere = None    for sphere in spheres:        t = sphere.intersect(ray)        if t and t < closest_t:            closest_t = t            closest_sphere = sphere    if closest_sphere is None:        return Vector(0, 0, 0)  # Hintergrundfarbe    hit_point = ray.origin + ray.direction * closest_t    normal = closest_sphere.normal_at(hit_point)    light_dir = (light_pos - hit_point).normalize()    # Diffuse Beleuchtung    diff = max(normal.dot(light_dir), 0) * closest_sphere.color    # Ambient term (Umgebungslicht)    ambient = ambient_color * closest_sphere.color    color = diff + ambient    return color

Die Funktion trace_ray verfolgt den Pfad eines Lichtstrahls durch die Szene. Sie prüft alle Kugeln auf Kollisionspunkte, berechnet die Trefferpunkte und Normalen und wendet diffuse und Umgebungsbeleuchtung an, um die Farbe des Pixels zu bestimmen.

Schritt 4: Rendering der Szene

def render(width, height, spheres, light_pos, ambient_color):    camera = Vector(0, 0, -1)    image = np.zeros((height, width, 3))    for y in range(height):        for x in range(width):            px = (x - width / 2) / width            py = (y - height / 2) / height            ray_dir = Vector(px, py, 1) - camera            ray = Ray(camera, ray_dir)            color = trace_ray(ray, spheres, light_pos, ambient_color)            image[y, x] = [color.x, color.y, color.z]    return image

Die Funktion render erstellt ein Bild der Szene, indem sie für jeden Pixel einen Strahl sendet und die Farbe berechnet. Die Kamera ist in diesem Beispiel auf den Ursprung und die Bildplane auf einen festen Punkt gesetzt.

Schritt 5: Hauptfunktion zum Ausführen des Raytracing

if __name__ == '__main__':    spheres = [        Sphere(Vector(0, 0, 5), 1, Vector(0, 0, 1)),  # Blaue Kugel        Sphere(Vector(2, 0, 7), 1, Vector(1, 0, 0))   # Rote Kugel    ]    light_pos = Vector(5, 5, -10)    ambient_color = Vector(0.1, 0.1, 0.1)    width, height = 400, 300    image = render(width, height, spheres, light_pos, ambient_color)    # Speichere das Bild in einer Datei    import matplotlib.pyplot as plt    plt.imshow(image)    plt.axis('off')    plt.savefig('raytracing_output.png')

In der Hauptfunktion erstellen wir einige Kugeln und eine Lichtquelle, starten den Renderprozess und speichern das resultierende Bild in einer Datei ab.

Erklärung

Dieses einfache Raytracing-Modell simuliert die Grundzüge der Beleuchtungsformel. Es verfolgt Lichtstrahlen durch die Szene, berechnet Kollisionen mit Objekten (Kugeln), bestimmt den Normalenvektor an den Kollisionspunkten und berechnet Beleuchtungseffekte wie diffuse Beleuchtung und Umgebungslicht. Diese Implementierung berücksichtigt die grundlegenden Komponenten der Beleuchtungsformel und zeigt, wie Raytracing zur Erstellung realistischer Bilder verwendet wird.

Aufgabe 4)

Shader-Programmierung in GLSL und HLSLShader sind kleine Programme, die auf der GPU laufen und für unterschiedlichste Grafik- und Berechnungsaufgaben verwendet werden. In dieser Aufgabe wirst Du Dich mit den Basics der Shader-Programmierung in GLSL (OpenGL Shading Language) und HLSL (High Level Shading Language) beschäftigen. Man unterscheidet dabei verschiedene Typen von Shadern, wie zum Beispiel Vertex Shader und Fragment Shader, die jeweils unterschiedliche Aufgaben übernehmen. In der Forschung, z.B. bei PK-Netzen und neuralen Rendering-Techniken, sind Shader ein wichtiges Werkzeug.

a)

Teilaufgabe 1: Erkläre den Unterschied zwischen GLSL und HLSL. Welche Besonderheiten haben diese beiden Shader-Sprachen und für welche Plattformen sind sie gedacht?

Lösung:

  • Unterschied zwischen GLSL und HLSL:
  • GLSL (OpenGL Shading Language):GLSL ist die Shading-Sprache, die speziell für OpenGL entwickelt wurde. Sie ist eng in das OpenGL-Framework integriert und ermöglicht direkte Manipulationen und Berechnungen auf der Grafikkarte. GLSL ist für eine Vielzahl von Plattformen geeignet, darunter Windows, macOS, Linux und mobile Betriebssysteme wie Android. Ein besonderes Merkmal von GLSL ist seine Integration mit den OpenGL-Pipeline-Stufen, wie Vertex- und Fragment-Shadern.GLSL nutzt eine ähnliche Syntax wie C, was es Programmierern erleichtert, sich schnell einzuarbeiten.
  • HLSL (High Level Shading Language):HLSL wurde von Microsoft für die Verwendung mit dem Direct3D-Framework im DirectX-SDK entwickelt. Es ist primär für Windows-Plattformen gedacht und wird in der Spieleentwicklung häufig verwendet. HLSL integriert sich nahtlos in die Direct3D-Pipeline und ähnelt in seiner Syntax ebenfalls der Programmiersprache C. Ein besonderes Merkmal von HLSL ist seine Fähigkeit, gut mit den Microsoft-Entwicklungswerkzeugen und -Umgebungen, wie Visual Studio, zu arbeiten.
    • Zusammengefasst:- GLSL: OpenGL, plattformübergreifend, Linux, Windows, macOS, Android, C-ähnliche Syntax- HLSL: Direct3D, Windows, Primär verwendung in der Spieleentwicklung, C-ähnliche Syntax, Integration in Microsoft-Entwicklungswerkzeuge

b)

Teilaufgabe 2: Schreibe einen einfachen Vertex Shader in GLSL, der die Position eines Vertex verdoppelt. Verwende hierzu eine beliebige Input-Variable für die Position des Vertex.

Lösung:

  • Teilaufgabe 2:
  • Hier ist ein einfacher Vertex Shader in GLSL, der die Position eines Vertex verdoppelt:
 #version 330 core // Input-Variable für die Position des Vertexlayout(location = 0) in vec3 aPos;// Uniform für die Model-View-Projection-Matrixuniform mat4 mvp;void main(){    // Verdopple die Position des Vertex    vec3 doubledPos = aPos * 2.0;    // Setze die verdoppelte Position als neue Position des Vertex    gl_Position = mvp * vec4(doubledPos, 1.0);}
  • Erklärung der Hauptaspekte des Shaders:
    • #version 330 core: Gibt die GLSL-Version an, die verwendet wird (in diesem Fall 3.30).
    • layout(location = 0) in vec3 aPos: Deklariert eine Eingabe-Variable für die Vertexposition. Diese Variable wird vom Vertex-Array-Objekt an den Shader übergeben.
    • uniform mat4 mvp: Deklariert eine uniform-Variable für die Model-View-Projection-Matrix. Diese Matrix wird verwendet, um die Vertex-Position im Raum zu transformieren.
    • vec3 doubledPos = aPos * 2.0: Verdoppelt die Position des Vertex in allen drei Richtungen (x, y, z).
    • gl_Position = mvp * vec4(doubledPos, 1.0): Setzt die verdoppelte Position als neue Position des Vertex, nachdem sie mit der MVP-Matrix transformiert wurde, und weist sie der vordefinierten Ausgabe-Variable gl_Position zu.

    c)

    Teilaufgabe 3: Übersetze den folgenden HLSL-Shader in GLSL. Der HLSL-Shader berechnet die Farbe eines Pixels basierend auf der Lichtintensität, die als Input-Variable gegeben ist:

    'sampler2D texture : register(s0);float4 main(float4 color : COLOR, float2 tex : TEXCOORD0) : COLOR{    float4 texColor = tex2D(texture, tex);    return color * texColor * 0.5;}'

    Lösung:

    • Teilaufgabe 3:
    • Hier ist der HLSL-Shader in GLSL übersetzt:
    #version 330 core// Sampler für die Texturuniform sampler2D texture;// Input-Variable für die Farbe und die Texturkoordinatenin vec4 color;in vec2 texCoord;// Output-Variable für die Farbe der Fragmenteout vec4 FragColor;void main(){    // Abfragen der Texturfarbe bei texCoord    vec4 texColor = texture(texture, texCoord);    // Berechung der endgültigen Farbe    FragColor = color * texColor * 0.5;}
  • Erklärung der Umwandlung:
    • #version 330 core: Gibt die GLSL-Version an, die verwendet wird (in diesem Fall 3.30).
    • uniform sampler2D texture: Deklariert einen Sampler für die Textur, der verwendet wird, um Texturfarben abzufragen.
    • in vec4 color: Deklariert eine Eingabe-Variable für die Farbe, die vom Vertex-Shader übergeben wird.
    • in vec2 texCoord: Deklariert eine Eingabe-Variable für die Texturkoordinaten, die ebenfalls vom Vertex-Shader übergeben wird.
    • out vec4 FragColor: Deklariert eine Ausgabe-Variable für die endgültige Fragmentfarbe.
    • vec4 texColor = texture(texture, texCoord): Fragt die Texturfarbe bei der gegebenen Texturkoordinate ab.
    • FragColor = color * texColor * 0.5: Berechnet die endgültige Farbe des Pixels basierend auf der gegebenen Farbe, der Texturfarbe und der Lichtintensität (hier konstant als 0.5 angenommen).
    Sign Up

    Melde dich kostenlos an, um Zugriff auf das vollständige Dokument zu erhalten

    Mit unserer kostenlosen Lernplattform erhältst du Zugang zu Millionen von Dokumenten, Karteikarten und Unterlagen.

    Kostenloses Konto erstellen

    Du hast bereits ein Konto? Anmelden