Programmazione
grafica in Java
Creare effetti di luce
tramite il filtro LightOp
Redazione a cura di Luca Imperatore
Creare effetti di luce
tramite il filtro LightOp
Nell’esempio che segue vogliamo creare degli effetti di
luce su di una composizione utilizzando il filtro LightOp
di GLF.
L’uso di questo filtro ci permette di dare alla composizione
un aspetto realistico grazie all’utilizzo di mappe di rilievo e
di luci opportunamente posizionate.
La composizione è formata da tre livelli: lo sfondo, il testo
e l’ombra del testo.
Il livello di sfondo è realizzato caricando l’immagine di una
tessitura. Per dare un aspetto realistico allo sfondo
facciamo agire su di esso il filtro LightOp. Prepariamo
per questo una mappa di rilievo tramite la classe
ElevationMap. Diamo al parametro boolean il valore
true in modo da interpretare i valori di luminosità come
rilievo a sporgere.
Prepariamo le luci che illuminano la composizione: una
luce direzionale (DirectionalLight) proveniente
dall’angolo in alto a sinistra e delle luci spot
(SpotLight) provenienti dall’alto e dal basso. Possiamo
scegliere quali luci “accendere” agendo sui valori true e
false delle rispettive variabili in fase di inizializzazione
come vedremo successivamente in un esempio.
2
Quindi possiamo creare la LitSurface alla quale
passiamo come parametro la mappa di rilievo e alla quale
aggiungiamo le luci precedentemente create. Infine
trasformiamo lo sfondo in un livello di forma
(ShapeLayer) necessario per far agire il filtro LightOp
su di esso.
La realizzazione del livello di testo segue gli stessi passi.
Per avere un aspetto tridimensionale trasformiamo il testo
in un livello di forma. Creiamo un nuovo filtro LightOp
nel quale i parametri della LitSurface sono una nuova
ElevationMap e le stesse luci create precedentemente.
Quindi facciamo agire il filtro sulla ShapeLayer.
Aggiungiamo infine un livello di ombra creato traslando la
Shape del testo e sfocandola con il filtro ConvolveOp.
Il risultato finale è il seguente:
3
Come prima cosa inizializziamo le variabili che ci
serviranno per la composizione:
//Font e colori del testo, colore dello sfondo e dell’ombra
String text = "Java 2D is Hot!";
Color backgroundColor = new Color(204, 102,
51);
Color textColor = new Color(118, 114, 195);
Dimension size = new Dimension(600, 200);
Font font = new Font("Curlz MT",
Font.PLAIN, 90);
File textureFile = new
File("res/images/syberia/syberia35.jpg");
Color shadowColor = new Color(0, 0, 0);
double lightIntensity = 2;
//Posizionamento dell’ombra del testo
int shadowOffsetX = 5, shadowOffsetY = 5;
float shadowShearX = 0, shadowShearY = 0;
//Luce spot dall’alto
Color lightTopRampColor = Color.white;
boolean lightTopRamp = true;
//Luce spot dal basso
Color lightBottomRampColor = Color.white;
boolean lightBottomRamp = false;
4
//Luce direzionale
Color lightSunColor = Color.white;
boolean lightSun = true;
Trasformiamo il testo in un oggetto di tipo Shape che ci
servirà successivamente per creare l’ombra e il livello del
testo da filtrare:
Shape shape = TextLayer.makeTextBlock(text,
font, w, textTextAlignment);
Position textPosition = new
Position(textAnchor, 0, 0);
shape =
textPosition.createTransformedShape(shape,
cmp.getBounds());
Creiamo il livello di sfondo caricando l’immagine in una
BufferedImage e modelliamo un rilievo tramite una
mappa di rilievo (ElevationMap):
String textureFileName =
textureFile.getPath();
BufferedImage texture =
Toolbox.loadImage(textureFileName,
BufferedImage.TYPE_BYTE_GRAY);
5
ElevationMap map = new
ElevationMap(texture, true, 5);
Creiamo una LitSurface per lo sfondo alla quale
aggiungiamo la luce direzionale proveniente dall’angolo in
alto a sinistra:
LitSurface bkgSurface = new LitSurface(.3,
1, 1, materialType, map);
if(lightSun)
bkgSurface.addLight(new
DirectionalLight(new double[]{-40, -40,
40}, lightSunIntensity, lightSunColor));
A questo punto prepariamo le luci spot (SpotLight)
dall’alto e dal basso che aggiungiamo alla LitSurface
appena creata:
//Creiamo la luce spot dall’alto
SpotLight[] topSpots =
LightsStudio.getLightRamp(lightBounds, 4,
Anchor.TOP,
new double[]{lightIntensity,
lightIntensity, lightIntensity,
lightIntensity},
6
new Color[] {lightTopRampColor,
lightTopRampColor, lightTopRampColor,
lightTopRampColor}, .5);
if(lightTopRamp)
bkgSurface.addLights(topSpots);
//Creiamo la luce spot dal basso
SpotLight[] bottomSpots =
LightsStudio.getLightRamp(lightBounds, 3,
Anchor.BOTTOM,
new double[]{lightIntensity,
lightIntensity, lightIntensity},
new Color[]{lightBottomRampColor,
lightBottomRampColor,
lightBottomRampColor}, 0);
if(lightBottomRamp)
bkgSurface.addLights(bottomSpots);
Infine creiamo il filtro LightOp e lo facciamo agire sul
livello di sfondo dopo averlo opportunamente trasformato
in un livello di forma:
LightOp op = new LightOp(bkgSurface);
ShapeLayer backgroundLayer = new
ShapeLayer(cmp, cmp.getBounds(), new
FillRenderer(backgroundColor));
backgroundLayer.setRasterFilter(op);
7
E’ interessante notare come possiamo modificare la
luminosità dello sfondo solamente agendo sui valori true
e false delle variabili della luce direzionale e delle luci
spot in fase di inizializzazione:
Luci spot dall’alto “accese” e dal basso “spente”. Luce direzionale “spenta”
Luci spot dall’alto”spente” e dal basso “accese”. Luce direzionale “spenta”
Luci spot dall’alto “spente” e dal basso “accese”. Luce direzionale “accesa”
8
Creiamo ora l’ombra del testo. Per dare un effetto
realistico all’ombra utilizziamo il filtro ConvolveOp per
ottenere un effetto di sfocatura e una
AffineTransform per traslare la Shape del testo:
ConvolveOp blurOp = new ConvolveOp(new
GaussianKernel(5));
AffineTransform shadowTransform =
AffineTransform.getShearInstance(shadowShea
rX, shadowShearY);
ShapeLayer shadowLayer = new
ShapeLayer(cmp, shape, new
FillRenderer(shadowColor));
shadowLayer.setRasterFilter(blurOp, new
Dimension(5, 5));
shadowTransform.setToTranslation(shadowOffs
etX, shadowOffsetY);
shadowLayer.setTransform(shadowTransform);
Dobbiamo ora dare un effetto tridimensionale al testo.
Come per l’immagine di sfondo creiamo una
ElevationMap per modellare un rilievo:
ElevationMap textMap = new
ElevationMap(shape, 7, true, 10);
9
Creiamo una LitSurface per il testo alla quale
aggiungiamo la luce direzionale e le luci spot create
precedentemente:
LitSurface textSurface = new LitSurface(.3,
1, 1, materialType, textMap);
if(lightSun)
textSurface.addLight(new
DirectionalLight(new double[]{-40, -40,
40}, lightSunIntensity, lightSunColor));
if(lightTopRamp)
textSurface.addLights(topSpots);
if(lightBottomRamp)
textSurface.addLights(bottomSpots);
Quindi trasformiamo il testo in un livello di forma sul
quale facciamo agire un nuovo filtro LightOp:
ShapeLayer embossedText = new
ShapeLayer(cmp, cmp.getBounds(), new
FillRenderer(textColor));
embossedText.setRasterFilter(new
LightOp(textSurface));
10
Per far agire il filtro solo sul livello di testo dobbiamo
usare l'oggetto shape per creare una maschera ed
eseguire un ritaglio (clipping). Tutto ciò che si trova
all'interno dello shape rimane visibile ciò che sta al di
fuori viene mascherato.
embossedText.setLayerMask(shape);
L’ultima operazione consiste nel posizionare in una pila i
tre livelli che abbiamo creato. Il livello più basso è quello
di background, più su viene quello con l’ombra, ed infine
il testo:
cmp.setLayers(new Layer[]{ backgroundLayer,
shadowLayer, embossedText });
Di seguito il codice completo del programma.
11
Lights.java
import
import
import
import
import
import
import
import
import
java.awt.*;
java.awt.geom.*;
java.awt.image.*;
java.awt.event.*;
java.io.*;
javax.swing.*;
com.sun.glf.*;
com.sun.glf.goodies.*;
com.sun.glf.util.*;
public class Lights implements
CompositionFactory {
//Inizializziamo le variabili
String text = "Java 2D is Hot!";
Color backgroundColor = new Color(204, 102,
51);
Color textColor = new Color(118, 114, 195);
Dimension size = new Dimension(600, 200);
Font font = new Font("Curlz MT", Font.PLAIN,
90);
File textureFile = new
File("res/images/syberia/syberia35.jpg");
Color shadowColor = new Color(0, 0, 0);
double lightIntensity = 2;
//Posizionamento dell’ombra del testo
int shadowOffsetX = 5, shadowOffsetY = 5;
float shadowShearX = 0, shadowShearY = 0;
//Luce spot dall’alto
Color lightTopRampColor = Color.white;
boolean lightTopRamp = true;
//Luce spot dal basso
Color lightBottomRampColor = Color.white;
boolean lightBottomRamp = false;
12
//Luce direzionale
Color lightSunColor = Color.white;
boolean lightSun = true;
double lightSunIntensity = 0.2;
int materialType = 16;
//Posizione del testo
Anchor textAnchor = Anchor.CENTER;
TextAlignment textTextAlignment =
TextAlignment.CENTER;
public Composition build(){
LayerComposition cmp = new
LayerComposition(size);
int w = size.width;
int h = size.height;
//Trasformiamo il testo in una forma che ci servirà
successivamente per creare l'ombra e il livello del testo
Shape shape =
TextLayer.makeTextBlock(text, font,
textTextAlignment);
Position textPosition = new
Position(textAnchor, 0, 0);
shape =
textPosition.createTransformedShape
(shape , cmp.getBounds());
w,
//Creiamo il livello di sfondo caricando l’immagine in una
BufferedImage
String textureFileName =
textureFile.getPath();
BufferedImage texture =
Toolbox.loadImage(textureFileName,
BufferedImage.TYPE_BYTE_GRAY);
13
//Creiamo la mappa di rilievo
long curTime =
System.currentTimeMillis();
ElevationMap map = null;
if(texture != null){
System.out.print("\nProcessing
background
elevations .... ");
map = new ElevationMap(texture, true,
5);
System.out.println("... Done : " +
(System.currentTimeMillis()curTime)/1000 + "(s)");
}
//Creiamo la LitSurface per lo sfondo
LitSurface bkgSurface = new
LitSurface(.3, 1, 1, materialType, map);
if(lightSun)
bkgSurface.addLight(new
DirectionalLight(new double[]{-40, -40,
40}, lightSunIntensity,lightSunColor));
//Creiamo la luce spot dall'alto
int hm = 1;
Rectangle lightBounds = new Rectangle(0,
hm, w, h-2*hm);
SpotLight[] topSpots =
LightsStudio.getLightRamp(lightBounds, 4,
Anchor.TOP,
new double[]{lightIntensity,
lightIntensity, lightIntensity,
lightIntensity},
new Color[] {lightTopRampColor,
lightTopRampColor, lightTopRampColor,
14
lightTopRampColor}, .5);
if(lightTopRamp)
bkgSurface.addLights(topSpots);
//Creiamo la luce spot dal basso
SpotLight[] bottomSpots =
LightsStudio.getLightRamp(lightBounds, 3,
Anchor.BOTTOM,
new double[]{lightIntensity,
lightIntensity, lightIntensity},
new Color[]{lightBottomRampColor,
lightBottomRampColor,
lightBottomRampColor,}, 0);
if(lightBottomRamp)
bkgSurface.addLights(bottomSpots);
LightOp op = new LightOp(bkgSurface);
//Trasformiamo lo sfondo in un livello di forma sul quale
facciamo agire il filtro LightOp
ShapeLayer backgroundLayer = new
ShapeLayer(cmp, cmp.getBounds(), new
FillRenderer(backgroundColor));
backgroundLayer.setRasterFilter(op);
//Creiamo il livello dell'ombra
ConvolveOp blurOp = new ConvolveOp(new
GaussianKernel(5));
AffineTransform shadowTransform =
AffineTransform.getShearInstance
(shadowShearX, shadowShearY);
ShapeLayer shadowLayer = new
ShapeLayer(cmp, shape, new
FillRenderer(shadowColor));
shadowLayer.setRasterFilter(blurOp, new
Dimension(5, 5));
15
shadowTransform.setToTranslation
(shadowOffsetX, shadowOffsetY);
shadowLayer.setTransform(shadowTransform)
;
//Diamo un effetto tridimensionale al testo creando una
mappa di rilievo
curTime = System.currentTimeMillis();
System.out.print("\nCreating text
elevation map .... ");
ElevationMap textMap = new
ElevationMap(shape, 7, true, 10);
//Creiamo una LitSurface per il testo alla quale
aggiungiamo la luce direzionale e le luci spot create
precedentemente
LitSurface textSurface = new
LitSurface(.3, 1, 1, materialType,
textMap);
if(lightSun)
textSurface.addLight(new
DirectionalLight(new double[]{-40,
-40, 40}, lightSunIntensity,
lightSunColor));
if(lightTopRamp)
textSurface.addLights(topSpots);
if(lightBottomRamp)
textSurface.addLights(bottomSpots);
System.out.println("... Done : " +
(System.currentTimeMillis()-curTime)/1000
+ "(s)");
//Trasformiamo il testo in un livello di forma sul quale
facciamo agire un nuovo filtro LightOp
ShapeLayer embossedText = new
ShapeLayer(cmp, cmp.getBounds(), new
FillRenderer(textColor));
16
embossedText.setRasterFilter(new
LightOp(textSurface));
//Per far agire il filtro solo sul livello di testo usiamo
l'oggetto shape per creare una maschera ed eseguire un
ritaglio
embossedText.setLayerMask(shape);
//Posizioniamo i livelli nella pila
cmp.setLayers(new Layer[]{
backgroundLayer, shadowLayer,
embossedText });
return cmp;
}
public static void main(String args[]){
JFrame f = new JFrame();
f.setDefaultCloseOperation
(JFrame.EXIT_ON_CLOSE);
Lights sf = new Lights();
CompositionComponent cc = new
CompositionComponent(sf.build());
f.getContentPane().add(cc);
f.pack();
f.setVisible(true);
}
}
17
Scarica

Java 2D - Luca Imperatore