Post new topic This topic is locked, you cannot edit posts or make further replies.  [ 1 post ] 
Author Message
 Post subject: Noise-based textures
PostPosted: August 11th, 2011, 10:54 am 
Runite Member
User avatar
Offline

Joined: February 18th, 2007, 1:28 pm
Posts: 598
Location: Mother's basement us
RS Name: Nightgunner5
RS Status: P2P
Clan Name: Bits and Bytes
I've been very interested in the way RuneScape's terrain textures work. I heard on an RSBandBUpdate! that there are no images, only noise plus a special mathematical formula. So far, I've made something that looks very similar to the Grand Exchange's tiled floor using nothing but Simplex noise and some algebra.

tile-0-gray.png
Image
tile-0-blue.png (looks like the wizard in Ardougne's floor to me)
Image
tile-1-gray.png
Image

Code:
      TileTextureGenerator gen;
      BufferedImage img;
      gen = new TileTextureGenerator(0);
      img = gen.render(0, 0, 512, 512, 4, Color.GRAY);
      ImageIO.write(img, "png", new File("tile-0-gray.png"));
      img = gen.render(0, 0, 512, 512, 4, new Color(0x9DCBE3));
      ImageIO.write(img, "png", new File("tile-0-blue.png"));
      gen = new TileTextureGenerator(1);
      img = gen.render(0, 0, 512, 512, 4, Color.GRAY);
      ImageIO.write(img, "png", new File("tile-1-gray.png"));


Spoiler for Full source code:

Code:
package net.llamaslayers.noisetextures;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class Main {
   public static void main(String[] args) throws IOException {
      TileTextureGenerator gen;
      BufferedImage img;
      gen = new TileTextureGenerator(0);
      img = gen.render(0, 0, 512, 512, 4, Color.GRAY);
      ImageIO.write(img, "png", new File("tile-0-gray.png"));
      img = gen.render(0, 0, 512, 512, 4, new Color(0x9DCBE3));
      ImageIO.write(img, "png", new File("tile-0-blue.png"));
      gen = new TileTextureGenerator(1);
      img = gen.render(0, 0, 512, 512, 4, Color.GRAY);
      ImageIO.write(img, "png", new File("tile-1-gray.png"));
   }
}
Code:
package net.llamaslayers.noisetextures;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.util.Random;
import org.bukkit.util.noise.SimplexOctaveGenerator;

public abstract class AbstractTextureGenerator {
   private final long seed;

   public AbstractTextureGenerator(long seed) {
      this.seed = seed;
   }

   protected final Random getSeed() {
      return new Random(seed);
   }

   protected final SimplexOctaveGenerator getNoise(Random seed, int octaves) {
      return new SimplexOctaveGenerator(seed, octaves);
   }

   public final BufferedImage render(int x, int y, int width, int height, int step, Color base) {
      BufferedImage image = new BufferedImage(width * step, height * step, BufferedImage.TYPE_INT_RGB);
      int r = base.getRed();
      int g = base.getGreen();
      int b = base.getBlue();

      byte[] tones = generate(x, y, width, height, step);

      for (int i = 0; i < width * step; i++) {
         for (int j = 0; j < height * step; j++) {
            image.setRGB(i, j, getRGB(r, g, b, tones[i * width * step + j]));
         }
      }

      return image;
   }

   // Doesn't work very well, if at all.
   public final BufferedImage render3D(Color base, int width, int height,
                              double cameraX, double cameraY, double cameraZ,
                              double pitch, double yaw, double scale) {
      BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
      if (cameraZ <= 0) {
         return image;
      }

      int r = base.getRed();
      int g = base.getGreen();
      int b = base.getBlue();

      for (int x = 0; x < width; x++) {
         for (int y = 0; y < height; y++) {
            double tx = x / scale - cameraX;
            double ty = y / scale - cameraY;
            double tz = -cameraZ;
            double distance;
            double angle;

            // pitch
            distance = Math.hypot(tx, tz);
            angle = Math.atan2(tz, tx);
            tx = Math.cos(pitch + angle) * distance;
            tz = Math.sin(pitch + angle) * distance;

            // yaw
            distance = Math.hypot(tx, ty);
            angle = Math.atan2(ty, tx);
            tx = Math.cos(yaw + angle) * distance;
            ty = Math.sin(yaw + angle) * distance;

            double transformedX = tx / tz * scale * scale;
            double transformedY = ty / tz * scale * scale;

            //System.out.println(x + ", " + y + " -> " + transformedX + ", " + transformedY);

            image.setRGB(x, y, getRGB(r, g, b, getTone(transformedX, transformedY)));
         }
      }

      return image;
   }

   private int getRGB(int r, int g, int b, byte tone) {
      int t = tone;
      if (tone < 0) {
         tone += 256;
      }
      tone -= 128;
      return (Math.min(Math.max(r + tone, 0), 255) << 16)
            | (Math.min(Math.max(g + tone, 0), 255) << 8)
            | (Math.min(Math.max(b + tone, 0), 255));
   }

   public final int transformRGB(int rgb, byte tone) {
      rgb = rgb & 0xffffff;
      return getRGB(rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff, tone);
   }
   protected final int mod(int n, int m) {
      return ((n % m) + m) % m;
   }
   protected final double mod(double n, double m) {
      return ((n % m) + m) % m;
   }

   public abstract byte getTone(double x, double y);
   protected abstract byte[] generate(int x, int y, int width, int height, int step);
}
Code:
package net.llamaslayers.noisetextures;

import java.util.Random;
import org.bukkit.util.noise.NoiseGenerator;
import org.bukkit.util.noise.SimplexOctaveGenerator;

public class TileTextureGenerator extends AbstractTextureGenerator {
   public static final int TILE_SIZE = 128;
   private final SimplexOctaveGenerator noise;
   private final SimplexOctaveGenerator dirty;
   private final SimplexOctaveGenerator dirt;
   private final SimplexOctaveGenerator color;

   public TileTextureGenerator(long seed) {
      super(seed);
      Random r = getSeed();
      color = getNoise(r, 1);
      dirty = getNoise(r, 1);
      dirt = getNoise(r, 8);
      dirt.setScale(1 / 16.0);
      noise = getNoise(r, 8);
      noise.setScale(1 / 32.0);
   }

   @Override
   public byte getTone(double x, double y) {
      int initial = (int) Math.min(Math.max((color.noise(NoiseGenerator.floor(x / TILE_SIZE), NoiseGenerator.floor(y / TILE_SIZE), 0.5, 0.5, true) / 2
            + noise.noise(x, y, 0.5, 0.5, true) / 24) * 64 + 128, 0), 255);
      if (dirty.noise(NoiseGenerator.floor(x / TILE_SIZE), NoiseGenerator.floor(y / TILE_SIZE), 0.5, 0.5) < 0.5) {
         return (byte) initial;
      }

      int distance = (int) Math.min(Math.min(Math.min(TILE_SIZE - mod(x, TILE_SIZE), mod(x, TILE_SIZE)),
                                    Math.min(TILE_SIZE - mod(y, TILE_SIZE), mod(y, TILE_SIZE))), TILE_SIZE / 4);
      return (byte) Math.min(Math.max(distance + initial
            + dirt.noise(x, y, 0.5, 0.5, true) * 8, 0), 255);
   }

   @Override
   protected byte[] generate(int x, int y, int width, int height, int step) {
      byte[] b = new byte[width * height * step * step];

      for (int i = 0; i < width; i++) {
         for (int j = 0; j < height; j++) {
            for (int xStep = 0; xStep < step; xStep++) {
               for (int yStep = 0; yStep < step; yStep++) {
                  b[((i * step + xStep) * width + j) * step + yStep] = (byte) ((color.noise(NoiseGenerator.floor((double) (x + i) / TILE_SIZE), NoiseGenerator.floor((double) (y + j) / TILE_SIZE), 0.5, 0.5, true) / 2
                        + noise.noise(x + i + ((double) xStep / step), y + j + ((double) yStep / step), 0.5, 0.5, true) / 24) * 64 + 128);
               }
            }
         }
      }

      for (int i = 0; i < width; i++) {
         for (int j = 0; j < height; j++) {
            if (dirty.noise(NoiseGenerator.floor((double) (x + i) / TILE_SIZE),
                        NoiseGenerator.floor((double) (y + j) / TILE_SIZE), 0.5, 0.5) < 0.5) {
               continue;
            }
            int distance = Math.min(Math.min(Math.min(TILE_SIZE - mod(x + i, TILE_SIZE), mod(x + i, TILE_SIZE)),
                                     Math.min(TILE_SIZE - mod(y + j, TILE_SIZE), mod(y + j, TILE_SIZE))), TILE_SIZE / 4);
            for (int xStep = 0; xStep < step; xStep++) {
               for (int yStep = 0; yStep < step; yStep++) {
                  int tone = b[((i * step + xStep) * width + j) * step + yStep] < 0
                        ? b[((i * step + xStep) * width + j) * step + yStep] + 256
                        : b[((i * step + xStep) * width + j) * step + yStep];
                  b[((i * step + xStep) * width + j) * step + yStep] = (byte) Math.min(Math.max(distance + tone
                        + dirt.noise(x + i + xStep / (double) step, y + j + yStep / (double) step, 0.5, 0.5, true) * 8, 0), 255);
               }
            }
         }
      }

      return b;
   }
}

_________________

Shane wrote:
Just be sure to let me know if they're male or female, sometimes it can be hard to tell :P


Top
 Profile  
 
 Post subject: Register and login to get these in-post ads to disappear
PostPosted: August 11th, 2011, 10:54 am 
Runite Member

Joined: September 9th, 2004, 1:47am
Posts: 9047
Location: In your web browserz


Top
  
 
Display posts from previous:  Sort by  

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Jump to: