shadertool.nodes.input.PerlinNoiseNode.java Source code

Java tutorial

Introduction

Here is the source code for shadertool.nodes.input.PerlinNoiseNode.java

Source

package shadertool.nodes.input;

import java.awt.event.ActionEvent;
import java.util.Random;

import javax.swing.AbstractAction;

import org.lwjgl.opengl.GL11;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.opengl.shader.ShaderProgram;

import shadertool.nodes.IOType;
import shadertool.nodes.InputNode;
import shadertool.nodes.Output;
import shadertool.ui.PerlinNoiseSelector;
import shadertool.ui.SizeSelector;

public class PerlinNoiseNode extends InputNode implements SizeInterface {
    private static final long serialVersionUID = 1L;

    protected transient ShaderProgram shader;
    protected int width = 300;
    protected int height = 300;

    protected float repX = 0.0f;
    protected float repY = 0.0f;
    protected float scaleX = 0.01f;
    protected float scaleY = 0.01f;
    protected float persistence = 0.1f;
    protected int octaves = 4;

    private Output imgOut = new Output("Ruido", IOType.Image2f);

    protected static final Random rand = new Random();

    public PerlinNoiseNode() throws SlickException {
        this("Perlin");
    }

    protected PerlinNoiseNode(String name) throws SlickException {
        super(name);

        outputs.add(imgOut);
    }

    public void setProperties(float scaleX, float scaleY, float repX, float repY, int octaves, float persistence)
            throws SlickException {
        this.scaleX = scaleX;
        this.scaleY = scaleY;
        this.repX = repX;
        this.repY = repY;
        this.octaves = octaves;
        this.persistence = persistence;
        updateShader();
    }

    public float getRepX() {
        return repX;
    }

    public float getRepY() {
        return repY;
    }

