Created
August 17, 2012 21:46
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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