Skip to content

Instantly share code, notes, and snippets.

@chandlerprall
Created August 17, 2012 21:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chandlerprall/3382986 to your computer and use it in GitHub Desktop.
Save chandlerprall/3382986 to your computer and use it in GitHub Desktop.
meh - its old code, lots to improve. Haven't touched it in months
TerraFrame.TerrainShader = function ( shader_config ) {
var i, vertexShader, fragmentShader;
shader_config = TerraFrame.utils.merge(
{
uniforms: [],
textures: [],
pieces: [],
pervertex_textures: []
},
shader_config || { }
);
this.uniforms = { };
this.attributes = { };
// Setup any pervertex_texture handling
if ( shader_config.pervertex_textures.length > 0 ) {
this.__defineGetter__( 'pervertexTextures', function() {
var i;
if ( this.$pervertexTextures ) {
return this.$pervertexTextures;
}
this.$pervertexTextures = { };
for ( i = 0; i < shader_config.pervertex_textures.length; i++ ) {
this.$pervertexTextures[ shader_config.pervertex_textures[i] ] = new (function( shader, texture_index ) {
var vec4_part, attribute = shader.attributes[ 'pervertex_textures_' + Math.floor(i / 4) ];
switch ( texture_index % 4 ) {
case 0:
vec4_part = 'x';
break;
case 1:
vec4_part = 'y';
break;
case 2:
vec4_part = 'z';
break;
case 3:
vec4_part = 'w';
break;
}
this.length = attribute.value.length;
this.set = function ( vertex, value ) {
attribute.value[vertex][vec4_part] = value;
};
this.get = function ( vertex ) {
return attribute.value[vertex][vec4_part];
};
this.__defineGetter__( 'needsUpdate', function () {
return attribute.needsUpdate;
});
this.__defineSetter__( 'needsUpdate', function ( value ) {
attribute.needsUpdate = value;
});
})(this, i);
}
return this.$pervertexTextures;
});
}
this.__defineGetter__( 'vertexShader', function( ) {
var i, attributes, uniforms, varyings, main, main_close, shader = [];
if ( vertexShader ) {
return vertexShader
} else {
attributes = [];
uniforms = [];
varyings = [
'varying vec2 vUv;',
'varying vec3 vPosition;',
'varying vec3 vNormal;'
];
main = [
'void main()',
'{',
'vUv = uv;',
'vPosition = position;',
'vNormal = normal;',
'gl_Position = projectionMatrix * modelViewMatrix * vec4(vPosition, 1);',
];
if ( shader_config.pervertex_textures.length > 0 ) {
for ( i = 0; i < Math.ceil( shader_config.pervertex_textures.length / 4 ); i++ ) {
this.attributes['pervertex_textures_' + i] = { type: 'v4', value: [] };
attributes.push( 'attribute vec4 pervertex_textures_' + i + ';' );
varyings.push( 'varying vec4 vpervertex_textures_' + i + ';' );
main.push( 'vpervertex_textures_' + i + ' = pervertex_textures_' + i + ';' );
}
}
main_close = [
'}'
];
Array.prototype.push.apply( shader, attributes );
Array.prototype.push.apply( shader, uniforms );
Array.prototype.push.apply( shader, varyings );
Array.prototype.push.apply( shader, main );
Array.prototype.push.apply( shader, main_close );
return vertexShader = shader.join('\n');
}
} );
this.__defineGetter__( 'fragmentShader', function( ) {
var i, temp, varyings, uniforms, texture_uniforms, definitions, textures, main, main_close, shader = [];
if ( fragmentShader ) {
return fragmentShader;
} else {
uniforms = [];
for ( i = 0; i < shader_config.uniforms.length; i++ ) {
this.uniforms[ shader_config.uniforms[i].name ] = shader_config.uniforms[i];
uniforms.push( 'uniform ' + this.getGLSLType( shader_config.uniforms[i].type ) + ' ' + shader_config.uniforms[i].name + ';' );
}
varyings = [
'varying vec2 vUv;',
'varying vec3 vPosition;',
'varying vec3 vNormal;'
];
texture_uniforms = [];
for ( i = 0; i < shader_config.textures.length; i++ ) {
this.uniforms[ shader_config.textures[i].name + '_uniform' ] = { type: 't', value: i, texture: TerraFrame.Assets.getAsset( 'texture', shader_config.textures[i].asset ) };
texture_uniforms.push( 'uniform sampler2D ' + shader_config.textures[i].name + '_uniform;' );
}
definitions = [];
if ( typeof this.uniforms.sunlight_dir !== 'undefined' ) {
definitions.push( '#define CUSTOM_SUNLIGHT' );
}
if ( shader_config.pervertex_textures.length > 0 ) {
for ( i = 0; i < Math.ceil( shader_config.pervertex_textures.length / 4 ); i++ ) {
varyings.push( 'varying vec4 vpervertex_textures_' + i + ';' );
}
}
main = [
'void main()',
'{',
'gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);'
];
textures = [];
for ( i = 0; i < shader_config.textures.length; i++ ) {
temp = 'vec2(';
temp += (shader_config.textures[i].repeat && shader_config.textures[i].repeat.x) ? 'vUv.x * ' + TerraFrame.TerrainShader.EnsureFloat( shader_config.textures[i].repeat.x ) : 'vUv.x';
temp += (shader_config.textures[i].repeat && shader_config.textures[i].repeat.y) ? ',vUv.y * ' + TerraFrame.TerrainShader.EnsureFloat( shader_config.textures[i].repeat.y ) : ',vUv.y';
temp += ')';
textures.push( 'vec4 ' + shader_config.textures[i].name + ' = texture2D(' + shader_config.textures[i].name + '_uniform, ' + temp + ');' );
}
main_close = [
'}'
];
Array.prototype.push.apply( shader, uniforms );
Array.prototype.push.apply( shader, varyings );
Array.prototype.push.apply( shader, texture_uniforms );
Array.prototype.push.apply( shader, definitions );
Array.prototype.push.apply( shader, main );
Array.prototype.push.apply( shader, textures );
for ( i = 0; i < shader_config.pieces.length; i++ ) {
if ( shader_config.pieces[i] instanceof Array ) {
Array.prototype.push.apply( shader, shader_config.pieces[i] );
} else {
shader.push( shader_config.pieces[i] );
}
}
Array.prototype.push.apply( shader, main_close );
return fragmentShader = shader.join( '\n' );
}
} );
};
TerraFrame.TerrainShader.getUniqueVariable = function() {
var index = 0;
return function() {
return 'unique_' + index++;
};
}();
TerraFrame.TerrainShader.Chunks = {
sunlight: function( min, max ) {
return [
'// Sunlight shading',
'#ifdef CUSTOM_SUNLIGHT',
'vec3 light_dir = sunlight_dir;',
'#else',
'vec3 light_dir = vec3(-.5, .8, .7);',
'#endif',
'float nxDir = clamp(dot(vNormal, light_dir), ' + ( min || '.4' ) + ', ' + ( max || '1.0' ) + ');',
'gl_FragColor.rgb = nxDir * gl_FragColor.rgb;',
].join( '\n' )
},
strata: function( config ) {
if ( !config ) {
throw 'Strata shader chunk must be used with parameters.';
}
var i,
chunk = [],
conditions = [],
opacityvar = TerraFrame.TerrainShader.getUniqueVariable(), // holds opacity of the texture, 0.0 - 1.0
slopevar = TerraFrame.TerrainShader.getUniqueVariable(), // holds face's slope in radians
opacity_chunk = ['float ' + opacityvar + ' = 1.0;'];
// Elevation
if ( config.elevation ) {
if ( config.elevation.fade ) {
config.elevation.fade = TerraFrame.TerrainShader.EnsureFloat( config.elevation.fade );
}
if ( config.elevation.min !== undefined ) {
config.elevation.min = TerraFrame.TerrainShader.EnsureFloat( config.elevation.min );
conditions.push( 'vPosition.y >= ' + config.elevation.min );
if ( config.elevation.fade ) {
opacity_chunk.push(
'if (' + config.elevation.min + ' <= vPosition.y && vPosition.y <= ' + config.elevation.min + ' + ' + config.elevation.fade + ') {',
opacityvar + ' -= 1.0 - smoothstep(' + config.elevation.min + ',' + config.elevation.min + ' + ' + config.elevation.fade + ', vPosition.y);',
'}'
);
}
}
if ( config.elevation.max !== undefined ) {
config.elevation.max = TerraFrame.TerrainShader.EnsureFloat( config.elevation.max );
conditions.push( 'vPosition.y <= ' + config.elevation.max );
if ( config.elevation.fade ) {
opacity_chunk.push(
'if (' + config.elevation.max + ' - ' + config.elevation.fade + ' <= vPosition.y && vPosition.y <= ' + config.elevation.max + ') {',
opacityvar + ' -= smoothstep(' + config.elevation.max + ' - ' + config.elevation.fade + ', ' + config.elevation.max + ', vPosition.y);',
'}'
)
}
}
}
// Slope
if ( config.slope ) {
if ( config.slope.fade ) {
config.slope.fade = TerraFrame.TerrainShader.EnsureFloat( config.slope.fade );
}
if ( config.slope.min !== undefined ) {
config.slope.min = TerraFrame.TerrainShader.EnsureFloat( config.slope.min );
conditions.push( slopevar + ' >= ' + config.slope.min );
if ( config.slope.fade ) {
opacity_chunk.push(
'if (' + config.slope.min + ' <= ' + slopevar + ' && ' + slopevar + ' <= ' + config.slope.min + ' + ' + config.slope.fade + ') {',
opacityvar + ' -= 1.0 - smoothstep(' + config.slope.min + ',' + config.slope.min + ' + ' + config.slope.fade + ', ' + slopevar + ');',
'}'
);
}
}
if ( config.slope.max !== undefined ) {
config.slope.max = TerraFrame.TerrainShader.EnsureFloat( config.slope.max );
conditions.push( slopevar + ' <= ' + config.slope.max );
if ( config.slope.fade ) {
opacity_chunk.push(
'if (' + config.slope.max + ' - ' + config.slope.fade + ' <= ' + slopevar + ' && ' + slopevar + ' <= ' + config.slope.max + ') {',
opacityvar + ' -= smoothstep(' + config.slope.max + ' - ' + config.slope.fade + ', ' + config.slope.max + ', ' + slopevar + ');',
'}'
)
}
}
}
for ( i = 0; i < conditions.length; i++ ) {
chunk.unshift( 'if (' + conditions[i] + ') {' );
chunk.push( '}' );
}
chunk.splice( chunk.length / 2, 0, opacity_chunk.join( '\n' ), 'gl_FragColor = mix(gl_FragColor, ' + config.texture + ', clamp(' + opacityvar + ', 0.0, 1.0));' );
if ( config.slope ) {
chunk.unshift( 'float ' + slopevar + ' = acos(dot(vec3(0.0, 1.0, 0.0), normalize(vNormal)));' );
}
return chunk.join('\n');
},
pervertexTextures: function(textures) {
var i, chunk = [];
for ( i = 0; i < textures.length; i++ ) {
chunk.push( 'gl_FragColor = mix(gl_FragColor, ' + textures[i] + ', vpervertex_textures_' + Math.floor(i / 4) + '[' + ( ( i / 4 ) % 1 ) * 4 + ']);' );
}
return chunk.join('\n');
}
};
TerraFrame.TerrainShader.Chunks.sunlight.toString = function ( ) { return TerraFrame.TerrainShader.Chunks.sunlight(); };
TerraFrame.TerrainShader.prototype.getGLSLType = function ( type ) {
switch ( type ) {
case 'i':
return 'int';
break;
case 'f':
return 'float';
break;
case 'v2':
return 'vec2';
break;
case 'v3':
return 'vec3';
break;
case 'v4':
return 'vec4';
break;
case 'c':
return 'col';
break;
case 'm4':
return 'mat4';
break;
case 'fv1':
return 'uFloatArray[%s]';
default:
throw 'Uniform type "' + type + '" not recognized.';
break;
};
};
TerraFrame.TerrainShader.EnsureFloat = function( value ) {
if ( value.toString().indexOf( '.' ) === -1 ) {
return value + '.0';
}
return value;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment