Monday, March 15, 2010

Java3D offscreen rendering and changing colours

I just ran into an irritating quirk (perhaps bug?) in Java3D. I'm using the offscreen rendering feature of Canvas3D to render a simple 3D scene to a PNG file. The requirement is simply to produce a PNG given certain input parameters, notably colour.

My first attempt was very straight-forward; create my scene, with the correct colours and render it to the PNG. Repeat as required. The last part, repeat as required, is problematic. Each time you create an offscreen Canvas3D you create an OpenGL PBuffer. My graphics card as 32 pbuffers so the 33rd time you render the scene, the program crashes.

A second attempt is obviously required. The requirement being to use only one Canvas3D object and thus only one scene graph and to change the colour of the scene before re-rendering it. First, some background, my objects are Boxes and to set their colour you have to set an appropriately configured Appearance. For example:
final Material material = new Material();
final Appearance appearance = new Appearance();
final Box cube = new Box(1, 1, 1, appearance);


material.setDiffuseColor(someColor);
appearance.setMaterial(material);
This simply creates a cube using the Box utility class and sets a color on it. Java3D will optimize the scene graph and part of that optimization assumes that the scene doesn't change, unless you explicitly state that it can. Changing the colour would be one of those things you have to state. There are plenty of forum topics about changing the colour of a Box and they usually suggest changing the Appearance object, like so:
final Box cube = new Box(1, 1, 1, Box.ENABLE_APPEARANCE_MODIFY, someAppearance);


[...] // Some time later
box.setAppearance(aDifferentAppearance);
The ENABLE_APPEARANCE_MODIFY flag simply tells Java3D to assume that the Appearance object associated with the Box can change. This should allow you to change the appearance at will. Except in offscreen rendering mode, this doesn't work. Not only does it not work, but any initial colour you set is ignored and you get a white Box. After much messing about, I found that ENABLE_APPEARANCE_MODIFY was the cause. The solution to this is to change the colour on the material directly and not simply set a new Apperance.
final Material material = new Material();
final Appearance appearance = new Appearance();
final Box cube = new Box(1, 1, 1, appearance);


material.setCapability(Material.ALLOW_COMPONENT_WRITE);
material.setDiffuseColor(someColor);
appearance.setMaterial(material);
[...] // Some time later
material.setDiffuseColor(aDifferentColor);
And there you have it, Boxes that change colour in an offscreen rendered scene.

1 comment:

Simpleprogrammer said...

I am using a method using screen capture. Calling the function save image will save the file. The .jpg file is saving fine. Not tested the png part.

public void saveimage() throws AWTException, IOException
{
// capture the whole screen
BufferedImage onimage = new Robot().createScreenCapture( new Rectangle( 5, 25, 1280, 725));

// Save as JPEG
File file = new File("screencapture.jpg");
ImageIO.write(onimage, "jpg", file);

// Save as PNG
// File file = new File("screencapture.png");
// ImageIO.write(screencapture, "png", file);

}

---------------------------------

Requesting help

Can you post in this blog how to make a cube appear on clicking a Jbutton on a JMenu item. I used actionPerformed section i tried to insert the code code for the cube like contents.addChild(cube()); which contains the code for the cube. How to add it to the Universe and make it visible in Canvas3D. Kindly help.