    protected void updateShader() throws SlickException {
        if (shader != null)
            shader.release();

        // Cdigo perlin 3D
        // Tomado de https://github.com/ashima/webgl-noise/blob/master/src/classicnoise3D.glsl
        // Liberado bajo licencia MIT (ver LICENSE-MIT.txt)
        String pixel = "#version 120" + "\n" + "uniform sampler2D tex;" + "\n" + "vec4 mod289(vec4 x)" + "\n" + "{"
                + "\n" + "  return x - floor(x * (1.0 / 289.0)) * 289.0;" + "\n" + "}" + "\n" + "" + "\n"
                + "vec4 permute(vec4 x)" + "\n" + "{" + "\n" + "  return mod289(((x*34.0)+1.0)*x);" + "\n" + "}"
                + "\n" + "" + "\n" + "vec4 taylorInvSqrt(vec4 r)" + "\n" + "{" + "\n"
                + "  return 1.79284291400159 - 0.85373472095314 * r;" + "\n" + "}" + "\n" + "" + "\n"
                + "vec2 fade(vec2 t) {" + "\n" + "  return t*t*t*(t*(t*6.0-15.0)+10.0);" + "\n" + "}" + "\n" + ""
                + "\n";

        if (repX == 0.0f && repY == 0.0f) {
            pixel += "float cnoise(vec2 P)" + "\n" + "{" + "\n"
                    + "  vec4 Pi = floor(P.xyxy) + vec4(0.0, 0.0, 1.0, 1.0);" + "\n"
                    + "  vec4 Pf = fract(P.xyxy) - vec4(0.0, 0.0, 1.0, 1.0);" + "\n"
                    + "  Pi = mod289(Pi); // To avoid truncation effects in permutation" + "\n"
                    + "  vec4 ix = Pi.xzxz;" + "\n" + "  vec4 iy = Pi.yyww;" + "\n" + "  vec4 fx = Pf.xzxz;" + "\n"
                    + "  vec4 fy = Pf.yyww;" + "\n" + "" + "\n" + "  vec4 i = permute(permute(ix) + iy);" + "\n"
                    + "" + "\n" + "  vec4 gx = fract(i * (1.0 / 41.0)) * 2.0 - 1.0 ;" + "\n"
                    + "  vec4 gy = abs(gx) - 0.5 ;" + "\n" + "  vec4 tx = floor(gx + 0.5);" + "\n"
                    + "  gx = gx - tx;" + "\n" + "" + "\n" + "  vec2 g00 = vec2(gx.x,gy.x);" + "\n"
                    + "  vec2 g10 = vec2(gx.y,gy.y);" + "\n" + "  vec2 g01 = vec2(gx.z,gy.z);" + "\n"
                    + "  vec2 g11 = vec2(gx.w,gy.w);" + "\n" + "" + "\n"
                    + "  vec4 norm = taylorInvSqrt(vec4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11)));"
                    + "\n" + "  g00 *= norm.x;" + "\n" + "  g01 *= norm.y;" + "\n" + "  g10 *= norm.z;" + "\n"
                    + "  g11 *= norm.w;" + "\n" + "" + "\n" + "  float n00 = dot(g00, vec2(fx.x, fy.x));" + "\n"
                    + "  float n10 = dot(g10, vec2(fx.y, fy.y));" + "\n"
                    + "  float n01 = dot(g01, vec2(fx.z, fy.z));" + "\n"
                    + "  float n11 = dot(g11, vec2(fx.w, fy.w));" + "\n" + "" + "\n"
                    + "  vec2 fade_xy = fade(Pf.xy);" + "\n"
                    + "  vec2 n_x = mix(vec2(n00, n01), vec2(n10, n11), fade_xy.x);" + "\n"
                    + "  float n_xy = mix(n_x.x, n_x.y, fade_xy.y);" + "\n" + "  return 2.3 * n_xy;" + "\n" + "}"
                    + "\n" + "" + "\n";
        } else {
            pixel += "float pnoise(vec2 rep, vec2 P)" + "\n" + "{" + "\n"
                    + "  vec4 Pi = floor(P.xyxy) + vec4(0.0, 0.0, 1.0, 1.0);" + "\n"
                    + "  vec4 Pf = fract(P.xyxy) - vec4(0.0, 0.0, 1.0, 1.0);" + "\n"
                    + "  Pi = mod(Pi, rep.xyxy); // To create noise with explicit period" + "\n"
                    + "  Pi = mod289(Pi);        // To avoid truncation effects in permutation" + "\n"
                    + "  vec4 ix = Pi.xzxz;" + "\n" + "  vec4 iy = Pi.yyww;" + "\n" + "  vec4 fx = Pf.xzxz;" + "\n"
                    + "  vec4 fy = Pf.yyww;" + "\n" + "" + "\n" + "  vec4 i = permute(permute(ix) + iy);" + "\n"
                    + "" + "\n" + "  vec4 gx = fract(i * (1.0 / 41.0)) * 2.0 - 1.0 ;" + "\n"
                    + "  vec4 gy = abs(gx) - 0.5 ;" + "\n" + "  vec4 tx = floor(gx + 0.5);" + "\n"
                    + "  gx = gx - tx;" + "\n" + "" + "\n" + "  vec2 g00 = vec2(gx.x,gy.x);" + "\n"
                    + "  vec2 g10 = vec2(gx.y,gy.y);" + "\n" + "  vec2 g01 = vec2(gx.z,gy.z);" + "\n"
                    + "  vec2 g11 = vec2(gx.w,gy.w);" + "\n" + "" + "\n"
                    + "  vec4 norm = taylorInvSqrt(vec4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11)));"
                    + "\n" + "  g00 *= norm.x;" + "\n" + "  g01 *= norm.y;" + "\n" + "  g10 *= norm.z;" + "\n"
                    + "  g11 *= norm.w;" + "\n" + "" + "\n" + "  float n00 = dot(g00, vec2(fx.x, fy.x));" + "\n"
                    + "  float n10 = dot(g10, vec2(fx.y, fy.y));" + "\n"
                    + "  float n01 = dot(g01, vec2(fx.z, fy.z));" + "\n"
                    + "  float n11 = dot(g11, vec2(fx.w, fy.w));" + "\n" + "" + "\n"
                    + "  vec2 fade_xy = fade(Pf.xy);" + "\n"
                    + "  vec2 n_x = mix(vec2(n00, n01), vec2(n10, n11), fade_xy.x);" + "\n"
                    + "  float n_xy = mix(n_x.x, n_x.y, fade_xy.y);" + "\n" + "  return 2.3 * n_xy;" + "\n" + "}\n";
        }

        // Cdigo propio de llamada a cnoise/pnoise. Aplica varias octavas para obtener ruido
        pixel += "float perlin2d(vec2 pos) {\n" + "   float total = 0;\n" +
        // Para cada octava...
                "   for (int i = 0; i < " + octaves + "; i++) {\n" +
                // ...calculamos frecuencia y amplitud...
                "      float freq = 2 * i;\n" + "      float amp = " + persistence + " * i;\n" +

                "      total += ";
        // ...y sumamos al total el resultado obtenido.
        if (repX == 0.0f && repY == 0.0f) {
            pixel += "cnoise(";
        } else {
            pixel += "pnoise(vec2(" + repX + "," + repY + "), ";
        }

        pixel += "pos * freq) * amp;\n" + "   }\n" + "    return (total / " + octaves + ") / 2 + 0.5 ;\n" + "}\n"
                + "void main() {" + "\n" + "  vec2 pos = gl_FragCoord.xy + vec2(" + rand.nextInt(10000) + ", "
                + rand.nextInt(10000) + "); \n" + "  float noise = perlin2d(pos * vec2(" + scaleX + ", " + scaleY
                + ")); \n" + "  gl_FragColor = vec4(noise, noise, noise, 1.0); \n" + "}\n";

        try {
            shader = new ShaderProgram(DEFAULT_VERTEX_SHADER, pixel);
        } catch (SlickException ex) {
            ex.printStackTrace();
            System.out.println(pixel);
        }
    }

