Newsletter sign-up
View all newsletters

Sign up for our technology specific newsletters.

Enterprise Java
Email Address:

Draw textured spheres

Learn a few basics of computer graphics programming -- texture mapping, shading, and perspective -- and how to display your creation with Java's java.awt.image package

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
I had originally planned on writing this month's article espousing the minutiae of implementing MOM with callback over RMI, using HTTP proxies, CGI bouncers, network PDUs, and other TLAs. Then, upon waking from twenty-six days of comatose inertia brought on by a massive overconsumption of purloined candy, I had but one thing on my mind: Easter eggs.

An Easter egg

As a result, this article is instead about developing a Java applet that draws textured Easter eggs. The textures are just a tile pattern built from a straightforward mathematical function of sines and cosines. We will transform these planar textures onto a sphere's surface to produce our finished product. To quickly draw these images to the screen we will render them into Java Image objects using classes from the java.awt.image package, letting the browser take care of any issues involved in actually displaying the resulting pictures. See Resources for the complete source code.

I must admit that the original inspiration for this article comes from Clifford A. Pickover's Computers, Pattern, Chaos and Beauty: Graphics from an Unseen World (St. Martin's Press, ISBN: 031206179X). If pretty computer-generated pictures interest you, I recommend you pick up a copy of this book.

Creating a texture

The first issue we encounter when generating our eggs is what texture to use. So as not to unduly restrict ourselves I'm going to start by defining a generic Texture interface that can be supported by a variety of different texture functions.

A texture function

public interface Texture {
  public RGB getTexel (double i, double j);
}


An implementation of this interface must provide a method that returns the color of the texture element at the specified texture coordinate (i,j). The texture coordinate will be a value 0.0 <= i,j < 1.0, meaning that a texture function will define a texture over a square domain paramaterized by i and j. The texture function should, however, accept values outside this range, clipping, replicating, or extending the texture as appropriate. The value returned from the getTexel() method is of type RGB:

public class RGB {
  double r, g, b;
  public RGB (double r, double g, double b) {
    this.r = r;
    this.g = g;
    this.b = b;
  }
  public RGB (int rgb) {
    r = (double) (rgb >> 16 & 0xff) / 255;
    g = (double) (rgb >> 8 & 0xff) / 255;
    b = (double) (rgb >> 0 & 0xff) / 255;
  }
  public void scale (double scale) {
    r *= scale;
    g *= scale;
    b *= scale;
  }
  public void add (RGB texel) {
    r += texel.r;
    g += texel.g;
    b += texel.b;
  }
  public int toRGB () {
    return 0xff000000 | (int) (r * 255.99) << 16 |
      (int) (g * 255.99) << 8 | (int) (b * 255.99) << 0;
  }
}


Our RGB class is similar to the standard Color class, except that it stores RGB colors in double precision; the color components should have values 0.0 <= r,g,b <= 1.0. We also provide some helper methods to convert, scale, and combine colors.

An image-based texture
This class implements a texture that uses an Image object as a source. We can use this class to map images onto a sphere by first converting the image into an array of integer RGB values (using the java.awt.image.PixelGrabber class) and then using this array to calculate texel values (as pixel is to picture element, so texel is to texture element).

An image-mapped sphere

import java.awt.*;
import java.awt.image.*;
public class ImageTexture implements Texture {
  int[] imagePixels;
  int imageWidth, imageHeight;
  public ImageTexture (Image image, int width, int height) throws InterruptedException {
    PixelGrabber grabber = new PixelGrabber (image, 0, 0, width, height, true);
    if (!grabber.grabPixels ())
      throw new IllegalArgumentException ("Invalid image; pixel grab failed.");
    imagePixels = (int[]) grabber.getPixels ();
    imageWidth = grabber.getWidth ();
    imageHeight = grabber.getHeight ();
  }
  public RGB getTexel (double i, double j) {
    return new RGB (imagePixels[(int) (i * imageWidth % imageWidth) +
      imageWidth * (int) (j * imageHeight % imageHeight)]);
  }
}


Note that we simply convert the texture coordinate into an integer location on the surface of the image and then return the image color at that exact point. If the texture is sampled at a greater or lower frequency than the original image, the result will be jagged as pixels are skipped or replicated. Properly addressing this problem requires us to interpolate between colors of the image; however, such a task is difficult to do properly when we don't know where the texture will finally be displayed. Ideally, we would determine the amount of texture area covered by a single pixel on screen, and would then sample this amount of the actual texture. This approach is not practical, however, so we will not attempt to address it; supersampling, which we will examine later, is a much simpler way to reduce the effects of the problem.

An algorithmic texture
We may wish to experiment with an alternate texture, a completely artificial mathematical function. We could go with something like the Mandelbrot set or a Lyapanov function, but we'll instead go with a texture computed from the sin() function (described in Pickover's book).

public class SineTexture implements Texture {
  double multiplier, scale;
  int modFunction;
  public SineTexture (double multiplier, double scale, int modFunction) {
    this.multiplier = multiplier;
    this.scale = scale;
    this.modFunction = modFunction;
  }
  public RGB getTexel (double i, double j) {
    i *= multiplier;
    j *= multiplier;
    double f = scale * (Math.sin (i) + Math.sin (j));
    return ((int) f % modFunction == 0)
      ? new RGB (1.0, 0.0, 0.0)
      : new RGB (0.0, 1.0, 0.0);
  }
}


This class computes a simple sinusoidal function of (i,j). If the result, modulo a certain value, is 0, it returns bright red; otherwise, it returns bright green. The function uses three constants that control details of the resulting texture.

Mapping from sphere-space to texture-space

Now that we have a texture function, we must decide how to map a square, flat texture onto the closed surface of a sphere; or in other words, how to transform a point on the surface of the sphere into an (i,j) texture coordinate.

An obvious transformation is simply from longitude to i and latitude to j. The primary problem with this solution is that near the poles, the i coordinate will be greatly compressed: Walking around the earth at latitude 89o North is a lot quicker than at latitude 0. In other words, our uniform flat texture will be squashed at the poles.

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Comments (2)
Login
Forgot your account info?

how to do this in RMIBy Anonymous on June 7, 2009, 6:13 amhow to draw in RMI? Thanks

Reply | Read entire comment

geometryBy Anonymous on February 26, 2009, 3:30 amwat is the mathmatical name to the easter egg? Can you help by saying on the screen please!thankyou.

Reply | Read entire comment

View all comments

Add comment
Anonymous comments subject to approval. Register here for member benefits.
Have a JavaWorld account? Log in here. Register now for a free account.
Resources
  • Download this article and the complete source as a gzipped TAR file http://www.javaworld.com/javaworld/jw-06-1998/step/jw-06-step.tar.gz
  • Download this article and the complete source as a ZIP file http://www.javaworld.com/javaworld/jw-06-1998/step/jw-06-step.zip
  • If graphical images are your thing, I highly recommend that you pick up a copy of Clifford A. Pickover's Computers, Pattern, Chaos and BeautyGraphics from an Unseen World (St. Martin's Press, ISBN031206179X)
  • For details on just about every graphical algorithm, Computer GraphicsPrinciples and Practice by Foley, Feiner, van Dam, and Hughes (Addison Wesley, ISBN0201848406), is a must.
  • YaRay -- Yet Another Raytracer -- in Java, with source! http://www.oroinc.com/products/YaRay.html
  • Previous Step by Step columns