Skip to content

Instantly share code, notes, and snippets.

@bioid
Last active August 29, 2015 14:07
Show Gist options
  • Save bioid/b01733b5468bbf839663 to your computer and use it in GitHub Desktop.
Save bioid/b01733b5468bbf839663 to your computer and use it in GitHub Desktop.
Procedurally generated asteroids using perlin noise shader, implemented in elation engine using three.js
elation.component.add('engine.things.asteroid', function() {
this.postinit = function(){
this.defineProperties({
'seed': { type: 'float', default: Math.random() * 10 },
'factor1': { type: 'float', default: 1.2 }, // num ridges?
'factor2': { type: 'float', default: 1 }, // amount of deformation/ height of ridges?
'factor3': { type: 'float', default: 3 }, // elongation?
'factor4': { type: 'float', default: 1.3 }, // i really have no idea what any of these do
});
}
this.createObject3D = function() {
var shaderUniforms = {
'seed': this.properties.seed,
'factor1': this.properties.factor1,
'factor2': this.properties.factor2,
'factor3': this.properties.factor3,
'factor4': this.properties.factor4,
};
var mat = elation.engine.materials.getShaderMaterial('perlinShader', shaderUniforms);
var geo = new THREE.IcosahedronGeometry(20, 4);
this.refresh();
return new THREE.Mesh(geo, mat);
}
}, elation.engine.things.generic);
elation.engine.materials.addChunk('ashimaPerlin', {
uniforms: {
seed: {
'type': "f",
'value': Math.random(),
},
factor4: {
'type': "f",
'value': Math.random()
},
factor1: {
'type': 'f',
'value': 5.0
},
factor2: {
'type': 'f',
'value': 10.0
},
factor3: {
'type': 'f',
'value': 5.0
},
},
vertex_pars: [
//
// GLSL textureless classic 3D noise "cnoise",
// with an RSL-style periodic variant "pnoise".
// Author: Stefan Gustavson (stefan.gustavson@liu.se)
// Version: 2011-10-11
//
// Many thanks to Ian McEwan of Ashima Arts for the
// ideas for permutation and gradient selection.
//
// Copyright (c) 2011 Stefan Gustavson. All rights reserved.
// Distributed under the MIT license. See LICENSE file.
// https://github.com/ashima/webgl-noise
//
'varying vec2 vUv;',
'varying float noise;',
'uniform float seed;',
'uniform float factor4;',
'uniform float factor1;',
'uniform float factor2;',
'uniform float factor3;',
'vec3 mod289(vec3 x)',
'{',
'return x - floor(x * (1.0 / 289.0)) * 289.0;',
'}',
'vec4 mod289(vec4 x)',
'{',
'return x - floor(x * (1.0 / 289.0)) * 289.0;',
'}',
'vec4 permute(vec4 x)',
'{',
'return mod289(((x*34.0)+1.0)*x);',
'}',
'vec4 taylorInvSqrt(vec4 r)',
'{',
'return 1.79284291400159 - 0.85373472095314 * r;',
'}',
'vec3 fade(vec3 t) {',
'return t*t*t*(t*(t*6.0-15.0)+10.0);',
'}',
'// Classic Perlin noise',
'float cnoise(vec3 P)',
'{',
'vec3 Pi0 = floor(P); // Integer part for indexing',
'vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1',
'Pi0 = mod289(Pi0);',
'Pi1 = mod289(Pi1);',
'vec3 Pf0 = fract(P); // Fractional part for interpolation',
'vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0',
'vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);',
'vec4 iy = vec4(Pi0.yy, Pi1.yy);',
'vec4 iz0 = Pi0.zzzz;',
'vec4 iz1 = Pi1.zzzz;',
'vec4 ixy = permute(permute(ix) + iy);',
'vec4 ixy0 = permute(ixy + iz0);',
'vec4 ixy1 = permute(ixy + iz1);',
'vec4 gx0 = ixy0 * (1.0 / 7.0);',
'vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5;',
'gx0 = fract(gx0);',
'vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);',
'vec4 sz0 = step(gz0, vec4(0.0));',
'gx0 -= sz0 * (step(0.0, gx0) - 0.5);',
'gy0 -= sz0 * (step(0.0, gy0) - 0.5);',
'vec4 gx1 = ixy1 * (1.0 / 7.0);',
'vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5;',
'gx1 = fract(gx1);',
'vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);',
'vec4 sz1 = step(gz1, vec4(0.0));',
'gx1 -= sz1 * (step(0.0, gx1) - 0.5);',
'gy1 -= sz1 * (step(0.0, gy1) - 0.5);',
'vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);',
'vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);',
'vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);',
'vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);',
'vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);',
'vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);',
'vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);',
'vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);',
'vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));',
'g000 *= norm0.x;',
'g010 *= norm0.y;',
'g100 *= norm0.z;',
'g110 *= norm0.w;',
'vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));',
'g001 *= norm1.x;',
'g011 *= norm1.y;',
'g101 *= norm1.z;',
'g111 *= norm1.w;',
'float n000 = dot(g000, Pf0);',
'float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));',
'float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));',
'float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));',
'float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));',
'float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));',
'float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));',
'float n111 = dot(g111, Pf1);',
'vec3 fade_xyz = fade(Pf0);',
'vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);',
'vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);',
'float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); ',
'return 2.2 * n_xyz;',
'}',
'// Classic Perlin noise, periodic variant',
'float pnoise(vec3 P, vec3 rep)',
'{',
'vec3 Pi0 = mod(floor(P), rep); // Integer part, modulo period',
'vec3 Pi1 = mod(Pi0 + vec3(1.0), rep); // Integer part + 1, mod period',
'Pi0 = mod289(Pi0);',
'Pi1 = mod289(Pi1);',
'vec3 Pf0 = fract(P); // Fractional part for interpolation',
'vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0',
'vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);',
'vec4 iy = vec4(Pi0.yy, Pi1.yy);',
'vec4 iz0 = Pi0.zzzz;',
'vec4 iz1 = Pi1.zzzz;',
'vec4 ixy = permute(permute(ix) + iy);',
'vec4 ixy0 = permute(ixy + iz0);',
'vec4 ixy1 = permute(ixy + iz1);',
'vec4 gx0 = ixy0 * (1.0 / 7.0);',
'vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5;',
'gx0 = fract(gx0);',
'vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);',
'vec4 sz0 = step(gz0, vec4(0.0));',
'gx0 -= sz0 * (step(0.0, gx0) - 0.5);',
'gy0 -= sz0 * (step(0.0, gy0) - 0.5);',
'vec4 gx1 = ixy1 * (1.0 / 7.0);',
'vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5;',
'gx1 = fract(gx1);',
'vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);',
'vec4 sz1 = step(gz1, vec4(0.0));',
'gx1 -= sz1 * (step(0.0, gx1) - 0.5);',
'gy1 -= sz1 * (step(0.0, gy1) - 0.5);',
'vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);',
'vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);',
'vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);',
'vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);',
'vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);',
'vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);',
'vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);',
'vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);',
'vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));',
'g000 *= norm0.x;',
'g010 *= norm0.y;',
'g100 *= norm0.z;',
'g110 *= norm0.w;',
'vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));',
'g001 *= norm1.x;',
'g011 *= norm1.y;',
'g101 *= norm1.z;',
'g111 *= norm1.w;',
'float n000 = dot(g000, Pf0);',
'float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));',
'float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));',
'float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));',
'float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));',
'float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));',
'float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));',
'float n111 = dot(g111, Pf1);',
'vec3 fade_xyz = fade(Pf0);',
'vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);',
'vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);',
'float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); ',
'return 2.2 * n_xyz;',
'}',
'',
'float turbulence( vec3 p ) {',
' float w = 100.0;',
' float t = -.5;',
' for (float f = 1.0 ; f <= 10.0 ; f++ ){',
' float power = pow( 2.0, f );',
' t += abs( pnoise( vec3( power * p ), vec3( 10.0, 10.0, 10.0 ) ) / power );',
' }',
' return t;',
'}',
].join('\n'),
vertex: [
' vUv = uv;',
// get a turbulent 3d noise using the normal, normal to high freq
' noise = factor4 * 10.0 * -.10 * turbulence( factor1 * .5 * normal + seed );',
// get a 3d noise using the position, low frequency
' float b = factor3 * 5.0 * pnoise( factor2 * .03 * position + vec3(2.0 * seed), vec3( 100.0 ) );',
// compose both noises'
' float displacement = - 10. * noise + b;',
// move the position along the normal and transform it'
' vec3 newPosition = position + normal * displacement;',
' gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );',
].join('\n'),
fragment_pars: [
'varying vec2 vUv;',
'varying float noise;',
].join('\n'),
fragment: [
'vec3 color = vec3( vUv * ( 1. - 2. * noise ), 0.0 );',
'gl_FragColor = vec4( color.rgb, 1.0 );',
].join('\n')
});
elation.engine.materials.addChunk('asteroidFragment', {
uniforms: {
'tAsteroid': { type: 't', value: elation.engine.materials.getTexture('media/procworld/textures/asteroid.png') }
},
fragment_pars: [
'varying vec2 vUv;',
'varying float noise;',
'uniform sampler2D tAsteroid;',
].join('\n'),
fragment: [
// get a random offset
// lookup vertically in the texture, using noise and offset
// to get the right RGB colour
'vec2 tPos = vec2( 0, 1.0 - 1.3 * noise );',
'vec4 color = texture2D( tAsteroid, tPos );',
'gl_FragColor = vec4( color.rgb, 1.0 );',
].join('\n'),
});
elation.engine.materials.buildShader("perlinShader", {
uniforms: ['ashimaPerlin', 'asteroidFragment'],
chunks_vertex: ['ashimaPerlin'],
chunks_fragment: ['asteroidFragment'],
});
elation.require([
'engine.things.asteroid',
], function() {
elation.component.add('engine.things.asteroidfield', function() {
this.postinit = function() {
elation.events.add(this, 'thing_create', elation.bind(this, this.createChildren));
};
this.randomRange = function(min, max) {
var randNum = Math.random() * (min - max) + max;
return randNum;
};
this.calcPosition = function(i, factor) {
//return i * factor * Math.random();
return i * 40;
};
this.createObject3D = function() {
return new THREE.Object3D();
};
this.createChildren = function() {
this.asteroids = [];
for (i = 0; i < 10; i++) {
this.asteroids[i] = this.spawn('asteroid', 'asteroid' + i, {
'position': [this.calcPosition(i, 50), this.calcPosition(i, 50), this.calcPosition(i, -50)],
'seed': this.randomRange(1, 100),
'factor1': 1.2, //this.randomRange(.9, 1.2),
'factor2': 1, //this.randomRange(1, 1.2),
'factor3': 3, //this.randomRange(2.8, 3.2),
'factor4': 1.3, //this.randomRange(1.2, 1.5),
})
}
for (var i in this.asteroids) {
elation.events.add(this.asteroids[i], 'mousemove', elation.bind(this, this.handlemouse));
}
};
this.handlemouse = function() {
for (var i in this.asteroids) {
var u = this.asteroids[i].objects['3d'].material.uniforms;
for (var k in u) {
u[k].value.needsUpdate = true;
}
}
};
}, elation.engine.things.sector);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment