Schwarz = new Vector3D(0.0, 0.0, 0.0);
Weiß = new Vector3D(1.0, 1.0, 1.0);
Rot = new Vector3D(1.0, 0.0, 0.0);
Grün = new Vector3D(0.0, 1.0, 0.0);
Blau = new Vector3D(0.0, 0.0, 1.0);
Gelb = new Vector3D(1.0, 1.0, 0.0);
pixels[0 .. width-1][0 .. height-1].
1.
Das
Pixel[0][0]
befindet sich links oben im Viewport.
Der Zugriff auf die Pixel ist bereichsgesichert.
Liegt ein angegebenes Pixel außerhalb des Bildbereichs, so führt dies nicht zum Fehler bzw. Programmabsturz.
new Vector3D(2.0, 3.0, 9.0).
package tinyray.raytracer;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import javax.imageio.ImageIO;
/**
* Die Klasse Viewport repräsentiert den visuellen Bereich, der zum Beispiel
* nach dem Rendervorgang das Bild darstellt.
*/
public class Viewport {
/**
* Die Bildelemente als zweidimensionale Matrix. Jedes Bildelement wird
* dargestellt durch einen dreidimensionalen Farbvektor.
*/
protected Vector3D[][] pixels;
/**
* Initialisiert das Bild mit der Höhe und der Breite.
* @param width Die Breite.
* @param height Die Höhe.
*/
public Viewport(int width, int height) {
this.pixels = new Vector3D[Math.max(1, width)][Math.max(1, height)];
// Alle Pixel auf Schwarz setzen.
for (int y = 0; y < this.getHeight(); y++) {
for (int x = 0; x < this.getWidth(); x++) {
this.pixels[x][y] = Vector3D.ZERO;
}
}
}
/**
* Gibt die Breite des Bildes zurück.
* @return Die Breite.
*/
public int getWidth() {
return this.pixels.length;
}
/**
* Gibt die Höhe des Bildes zurück.
* @return Die Höhe.
*/
public int getHeight() {
return this.pixels[0].length;
}
/**
* Prüft, ob sich die Koordinate (x, y) innerhalb des Bildes befindet.
* @param x Die x-Komponente.
* @param y Die y-Komponente.
* @return true, falls ja.
*/
public boolean isInViewport(int x, int y) {
return (0 <= x) && (x < this.getWidth())
&& (0 <= y) && (y < this.getHeight());
}
/**
* Gibt ein durch die Koordinate (x, y) bestimmtes Bildelement
* als Farbvektor zurück.
* Falls die Koordinate (x, y) nicht im Bereich des Bildes liegen sollte,
* ist der Farbvektor der Nullvektor (schwarz).
* @param x Die x-Komponente.
* @param y Die y-Komponente.
* @return Der Farbvektor.
*/
public Vector3D getPixel(int x, int y) {
if (this.isInViewport(x, y)) {
return this.pixels[x][y];
} else {
return Vector3D.ZERO;
}
}
/**
* Setzt ein durch die Koordinate (x, y) bestimmtes Bildelement.
* @param x Die x-Komponente.
* @param y Die y-Komponente.
* @param color Das Bildelement als Farbvektor.
*/
public void setPixel(int x, int y, Vector3D color) {
if (this.isInViewport(x, y)) {
this.pixels[x][y] = color;
}
}
/**
* Speichert das Bild als ASCII-PPM-Format in eine Datei.
* @param filename Der Name der Datei.
* @throws IOException Wird geworfen, wenn sich ein I/O-Fehler ereignet.
*/
public void storeAsAsciiPPM(String filename) throws IOException {
FileOutputStream file = new FileOutputStream(filename);
BufferedOutputStream buffered = new BufferedOutputStream(file);
this.storeAsAsciiPPM(buffered);
buffered.flush();
file.close();
}
/**
* Speichert das Bild als binäres PPM-Format in eine Datei.
* @param filename Der Name der Datei.
* @throws IOException Wird geworfen, wenn sich ein I/O-Fehler ereignet.
*/
public void storeAsBinaryPPM(String filename) throws IOException {
FileOutputStream file = new FileOutputStream(filename);
BufferedOutputStream buffered = new BufferedOutputStream(file);
this.storeAsBinaryPPM(buffered);
buffered.flush();
file.close();
}
/**
* Speichert das Bild als RAW-Format in eine Datei.
* @param filename Der Name der Datei.
* @throws IOException Wird geworfen, wenn sich ein I/O-Fehler ereignet.
*/
public void storeAsRAW(String filename) throws IOException {
FileOutputStream file = new FileOutputStream(filename);
BufferedOutputStream buffered = new BufferedOutputStream(file);
this.storeAsRAW(buffered);
buffered.flush();
file.close();
}
/**
* Speichert das Bild als BMP-Format in eine Datei.
* @param filename Der Name der Datei.
* @throws IOException Wird geworfen, wenn sich ein I/O-Fehler ereignet.
*/
public void storeAsBMP(String filename) throws IOException {
FileOutputStream file = new FileOutputStream(filename);
BufferedOutputStream buffered = new BufferedOutputStream(file);
this.storeAsBMP(buffered);
buffered.flush();
file.close();
}
/**
* Speichert das Bild als PNG-Format in eine Datei.
* @param filename Der Name der Datei.
* @throws IOException Wird geworfen, wenn sich ein I/O-Fehler ereignet.
*/
public void storeAsPNG(String filename) throws IOException {
FileOutputStream file = new FileOutputStream(filename);
BufferedOutputStream buffered = new BufferedOutputStream(file);
this.storeAsPNG(buffered);
buffered.flush();
file.close();
}
/**
* Speichert das Bild als JPG-Format in eine Datei.
* @param filename Der Name der Datei.
* @throws IOException Wird geworfen, wenn sich ein I/O-Fehler ereignet.
*/
public void storeAsJPG(String filename) throws IOException {
FileOutputStream file = new FileOutputStream(filename);
BufferedOutputStream buffered = new BufferedOutputStream(file);
this.storeAsJPG(buffered);
buffered.flush();
file.close();
}
/**
* Speichert das Bild als ASCII-PPM-Format in einen Ausgabestrom.
* @param output Der Ausgabestrom.
* @throws IOException Wird geworfen, wenn sich ein I/O-Fehler ereignet.
*/
public void storeAsAsciiPPM(OutputStream output) throws IOException {
PrintStream printStream = new PrintStream(output);
// Header
printStream.print("P3\n");
printStream.print(this.getWidth() + " " + this.getHeight() + "\n");
printStream.print("255\n");
// Bildpixel
for (int y = 0; y < this.getHeight(); y++) {
for (int x = 0; x < this.getWidth(); x++) {
Vector3D pixel = this.pixels[x][y];
printStream.print(" " + (red(pixel) & 0xFF));
printStream.print(" " + (green(pixel) & 0xFF));
printStream.print(" " + (blue(pixel) & 0xFF));
}
printStream.print("\n");
}
}
/**
* Speichert das Bild als binäres PPM-Format in einen Ausgabestrom.
* @param output Der Ausgabestrom.
* @throws IOException Wird geworfen, wenn sich ein I/O-Fehler ereignet.
*/
public void storeAsBinaryPPM(OutputStream output) throws IOException {
PrintStream printStream = new PrintStream(output);
// Header
printStream.print("P6\n");
printStream.print(this.getWidth() + " " + this.getHeight() + "\n");
printStream.print("255\n");
// Bildpixel
for (int y = 0; y < this.getHeight(); y++) {
for (int x = 0; x < this.getWidth(); x++) {
printStream.write(rgb(this.pixels[x][y]));
}
}
}
/**
* Speichert das Bild als RAW-Format in einen Ausgabestrom.
* @param output Der Ausgabestrom.
* @throws IOException Wird geworfen, wenn sich ein I/O-Fehler ereignet.
*/
public void storeAsRAW(OutputStream output) throws IOException {
PrintStream printStream = new PrintStream(output);
// Nur Bildpixel, keine Headerangaben!
for (int y = 0; y < this.getHeight(); y++) {
for (int x = 0; x < this.getWidth(); x++) {
printStream.write(rgb(this.pixels[x][y]));
}
}
}
/**
* Speichert das Bild als BMP-Format in einen Ausgabestrom.
* @param output Der Ausgabestrom.
* @throws IOException Wird geworfen, wenn sich ein I/O-Fehler ereignet.
*/
public void storeAsBMP(OutputStream output) throws IOException {
PrintStream printStream = new PrintStream(output);
// Header
printStream.print("BM");
long imgsize = 4;
imgsize *= this.getHeight();
imgsize *= ((3 * 8 * this.getWidth() + 31) / 32);
long size = 14 + 40 + imgsize;
for (int shift = 0; shift < 64; shift += 8) {
printStream.write((int)((size >> shift) & 0xFF));
}
printStream.write((int)(14 + 40));
printStream.write((int)0x00);
printStream.write((int)0x00);
printStream.write((int)0x00);
printStream.write((int)(40));
printStream.write((int)0x00);
printStream.write((int)0x00);
printStream.write((int)0x00);
for (int shift = 0; shift < 32; shift += 8) {
printStream.write((int)((this.getWidth() >> shift) & 0xFF));
}
for (int shift = 0; shift < 32; shift += 8) {
printStream.write((int)((this.getHeight() >> shift) & 0xFF));
}
printStream.write((int)(1));
printStream.write((int)0x00);
printStream.write((int)(24));
printStream.write((int)0x00);
// BI_RGB
printStream.write((int)0x00);
printStream.write((int)0x00);
printStream.write((int)0x00);
printStream.write((int)0x00);
for (int shift = 0; shift < 64; shift += 8) {
printStream.write((int)((imgsize >> shift) & 0xFF));
}
printStream.write((int)0x00);
printStream.write((int)0x00);
printStream.write((int)0x00);
printStream.write((int)0x00);
printStream.write((int)0x00);
printStream.write((int)0x00);
printStream.write((int)0x00);
printStream.write((int)0x00);
printStream.write((int)0x00);
printStream.write((int)0x00);
printStream.write((int)0x00);
printStream.write((int)0x00);
// Bildpixel
for (int y = this.getHeight() - 1; y >= 0; y--) {
for (int x = 0; x < this.getWidth(); x++) {
printStream.write(bgr(this.pixels[x][y]));
}
// Zeilenumbruch
int pad = (4 - ((3 * this.getWidth()) % 4)) & 0x03;
for (int i = 0; i < pad; i++) {
printStream.write((int)0x00);
}
}
}
/**
* Speichert das Bild als PNG-Format in einen Ausgabestrom.
* @param output Der Ausgabestrom.
* @throws IOException Wird geworfen, wenn sich ein I/O-Fehler ereignet.
*/
public void storeAsPNG(OutputStream output) throws IOException {
BufferedImage bi = new BufferedImage(
this.getWidth(),
this.getHeight(),
BufferedImage.TYPE_INT_RGB);
// Nur Bildpixel, keine Headerangaben!
for (int y = 0; y < this.getHeight(); y++) {
for (int x = 0; x < this.getWidth(); x++) {
bi.setRGB(x, y, RGB(this.pixels[x][y]));
}
}
ImageIO.write(bi, "png", output);
}
/**
* Speichert das Bild als JPG-Format in einen Ausgabestrom.
* @param output Der Ausgabestrom.
* @throws IOException Wird geworfen, wenn sich ein I/O-Fehler ereignet.
*/
public void storeAsJPG(OutputStream output) throws IOException {
BufferedImage bi = new BufferedImage(
this.getWidth(),
this.getHeight(),
BufferedImage.TYPE_INT_RGB);
// Nur Bildpixel, keine Headerangaben!
for (int y = 0; y < this.getHeight(); y++) {
for (int x = 0; x < this.getWidth(); x++) {
bi.setRGB(x, y, RGB(this.pixels[x][y]));
}
}
ImageIO.write(bi, "jpg", output);
}
/* Hilfsmethoden */
/**
* Rechnet einen Double-Wert in einen 8-Bit-Wert um.
* @param value Der Double-Wert.
* @return Der 8-Bit-Wert.
*/
private static byte component(double value) {
if (value <= 0.0) {
return (byte)0;
} else if (value >= 1.0) {
return (byte)255;
} else {
return (byte)(255 * value);
}
}
/**
* Berechnet von einem Farbvektor den 8-Bit-Rot-Wert.
* @param color Der Farbvektor.
* @return Der 8-Bit-Rot-Wert.
*/
public static byte red(Vector3D color) {
return component(color.getX());
}
/**
* Berechnet von einem Farbvektor den 8-Bit-Grün-Wert.
* @param color Der Farbvektor.
* @return Der 8-Bit-Grün-Wert.
*/
public static byte green(Vector3D color) {
return component(color.getY());
}
/**
* Berechnet von einem Farbvektor den 8-Bit-Blau-Wert.
* @param color Der Farbvektor.
* @return Der 8-Bit-Blau-Wert.
*/
public static byte blue(Vector3D color) {
return component(color.getZ());
}
/**
* Berechnet von einem Farbvektor die Rot-Grün-Blau-Werte.
* @param color Der Farbvektor.
* @return Die Rot-Grün-Blau-Werte.
*/
public static byte[] rgb(Vector3D color) {
byte[] result = new byte[3];
result[0] = red(color);
result[1] = green(color);
result[2] = blue(color);
return result;
}
/**
* Berechnet von einem Farbvektor den Rot-Grün-Blau-Wert.
* @param color Der Farbvektor.
* @return Der Rot-Grün-Blau-Wert.
*/
public static int RGB(Vector3D color) {
return ((red(color) & 0xFF) << 16)
| ((green(color) & 0xFF) << 8)
| (blue(color) & 0xFF);
}
/**
* Berechnet von einem Farbvektor die Blau-Grün-Rot-Werte.
* @param color Der Farbvektor.
* @return Die Blau-Grün-Rot-Werte.
*/
public static byte[] bgr(Vector3D color) {
byte[] result = new byte[3];
result[0] = blue(color);
result[1] = green(color);
result[2] = red(color);
return result;
}
/**
* Berechnet von einem Farbvektor eine Color-Instanz.
* @param color Der Farbvektor.
* @return Die Color-Instanz.
*/
public static Color color(Vector3D color) {
return new Color(
red(color) & 0xFF,
green(color) & 0xFF,
blue(color) & 0xFF);
}
}
package tinyray.examples;
import java.io.IOException;
import tinyray.raytracer.Vector3D;
import tinyray.raytracer.Viewport;
public class Plus {
public static void main(String[] arguments) {
// 7x7 Rasterbild erzeugen.
Viewport viewport = new Viewport(7, 7);
// Horizontalen Strich zeichnen.
viewport.setPixel(1, 3, new Vector3D(1.0, 1.0, 1.0));
viewport.setPixel(2, 3, new Vector3D(1.0, 1.0, 1.0));
viewport.setPixel(3, 3, new Vector3D(1.0, 1.0, 1.0));
viewport.setPixel(4, 3, new Vector3D(1.0, 1.0, 1.0));
viewport.setPixel(5, 3, new Vector3D(1.0, 1.0, 1.0));
// Vertikalen Strich zeichnen.
viewport.setPixel(3, 1, new Vector3D(1.0, 1.0, 1.0));
viewport.setPixel(3, 2, new Vector3D(1.0, 1.0, 1.0));
viewport.setPixel(3, 3, new Vector3D(1.0, 1.0, 1.0));
viewport.setPixel(3, 4, new Vector3D(1.0, 1.0, 1.0));
viewport.setPixel(3, 5, new Vector3D(1.0, 1.0, 1.0));
try {
// Pluszeichen in den verfügbaren Formaten speichern.
viewport.storeAsPNG("plus.png");
viewport.storeAsJPG("plus.jpg");
viewport.storeAsAsciiPPM("plus.ascii.ppm");
viewport.storeAsBinaryPPM("plus.binary.ppm");
viewport.storeAsRAW("plus.raw");
viewport.storeAsBMP("plus.bmp");
System.out.println("Plus wurde in 6 Formate gespeichert!");
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
}


P3
7 7
255
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 255 255 255 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 255 255 255 0 0 0 0 0 0 0 0 0
0 0 0 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 0 0 0
0 0 0 0 0 0 0 0 0 255 255 255 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 255 255 255 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
| Zeile | 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 | Text |
|
000 016 032 048 064 080 096 112 128 144 |
50 36 0A 37 20 37 0A 32 35 35 0A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
P6·7 7·255······ ················ ·········•••···· ··············•• •············••• ••••••••••••···· ········•••····· ·············••• ················ ·············· |
| Zeile | 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 | Text |
|
000 016 032 048 064 080 096 112 128 144 |
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
················ ··············•• •··············· ···•••·········· ··•••••••••••••• •············••• ················ ··•••··········· ················ ··· |
| Zeile | 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 | Text |
|
000 016 032 048 064 080 096 112 128 144 160 176 192 208 |
42 4D DE 00 00 00 00 00 00 00 36 00 00 00 28 00 00 00 07 00 00 00 07 00 00 00 01 00 18 00 00 00 00 00 A8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
BM•·······6···(· ················ ··•············· ················ ················ ·······•••······ ···············• ••·············· ·••••••••••••••• ···············• ••·············· ·······•••······ ················ ·············· |
package tinyray.examples;
import java.io.IOException;
import tinyray.raytracer.Vector3D;
import tinyray.raytracer.Viewport;
public class Deutschland {
/* Die drei Farben definieren. */
private static Vector3D SCHWARZ = Vector3D.ZERO;
private static Vector3D ROT = Vector3D.X;
private static Vector3D GOLD = new Vector3D(1.0, 1.0, 0.0);
/* Hauptprogramm */
public static void main(String[] arguments) {
// 50x30 Rasterbild erzeugen.
Viewport viewport = new Viewport(50, 3 * 10);
// Farben der Deutschlandfahne richtig plazieren.
for (int x = 0; x < 50; x++) {
for (int y = 0; y < 10; y++) {
viewport.setPixel(x, y, SCHWARZ);
}
for (int y = 10; y < 20; y++) {
viewport.setPixel(x, y, ROT);
}
for (int y = 20; y < 30; y++) {
viewport.setPixel(x, y, GOLD);
}
}
try {
// Deutschlandfahne speichern.
viewport.storeAsJPG("deutschland.jpg");
System.out.println("Fahne 'deutschland.jpg' wurde gespeichert!");
} catch (IOException e) {
// Falls Probleme beim Speichern :-(
System.err.println(e.getMessage());
}
}
}
