Hardwarebeschleunigte Implementierung des hierarchischen

Werbung
1
!
"
#
%
$
$&
%
'
Programm RadiosGL:
hierarchische Radiostiy-Verfahren von
Jason Rohrer 2000, Studienarbeit an Cornell Uni Ithaca/NY
2
() "
*
!
"
%
&
'
#$
(
)
*
-
+
",
.%
#
/
0
*
(
1
#
'
%
*
Motivation:
- trotz zahlreicher Optimierungen nicht ausreichend
- neue Generation der Grafikhardware:
programmierbare Grafikprozessoren (Vertex- / Pixel-Shader)
3
() "
'
+
+
2
-
3
4
$
2
$
7
'
89
:9
.9
5
'
6
5)
5"2
$
/
5
2
4
,) #
$
5
,)(
(-./
0#
0$
-
1
#
7
!
1
1
+
2
1
,
5
1
+
2
1
5
,
2
7
!
#
$
'
*
)!
Radiosity
)!
+
5
;
*
<
,
basiert auf Gleichgewicht zw. zugeführter Strahlungsenergie von Lichtquellen und
absorbierter Strahlungsenergie durch alle Oberflächen
Verfahren berechnet spezifische Beleuchtungsstärke jeder Fläche
Simulation der Auswirkungen der Lichtemission und –reflexion an diffusen Oberflächen
Energieerhaltung in einer geschlossenen Umgebung
gesamte von Fläche abgegebene Energie = Strahlung / Radiosity
Strahlungsenergiebilanz
es werden weder Energie zugeführt noch abgeführt
abgeschlossene Umgebung
es besteht ein Gleichgewicht zw. einfallender Lichtenergie und absorbierter/reflektierter
Lambertsche Strahler/Reflektoren
lokale Verfahren
globale Verfahren
ideal diffuse Reflexion/Strahlung = Lichtintensität
unabhängig vom Betrachterstandpunkt
Flat- / Gouraud- (interpoliert Intensität) / Phong-Shading (interpoliert Normale)
Raytracing (Strahlverfolgung) / Radiosity (Energiegleichgwicht)
6
+
=
-$
=
5
+*
,
+) !
$
Bi = E i + pi
j =1
- <-?
*
Aj
n
B j F ji
Ai
)?
' <'?
)
>
*
"
+&
?
)
(
'
)!
$/
)!
)!
$
*
)!
,
Bi − pi
?
#
?
Ai Fij = A j F ji
Bi = Ei + pi
$
$
$
)!
)!
*
&
!
Radiosity-Gleichung:
,
n
j =1
n
j =1
B j F ji
B j F ji = E i
= FINITE-ELMENTE-Methode
Einteilung ein einfache geometrische Objekte (Polygone)
Radiosity eines Patch i besteht aus (Strahlung vom Patch i, die er sendet):
Summe abgestrahltes und reflektiertes Licht
der Eigenemission Ei
dem Reflexionskoeffizenten pi mit
gewichteten Anteil der verschiedenen Bj mal den zugehörigen FF
reflektierte Licht:
Summe des einfallenden Lichts multipliziert mit Reflexionsvermögen
einfallendes Licht:
Summe des Lichts, das alle Flächen der Szene verläßt, multipliziert mit
Lichtanteil der eine Flächeneinheit des empfangenen Flächenelements
erreicht (Bj*Fji*Aj)
diskrete Flächenelemente:
endliche Größe
strahlt über gesamte Fläche gleichmäßig Licht ab und reflektiert Licht
opake, diffuse Lambertsche Strahler und Reflektoren
7
3
=
3 4$
=
"
'
*
+5
,>
'?
'
+5
,
2
)
!
!
7
!
!
'
7
=
*
35
Fij =
1
Ai
' < '?
@ <@?
?
cos θ i cos θ j
πr 2
Ai A j
)!
'
A
H ij dA j dAi
)!
?
)!
9 )!
)!
>
Formfaktor
repräsentiert Energiebeziehung zwischen den Patches
also Energieaustausch zw. 2 Flächen in geometrischer Hinsicht
Anteil der ankommenden Energie hängt vom
Winkel zwischen den Flächennormalen (relative Ausrichtung)
Abstand der Flächen zueinander (Entfernung)
Größe der Fläche von Aj ab
und den Verdeckungen
Formfaktor-Gleichung
als Flächendurchschnitt definiert (Patch-zu-Patch)
Betrachterstandpunkt unabhängig, da nur Geometrie-Abhängig
Neuberechnung der FF nur notwendig bei Neu-Anordnung der Szene
Sichtbarkeitsbestimmung notwendig
durch Radiosity-Voraussetzung (Energieerhaltung in geschlossener Umgebung)
besitzen FF bestimmte Eigenschaften:
Anzahl FF quadratisch zur Anzahl der Patches
Summe aller Fij einer Fläche i <= 1
für jede Fläche i gilt : Fii = 0 , d.h. transportiert keine Energie auf sich selbst
FF der Flächen i und j sind nicht kommutativ, d.h. Fij ungleich Fji
reziproke Beziehung zwischen Flächen i und j, d.h. Ai Fij = Aj Fji
8
3
=
1
2
Analytische Methoden:
- für einfache Elemente (Element-Element) + differentielle Flächen
- Voraussetzung:
vollständig Sichtbar
diff. Fläche auf Kreis
diff. Fläche auf Polygon
FF zwischen parallelen Rechtecken
FF zwischen senkrechten Rechtecken
Numerische Methoden:
- Gauß-Verfahren
FF-Berechnung (2-fach Integral), Stützpunkte sind:
Punkt-Paare
(bei Fläche-Fläche-Integral) oder
Punkt-Vektor-Paar
(bei Fläche-Raum-Integrale)
- Hemisphäre-Sampling
aufwendige Sichtbarkeitsbestimmung
FF von Patch i zu allen anderen Patches wird benötigt
- einmal Sichtbarkeitsberechnung durchführen
- FF = Aufsummierung der differentiellen FF
* NUSSELT-Analogie : FF von dAi auf Patch Aj = proportional zur Fläche der Doppelprojektion
auf die Kreisscheibe
Fläche auf Einheits-Hemisphäre = Raumwinkel ( dw = cos_wj / r2 )
Projektion auf Grundfläche = ( cos_wi/pi )
somit : ( cos_wi * cos_wj ) / ( pi * r2 )
* HEMICUBE
Kugel durch Würfel ersetzen, Sichtbarkeit über z-Buffer
* SINGLE-PLANE
von Sillons: statt Würfel ein Fläche
* Monte-Carlo-Raytracing: Umkehrung der Nusselt-Analogie
- Area-Sampling
* Monte-Carlo-Integration
Punkt auf Aj bestimmen und FF-Teile aufsummieren
* Uniforme-Flächenunterteilung
Einteilung in Teilflächen und dann approximieren
* Hierarchische-Unterteilung
Fläche wird adaptiv unterteilt, d.h wenn Änderung groß ist
( bei hohen Gradienten wird öfter unterteilt )
9
3
6
=
*
8
5
7
2
$ 34 9 3
+
)
+
+ +4
)?
!
'
*
?
)!
'?
'
Nusselt-Analogie (geometrische Intepretation des FF) :
FF als Verhältnis der Basisfläche einer Halbkugel zur Orthogonalprojektion der auf
die Halbkugel projizierten Fläche
( 1. Projektion die aus F_i sichtbaren Teile von F_j auf Halbkugel mit rad = 1 um dFi
2. Projektion dieser orthogonal auf kreisförmige Grundfläche der Halbkugel
3. Division durch die Kreisfläche )
Projektion auf Halbkugel entspricht Raumwinkel dw= cos_j / r²
Projektion auf Grundfläche der Halbkugel = multiplikation mit cos_i
Division durch Flächeninhalt des Einheitskreises = pi im Nenner
Fij wird durch FdAiAj approximiert
dAi liegt in der Mitte der Fläche i
Problem bei der Energieberechnung:
- abgestrahlte Energie ist richtungsabhängig
d.h. es muß bekannt sein, welcher Anteil der Kugeloberfläche dem entsprechenden Flächenstücken
zugeordnet ist
10
3
6
=
+
*
?
+ +4
:3
#
5
7
9 BCD ,
#
+
E
?
3
9
*
#
?
+
E
,
9
E
&
%
9 ; :3
# E
∆Fp =
$
)
cos θ i cos θ p
πr 2
+ :3 ,
∆A
2
2
cos θ p =
1
r
cos θ i =
1
r
r = xp + yp +1
2
∆Fp =
2
∆A
π ( x p + y p + 1) 2
2
2
cos θ p =
1
r
cos θ i =
zp
r
r = yp + zp +1
2
∆Fp =
2
z p ∆A
π ( y p + z p + 1) 2
2
2
Hemicube-Methode:
zur numerischen Berechnung wird Halbkugel durch Halbwürfel mit :
Zentrum im Ursprung
Normalvektor in z-Achse ersetzt
Oberseite des Würfels parallel zur Fläche
Formfaktoren werden in einer geschlossenen Umgebung approximiert
Rasterung
zugeordnet
Delta-Formfaktor
Für eine Fläche Fi gilt
Pixel-Aufteilung und jedem Patch wird ein Pixel
Beschreibt den Beitrag zu dem gesamten Formfaktor
Summe aller Fij = 1
Formfaktor Fij wird approximiert durch Aufsummierung der einzelnen Delta-FF
FdAiAj
Delta-FF
Qualität der Approximation abhängig von der Hemicube-Auflösung (Rasterung)
wegen der Symmetrie des Würfels werden nur:
1/8 der Oberseite
und ¼ einer einzigen Halbseite der Delta-FF berechnet
11
&<
$
=
B
$
− p
i
−
1
−
p
p
1
1
F
n
i
−
F
11
p
−
1
21
=
*
p
n
ji
2
F
22
...
F
n 1
−
p
n
F
= E
...
...
...
...
F
12
1
p
...
−
B jF
j =1
n 2
i
−
p
−
p
1
2
F
1 n
B
F
B
2 n
...
1
−
p
n
2
.
F
nn
B
E
1
=
E
2
.
E
n
1
n
2
5(
$
F*
+'
(
7+ G , ,
)"55 1'/ 2H 1
$
6
<
F
5(
$
E
#
+
F
"
+$
+
*
*
,
,$
*
-
"
(
E
,
(
Durch Lösen des Gleichungssystems werden
Energieverteilung der Flächen berechnet,
d.h. die spezifische Ausstrahlung der einzelnen Patches in der Szene
lineares Gleichungssystem:
K*B = E
Eigenschaften:
- abhängig von K
- diagonaldominant:
- regulär:
mit Koeffizientenmatrix K = 1-pF
| kij | < | kii |
mit i j
es existiert eindeutige Lösung
FULL-MATRIX-Methode =
zuerst alle FF berechnen (= n²), dann GS vollständig lösen und dann die Werte dargestellt
Speicheraufwendig
GATHERING-Methode =
Radiosity von Patch i wird durch einsammeln der übrigen Patches j in der Umgebung
bestimmt
SHOOTING-Methode =
jeweils energiereichste Patch ist Sender und verteilt seine Energie an die Empfänger in die
Umgebung
12
&<
=
$
+
%
CC,
5(
?
*
!
+
?
+
,
,
)
>
7
K
* 2 *I *)20*1*0/I)J I*20I
<< /' /202/2'52 2* "0
∆- L *
<< %
- L*
<<
<< '" /
*
77/20 I '/
- +
,
5*2)*
&
)
)? #
> 9-9
# ?
?
∆
L ? M + ∆- M) ? , M +'? < ' ,
∆-? L ∆-? ; ∆
-?
L -? ; ∆
∆- L N
<<
#
*
+ ∆- ' ,
<<
<< %
<<
(
?
(
F
O
Progressive Refinement (Cohen 1988)
ZIEL:
Variante des Southwells-Verfahren
nach jeder Iteration Rückschluß über gesamte Beleuchtung und
in jeder Iteration werden nur n_FF berechnet
in einer Iteration wird eine Zeile der Matrix gelöst
1. Patch i mit größter zu verteilenden Energie bestimmen
( = Shooting-Patch), anfangs Lichtquellen (dBi*Ai)
2. für Patch i
alle Fij zu allen sichtbaren Patches j
(= Shooting = Patches auf die die Energie verteilt wird) bestimmen
3. noch vorhandene (unverteilte) Energie dBi berechnen und gemäß FF zu den Energien
der anderen Patches j addieren
4. gehe zu schritt (2), solange bis Patch i keine Energie zur Verteilung mehr hat
Nachteil:
da jeweils das Shooting-Patch mit der größten Energie seine Radiosity verteilt
kann ein best. Patch mehrmals als Sender vorkommen
- d.h. entsprechend nochmalige Berechnung der n-Formfaktoren
- d.h. es erfordert mehr als n-Iterationen bis jedes Patch mind. ein mal seine Radiosity verteilt
- d.h. mehr als n-Iterationen bis Energiegleichgewicht erreicht wird und GS konvergiert
somit kann u.U. die Lösung länger dauern als bei der FULL-Matrix-Methode
Verfahren arbeitet mit festgelegter Patch-Auflösung
Erweiterung des Verfahrens durch ein Refine-Kriterium
- Grad der benötigten Patch-Unterteilung wird in der Algorithmus-Ausführung ermittelt und die
Szene
dem entsprechend unterteilt
- durch die Annahme: „Radiosity auf allen Patches ist konstant“ (constant radiosity assumption)
Unterteilung nur dann, wenn diese nicht mehr hinreichend erfüllt ist
- Patch-Unterteilung erfordert Neuberechnung aller vorhergegangenen Iterationen
(da sich auch die Eigenschaften dieses Patches sich geändert haben)
hierarchische Radiosity integriert diese Erweiterung
13
=
=
A
+
#
)!
9 9 BCD , $
*
*
Be = 1 ( B1 + B2 + B3 + B4 )
4
$
#
*
$
Bb = 1 ( B1 + B2 ) = 1 ( Ba + Bc )
2
2
Bb = B1 + B2 − Be
Ba = B1
Ba = 2 B1 − Be
# - <- <-
B1 = 1 ( Ba + Be )
2
# -: < -. < -P
# - <- <-
Berechnung der Vertex-Radiosities von den Patch-Radiosities
Aus der Radiosity der Flächen werden die Strahlung für die Eckpunkte interpoliert
durch Mittelung der Radiosities der an den Eckpunkten angrenzenden Flächen
Radiosity der inneren Eckpunkte
ist Durchschnitt der Radiosities der an die Eckpunkte angrenzenden Patches
Radiosity der am Rand liegenden Eckpunkte
WICHTIG: nur angrenzende Patches berücksichtigen
Bc = B2 = 2*B2-Be
Bd = ½(Ba-Bg)= ½ (B1+B3)
B2= ½ (Bc+Be)
|
|
|
Bi = B4 = 2*B4-Be
Bf = ½ (Bc+Bi) = ½ (B2+B4)
B3 = ½ (Bg+Be)
|
|
|
Bg = B3 = 2*B3-Be
Bh = ½ (Bg+Bi) = ½ (B3+B4)
B4 = ½ (Bi+Be)
14
+
Q
"
)
Q
)
<-
(
Q
-
<A
%
1.
2.
3.
4.
5.
Unterteilung der Szene in endliche Anzahl diskreter Flächenelemente
Formfaktoren berechnen
Gleichungssystem lösen
Berechnung der Eckpunkt-Farben
direkte Darstellung mit Interpolation auf Hardware (Bildschirm)
bei Veränderung der Beleuchtung:
- Koeffizientenmatrix (FF + Reflexionskoeffizient) unverändert, rechte Seite E
verändert
Gleichungssystem muß neu gelöst werden
bei Veränderung der Oberflächeneigenschaften:
- Koeffizientenmatrix ändert sich, FF muß nicht neu berechnet werden
Gleichungssystem muß neu gelöst werden
bei Veränderung der Geometrie:
- FF ändern sich und damit auch die Matrix
15
,),
) (-->?-(
@
+
2
-
/
$
R
/
R
5
/
'
Bottom-up-Verfahren:
Top-down-Verfahren:
/
-!
$ ))
#
S
weit entfernte Körper werden zu einem zusammengefasst
Anfang mit dem größten Körper und dann rekursive Unterteilung dieser
Hierarchische Radiosity:
- motiviert durch N-Body-Problem
welche Kraft wirkt bei N Planeten auf jeden einzelnen Planeten?
N(N-1)/2 = Interaktion O(N²)
durch Clusterbildung wird Interaktion auf O(N) reduziert (Planeten ab einem best. Abstand
wie ein Planet betrachten)
ZIEL:
Reduzierung der Interaktionen zwischen den Elementen
Reduzierung der Anzahl der FF-Berechnungen
Energieaustausch auf verschiedenen Ebenen im Patch-Baum:
jeder Knoten kann empfangen oder senden (nicht nur Blätter)
- Unterteilung nur dort, wo es erforderlich ist (Refine-Kriterium
BF)
- definiert Level für Energieaustausch durch Kombination zwischen Sender-/EmpfängerPolygon
Links
- berechnete FF werden zusammen mit Sichtbarkeitsinformation in den Links gespeichert
dadurch wird Neuberechnung vermieden)
- in einer Iteration wird jeweils alle Patches bearbeitet
dadurch wird in erster Iteration gesamtes direkte Licht verteilt und
nach der zweiten Iteration das gesamte einfach reflektierte Licht ….
nach wenigen Iterationen ist die konvergierte Radiosity-Lösung vorhanden
- Unterteilung und Radiosity-Verteilung werden getrennt, so daß explizite Neuverteilung
aufgrund einer Patchunterteilung nicht mehr notwendig ist
16
=
5
=
+
'
5
$
8,
:,
#
,
,
,
$ 5
$
$
$
5+ >?,
$
#
*
#
S
+,
?
A
5
? >
+
#
#
,
LINKS:
Links sind Gather-Links, d.h. Radiosity wird entlang der Links eingesammelt
Gathering statt shooting
erst alle Links bilden, dann Radiosity entlang dieser einsammeln
1) top-level-link (Initial-Link):
Startverlinkung generieren zw. allen gegenseitig sichtbaren Patches
(Root-Knoten miteinander verbinden)
2) lazy-link (modifizierte Startverlinkung):
Top-Level-Link nur dann, wenn Energiebeitrag relevant ist
Holzschuch et al. (1994):
zw. 65-95% der initialen Links wird nicht unterteilt, nicht alle Links tragen wegen ihrer
Energie einen relevanten Beitrag für die Berechnung bei Zusatz-Kriterium epsilon_link
für top-level-verlinkung (Energiebeitrag groß genug?)
3) Reducing-the-number-of-links:
Abbruchkriterium epsilon_reduce für die Flächenverfeinerung
refine-Kriterium
Überprüfung der 4 FF der Kindpatches mit Parent und der Abweichungen
untereinander für die Unterteilung
somit ist Methode unabhängig von der transportierten Energiemenge
(Vorab-Überprüfung)
17
=
#
$
+,
#
#
#
=
=
#
#
$
+5
M5 ,
5 9-I
5 ?9-I
$
-I
+
LN
-I
5
/
5
$
5
$
+5,T
+ > N,
+5> -)U> 'U ,
;L 5 9? M 5 9?9-I
;L 5 ?9 M 5 ?9 9-I
M >
L
-I
*;
-I
M5
M5
I? M 5
I? M 5
I?
I?
,
; -I
-I
#
L
-I
; -I
S
-I
L
+ > -I
-I ;L -I
M+
-I
L -I
+ -I ,
,
<
,
18
,)A %
$%B
$%B +
+
&
%
&
'
&
)
"
3
$
,L
,;)
1
$1
S
+)
,
4
& +/
)
+ &
>7 ?
>/ &
>V,
>V,
- Erste programmierbare GPUs im Jahre 2001
- Ab 2002 erweiterte GPU-Fähgikeiten:
Branching-Operationen / Fliesskomma-Arithmetik / Berechnung von
Texturkoordinaten
- Heutige GPUs:
haben feste und programmierbare Pipeline-Stufen
Feste Pipeline-Abschnitte:
Primitive Assembly (Clipping, Culling, …) / Rasterization
Framebuffer Operations (Alpha-Depth-Stencil-Tests) / Storage
Prog. Pipeline-Abschnitte:
Per-Vertex-Operations (Transformation, Beleuchtung,…)
Per-Fragment-Operations (Texel-Zuweisung, Nebel, Alphablending….)
Stream-Kernels
Eingabestrom vom vorherigen Abschnitt wird sequentiell weiterverarbeitet
und über I/O-Register weitergereicht
Vertex-Shader:
Transformation + Beleuchtungsberechnung von Eckpunkten
(T&L transformation and Lighting)
Fragment-Shader: Texturverarbeitung + Pixelfarben
19
,)/
6C
0 2%2'
!
$
E
D7
1
5
#
"
$
'
2
!
-
) &
!
*
/
&
' 2<
'
)H,
!
&
)
+
&*
)
+
*
>0
>)
+/ & > -
>5
>V9,
>V9,
-Im Jahre 2001 Microsoft HLSL (High-Lever-Shader-Language)
-Cg ursprünglich „C for Graphics“
jetzt umgeändert in „C for GPUs“
-Einfache Integration:
Compiler sitzt auf den API´s / partielle Integration = Kombination von Cg mit Assembler
und statischer GPU / unabhängig von der Anwendung
-Unabhängigkeit von anderen Komponenten: 3D-API-unabhängig und seperate Programmierung auf 2 Ebenen
-Erweiterbarkeit der Sprache:
alle in C und C++ enthaltenen Keywords sind auch in Cg reserviert
-SPRACH-SYNTAX:
Konnektoren =
bilden Datenfluß in der GPU, sind Eingänge und Ausgänge zwischen den Recheneinheiten
(sind wie erweiterbare C-Strukturen)
Profile =
bestimmen über nutzbare Features von Cg (Datentypen, Strukturen, Funktionen)
uniforme Variablen = als Inputkonnektor in den Eingangsvariablen, anzusiedeln in den konstanten Registern
der Prozessoren, NICHT TEIL des Datenflusses
Register-Bindung =
aus Cg wird Assembler generiert und um Zusammenhang Register - Cg Variablen nicht verloren
geht Variablen an Register anbinden, Variable wird einer Registerkomponente gleichgesetzt
-Vertex-Programm :
Input: Vertex
programm-spezifische Manipulationen
Output : transformierte Vertices
Assemblierung und Rasterisierung
-Fragment-Programm :
Input: transf. Vertex
programm-spezifische Manipulationen
Framebuffer
Output: Pixel
Framebuffer Operationen
-Compiler:
2 Compilierungsarten: run-time / klassische application-development Compilierung
-Run-time-Compilierung: Vorteil
hardwarespezifischer optimierter Assemblercode (BACK-END)
-Application-Development-Compilierung: Vorteil
benutzerdefinierter profilspezifischer Assemblercode
1) Cg-Runtime-API : aus 2 Komponenten
-2 Bibliotheken:
a) Interface für Cg-Compiler und dessen Ausgaben : unabhängig von 3D-API
b) Interface für 3D-API und ermöglicht Anbindung von Cg-Programmen an 3D-API-Zustand
und Parameter-Übergabe
2) CgFX:
-Register:
Erweiterung für diverse CPU-Funktionen, API- und Spracherweiterungen,
Funktionen Multipass-Rendering-Effekte und Verwaltung von Assembler-ShaderProgrammen
konstante (read) / temporäre (read/write)
adress-register (für relative Adressierung) / predicate-register (boolsche Werte für Flußsteuerung)
textur-sample-register (für Texturspeicherzugriff)
20
A) %
$&
6
%
3
E
:NNN
2
$
- RadiosGL entwickelt im Rahmen einer Studienarbeit an der Cornell Universität in Ithaka/New York
- Implementation in C++
- Package aus drei Komponenten: Utilities (MakeHemicube, Vrml2mdl)
Renderer RadiosGL
Viewer (RadiosGLView)
Funktionsweise:
1) mit Grafikprogramm Szenenbeschreibung in Vrml1.0 generieren und in Patches und Lighting
abspeichern
2) mit Vrml2mdl Applikation eine MDL-Datei (Szenendescriptor) für den Input von RadiosGL
generieren
(mdl-Datei = strukturierte ascii-Datei mit des Objekteigenschaften (PatchVertices mit
Farbe+Beleuchtung)
3) Hemicube-Daten erstellen mit vorgegebener Auflösung
(hc-Datei = binär-Datei mit den Delta-FF)
4) mdl-Datei und hc-Datei und Parameter für das Rendering in RadiosGL als Input eingeben
Radiosity-Berechnung
Ergebnis wird in ein RGL-File herausgeschrieben
RadiosGL:
RadiosGLView:
echtzeitfähiger Kommandozeilenbasierender Renderer für Szenen mit Dreiecksflächen
OpenGL basierend, echtzeitfähiger RGL-File-Szenen-Viewer
21
/)
3
%
$
+
2
22
%
%
+
$
2
A
*
$
23
F) '
24
25
26
Herunterladen