# Wie geht dieser Effekt?



## Revenant (1. Okt 2006)

Wow! Würde mich echt mal verdammt interessieren, wie man sowas hinbekommt:

www.lipfert-malik.de/webdesign/animation_java.html


----------



## André Uhres (2. Okt 2006)

Eine Spiegelung kann man mit Graphics#copyArea ezeugen. 
Die Animation ist eine Folge von Spiegelungen bei denen die y-Quelle verändert wurde.


----------



## Revenant (2. Okt 2006)

zufällig nen beispielcode zur hand andré? *g*


----------



## Campino (2. Okt 2006)

ähm...ich würde das als Film oder als Reihung von Bildern, die einfach übereinander geszeichnet werden, machen...


----------



## André Uhres (2. Okt 2006)

Campino hat gesagt.:
			
		

> ähm...ich würde das als Film oder als Reihung von Bildern, die einfach übereinander geszeichnet werden, machen...


Ja gut, das ist ja auch kein Problem wenn man die Bilder erstmal hat.
Die erforderlichen Bilder bekommst du auf die oben beschriebene Art.


----------



## kaie (2. Okt 2006)

Eigentlich ist sowas gar nicht mal so schwer zu programmieren, geht sogar komplett ohne Java Advanced Imaging und den ganzen Schnickschnack. Man muss nur jeweils die einzelnen Bildzeilen des gespiegelten Bereichs um einen gewissen Betrag versetzen. Ich habe einfach eine (physikalisch ziemlich unsinnige) Funktion angesetzt, die einigermaßen gut aussieht. Wer Verbesserungsvorschläge hat, möge sich bitte melden.


```
import java.awt.*;
import java.net.*;

import javax.swing.*;

public class Spiegelung extends JPanel implements Runnable
{
    private Image bild        = null;
    private int   phase       = 0;
    private int   wellenhoehe = 3;

    public Spiegelung(String s)
    {
        // Bild laden
        try
        {
            bild = new ImageIcon(new URL(s)).getImage();
        } catch (Exception e)
        {
            System.out.println("Bild nicht gefunden!");
        }

        // JPanel-Groesse setzen
        setPreferredSize(new Dimension(bild.getWidth(null), bild
                .getHeight(null)));

        // Aktualisier-Thread starten
        new Thread(this).start();
    }

    public void paint(Graphics g)
    {
        // Groesse des Bildes ermitteln
        int w = bild.getWidth(null);
        int h = bild.getHeight(null);

        // Position der Wassergrenze berechnen
        int m = h * 2 / 3;

        // Hintergrund zeichnen
        g.drawImage(bild, 0, 0, null);

        // Spiegelung zeichnen
        for (int y = m; y < h; y++)
        {
            int q = (int) (m - Math.pow(y - m, 1.1) + wellenhoehe
                    * Math.sin(y * .25 + phase * .5));
            g.drawImage(bild, 0, y, w, y + 1, 0, q, w, q + 1, null);
        }

        // halbtransparentes blau über die Spiegelung zeichnen
        g.setColor(new Color(0, 0, 255, 20));
        g.fillRect(0, m, w, h - m);
    }

    public void run()
    {
        while (true)
        {
            repaint();
            phase++;
            try
            {
                Thread.sleep(50);
            } catch (Exception e)
            {
            }
        }
    }

    public static void main(String[] args) throws Exception
    {
        // neuen Frame mit einer Spiegelung erzeugen
        JFrame f = new JFrame("Eine Beispielspiegelung");

        String url = "http://www.sz.ruhr-uni-bochum.de/imperia/md/images/sz/greihe75.jpg";
        f.getContentPane().add( new Spiegelung(url) );
        
        f.pack();
        f.setVisible(true);
    }

}
```

Viel Spass damit wünscht

*kaie*


----------



## André Uhres (2. Okt 2006)

kaie hat gesagt.:
			
		

> ..Wer Verbesserungsvorschläge hat, möge sich bitte melden..


Mein Vorschlag:

```
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
```
  
Ansonsten sieht das ja schon sehr gut aus.


----------



## Revenant (3. Okt 2006)

hui... 

könntest du für mich noch kurz ein paar worte zu deiner for schleife da verlieren .... hab da nich so den durchblick


----------



## kaie (4. Okt 2006)

In der Schleife passiert Folgendes:

Wir laufen vom oberen Rand des Wassers (den ich auf 2/3 der Gesamthöhe gesetzt habe) zeilenweise nach unten und zeichnen jeweils eine Zeile. Eine einfache Spiegelung würde man einfach mit der Zeile 
	
	
	
	





```
int q=m-(y-m);
```
 erreichen. Um einen leichten Tiefeneffekt zu erreichen, habe ich den Term in der Klammer mit dem Pi-mal-Daumen-Wert 1,1 potenziert:
	
	
	
	





