Im Folgenden sind die
Knoten-Elemente
des Raytracers implementiert.
package tinyray.raytracer;
/**
* Die Klasse Element ist die abstrakte Basisklasse des Raytracers.
* Eigentlich wäre hier ein Interface, anstatt einer abstrakten Klasse
* angebracht, aber Geschwindigkeitstest mit dem Raytracer haben ergeben,
* dass seltsamerweise die abstrakte Klasse als Basisklasse für die Knoten
* des Syntaxbaumes den Raytracer schneller macht.
*/
public abstract class Element {}
package tinyray.raytracer;
import java.util.Iterator;
import java.util.LinkedList;
/**
* Die Klasse Tinyray repräsentiert das Tinyray-Programm als Raytracer.
*/
public class Tinyray extends Element {
// Die globale Kamera.
private Camera camera;
// Der Hintergrund (die Leinwand).
private Background background;
// Das globale Umgebungslicht.
private Ambience ambience;
// Der globale Nebel.
private Fog fog;
// Die geometrischen Objekte der Szene.
private LinkedList<Geo> geos;
// Die Sonnen als Lichtquellen der Szene.
private LinkedList<Sun> suns;
/**
* Initialisiert den Raytracer.
*/
public Tinyray() {
super();
this.camera = new Camera();
this.background = new Background(new Vector3D(0.0));
this.ambience = new Ambience(new Vector3D(1.0));
this.fog = new Fog(new Vector3D(1.0), 0.0);
this.geos = new LinkedList<Geo>();
this.suns = new LinkedList<Sun>();
}
/**
* Gibt die Kamera des Raytracers zurück.
* @return Die Kamera.
*/
public Camera getCamera() {
return this.camera;
}
/**
* Setzt die Kamera des Raytracers.
* @param camera Die Kamera.
*/
public void setCamera(Camera camera) {
this.camera = camera;
}
/**
* Gibt den Hintergrund des Raytraces zurück.
* @return Der Hintergrund.
*/
public Background getBackground() {
return this.background;
}
/**
* Setzt den Hintergrund des Raytracers.
* @param background Der Hintergrund.
*/
public void setBackground(Background background) {
this.background = background;
}
/**
* Gibt die globale Ambienz des Raytracers zurück.
* @return Die globale Ambienz.
*/
public Ambience getAmbience() {
return this.ambience;
}
/**
* Setzt die globale Ambienz des Raytracers.
* @param ambience Die globale Ambienz.
*/
public void setAmbience(Ambience ambience) {
this.ambience = ambience;
}
/**
* Gibt den Nebel des Raytracers zurück.
* @return Der Nebel.
*/
public Fog getFog() {
return this.fog;
}
/**
* Setzt den Nebel des Raytracers.
* @param fog Der Nebel.
*/
public void setFog(Fog fog) {
this.fog = fog;
}
/**
* Gibt die Geoobjekte der Szene zurück.
* @return Die Geoobjekte.
*/
public Iterator<Geo> getGeos() {
return this.geos.iterator();
}
/**
* Fügt der Szene ein Geoobjekt hinzu.
* @param geo Das Geoobjekt.
*/
public void addGeo(Geo geo) {
this.geos.add(geo);
}
/**
* Gibt die Sonnen der Szene zurück.
* @return Die Sonnen.
*/
public Iterator<Sun> getSuns() {
return this.suns.iterator();
}
/**
* Fügt der Szene eine Sonne hinzu.
* @param sun Die Sonne.
*/
public void addSun(Sun sun) {
this.suns.add(sun);
}
/**
* Gibt den Raytracer als Tinyray-Code zurück.
*/
public String toString() {
String result = "Tinyray {";
result += "\n " + this.getCamera();
result += "\n " + this.getBackground();
result += "\n " + this.getAmbience();
result += "\n " + this.getFog();
Iterator<Geo> iterGeos = this.getGeos();
while (iterGeos.hasNext()) {
result += "\n " + iterGeos.next();
}
Iterator<Sun> iterSuns = this.getSuns();
while (iterSuns.hasNext()) {
result += "\n " + iterSuns.next();
}
result += "\n}";
return result;
}
/**
* Rendert die Szene des Raytracers und gibt das resultierende Bild auf
* den Viewport aus. Trifft ein Strahl (Ray) auf die Oberfläche eines
* Geoobjektes der Szene, so prallt dieser wieder ab. Die Tiefe legt fest,
* wie oft ein Strahl maximal abprallen kann.
* Damit die Szene nicht so pixelig wirkt, wird jedes Pixel des Viewports
* in ein quadratisches Raster unterteilt, damit gleich mehrere Strahlen
* durch ein Pixel geschossen, geschickt oder gelegt werden können.
* @param viewport Das resultierende Bild.
* @param maxdepth Die Tiefe.
* @param antialias Die Rasterbreite bzw. -höhe in einem Pixel.
*/
public void render(Viewport viewport, int maxdepth, int antialias) {
// Zusichern, dass antialias mindestens den Wert 1 hat.
antialias = Math.max(1, antialias);
// viewDir = normalize(cam.LookAt - cam.Location).
Vector3D viewDir = Vector3D.sub(this.camera.getLookAt(),
this.camera.getLocation()).getNormalized();
// viewRight = viewDir x cam.Up.
Vector3D viewRight = Vector3D.cross(viewDir, this.camera.getUp());
// viewUp = viewRight x viewDir.
Vector3D viewUp = Vector3D.cross(viewRight, viewDir);
// Breite des Viewport als Double.
double viewportWidth = (double)viewport.getWidth();
// Höhe des Viewport als Double.
double viewportHeight = (double)viewport.getHeight();
// Field Of View mit 30 Grad.
double wh = 2.0 * Math.tan(Math.PI / 180.0 * (30.0 / 2.0));
// Höhe und Breite des virtuellen Bildes ins richtige Verhaeltnis
// setzen (Schlagwort: aspectRatio).
double width = wh;
double height = wh;
if (viewportWidth > viewportHeight) {
width = viewportWidth / viewportHeight * height;
} else {
height = viewportHeight / viewportWidth * width;
}
// Rasterabstände des virtuellen Bildes ermitteln.
double dy = height / viewportHeight;
double dx = width / viewportWidth;
/* Der eigentliche Rendervorgang. */
double y = height / 2.0;
for (int sy = 0; sy < viewport.getHeight(); sy++) {
// Gibt die Renderinformation aus.
if (this.renderLineInfo(sy, viewport.getHeight())) {
return;
}
double x = -width / 2.0;
for (int sx = 0; sx < viewport.getWidth(); sx++) {
LinkedList<Vector3D> colors = new LinkedList<Vector3D>();
// Erzeugt in Abhängigkeit von antialias:
// (antialias * antialias)-viele Strahlen pro Pixel,
// wobei jeder Strahl in eine leicht andere Richtung zeigt.
for (int ai = 0; ai < antialias; ai++) {
for (int aj = 0; aj < antialias; aj++) {
double di = dx * (double)ai / (double)antialias;
double dj = dy * (double)aj / (double)antialias;
// Richtung des jeweiligen Strahles.
Vector3D dir = Vector3D.add(
viewDir,
Vector3D.add(
Vector3D.mult(x + di, viewRight),
Vector3D.mult(y + dj, viewUp)
)
);
// Erzeugt einen Strahl.
Ray theRay = new Ray(this, this.camera.getLocation(),
dir.getNormalized(), 0, maxdepth);
// Berechnet den Farbwert des Strahles.
colors.add(theRay.getColor());
}
}
// Berechnet den Farbwert des Pixels.
Vector3D color = Vector3D.ZERO;
Iterator<Vector3D> iterator = colors.listIterator();
while (iterator.hasNext()) {
color = Vector3D.add(color, iterator.next());
}
int raysPerPixel = antialias * antialias;
color = Vector3D.mult(1.0 / (double)raysPerPixel, color);
// Setzt den Farbwert des Pixels im Viewport.
viewport.setPixel(sx, sy, color);
x += dx;
}
y -= dy;
}
// Zu guter Letzt noch die Renderinformation ausgeben.
this.renderLineInfo(viewport.getHeight(), viewport.getHeight());
}
/**
* Gibt eine kurze Information über den Rendervorgang auf der Konsole aus.
* Bevor eine Zeile gerendert wird, sollte dem Anwender eine Fortschritts-
* information des Rendervorgangs angezeigt werden.
* @param line Die aktuelle Zeile.
* @param height Die Anzahl der zu rendernden Zeilen.
* @return true, falls der Rendervorgang abgebrochen werden soll.
* Hier wird der Rendervorgang nie abgebrochen, es wird stets
* falls zurückgegeben. Die Rückgabe von true, wäre beispielsweise
* interessant, wenn man den Rendervorgang vorzeitig abbrechen
* möchte.
*/
protected boolean renderLineInfo(int line, int height) {
System.out.print("Reihe " + line + " von " + height);
System.out.print(" (" + (line * 100 / height) + "%)");
System.out.print(" \r");
if (line == height) {
System.out.print("Rendering beendet!");
System.out.println(" ");
}
return false;
}
/**
* Die innere Klasse Ray stellt einen Strahl, der durch die Szene
* geschickt werden kann, dar.
*/
public final class Ray {
// Das Tinyray-Programm, welches die Szene kapselt.
private Tinyray tinyray;
// Der Ursprung des Strahles.
private Vector3D origin;
// Die Richtung des Strahles.
private Vector3D direction;
// Die aktuelle Rekursionstiefe.
private int depth;
// Die maximale Rekursionstiefe.
private int maxdepth;
/**
* Initialisiert den Strahl.
* @param tinyray Das dazugehörige Tinyray-Programm.
* @param origin Der Ursprung des Strahles.
* @param direction Die Richtung des Strahles.
* @param depth Die aktuelle Rekursionstiefe.
* @param maxdepth Die maximale Rekursionstiefe.
*/
public Ray(Tinyray tinyray, Vector3D origin, Vector3D direction,
int depth, int maxdepth) {
this.tinyray = tinyray;
this.origin = origin.copy();
this.direction = direction.copy();
this.depth = depth;
this.maxdepth = maxdepth;
}
/**
* Gibt den Ursprung des Strahles zurück.
* @return Der Ursprung.
*/
public Vector3D getOrigin() {
return this.origin;
}
/**
* Gibt die Richtung des Strahles zurück.
* @return Die Richtung.
*/
public Vector3D getDirection() {
return this.direction;
}
/**
* Berechnet den Farbwert, den der Strahl besitzt, wenn dieser durch
* die Szene geschickt wird.
* @return Der teuer berechnete Farbwert.
*/
public Vector3D getColor() {
Vector3D result = Vector3D.ZERO; // Schwarze Farbe
// Gewinner feststellen
Geo closest = null;
double minDist = Double.MAX_VALUE;
Iterator<Geo> geos = this.tinyray.getGeos();
while (geos.hasNext()) {
Geo geo = geos.next();
Geo.Distance geoDistance = geo.getIntersect(this);
if (geoDistance != null) {
double distance = geoDistance.getDistance();
if ((0.0 < distance) && (distance < minDist)) {
minDist = distance;
closest = geoDistance.getGeo();
}
}
}
if (closest == null) {
if (this.depth <= 0) {
result = this.tinyray.getBackground().getColor();
}
} else if (this.maxdepth < 0) {
// Ray-Casting, falls die maximale Tiefe negativ ist.
result = closest.getParameters().getColor();
} else {
/* Ray-Tracing */
Vector3D intersectionPosition = Vector3D.add(
this.origin,
Vector3D.mult(minDist, this.direction)
);
Vector3D normal = closest.getNormal(intersectionPosition);
// Berechnet den vom getroffenen Objekt reflektierten Strahl.
Ray reflectedRay = new Ray(this.tinyray, intersectionPosition,
this.direction.getReflectedAt(normal).getNormalized(),
this.depth + 1, this.maxdepth);
// Ermittelt in Abhängigkeit jeder Sonne einen Farbwert.
Iterator<Sun> suns = this.tinyray.getSuns();
while (suns.hasNext()) {
Sun sun = suns.next();
// Nur wenn der Strahl selbst nicht zur Sonne zeigt, wird
// die Farbe berechnet.
// Liegt zw. der Sonne und dem Geoobjekt ein Hindernis?
Ray rayOfSun = new Ray(this.tinyray, Vector3D.add(
intersectionPosition, Vector3D.mult(
Vector3D.EPSILON, sun.getDirection())),
sun.getDirection(), 0, this.maxdepth);
boolean somethingIntersected = false;
Iterator<Geo> geos_sun = this.tinyray.getGeos();
while (geos_sun.hasNext()) {
Geo geo = geos_sun.next();
Geo.Distance distance = geo.getIntersect(rayOfSun);
if (distance != null) {
if (distance.getDistance() > 0.0) {
somethingIntersected = true;
break;
}
}
}
// Falls kein Hindernis zw. Sonne und dem getroffenen
// Geoobjekt (closest) vorhanden ist, dann wird auf den
// aktuellen Farbwert ein weiterer Farbwert in
// Abhängigkeit der aktuellen Sonne hinzuaddiert.
if (!somethingIntersected) {
result = Vector3D.add(
result,
this.getPhongLightingColor(reflectedRay,
normal, closest, sun)
);
}
}
// Wenn nun alle Sonnen durchiteriert sind, wird noch das
// globale Umgebungslichtfarbe mit der lokalen, ambienten
// Farbe multipliziert, und schließlich zum aktuellen
// Farbwert hinzuaddiert.
result = Vector3D.add(
Vector3D.mult(
this.tinyray.getAmbience().getColor(),
closest.getParameters().getAmbient()
),
result
);
// Spiegelung mit Rekursion
if (this.depth < this.maxdepth) {
result = Vector3D.add(
result,
Vector3D.mult(
closest.getParameters().getMirror(),
reflectedRay.getColor()
)
);
}
}
// Fog-Berechnung
if (this.tinyray.getFog().getDensity() != 0.0) {
Vector3D fogColor = this.tinyray.getFog().getColor();
if (minDist >= 0.0) {
// Nebeldichte.
double d = this.tinyray.getFog().getDensity();
// Exponentieller (EXP2) Nebelwert.
double f = Math.exp(-(minDist * d) * (minDist * d));
f = Math.min(Math.max(f, 0.0), 1.0);
result = Vector3D.add(
Vector3D.mult(f, result),
Vector3D.mult(1.0 - f, fogColor)
);
} else {
result = fogColor;
}
}
// Endlich die Rückgabe des Farbwertes zum Strahl, der durch die
// Szene geschickt wurde.
return result;
}
// Farbberechnung mittels Phong-Beleuchtungsmodell.
private Vector3D getPhongLightingColor(Ray reflectedRay,
Vector3D normal, Geo geo, Sun sun) {
Vector3D result = Vector3D.ZERO;
/* Diffusen Farbwert berechnen (diffuse). */
double diffuseFactor = Math.max(
0.0,
Vector3D.dot(sun.getDirection(), normal.getNormalized())
);
Vector3D diffuse = Vector3D.mult(
diffuseFactor,
geo.getParameters().getColor()
);
/* Die Lichtreflection berechnen (specularity).
*
* Das Herzstück des Phong-Beleuchtungsmodells,
* die Spiegelung der Lichtquelle im geometrischen Objekt.
* Je besser das Licht ins Auge des Betrachters trifft,
* desto heller (perfekter) wird die Lichtspiegelung.
*/
Vector3D sunRefl = sun.getDirection().getReflectedAt(normal);
Vector3D eyeRefl = Vector3D.sub(
reflectedRay.origin,
this.origin
);
double sun_eye = Math.max(
0.0,
Vector3D.dot(
sunRefl.getNormalized(),
eyeRefl.getNormalized())
);
double exponent = Math.pow(
sun_eye,
geo.getParameters().getShininess()
);
double specular = geo.getParameters().getSpecular() * exponent;
Vector3D specularity = Vector3D.mult(specular, sun.getColor());
/* Zusammenführen der Farbanteile (diffuse und specularity). */
result = Vector3D.add(diffuse, specularity);
// Natürlich hängt der resultierende Farbwert von der Farbe
// der Sonne ab.
result = Vector3D.mult(sun.getColor(), result);
return result;
}
}
}
package tinyray.raytracer;
/**
* Die Klasse Geo ist die abstrakte Basisklasse für beliebige geometrische,
* dreidimensionale Objekte in der Szene (3D-Welt).
*/
public abstract class Geo extends Element {
// Die allgemeinen Parameter eines geometrischen Objekts.
private Parameters parameters;
/**
* Initialisiert das Geoobjekt mit den Parametern.
*/
public Geo() {
super();
this.parameters = new Parameters();
}
/**
* Gibt die Parameter des Geoobjekts zurück.
* @return Die Parameter.
*/
public Parameters getParameters() {
return this.parameters;
}
/**
* Setzt die Parameter des Geoobjekts.
* @param parameters Die Parameter.
*/
public void setParameters(Parameters parameters) {
this.parameters = parameters.copy();
}
/* Abstrakte Methoden */
/**
* Gibt den Normalenvektor, der Vektor, der senkrecht zu Oberfläche
* steht, zurück. Da die Oberfläche eines Geoobjektes auch gekrümmt
* sein kann und es viele, unterschiedliche Normalenvektoren zum Geoobjekt
* geben kann, wird die Normale bezüglich eines Oberflächenpunkts
* berechnet.
* @param pointOfSurface Der Oberflächenpunkt.
* @return Der Normalenvektor, welcher auf die Länge 1 normalisiert ist.
*/
public abstract Vector3D getNormal(Vector3D pointOfSurface);
/**
* Gibt die Entfernung zurück, die ein Strahl zurücklegen muss,
* um das Geoobjekt zu schneiden.
* @param ray Der Strahl.
* @return Die Entfernung.
*/
public abstract Geo.Distance getIntersect(Tinyray.Ray ray);
/**
* Gibt die Min-Werte des Geoobjekts zurück.
* Ein Geoobjekt beansprucht einen bestimmten Bereich in der 3D-Welt,
* dieser Bereich kann mit einer sogenannten Boundingbox umhüllt werden.
* Um eine Boundingbox erzeugen zu können, benötigt man mindestens zwei
* Eckpunkte, die Min-Werte stellen einen Eckpunkt der dazugehörigen
* Boundingbox dar.
* @return Die Min-Werte.
*/
public abstract Vector3D getMinValues();
/**
* Gibt die Max-Werte des Geoobjekts zurück.
* Die Max-Werte stellen einen Eckpunkt der dazugehörigen
* Boundingbox dar.
* @return Die Min-Werte.
*/
public abstract Vector3D getMaxValues();
/**
* Die Klasse Distance ist eine Hilfsklasse, mit der ein Entfernungswert
* einem Geoobjekt zugeordnet werden kann.
*/
public final class Distance {
// Das Geoobjekt, dem eine Entfernung zugeordnet wird.
private Geo geo;
// Der Entfernungswert.
private double distance;
/**
* Initialisiert die Entfernung meist vom Ursprung des Strahles
* zu einem Geoobjekt.
* @param geo Das Geoobjekt.
* @param distance Die Entfernung.
*/
public Distance(Geo geo, double distance) {
this.geo = geo;
this.distance = distance;
}
/**
* Gibt das Geoobjekt der eine Entfernung zugeordnet wurde zurück.
* @return Das Geoobjekt.
*/
public Geo getGeo() {
return this.geo;
}
/**
* Gibt die Entfernung zu einem Geoobjekt zurück.
* @return Die Entfernung.
*/
public double getDistance() {
return this.distance;
}
}
}
package tinyray.raytracer;
/**
* Die Klasse Geo repräsentiert ein Dreieck (geometrisches Objekt),
* welche in eine Szene plaziert werden kann.
*/
public final class Triangle extends Geo {
// Die drei Eckpunkte.
private Vector3D x;
private Vector3D y;
private Vector3D z;
// Hilfsvektor yx = y - x.
private Vector3D yx;
// Hilfsvektor zx = z - x.
private Vector3D zx;
// Hilfsvektor, die Normale zum Dreieck.
private Vector3D normal;
/**
* Initialisiert das Dreieck mit drei Ortsvektoren.
* Diese Ortsvektoren sind die Eckpunkte des Dreiecks.
* @param x Eckpunkt.
* @param y Eckpunkt.
* @param z Eckpunkt.
*/
public Triangle(Vector3D x, Vector3D y, Vector3D z) {
super();
this.x = x.copy();
this.y = y.copy();
this.z = z.copy();
this.yx = Vector3D.sub(this.x, this.y);
this.zx = Vector3D.sub(this.x, this.z);
// Die Normale muss nur einmal berechnet werden.
this.normal = Vector3D.cross(this.yx, this.zx).getNormalized();
}
/**
* Gibt den Normale des Dreiecks zurück.
* Die Normale ist ein Vektor, der senkrecht zur Oberfläche steht.
*/
public Vector3D getNormal(Vector3D v) {
return this.normal;
}
/**
* Gibt die Entfernung zurück, die ein Strahl zurücklegen muss,
* um das Dreieck zu schneiden.
*/
public Geo.Distance getIntersect(Tinyray.Ray ray) {
Vector3D a = this.yx;
Vector3D b = this.zx;
Vector3D c = ray.getDirection();
Vector3D p = Vector3D.sub(this.x, ray.getOrigin());
double det1 = Vector3D.det(p, b, c);
double det2 = Vector3D.det(a, p, c);
double det3 = Vector3D.det(a, b, p);
double detN = Vector3D.det(a, b, c);
if (detN != 0.0) {
double beta = det1 / detN;
double gamma = det2 / detN;
if ((0.0 < beta) && (0.0 < gamma) && (beta + gamma < 1.0)) {
double distance = (det3 / detN) - Vector3D.EPSILON;
return new Geo.Distance(this, distance);
}
}
return null;
}
/**
* Gibt die Min-Werte des Dreiecks zurück.
*/
public Vector3D getMinValues() {
double minX = Math.min(this.x.getX(), this.y.getX());
minX = Math.min(minX, this.z.getX()) - Vector3D.EPSILON;
double minY = Math.min(this.x.getY(), this.y.getY());
minY = Math.min(minY, this.z.getY()) - Vector3D.EPSILON;
double minZ = Math.min(this.x.getZ(), this.y.getZ());
minZ = Math.min(minZ, this.z.getZ()) - Vector3D.EPSILON;
return new Vector3D(minX, minY, minZ);
}
/**
* Gibt die Max-Werte des Dreiecks zurück.
*/
public Vector3D getMaxValues() {
double maxX = Math.max(this.x.getX(), this.y.getX());
maxX = Math.max(maxX, this.z.getX()) + Vector3D.EPSILON;
double maxY = Math.max(this.x.getY(), this.y.getY());
maxY = Math.max(maxY, this.z.getY()) + Vector3D.EPSILON;
double maxZ = Math.max(this.x.getZ(), this.y.getZ());
maxZ = Math.max(maxZ, this.z.getZ()) + Vector3D.EPSILON;
return new Vector3D(maxX, maxY, maxZ);
}
/**
* Gibt das Dreieck als Tinyray-Code zurück.
*/
public String toString() {
return "Triangle { " + this.x + "; " + this.y + "; " + this.z + "; "
+ this.getParameters() + " }";
}
}
package tinyray.raytracer;
/**
* Die Klasse Sphere repräsentiert eine Kugel (geometrisches Objekt),
* welche in eine Szene plaziert werden kann.
*/
public final class Sphere extends Geo {
// Der Zentrum der Kugel.
private Vector3D center;
// Der Radius der Kugel.
private double radius;
/**
* Initialisiert die Kugel mit Zentrum und Radius.
* @param center Das Zentrum.
* @param radius Der Radius.
*/
public Sphere(Vector3D center, double radius) {
super();
this.center = center.copy();
this.radius = radius;
}
/**
* Gibt den Normale der Kugel zurück.
* Die Normale ist ein Vektor, der senkrecht zur Oberfläche steht.
*/
public Vector3D getNormal(Vector3D pointOfSurface) {
return Vector3D.sub(pointOfSurface, this.center).getNormalized();
}
/**
* Gibt die Entfernung zurück, die ein Strahl zurücklegen muss,
* um die Kugel zu schneiden.
*/
public Geo.Distance getIntersect(Tinyray.Ray ray) {
double a = Vector3D.dot(ray.getDirection(), ray.getDirection());
if (a != 0.0) {
Vector3D oc = Vector3D.sub(ray.getOrigin(), this.center);
double b = 2.0 * Vector3D.dot(oc, ray.getDirection());
double c = Vector3D.dot(oc, oc) - this.radius * this.radius;
double disc = b * b - 4.0 * a * c;
if (disc >= 0.0) {
double x1 = (-b + Math.sqrt(disc)) / (a + a);
double x2 = (-b - Math.sqrt(disc)) / (a + a);
if ((x1 > 0.0) & (x2 > 0.0)) {
double x = Math.min(x1, x2);
return new Geo.Distance(this, x - Vector3D.EPSILON);
} else if (x1 > 0.0) {
return new Geo.Distance(this, x1 - Vector3D.EPSILON);
} else if (x2 > 0.0) {
return new Geo.Distance(this, x2 - Vector3D.EPSILON);
}
}
}
return null;
}
/**
* Gibt die Min-Werte der Kugel zurück.
*/
public Vector3D getMinValues() {
double minX = this.center.getX() - this.radius - Vector3D.EPSILON;
double minY = this.center.getY() - this.radius - Vector3D.EPSILON;
double minZ = this.center.getZ() - this.radius - Vector3D.EPSILON;
return new Vector3D(minX, minY, minZ);
}
/**
* Gibt die Max-Werte der Kugel zurück.
*/
public Vector3D getMaxValues() {
double maxX = this.center.getX() + this.radius + Vector3D.EPSILON;
double maxY = this.center.getY() + this.radius + Vector3D.EPSILON;
double maxZ = this.center.getZ() + this.radius + Vector3D.EPSILON;
return new Vector3D(maxX, maxY, maxZ);
}
/**
* Gibt die Kugel als Tinyray-Code zurück.
*/
public String toString() {
return "Sphere { " + this.center + "; " + this.radius + "; "
+ this.getParameters() + " }";
}
}
package tinyray.raytracer;
/**
* Die Klasse Geo repräsentiert eine Ebene (unendliches Geoobjekt),
* welche in eine Szene plaziert werden kann.
*/
public final class Plane extends Geo {
// Drei Punkte der Ebene.
private Vector3D x;
private Vector3D y;
private Vector3D z;
// Hilfsvektor yx = y - x.
private Vector3D yx;
// Hilfsvektor zx = z - x.
private Vector3D zx;
// Hilfsvektor, die Normale zur Ebene.
private Vector3D normal;
/**
* Initialisiert die Ebene mit drei Ortsvektoren.
* Diese Ortsvektoren (Punkte) liegen genau in der Ebene.
* @param x Ein Ortsvektor.
* @param y Ein Ortsvektor.
* @param z Ein Ortsvektor.
*/
public Plane(Vector3D x, Vector3D y, Vector3D z) {
super();
this.x = x.copy();
this.y = y.copy();
this.z = z.copy();
this.yx = Vector3D.sub(this.x, this.y);
this.zx = Vector3D.sub(this.x, this.z);
this.normal = Vector3D.cross(this.yx, this.zx).getNormalized();
}
/**
* Gibt die Normale der Ebene zurück.
* Die Normale ist ein Vektor, der senkrecht zur Oberfläche steht.
*/
public Vector3D getNormal(Vector3D pointOfSurface) {
return this.normal;
}
/**
* Gibt die Entfernung zurück, die ein Strahl zurücklegen muss,
* um die Ebene zu schneiden.
*/
public Geo.Distance getIntersect(Tinyray.Ray ray) {
Vector3D a = this.yx;
Vector3D b = this.zx;
Vector3D c = ray.getDirection();
Vector3D p = Vector3D.sub(this.x, ray.getOrigin());
double det3 = Vector3D.det(a, b, p);
double detN = Vector3D.det(a, b, c);
if (detN == 0.0) {
return null;
} else {
return new Geo.Distance(this, (det3 / detN) - Vector3D.EPSILON);
}
}
/**
* Gibt die Min-Werte der Ebene zurück.
* Da die Ebene unendliche Ausmaße hat, so erhält man die
* kleinstmöglichen Werte (Double.MIN_VALUE).
* Eine Ebene in eine Boundingbox zu integrieren,
* ist zwar möglich, würde aber wenig Sinn machen,
* da sonst die Boundingbox unendliche Größe hätte.
*/
public Vector3D getMinValues() {
return new Vector3D(Double.MIN_VALUE);
}
/**
* Gibt die Max-Werte der Ebene zurück.
* Da die Ebene unendliche Ausmaße hat, so erhält man die
* größtmöglichen Werte (Double.MAX_VALUE).
* Eine Ebene in eine Boundingbox zu integrieren,
* würde demnach wenig Sinn machen.
*/
public Vector3D getMaxValues() {
return new Vector3D(Double.MAX_VALUE);
}
/**
* Gibt die Ebene als Tinyray-Code zurück.
*/
public String toString() {
return "Plane { " + this.x + "; " + this.y + "; " + this.z +
"; " + this.getParameters() + " }";
}
}
package tinyray.raytracer;
import java.util.Iterator;
import java.util.LinkedList;
/**
* Die Klasse Bounding repräsentiert die sogenannte Boundingbox.
* Mit der Boundingbox wird eine Menge anderer geometrischer Objekte
* umschlossen. So kann die Effizienz des Schnittpunkttests verbessert
* werden. Erst wenn die Boundingbox geschnitten wird, werden die
* enthaltenen Goeobjekte auf Schnitt getestet.
*/
public final class Bounding extends Geo {
// Liste der enthaltenen Geoobjekte.
private LinkedList<Geo> geos;
// Zwei Eckpunkte der Boundingbox.
private Vector3D minValues;
private Vector3D maxValues;
// Die Boundingbox besteht aus sechs Vierecken.
private Bounding.Rectangle[] boundingBox;
/**
* Initialisiert die Boundingbox, welche noch keine geometrischen
* Objekte enthält.
*/
public Bounding() {
super();
this.geos = new LinkedList<Geo>();
this.minValues = Vector3D.ZERO;
this.maxValues = Vector3D.ZERO;
this.boundingBox = new Bounding.Rectangle[6];
this.updateBoundingBox();
}
/**
* Fügt der Boundingbox ein Geoobjekt hinzu.
* Die Ausmaße der Boundingbox passen sich der Größe des Geoobjekts an.
* @param geo Das Geoobjekt.
*/
public void addGeo(Geo geo) {
Vector3D geoMin = geo.getMinValues().copy();
Vector3D geoMax = geo.getMaxValues().copy();
if (this.geos.isEmpty()) {
this.minValues = geoMin;
this.maxValues = geoMax;
} else {
double minX = Math.min(this.minValues.getX(), geoMin.getX());
double minY = Math.min(this.minValues.getY(), geoMin.getY());
double minZ = Math.min(this.minValues.getZ(), geoMin.getZ());
this.minValues = new Vector3D(minX, minY, minZ);
double maxX = Math.max(this.maxValues.getX(), geoMax.getX());
double maxY = Math.max(this.maxValues.getY(), geoMax.getY());
double maxZ = Math.max(this.maxValues.getZ(), geoMax.getZ());
this.maxValues = new Vector3D(maxX, maxY, maxZ);
}
this.geos.add(geo);
this.updateBoundingBox();
}
/**
* Gibt einen Nullvektor zurück.
* Da die Boundingbox kein sichtbares Objekt ist,
* sondern nur ein Kontainer für andere Geoobjekte,
* so wird die Oberflächennormale zur Farbbestimmung nicht benötigt,
* weshalb auch der Nullvektor zurückgegeben wird.
*/
public Vector3D getNormal(Vector3D vector3D) {
return Vector3D.ZERO;
}
/**
* Gibt die Entfernung zurück, die ein Strahl zurücklegen muss,
* um die Boundingbox zu schneiden.
*/
public Geo.Distance getIntersect(Tinyray.Ray ray) {
boolean testGeos = false;
// Rentabilität prüfen
if (this.geos.size() <= 6) {
// Da die Boundingbox höchstens 6 Geo-Objekte umschließt,
// wäre der Hittest der Boundingbox aufwendiger als der
// Hittest all seiner Geo-Objekte.
testGeos = true;
} else {
// Prüfen, ob Boundingbox vom Ray getroffen wird.
for (int i = 0; i < this.boundingBox.length; i++) {
if (this.boundingBox[i].hitTest(ray)) {
testGeos = true;
break;
}
}
}
if (testGeos) {
double minDistance = Double.MAX_VALUE;
// Gewinner feststellen
Geo closest = null;
Iterator<Geo> iterator = this.geos.iterator();
while (iterator.hasNext()) {
Geo geo = iterator.next();
Geo.Distance geoDistance = geo.getIntersect(ray);
if (geoDistance != null) {
double distance = geoDistance.getDistance();
if ((0.0 < distance) && (distance < minDistance)) {
minDistance = distance;
closest = geoDistance.getGeo();
}
}
}
// Wenn ein Gewinner feststeht, dann wird die Distanz
// zu ihm zurückgegeben.
if (closest != null) {
return new Geo.Distance(closest, minDistance);
}
}
return null;
}
/**
* Gibt die Min-Werte der Boundingbox zurück.
*/
public Vector3D getMinValues() {
return this.minValues;
}
/**
* Gibt die Max-Werte der Boundingbox zurück.
*/
public Vector3D getMaxValues() {
return this.maxValues;
}
/**
* Gibt die Boundingbox als Tinyray-Code zurück.
*/
public String toString() {
String result = "Bounding {\n";
result += " // Min = " + this.minValues + "\n";
result += " // Max = " + this.maxValues + "\n";
Iterator<Geo> iterator = this.geos.iterator();
while (iterator.hasNext()) {
result += " " + iterator.next() + "\n";
}
result += " }";
return result;
}
// Aktualisiert die Boundingbox mit Hilfe der Min- und Max-Werte.
private void updateBoundingBox() {
Vector3D c1 = new Vector3D(this.minValues.getX(),
this.minValues.getY(), this.maxValues.getZ());
Vector3D c2 = new Vector3D(this.maxValues.getX(),
this.minValues.getY(), this.maxValues.getZ());
Vector3D c3 = this.maxValues.copy();
Vector3D c4 = new Vector3D(this.minValues.getX(),
this.maxValues.getY(), this.maxValues.getZ());
Vector3D c5 = this.minValues.copy();
Vector3D c6 = new Vector3D(this.maxValues.getX(),
this.minValues.getY(), this.minValues.getZ());
Vector3D c7 = new Vector3D(this.maxValues.getX(),
this.maxValues.getY(), this.minValues.getZ());
Vector3D c8 = new Vector3D(this.minValues.getX(),
this.maxValues.getY(), this.minValues.getZ());
this.boundingBox[0] = new Bounding.Rectangle(c1, c4, c2); // front
this.boundingBox[1] = new Bounding.Rectangle(c6, c7, c5); // back
this.boundingBox[2] = new Bounding.Rectangle(c4, c8, c3); // top
this.boundingBox[3] = new Bounding.Rectangle(c1, c5, c2); // bottom
this.boundingBox[4] = new Bounding.Rectangle(c1, c4, c5); // left
this.boundingBox[5] = new Bounding.Rectangle(c2, c3, c6); // right
}
// Die Klasse Rectangle repräsentiert eine rechtwicklige Fläche
// der Boundingbox.
private final class Rectangle {
// Drei Eckpunkte der Fläche.
private Vector3D x;
private Vector3D y;
private Vector3D z;
// Zwei unabhänige Richtungsvektoren der Fläche.
private Vector3D yx;
private Vector3D zx;
// Initialisiert die Fläche mit drei Eckpunkten.
private Rectangle(Vector3D x, Vector3D y, Vector3D z) {
this.x = x.copy();
this.y = y.copy();
this.z = z.copy();
this.yx = Vector3D.sub(this.x, this.y);
this.zx = Vector3D.sub(this.x, this.z);
}
// Der Schnittpunkttest zur Fläche.
private boolean hitTest(Tinyray.Ray ray) {
Vector3D a = this.yx;
Vector3D b = this.zx;
Vector3D c = ray.getDirection();
Vector3D p = Vector3D.sub(this.x, ray.getOrigin());
double det1 = Vector3D.det(p, b, c);
double det2 = Vector3D.det(a, p, c);
double detN = Vector3D.det(a, b, c);
if (detN == 0.0) {
return false;
} else {
double beta = det1 / detN;
double gamma = det2 / detN;
return (0.0 < beta)
&& (0.0 < gamma)
&& (beta < 1.0)
&& (gamma < 1.0);
}
}
}
}
package tinyray.raytracer;
/**
* Die Klasse Camera repräsentiert eine Kamera, die die Szene von einer
* bestimmten Position aus betrachtet.
*/
public final class Camera extends Element {
// Die Position der Kamera.
private Vector3D location;
// Die Kamera ist auf den Punkt lookAt ausgerichtet.
private Vector3D lookAt;
// Wo ist oben? up ist die Antwort.
private Vector3D up;
/**
* Initialisiert die Kamera.
*/
public Camera() {
super();
this.location = new Vector3D(0.0, 0.0, 10.0);
this.lookAt = new Vector3D(0.0);
this.up = new Vector3D(0.0, 1.0, 0.0);
}
/**
* Gibt die Position der Kamera zurück.
* @return Die Position.
*/
public Vector3D getLocation() {
return this.location;
}
/**
* Setzt die Position der Kamera.
* @param location Die Position.
*/
public void setLocation(Vector3D location) {
this.location = location.copy();
}
/**
* Gibt den Punkt zurück, auf den die Kamera gerichtet ist.
* Dieser Punkt liegt genau in der Mitte des resultierenden Bildes.
* @return Der Punkt.
*/
public Vector3D getLookAt() {
return this.lookAt;
}
/**
* Setzt den Punkt, auf den die Kamera zeigt.
* @param lookAt Der Punkt.
*/
public void setLookAt(Vector3D lookAt) {
this.lookAt = lookAt.copy();
}
/**
* Gibt den Up-Vektor zurück.
* @return Der Up-Vektor.
*/
public Vector3D getUp() {
return this.up;
}
/**
* Setzt den Up-Vektor.
* @param up Der Up-Vektor.
*/
public void setUp(Vector3D up) {
this.up = up.copy();
}
/**
* Gibt die Kamera als Tinyray-Code zurück.
*/
public String toString() {
return "Camera { " + this.location + "; " + this.lookAt
+ "; " + this.up + " }";
}
}
package tinyray.raytracer;
/**
* Die Klasse Background repräsentiert die Leinwand im Hintergrund der Szene.
*/
public final class Background extends Element {
// Die Hintergrundfarbe.
private Vector3D color;
/**
* Initialisiert die Leinwand mit einer Farbe.
* @param color Die Hintergrundfarbe.
*/
public Background(Vector3D color) {
super();
this.color = color.copy();
}
/**
* Gibt die Hintergrundfarbe als Vector zurück.
* @return Die Hintergrundfarbe.
*/
public Vector3D getColor() {
return this.color;
}
/**
* Gibt das Leinwand als Tinyray-Code zurück.
*/
public String toString() {
return "Background { " + this.color + " }";
}
}
package tinyray.raytracer;
/**
* Die Klasse Ambience repräsentiert das globale Umgebungslicht,
* welches alle Szenenobjekte gleichermaßen beleuchtet.
*/
public final class Ambience extends Element {
// Die Farbe des Umgebungslichtes.
private Vector3D color;
/**
* Initialisiert das Umgebungslicht mit einer Farbe.
* @param color Die Farbe des Umgebungslichtes.
*/
public Ambience(Vector3D color) {
super();
this.color = color.copy();
}
/**
* Gibt die Farbe des Umgebungslichtes zurück.
* @return Die ambiente Farbe.
*/
public Vector3D getColor() {
return this.color;
}
/**
* Gibt das Umgebungslicht als Tinyray-Code zurück.
*/
public String toString() {
return "Ambience { " + this.color + " }";
}
}
package tinyray.raytracer;
/**
* Die Klasse Fog repräsentiert den Nebel der Szene.
*/
public final class Fog extends Element {
// Die Nebelfarbe.
private Vector3D color;
// Die Nebeldichte.
private double density;
/**
* Initialisiert den Nebel mit einer Farbe und einer Dichte.
* @param color Die Nebelfarbe.
* @param density Die Nebeldichte.
*/
public Fog(Vector3D color, double density) {
super();
this.color = color.copy();
this.density = density;
}
/**
* Gibt die Farbe des Nebels zurück.
* @return Die Nebelfarbe.
*/
public Vector3D getColor() {
return this.color;
}
/**
* Gibt die Dichte des Nebels zurück.
* @return Die Nebeldichte.
*/
public double getDensity() {
return this.density;
}
/**
* Gibt den Nebel als Tinyray-Code zurück.
*/
public String toString() {
return "Fog { " + this.color + "; " + this.density + " }";
}
}
package tinyray.raytracer;
/**
* Die Klasse Parameters repräsentiert die allgemeinen Parameter
* eines geometrischen Objekts wie zum Beispiel die Farbwerte.
*/
public final class Parameters extends Element {
// Die vom Licht abhängige, diffuse Farbe.
private Vector3D color;
// Die vom Licht unabhängige, ambiente Farbe.
private Vector3D ambient;
// Die Materialkonstante für die Lichtreflektion (Phong).
private double specular;
// Die Oberflächenbeschaffenheit für die Lichtreflektion (Phong).
private double shininess;
// Die Reflektionskonstante.
private double mirror;
/**
* Initialisiert die allgemeinen Parameter eines GeoObjekts.
*/
public Parameters() {
super();
this.color = new Vector3D(0.5);
this.ambient = new Vector3D(0.1);
this.specular = 0.5;
this.shininess = 1000.0;
this.mirror = 0.4;
}
/**
* Klont die Parameter.
* @return Der Klon.
*/
public Parameters copy() {
Parameters result = new Parameters();
result.color = this.color.copy();
result.ambient = this.ambient.copy();
result.specular = this.specular;
result.shininess = this.shininess;
result.mirror = this.mirror;
return result;
}
/**
* Gibt die lichtabhängige, diffuse Farbe zurück.
* @return Die diffuse Farbe.
*/
public Vector3D getColor() {
return this.color;
}
/**
* Setzt die lichtabhängige, diffuse Farbe.
* @param color Die diffuse Farbe.
*/
public void setColor(Vector3D color) {
this.color = color.copy();
}
/**
* Gibt die lichtabhängige, ambiente Farbe zurück.
* @return Die ambiente Farbe.
*/
public Vector3D getAmbient() {
return this.ambient;
}
/**
* Setzt die lichtunabhängige, ambiente Farbe.
* @param ambient Die ambiente Farbe.
*/
public void setAmbient(Vector3D ambient) {
this.ambient = ambient.copy();
}
/**
* Gibt die Materialkonstante für die Lichtreflektion zurück.
* Dieser Parameter wird benötigt, um das Phong-Lighting realisieren
* zu können.
* @return Die Materialkonstante.
*/
public double getSpecular() {
return this.specular;
}
/**
* Setzt die Materialkonstante für die Lichtreflektion.
* @param specular Die Materialkonstante.
*/
public void setSpecular(double specular) {
this.specular = specular;
}
/**
* Gibt die Oberflächenbeschaffenheit für die Lichtreflektion zurück.
* Dieser Parameter wird benötigt, um das Phong-Lighting realisieren
* zu können.
* @return Die Oberflächenbeschaffenheit.
*/
public double getShininess() {
return this.shininess;
}
/**
* Setzt die Oberflächenbeschaffenheit für die Lichtreflektion.
* Ist der Wert kleiner als 32, dann ist das dazugehörige Geoobjekt rau,
* ist er größer oder gleich 32, dann glatt.
* Je größer der Wert, desto perfekter die Spiegelwirkung (nur im Bezug
* auf die Lichtreflektion).
* @param shininess Die Oberflächenbeschaffenheit.
*/
public void setShininess(double shininess) {
this.shininess = shininess;
}
/**
* Gibt die Reflektionskonstante zurück.
* Dieser Wert ist verantwortlich dafür, wie stark sich andere Geoobjekte
* in diesem Geoobjekt spiegeln können.
* @return Die Reflektionskonstante.
*/
public double getMirror() {
return this.mirror;
}
/**
* Setzt die Reflektionskonstante.
* @param mirror Die Reflektionskonstante.
*/
public void setMirror(double mirror) {
this.mirror = mirror;
}
/**
* Gibt die Parameter als Tinyray-Code zurück.
*/
public String toString() {
String result = "";
result += this.getColor() + "; ";
result += this.getAmbient() + "; ";
result += this.getSpecular() + "; ";
result += this.getShininess() + "; ";
result += this.getMirror();
return result;
}
}
package tinyray.raytracer;
/**
* Die Klasse Sun repräsentiert eine Sonne, also eine Lichtquelle,
* die sich in unendlich weiter Entfernung zur Szene befindet.
* Die "Sonne" wird auf der Erde als parallele Lichtquelle empfunden.
*/
public final class Sun extends Element {
// Die Richtung, in der sich die Sonne befindet.
private Vector3D direction;
// Die Farbe der Sonne.
private Vector3D color;
/**
* Initialisiert die Sonne mit der Richtung, in der sie sich befindet
* und mit der Sonnenfarbe.
* @param direction Die Richtung zur Sonne.
* @param color Die Sonnenfarbe.
*/
public Sun(Vector3D direction, Vector3D color) {
super();
this.direction = direction.getNormalized();
this.color = color.copy();
}
/**
* Gibt die Richtung, in der sich die Sonne befindet, zurück.
* @return Die Richtung zur Sonne.
*/
public Vector3D getDirection() {
return this.direction;
}
/**
* Gibt die Farbe der Sonne als Vektor zurück.
* @return Die Farbe.
*/
public Vector3D getColor() {
return this.color;
}
/**
* Gibt die Sonne als Tinyray-Code zurück.
*/
public String toString() {
return "Sun { " + this.direction + "; " + this.color + " }";
}
}
package tinyray.raytracer;
/**
* Die Klasse Vector3D repräsentiert einen Vektor im dreidimensionalen Raum.
* Der Grundkörper des Vektors (Skalar) ist vom Typ double.
*/
public final class Vector3D extends Element {
/**
* Der maximal tolerierbare Fehler zum Beispiel
* beim Vergleich von Skalarwerten.
*/
public final static double EPSILON = 0.00000000001;
/**
* Der Nullvektor.
*/
public final static Vector3D ZERO = new Vector3D(0.0);
/**
* Der Basisvektor in X-Richtung.
*/
public final static Vector3D X = new Vector3D(1.0, 0.0, 0.0);
/**
* Der Basisvektor in Y-Richtung.
*/
public final static Vector3D Y = new Vector3D(0.0, 1.0, 0.0);
/**
* Der Basisvektor in Z-Richtung.
*/
public final static Vector3D Z = new Vector3D(0.0, 0.0, 1.0);
// Die X-Komponente.
private double x;
// Die Y-Komponente.
private double y;
// Die Z-Komponente.
private double z;
/**
* Initialisiert den Vektor mit den drei Komponenten.
* @param x Die X-Komponente.
* @param y Die Y-Komponente.
* @param z Die Z-Komponente.
*/
public Vector3D(double x, double y, double z) {
super();
this.x = x;
this.y = y;
this.z = z;
}
/**
* Initialisiert die drei Komponenten des Vektors mit einem Skalarwert.
* @param scalar Der Skalarwert.
*/
public Vector3D(double scalar) {
this(scalar, scalar, scalar);
}
/**
* Initialisiert den Vektor mit den Komponenten gegebenen Vektors.
* @param vector Der gegebene Vektor.
*/
public Vector3D(Vector3D vector) {
this(vector.x, vector.y, vector.z);
}
/**
* Klont den Vektor.
* @return Der Klon.
*/
public Vector3D copy() {
return new Vector3D(this.x, this.y, this.z);
}
/**
* Gibt die X-Komponente des Vektors zurück.
* @return Die X-Komponente.
*/
public double getX() {
return this.x;
}
/**
* Gibt die Y-Komponente des Vektors zurück.
* @return Die Y-Komponente.
*/
public double getY() {
return this.y;
}
/**
* Gibt die Z-Komponente des Vektors zurück.
* @return Die Z-Komponente.
*/
public double getZ() {
return this.z;
}
/**
* Gibt die Länge des Vektors zurück.
* @return Die Länge des Vektors.
*/
public double getMagnitude() {
return Math.sqrt(Vector3D.dot(this, this));
}
/**
* Gibt eine Kopie, welche auf die Länge 1 normiert ist, zurück.
* @return Die normalisierte Kopie.
*/
public Vector3D getNormalized() {
double magnitude = this.getMagnitude();
if (magnitude > Vector3D.EPSILON) {
return new Vector3D(
this.x / magnitude,
this.y / magnitude,
this.z / magnitude);
} else {
return Vector3D.ZERO;
}
}
/**
* Berechnet den Reflektionsvektor bezüglich einer gegebenen Normalen.
* @param n Die Normale.
* @return Der Reflektionsvektor.
*/
public Vector3D getReflectedAt(Vector3D n) {
return Vector3D.sub(
this,
Vector3D.mult(2.0 * Vector3D.dot(n, this), n));
}
/**
* Gibt den Vektor als Tinyray-Code zurück.
*/
public String toString() {
return "[" + this.x + ", " + this.y + ", " + this.z + "]";
}
/**
* Berechnet die Determinante von drei Spaltenvektoren (3x3-Matrix).
* @param a Spaltenvektor.
* @param b Spaltenvektor.
* @param c Spaltenvektor.
* @return Die Determinante.
*/
public static double det(Vector3D a, Vector3D b, Vector3D c) {
return
+ a.x * b.y * c.z
+ b.x * c.y * a.z
+ c.x * a.y * b.z
- c.x * b.y * a.z
- b.x * a.y * c.z
- a.x * c.y * b.z;
}
/**
* Berechnet das Skalarprodukt zwischen zwei Vektoren.
* @param v1 Vektor.
* @param v2 Vektor.
* @return Das Skalarprodukt.
*/
public static double dot(Vector3D v1, Vector3D v2) {
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}
/**
* Berechnet das Kreuzprodukt zwischen zwei Vektoren.
* @param v1 Vektor.
* @param v2 Vektor.
* @return Das Kreuzprodukt.
*/
public static Vector3D cross(Vector3D v1, Vector3D v2) {
return new Vector3D(
v1.y * v2.z - v1.z * v2.y,
v1.z * v2.x - v1.x * v2.z,
v1.x * v2.y - v1.y * v2.x);
}
/**
* Berechnet die Summe zweier Vektoren.
* @param v1 Vektor..
* @param v2 Vektor.
* @return Die Summe.
*/
public static Vector3D add(Vector3D v1, Vector3D v2) {
return new Vector3D(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
}
/**
* Berechnet die Differenz zweier Vektoren.
* @param v1 Der Vektor als Minuend.
* @param v2 Der Vektor als Subtrahend.
* @return Die Differenz.
*/
public static Vector3D sub(Vector3D v1, Vector3D v2) {
return new Vector3D(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
}
/**
* Berechnet das Produkt zwischen Skalar und Vektor.
* @param scalar Der Skalarwert.
* @param v Der Vektor.
* @return Das Produkt.
*/
public static Vector3D mult(double scalar, Vector3D v) {
return new Vector3D(scalar * v.x, scalar * v.y, scalar * v.z);
}
/**
* Berechnet das Vektorprodukt zweier Vektoren.
* Das Vektorprodukt ist die komponentenweise Multiplikation
* aller beteiligten Vektoren.
* @param v1 Vektor.
* @param v2 Vektor.
* @return Das Vektorprodukt.
*/
public static Vector3D mult(Vector3D v1, Vector3D v2) {
return new Vector3D(v1.x * v2.x, v1.y * v2.y, v1.z * v2.z);
}
}
package tinyray.raytracer;
/**
* Die Klasse Real repräsentiert eine Fließkommazahl.
*/
public final class Real extends Element {
// Die Fließkommazahl.
private double value;
/**
* Initialisiert die Fließkommazahl mit einem Double-Wert.
* @param value Der Double-Wert.
*/
public Real(double value) {
super();
this.value = value;
}
/**
* Gibt die Fließkommazahl als Double-Wert zurück.
* @return Die Fließkommazahl.
*/
public double getValue() {
return this.value;
}
/**
* Gibt die Fließkommazahl als Tinyray-Code zurück.
*/
public String toString() {
return "" + this.value;
}
}