Procedural Texturing

Hi,

I’m trying to generate a procedural texture based on a heightmap and a collection of images that have a low, optimal, and high value. Where the final image is a blending of these textures depending on the height of the heighmap. I’m having a bit of a brain fart though as how to best get the colors. Here is the class as it stands now, please note the commented section in createTexture() as this is where my main problem lies…


package monkey.texture;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

import javax.swing.ImageIcon;

import monkey.locale.external.AbstractHeightMap;

public class ProceduralTexture {
    private ImageIcon proceduralTexture;
    private AbstractHeightMap heightMap;
    private ArrayList textureList;
    private int size;
    
    public ProceduralTexture(AbstractHeightMap heightMap) {
        textureList = new ArrayList();
        this.heightMap = heightMap;
        this.size = heightMap.getSize();
    }
    
    public void createTexture() {
        BufferedImage img = new BufferedImage(size,size,
                BufferedImage.TYPE_INT_RGB);
        
        float red = 0.0f;
        float green = 0.0f;
        float blue = 0.0f;
        
        for(int x = 0; x < size; x++) {
            for(int z = 0; z < size; z++) {
                
                for(int i = 0; i < textureList.size(); i++) {
                    //0 to 1 scalar, how much to use.
                    float scalar = 
                        getTextureScale(heightMap.getTrueHeightAtPoint(x,z), i);
                    //red += texture(i)'s red value * scalar;
                    //green += texture(i)'s green value * scalar;
                    //blue += texture(i)'s blue value * scalar;
                }
                
                //set img's (x,z) pixel to (red,green,blue).
                
                //red = green = blue = 0
            }
        }
        
        //create ImageIcon proceduralTexture from img.
    }
    
    public void addTexture(ImageIcon image, int low, int optimal, int high) {
        BufferedImage img = new BufferedImage(image.getIconWidth(), 
                image.getIconHeight(), BufferedImage.TYPE_INT_RGB);
        Graphics2D g = (Graphics2D)img.getGraphics();
        g.drawImage(image.getImage(), null, null);
        g.dispose();
        
        TextureTile tile = new TextureTile();
        tile.highHeight = high;
        tile.optimalHeight = optimal;
        tile.lowHeight = low;
        tile.imageData = img;
        textureList.add(tile);
    }
    
    public ImageIcon getImageIcon() {
        return proceduralTexture;
    }
    
    private float getTextureScale(int height, int tileIndex) {
        TextureTile tile = (TextureTile)textureList.get(tileIndex);
        
        if(height < tile.optimalHeight && height > tile.lowHeight) {
            return ((float)(height - tile.lowHeight)) / 
                    (tile.optimalHeight - tile.lowHeight);
        } else if(height > tile.optimalHeight && height < tile.highHeight) {
            return ((float)(tile.highHeight - height)) / 
                    (tile.highHeight - tile.optimalHeight);
        } else if(height == tile.optimalHeight) {
            return 1.0f;
        } else {
            return 0.0f;
        }
    }
    
    private class TextureTile {
        public BufferedImage imageData;
        public int lowHeight;
        public int optimalHeight;
        public int highHeight;
    }
    
    

}


I’m handling everything as BufferedImages right now, but if there is a better way to go about it, please by all means let me know.

Maybe it helps if you tell what is the visuals you want for your final blending and what shapes are you using for the particular images.

Just as a note, procedural textures don’t work with pixel coordinates or pixels. They use logical coordinates and math formulas and because of that they are smaller and can draw any clip of the procedural in an image for any resolution required.

It’s a tradeoff between space and computation power. Procedural textures don’t need mipmaping and they are rendered with the best quality possible at any time but they are slow to compute on CPU. Pixel and vertex shaders are nothing more than procedural textures working on a GPU.

Ok, let me clarify a little bit. I may have not picked the greatest of subject titles. I’m not having any trouble calculating the percentage per texture, etc. I’m having trouble getting the pixel from the input textures and setting the pixel for the output.

I basically need to get the red, green and blue values for each texture that applies, multiply that by a factor (0 to 1) and add them together. I then set this total as the pixel for the final texture. I just can’t figure out how to get the individual color values.

java.awt.PixelGrabber might work for you?

Or, if not, get the graphics of a BufferedImage, draw your source image onto the BufferedImage then use the getRGB() function on BufferedImage. Incidently, the value returned also contains the Alpha if its present.

I imagine your could also do this using the java.awt.image.ImageFilter stuff, but I suspect this would be more complicated. (but maybe more appropriate?)

Kev

I’ve tried getRGB() from the BufferedImage. However, that returns a packed int for the RGB, and to tell you the truth, I wasn’t sure how to retrieve the individual color values from this int. Nor do I know how to “repack” the modified colors into a new RGB value to put into the output image.

Assuming your BufferedImage is ARGB (or is it RGBA) ? Then the value you get back will be 8 bits per channel, so you can do something like (don’t quote me)

int value = image.getRGB(x,y);

int alpha = (value & 0xFF000000) << 24;
int red = (value & 0x00FF0000) << 16;
int green = (value & 0x0000FF00) << 8;
int blue = (value & 0x000000FF);

I’m guessing this isn’t dead on, but I suspect you’ll get the idea :slight_smile:

Kev

I believe you can get the RGB values as an int[] if you get Raster from the BufferedImage via getRaster, then do a getPixels (?) from the Raster.

[quote]I believe you can get the RGB values as an int[] if you get Raster from the BufferedImage via getRaster, then do a getPixels (?) from the Raster.
[/quote]
Yes, you can do something like this


Image output = new BufferedImage(x, y, BufferedImage.TYPE_INT_RGB); 
DataBufferInt data = (DataBufferInt) ((BufferedImage) output).getRaster().getDataBuffer(); 
int[] pixels = data.getData(); 

and then retrieve the color-values like this:


int r= (pixels[pos] & 0x00ff0000) >> 16;
int g= (pixels[pos] & 0x0000ff00) >> 8;
int b= (pixels[pos] & 0x000000ff);

You may also write them back into the array after doing your modifications

pixels[pos]=r<<16 | g<<8 | b

Note that i ignored the alpha channel in these examples. If needed, it has to be processed in a similar manner.

FANTASTIC!

Got it working, and it looks great!!!

Thanks everyone for your help.

Almost. due to the signed characteristics of the java numbers, the operations willl eventually give false results with alpha if the sign bit is set. Be sure to use >>> instead of >>.
for more infos, see:
http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#5121

Cooo, never knew about that one…

Thanks for jumping in there pepe!

Kev

Maybe not a really important point, but

int alpha = (pixel >> 24) & 0xff;
int red = (pixel >> 16) & 0xff;
int green = (pixel >> 8) & 0xff;
int blue = pixel & 0xff;

will give you the correct results, even for alpha (because of the & 0xff mask which would otherwise not be necessary).

True.
Don’t know if one of those is better, certainly none, but i had to point the >>> thing, because it might help on certain conditions.