    @Override
    public void initialize() throws SlickException {
        actions.add(new AbstractAction("Modificar propiedades") {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent arg0) {
                new PerlinNoiseSelector(PerlinNoiseNode.this).setVisible(true);
            }
        });
        actions.add(new AbstractAction("Cambiar tamao") {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent arg0) {
                new SizeSelector(PerlinNoiseNode.this).setVisible(true);
            }
        });

        updateShader();
    }

    @Override
    public void run() throws SlickException {

        Output imgOut = outputs.get(0);
        imgOut.create(width, height);
        Graphics g = imgOut.img.getGraphics();
        Graphics.setCurrent(g);

        shader.bind();

        // Dibujar cuadrado blanco (tamao de textura)
        GL11.glTranslatef(0, 0, 0);
        GL11.glBegin(GL11.GL_QUADS);
        GL11.glVertex3f(0, 0, 0);
        GL11.glVertex3f(0, height, 0);
        GL11.glVertex3f(width, height, 0);
        GL11.glVertex3f(width, 0, 0);
        GL11.glEnd();

        g.flush();
        shader.unbind();

        imgOut.updated();
    }

    @Override
    public int getWidth() {
        return width;
    }

    @Override
    public int getHeight() {
        return height;
    }

    @Override
    public void setWidth(int width) {
        this.width = width;
    }

    @Override
    public void setHeight(int height) {
        this.height = height;
    }

    public float getScaleX() {
        return scaleX;
    }

    public float getScaleY() {
        return scaleY;
    }

    public float getPersistence() {
        return persistence;
    }

    public void setPersistence(float persistence) {
        this.persistence = persistence;
    }

    public int getOctaves() {
        return octaves;
    }

    public void setOctaves(int octaves) {
        this.octaves = octaves;
    }

}