Shadowart
Shadowart Status: stable | |
---|---|
Beschreibung | Silhouetten Schablonen für Schattenbild |
Autor: | spezi ptflea |
Version | 1 |
PayPal |
Beschreibung
Als Idee für die Bamberger Lichthöfe 2013 haben wir, inspiriert durch die Arbeiten von Kumo Yamashita, versucht diverse schattenwerfende Objekte zu basteln. Dabei wurde eine Silhouette entsprechend der Lichtquelle angepasst, damit im Schatten das Ausgangssbild rekonstruiert wird. Die verzerrten Konturen wurden für die Lichthöfe aus Sperrholz mit der CNC Fräse ausgefräst.
Erstellung des Schattenobjekts
Erste Versuche
Wir haben uns der Verzerrung mit dem Versuch und Irrtumprinzip genähert.
Zuerst mussten wir feststellen, dass eine Stauchung nicht ausreicht, sondern zusätzlich eine Trapezverzerrung notwendig ist. Weiterhin ist die Stauchung nicht linear, sondern wird nach oben stärker.
Mit Gimp (gimp-tool-perspective) lässt sich eine Trapezverzerrung mit nichtlinearer Stauchung erstellen. Danach wurde das Bild mit Inkscape (tutorial-tracing) in eine Vektorgrafik umgewandelt und noch stärker gestaucht.
Die fertigen Vektordaten wurden dann mit der CNC Fräse aus 4mm Pappelsperrholz ausgefräst.
Verbesserung der Genauigkeit
Das beschriebene Vorgehen hat zwar zu brauchbaren Ergebnissen geführt, ist jedoch umständlich zu handhaben und es ist nicht möglich genau zu arbeiten. Z.B. den Abstand der Lichtquelle genau einzustellen.
ptflea hat sich der Sache angenommen und ein Programm in Processing geschrieben das SVG-Dateien aus Inkscape entgegen nimmt und die Verzerrung in Abhängigkeit der Entfernung der Lichtquelle berechnet.
Das Programm zerlegt das Zielobjekt in einzelne Punkte und verbindet jeden einzelnen Punkt im dreidimensionalen Raum mit der Lichtquelle. Die Schnittpunkte mit der Zielebene ( entspricht dem verzerrten Objekt) werden miteinander verbunden und als SVG-Datei gespeichert.
Programm in Processing
Die Ausgangsdaten werden in Inkscape in der gewünschten 'Schattenwurfgrösse' in mm erstellt und als SVG-Datei im Ordner 'data' im Sketchordner abgelegt werden.
Die Ausgabedatei liegt im Sketchordner mit der Namensergänzung '_Shadow'.
Die fertige Datei muss noch in Inkscape bearbeitet werden, da die Linien nicht geschlossen sind:
- Objekt markieren und mit 'F2' in den Knotenbearbeitungsmodus wechseln.
- Mit 'STRG'+'A' alles markieren und in der Symbolleiste 'Gewählte Endknoten durch ein neues Segment verbinden' klicken.
Die Verarbeitung der SVG-Dateien übernimmt die Geomerative-Libary
Hier ist der Code:
1import geomerative.*; // http://www.ricardmarxer.com/geomerative/
2
3
4//SVG muss auf dem Kopf stehen und links oben angelegt sein, in mm der gewünschten Grösse
5//Input-SVG muss im Data-Ordner liegen
6//Output-SVG liegt im Sketch-Ordner -> Originalname+_'Shadow'
7String Dateiname = "space_invader";
8
9//Koordianten der Lichtquelle in mm
10int lichtY = 150; //Entfernung
11int lichtZ = 80; //Höhe
12// x wird immer in die Mitte des Objektes gelegt -> siehe Variable 'mitte'
13
14float mm2px_faktor = 3.54331; // Umrechung der px von Inkscape zu mm 1mm entspricht 3.54331px
15int trennwert = 10; //Inseln erkennen und nicht verbinden -> kleine Zahl wenn Insel sehr nahe beieinander
16float scale = 1 ; //Grösse der Darstellung auf dem Bildschirm
17
18String[] SVGdatei = new String[2]; //String Array zur koordinatenausgabe
19RPoint[] points; //Array für Koordinaten
20
21int i = 0; // Zähler
22float mitte = 0; // Mitte der X-Achse des Objektes
23float PXlast = 0; // vorheriger x-Punkt um Linie zeichnen zu können
24float PYlast = 0; // vorheriger y-Punkt um Linie zeichnen zu können
25
26//3D-Raum mit Schnittebene
27PVector[] face = new PVector[3];
28PVector n;//normal
29Ray r1;
30
31
32void setup(){
33
34
35 size(1000,600,P3D); // Grösse der Anzeigefläche
36 //Fläche der 'Verzerrungsebene' definieren
37 face[0] = new PVector(-100,0,0);
38 face[1] = new PVector(0,0,100);
39 face[2] = new PVector(100,0,0);
40
41
42 r1 = new Ray(new PVector(-100,160,50),new PVector(0,200,300));
43
44 // Load given svg file in sketch/data
45 RG.init(this);
46 RShape objShape = RG.loadShape(Dateiname + ".svg");
47 // Linien in Punkte zerlegen
48 points = objShape.getPoints();
49
50 background(255);
51 translate(width/2, height/2,-500);
52 rotateX(map(90,0,height,-PI,PI));
53 rotateY(map(90,0,width,-PI,PI));
54
55 PVector c = new PVector();//centroid
56 for(PVector p : face) c.add(p);
57 c.div(3.0);
58 PVector cb = PVector.sub(face[2],face[1]);
59 PVector ab = PVector.sub(face[0],face[1]);
60 n = cb.cross(ab);//compute normal
61
62
63 //Mittelpunkt der x-Achse des Objektes festelegen -> X der Lichtquelle
64 for (int x=0; x<points.length-1; x++) {
65 if (points[x].x > mitte) {
66 mitte = points[x].x;
67 }
68 }
69 mitte = mitte/2;
70
71 noLoop(); // damit draw() nur ein mal ausgeführt wird
72
73 //SVG Datei Basics
74 SVGdatei[0] = "<svg\n version=\"1.1\"\n width=\"210mm\"\n height=\"297mm\"\n id=\"svg2\">\n <g id=\"layer1\">\n <path\n d=\"";
75 SVGdatei[1] = " id=\"path5917\"\n style=\"fill:none;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none\" />\n </g>\n</svg>";
76 }
77
78
79void draw(){
80 //umrechung der Lichtposition von mm zu px, da Inkspace im SVG px verwendet
81 lichtY = (int)(-lichtY * mm2px_faktor); //Entfernung
82 lichtZ = (int)(lichtZ * mm2px_faktor);
83
84
85 // Solange noch Punkte im Array sind Linie von Punkt zu Punkt zeichnen
86 for (int i=0; i < points.length - 1; i++) {
87
88 //Vector von der Lichtposition zu einem Punkt des Objektes anlegen
89 r1 = new Ray(new PVector(mitte,lichtY,lichtZ),new PVector(points[i].x, points[i].y,0));
90
91 //http://paulbourke.net/geometry/planeline/
92 //line to plane intersection u = N dot ( P3 - P1 ) / N dot (P2 - P1), P = P1 + u (P2-P1), where P1,P2 are on the line and P3 is a point on the plane
93 //Schnittpunkt mit der 'Verzerrungsebene' berechnen
94 PVector P2SubP1 = PVector.sub(r1.end,r1.start);
95 PVector P3SubP1 = PVector.sub(face[0],r1.start);
96 float u = n.dot(P3SubP1) / n.dot(P2SubP1);
97 PVector P = PVector.add(r1.start,PVector.mult(P2SubP1,u));
98 strokeWeight(1);
99 n.normalize();
100
101 //original zeichnen
102 point(points[i].x, points[i].y);
103
104 // Inseln erkennen und nicht verbinden
105 if ((PXlast < 0.001) || (abs(PXlast-P.x) > trennwert) || (abs(P.x-PXlast) > trennwert) || (abs(PYlast-P.z) > trennwert) || (abs(P.z-PYlast) > trennwert)) {
106 PXlast = P.x;
107 PYlast = P.z;
108 SVGdatei[0] = SVGdatei[0] + "M "; // Trennung der Linie einfügen
109 }
110
111 //verzerrtes Objekt zeichnen
112 line(PXlast*scale,PYlast*scale,0,P.x*scale,P.z*scale,0);
113
114 //Punkte in array speichern zur Ausgabe
115 SVGdatei[0] = SVGdatei[0] + PXlast + "," + PYlast + " ";
116
117 //letzten Punkt merken um die Punkte zu verbinden
118 PXlast = P.x;
119 PYlast = P.z;
120 }
121
122 //Punkte in die SVG-Datein schreiben
123 SVGdatei[0] = SVGdatei[0] + "\""; // Anführungsstriche einfügen
124 saveStrings(Dateiname + "_Shadow.svg", SVGdatei);
125 }
126
127
128class Ray{
129 PVector start = new PVector(),end = new PVector();
130 Ray(PVector s,PVector e){ start = s ; end = e; }
131}
Beispiel: Space Invader
Ziel ist ein ca. 42 x 30 cm großer Space Invader der mit einer LED beleucht wird die 15 cm entfernt ist und einen Abstand zur Wand von 8 cm hat.
Installation
Die gefrästen Objekte wurden mit Kreppband an der Wand befästigt ..
Als Lichtquelle diente jeweils eine PowerLED mit Abstrahlwinkel: 120°