```
int q=(int)(m-Math.pow(y-m,1.1));
```
Auf diesen Wert addieren wir nun noch einen Sinus-Anteil drauf, dessen Amplitude die Wellenhöhe ist. Für die Phasenverschiebung werden die y-Koordinate und die zeitlich wachsende Phasenkomponente addiert, jeweils mit einem durch trial-and-error ermittelten Skalierungsfaktor. Und schon habe wir:
	
	
	
	





```
int q = (int) (m - Math.pow(y - m, 1.1) + wellenhoehe * Math.sin(y * .25 + phase * .5));
```
Diese Zeilenangabe können wir nun direkt zum Zeichnen der Zeile verwenden.

Alles klar?


----------



## Revenant (5. Okt 2006)

Respekt man Respekt... der Alghorytmus is schon ziemlich äh "fortgeschritten" :roll:  ... hm. Ich hab so nen ähnlichen Effekt noch mit nem Kreis gesehn - dort wo die Maus über dem Bild war hat es sich immer so "verzerrt", als ob ein Tropfen ins Wasser fallen würde. 

Wie geht sowas dann?

Und vor allem kennt jemand Fachliteratur für sowas? Suche schon seit geraumer Zeit ein Buch in der Richtung, aber da scheint es nichts zu geben.

EDIT: 
Also hier mal ne Sammlung an Sachen die mich so interressieren:

http://www.die3sphaere.de/bilder-galerie/java/java6/java-bild-6.html

http://www.die3sphaere.de/bilder-galerie/java/java1/java-bild-1.html

http://www.die3sphaere.de/bilder-galerie/java/java5/java-bild-5.html


----------



## Revenant (7. Okt 2006)

*push*


----------



## kaie (8. Okt 2006)

OK, es ist Sonntag, ich habe Langeweile, und so habe ich mich von Deinem plumpen Push provozieren lassen, mich auch an eines der anderen Beispiele zu wagen: das BumpMapping (im letzten Beispiel).

Meine Lösung sieht momentan so aus. Ist noch nicht perfekt, erfüllt aber seinen Zweck.

Als Literatur über BumpMapping habe ich folgende Seite verwendet: http://www.whisqu.se/per/docs/graphics12.htm. Alle wichtigen Informationen über den verwendeten Algorithmus findet man dort.

Bei den Erd-Texturen habe ich mich mal ganz dreist hier bedient: http://www.celestiamotherlode.net/catalog/earth.php (alle Bilder skaliert auf 500x250)

Und hier ist der Quelltext:

```
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;

import javax.swing.*;

public class BumpMapping extends JPanel implements MouseMotionListener
{
    // Attribute
    private Image   bild;
    private Image   bump;
    private int     lichtX = 50;
    private int     lichtY = 70;
    private int[][] map;
    private int[][] bumpX;
    private int[][] bumpY;
    private int     w;
    private int     h;

    public BumpMapping(Image bild, Image bump)
    {
        // Bilder laden
        this.bild = bild;
        this.bump = bump;
        // Bildgröße merken
        w = bump.getWidth(this);
        h = bump.getHeight(this);
        // Tabellen berechnen
        environMap();
        bumpMap();
        // Größe und Listener setzen
        setPreferredSize(new Dimension(w, h));
        addMouseMotionListener(this);
    }

    // Normalenvektoren für die Lampe berechnen
    public void environMap()
    {
        map = new int[256][256];
        for (int y = 0; y < 256; y++)
        {
            for (int x = 0; x < 256; x++)
            {
                double nX = (x - 128) / 128f;
                double nY = (y - 128) / 128f;
                double nZ = Math.max(0, 1 - Math.sqrt(nX * nX + nY * nY));
                map[x][y] = (int) (nZ * 255);
            }
        }
    }

    // Normalen der Bumpmap-Oberfläche berechnen
    public void bumpMap()
    {
        BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        bi.getGraphics().drawImage(bump, 0, 0, null);
        bumpX = new int[w][h];
        bumpY = new int[w][h];
        int[] rgb = bi.getRGB(0, 0, w, h, null, 0, w);
        for (int y = 0; y < h; y++)
        {
            for (int x = 0; x < w; x++)
            {
                bumpX[x][y] = (rgb[Math.min(w - 1, x + 1) + y * w] & 255)
                        - (rgb[Math.max(0, x - 1) + w * y] & 255);
                bumpY[x][y] = (rgb[x + w * Math.min(h - 1, y + 1)] & 255)
                        - (rgb[x + w * Math.max(0, y - 1)] & 255);
            }
        }
    }

    public void paintComponent(Graphics graphics)
    {
        // Hintergrund und normales Bild zeichnen
        super.paintComponent(graphics);
        graphics.drawImage(bild, 0, 0, null);

        // neues Bild als Array erzeugen
        int[] erg = new int[w * h];
        int pos = 0;

        // Punkte setzen
        for (int y = 0; y < h; y++)
        {
            for (int x = 0; x < w; x++)
            {
                int px = Math.max(0, Math.min(255, -bumpX[x][y] + 127
                        + (x - lichtX) / 3));
                int py = Math.max(0, Math.min(255, -bumpY[x][y] + 127
                        + (y - lichtY) / 3));
                int f = map[px][py];
                erg[pos++] = f | (f << 8) | (f << 16) | f << 24;
            }
        }
        // Bild erzeugen und zeichnen
        BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        bi.setRGB(0, 0, w, h, erg, 0, w);
        graphics.drawImage(bi, 0, 0, null);
    }

    // MouseListener
    public void mouseDragged(MouseEvent me)
    {
    }

    public void mouseMoved(MouseEvent me)
    {
        lichtX = me.getX();
        lichtY = me.getY();
        repaint();
    }

    // Hauptmethode
    public static void main(String[] args) throws Exception
    {
        // BumpMapping-Bild erzeugen
        BumpMapping b = new BumpMapping(new ImageIcon("earth.jpg").getImage(),new ImageIcon("bump.jpg").getImage());

        // Fenster anzeigen
        JFrame f = new JFrame("Beispiel für Bumpmapping");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(b);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}
```

