threex
Version:
Game Extensions for three.js http://www.threejsgames.com/extensions/
414 lines (246 loc) • 10.1 kB
JavaScript
THREE.WebGLRenderer.ShaderBuilder = function ( renderer, info ) {
this.renderer = renderer;
this.info = info;
this.programs = [],
this.programs_counter = 0;
};
THREE.extend( THREE.WebGLRenderer.ShaderBuilder.prototype, {
buildProgram: function ( shaderID, fragmentShader, vertexShader, uniforms, attributes, defines, parameters ) {
var renderer = this.renderer;
var p, pl, d, program, code;
var chunks = [];
// Generate code
if ( shaderID ) {
chunks.push( shaderID );
} else {
chunks.push( fragmentShader );
chunks.push( vertexShader );
}
for ( d in defines ) {
chunks.push( d );
chunks.push( defines[ d ] );
}
for ( p in parameters ) {
chunks.push( p );
chunks.push( parameters[ p ] );
}
code = chunks.join();
// Check if code has been already compiled
for ( p = 0, pl = this.programs.length; p < pl; p ++ ) {
var programInfo = this.programs[ p ];
if ( programInfo.code === code ) {
//console.log( "Code already compiled." /*: \n\n" + code*/ );
programInfo.usedTimes ++;
return programInfo.program;
}
}
var shadowMapTypeDefine = "SHADOWMAP_TYPE_BASIC";
if ( parameters.shadowMapType === THREE.PCFShadowMap ) {
shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF";
} else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) {
shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF_SOFT";
}
//console.log( "building new program " );
//
var customDefines = this.generateDefines( defines );
//
var prefix_vertex = [
"precision " + renderer.precision + " float;",
customDefines,
renderer.supportsVertexTextures ? "#define VERTEX_TEXTURES" : "",
parameters.gammaInput ? "#define GAMMA_INPUT" : "",
parameters.gammaOutput ? "#define GAMMA_OUTPUT" : "",
parameters.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "",
"#define MAX_DIR_LIGHTS " + parameters.maxDirLights,
"#define MAX_POINT_LIGHTS " + parameters.maxPointLights,
"#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights,
"#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights,
"#define MAX_SHADOWS " + parameters.maxShadows,
"#define MAX_BONES " + parameters.maxBones,
parameters.map ? "#define USE_MAP" : "",
parameters.envMap ? "#define USE_ENVMAP" : "",
parameters.lightMap ? "#define USE_LIGHTMAP" : "",
parameters.bumpMap ? "#define USE_BUMPMAP" : "",
parameters.normalMap ? "#define USE_NORMALMAP" : "",
parameters.specularMap ? "#define USE_SPECULARMAP" : "",
parameters.vertexColors ? "#define USE_COLOR" : "",
parameters.skinning ? "#define USE_SKINNING" : "",
parameters.useVertexTexture ? "#define BONE_TEXTURE" : "",
parameters.boneTextureWidth ? "#define N_BONE_PIXEL_X " + parameters.boneTextureWidth.toFixed( 1 ) : "",
parameters.boneTextureHeight ? "#define N_BONE_PIXEL_Y " + parameters.boneTextureHeight.toFixed( 1 ) : "",
parameters.morphTargets ? "#define USE_MORPHTARGETS" : "",
parameters.morphNormals ? "#define USE_MORPHNORMALS" : "",
parameters.perPixel ? "#define PHONG_PER_PIXEL" : "",
parameters.wrapAround ? "#define WRAP_AROUND" : "",
parameters.doubleSided ? "#define DOUBLE_SIDED" : "",
parameters.flipSided ? "#define FLIP_SIDED" : "",
parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "",
parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "",
parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "",
parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "",
parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "",
"uniform mat4 modelMatrix;",
"uniform mat4 modelViewMatrix;",
"uniform mat4 projectionMatrix;",
"uniform mat4 viewMatrix;",
"uniform mat3 normalMatrix;",
"uniform vec3 cameraPosition;",
"attribute vec3 position;",
"attribute vec3 normal;",
"attribute vec2 uv;",
"attribute vec2 uv2;",
"#ifdef USE_COLOR",
"attribute vec3 color;",
"#endif",
"#ifdef USE_MORPHTARGETS",
"attribute vec3 morphTarget0;",
"attribute vec3 morphTarget1;",
"attribute vec3 morphTarget2;",
"attribute vec3 morphTarget3;",
"#ifdef USE_MORPHNORMALS",
"attribute vec3 morphNormal0;",
"attribute vec3 morphNormal1;",
"attribute vec3 morphNormal2;",
"attribute vec3 morphNormal3;",
"#else",
"attribute vec3 morphTarget4;",
"attribute vec3 morphTarget5;",
"attribute vec3 morphTarget6;",
"attribute vec3 morphTarget7;",
"#endif",
"#endif",
"#ifdef USE_SKINNING",
"attribute vec4 skinIndex;",
"attribute vec4 skinWeight;",
"#endif",
""
].join("\n");
var prefix_fragment = [
"precision " + renderer.precision + " float;",
( parameters.bumpMap || parameters.normalMap ) ? "#extension GL_OES_standard_derivatives : enable" : "",
customDefines,
"#define MAX_DIR_LIGHTS " + parameters.maxDirLights,
"#define MAX_POINT_LIGHTS " + parameters.maxPointLights,
"#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights,
"#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights,
"#define MAX_SHADOWS " + parameters.maxShadows,
parameters.alphaTest ? "#define ALPHATEST " + parameters.alphaTest: "",
parameters.gammaInput ? "#define GAMMA_INPUT" : "",
parameters.gammaOutput ? "#define GAMMA_OUTPUT" : "",
parameters.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "",
( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "",
( parameters.useFog && parameters.fogExp ) ? "#define FOG_EXP2" : "",
parameters.map ? "#define USE_MAP" : "",
parameters.envMap ? "#define USE_ENVMAP" : "",
parameters.lightMap ? "#define USE_LIGHTMAP" : "",
parameters.bumpMap ? "#define USE_BUMPMAP" : "",
parameters.normalMap ? "#define USE_NORMALMAP" : "",
parameters.specularMap ? "#define USE_SPECULARMAP" : "",
parameters.vertexColors ? "#define USE_COLOR" : "",
parameters.metal ? "#define METAL" : "",
parameters.perPixel ? "#define PHONG_PER_PIXEL" : "",
parameters.wrapAround ? "#define WRAP_AROUND" : "",
parameters.doubleSided ? "#define DOUBLE_SIDED" : "",
parameters.flipSided ? "#define FLIP_SIDED" : "",
parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "",
parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "",
parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "",
parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "",
"uniform mat4 viewMatrix;",
"uniform vec3 cameraPosition;",
""
].join("\n");
program = renderer.compileShader(prefix_vertex + vertexShader, prefix_fragment + fragmentShader);
//console.log( prefix_fragment + fragmentShader );
//console.log( prefix_vertex + vertexShader );
program.uniforms = {};
program.attributes = {};
var identifiers, u, a, i;
// cache uniform locations
identifiers = [
'viewMatrix', 'modelViewMatrix', 'projectionMatrix', 'normalMatrix', 'modelMatrix', 'cameraPosition',
'morphTargetInfluences'
];
if ( parameters.useVertexTexture ) {
identifiers.push( 'boneTexture' );
} else {
identifiers.push( 'boneGlobalMatrices' );
}
for ( u in uniforms ) {
identifiers.push( u );
}
this.cacheUniformLocations( program, identifiers );
// cache attributes locations
identifiers = [
"position", "normal", "uv", "uv2", "tangent", "color",
"skinIndex", "skinWeight", "lineDistance"
];
for ( i = 0; i < parameters.maxMorphTargets; i ++ ) {
identifiers.push( "morphTarget" + i );
}
for ( i = 0; i < parameters.maxMorphNormals; i ++ ) {
identifiers.push( "morphNormal" + i );
}
for ( a in attributes ) {
identifiers.push( a );
}
this.cacheAttributeLocations( program, identifiers );
program.id = this.programs_counter ++;
this.programs.push( { program: program, code: code, usedTimes: 1 } );
this.info.memory.programs = this.programs.length;
return program;
},
generateDefines: function ( defines ) {
var value, chunk, chunks = [];
for ( var d in defines ) {
value = defines[ d ];
if ( value === false ) continue;
chunk = "#define " + d + " " + value;
chunks.push( chunk );
}
return chunks.join( "\n" );
},
// Shader parameters cache
cacheUniformLocations: function ( program, identifiers ) {
var i, l, id, renderer = this.renderer;
for ( i = 0, l = identifiers.length; i < l; i ++ ) {
id = identifiers[ i ];
program.uniforms[ id ] = renderer.getUniformLocation( program, id );
}
},
cacheAttributeLocations: function ( program, identifiers ) {
var i, l, id, renderer = this.renderer;
for( i = 0, l = identifiers.length; i < l; i ++ ) {
id = identifiers[ i ];
program.attributes[ id ] = renderer.getAttribLocation( program, id );
}
},
removeProgram: function ( program ) {
var i, il, programInfo;
var deleteProgram = false;
var programs = this.programs;
for ( i = 0, il = programs.length; i < il; i ++ ) {
programInfo = programs[ i ];
if ( programInfo.program === program ) {
programInfo.usedTimes --;
if ( programInfo.usedTimes === 0 ) {
deleteProgram = true;
}
break;
}
}
if ( deleteProgram === true ) {
// avoid using array.splice, this is costlier than creating new array from scratch
var newPrograms = [];
for ( i = 0, il = programs.length; i < il; i ++ ) {
programInfo = programs[ i ];
if ( programInfo.program !== program ) {
newPrograms.push( programInfo );
}
}
this.programs = newPrograms;
this.renderer.deleteProgram( program );
this.info.memory.programs --;
}
}
} );