Ich hoffe, ich baue die beiden anderen Beispielseiten nicht auch noch nach. Sonst ist wieder der ganze Sonntag vorbei...

*kaie*


----------



## kaie (8. Okt 2006)

Und zu guter letzt hier noch ein Effekt mit Regentropfen. Das Bild stammt aus der Wikipedia, die Tropfen sind einfach größer werdende Ellipsen mit Transparenzen. Wie bei den anderen Effekten gilt auch hier: ist noch lange nicht perfekt, aber das Prinzip wird hoffentlich deutlich. Viel Spass mit dem Quelltext:

```
import java.awt.*;
import java.net.*;

import javax.swing.*;

public class Pfuetze extends JPanel implements Runnable
{
    // Attribute
    private Image   bild;
    private int     w;
    private int     h;
    private int[][] welle  = new int[100][3];
    private int     anzahl = 0;

    public Pfuetze(Image bild)
    {
        // Bilder laden
        this.bild = bild;
        // Bildgröße merken
        w = bild.getWidth(this);
        h = bild.getHeight(this);
        // Größe und Listener setzen
        setPreferredSize(new Dimension(w, h));
        new Thread(this).start();
    }

    public void paintComponent(Graphics g)
    {
        // Hintergrund und normales Bild zeichnen
        super.paintComponent(g);
        g.drawImage(bild, 0, 0, null);
        ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
        
        for( int i=0; i<anzahl; i++ )
        {
            for( int j=-2; j<=2; j++ )
            {
                g.setColor( new Color(200-j*j*30,200-j*j*30,200-j*j*30,(150-welle[i][2]*5)/(3-Math.abs(j))) );
                g.drawOval(welle[i][0]-welle[i][2]+j,welle[i][1]-welle[i][2]*2/5,welle[i][2]*2,welle[i][2]*2*2/5);
            }
        }

    }

    public void run()
    {
        while (true)
        {
            try
            {
                Thread.sleep(50);
            } catch (Exception e)
            {
            }
            
            // neue Tropfen erzeugen
            if( Math.random()<.8 )
            {
                welle[anzahl][0] = (int)(Math.random()*w);
                welle[anzahl][1] = (int)(Math.random()*h);
                welle[anzahl][2] = 0;
                anzahl=(anzahl+1)%100;
            }
            
            // Tropfen weiterbewegen und löschen
            for( int i=0; i<anzahl; i++ )
            {
                welle[i][2]++;
                if( welle[i][2]==30 )
                {
                    welle[i]=welle[anzahl-1];
                    welle[anzahl-1]=new int[3];
                    anzahl--;
                    i--;
                }
            }
            
            repaint();
        }
    }

    // Hauptmethode
    public static void main(String[] args) throws Exception
    {
        // BumpMapping-Bild erzeugen
        Pfuetze b = new Pfuetze(new ImageIcon("bild.jpg").getImage());

        // Fenster anzeigen
        JFrame f = new JFrame("Beispiel für Regenpfützen");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(b);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}
```
So, hoffe damit erstmal alle Bildeffekte erschlagen zu haben. Jetzt wünscht aber wirklich noch ein schönes Restwochenende
*kaie*


----------



## Revenant (8. Okt 2006)

wow....

so langsam schockierst du mich oO aber big thx


----------



## LoN_Nemesis (9. Okt 2006)

Dieser Thread ist es mal wert gebookmarkt zu werden. Vielen Dank kaie, das ist beeindruckend.


----------



## Azrahel (19. Okt 2006)

LoN_Nemesis hat gesagt.:
			
		

> Dieser Thread ist es mal wert gebookmarkt zu werden. Vielen Dank kaie, das ist beeindruckend.



Auf jeden Fall, gute Arbeit kaie


----------

