API Docs for:
Show:

File: ../src/shaders.js

///@INFO: BASE
/* Basic shader manager 
	- Allows to load all shaders from XML
	- Allows to use a global shader
*/

//************************************
/**
* Shaders is the static class in charge of loading, compiling and storing shaders for reuse.
*
* @class Shaders
* @namespace LS
* @constructor
*/

var Shaders = {

	snippets: {},//to save source snippets
	shader_blocks_by_id: new Map(),//to save shader block
	shader_blocks: [],
	num_shaderblocks: 0, //used to know the index

	global_extra_code: null,
	dump_compile_errors: true, //dump errors in console
	on_compile_error: null, //callback 


	/**
	* Initializes the shader manager
	*
	* @method init
	* @param {string} url a url to a shaders.xml can be specified to load the shaders
	*/
	init: function(url, ignore_cache)
	{
		//this.shader_blocks = {};//do not initialize, or we will loose all

		//base intro code for shaders
		this.global_extra_code = String.fromCharCode(10) + "#define WEBGL\n";
		if( gl.webgl_version == 2 || gl.extensions.OES_standard_derivatives )
			this.global_extra_code += "#define STANDARD_DERIVATIVES\n";
		if( gl.webgl_version == 2 || gl.extensions.WEBGL_draw_buffers )
			this.global_extra_code += "#define DRAW_BUFFERS\n";
	},

	/**
	* Reloads the XML file with the shaders, useful when editing the file
	*
	* @method reloadShaders
	* @param {function} on_complete call when the shaders have been reloaded
	*/
	reloadShaders: function(on_complete)
	{
		//TODO: crawl all materials and clear shaders
	},

	/**
	* Compiles a shader, the vertex and fragment shader are cached indepently to speed up compilations but a unique name must be provided
	*
	* @method compileShader
	* @param {string} vs_code the final source code for the vertex shader
	* @param {string} fs_code the final source code for the fragment shader
	* @param {string} name an unique name that should be associated with this shader
	* @return {GL.Shader} shader
	*/
	compile: function( vs_code, fs_code, name )
	{
		if(!name)
			throw("compileShader must have a name specified");

		if(!gl)
			return null;
		var shader = null;
		try
		{
			vs_code = this.global_extra_code + vs_code;
			fs_code = this.global_extra_code + fs_code;

			//speed up compilations by caching shaders compiled
			var vs_shader = this.compiled_shaders[name + ":VS"];
			if(!vs_shader)
				vs_shader = this.compiled_shaders[name + ":VS"] = GL.Shader.compileSource(gl.VERTEX_SHADER, vs_code);
			var fs_shader = this.compiled_shaders[name + ":FS"];
			if(!fs_shader)
				fs_shader = this.compiled_shaders[name + ":FS"] = GL.Shader.compileSource(gl.FRAGMENT_SHADER, fs_code);

			var old = getTime();
			shader = new GL.Shader( vs_shader, fs_shader );
			if(this.debug)
				console.log("Shader compile time: ", (getTime() - old).toFixed(3), "ms");
			shader.name = name;
			//console.log("Shader compiled: " + name);
		}
		catch (err)
		{
			if(this.dump_compile_errors)
			{
				this.dumpShaderError(name, err, vs_code, fs_code );
				this.dump_compile_errors = false; //disable so the console dont get overflowed
			}

			if(this.on_compile_error)
				this.on_compile_error(err);

			return null;
		}
		return shader;
	},

	clearShaderCodeCache: function()
	{
		var scs = [];

		//get all shadercodes...
		var shadercodes = LS.StandardMaterial.shader_codes;
		for(var i in shadercodes)
			scs.push( shadercodes[i] );

		var res = LS.ResourcesManager.resources;
		for(var i in res)
			if( res[i].constructor === LS.ShaderCode )
				scs.push( res[i] );

		//clear caches
		for(var i in scs)
		{
			var sb = scs[i];
			sb.clearCache();
		}
	},

	dumpShaderError: function( name, err, vs_code, fs_code )
	{
		console.error("Error compiling shader: " + name);
		console.log(err);
		console.groupCollapsed("Vertex Shader Code");
		//console.log("VS CODE\n************");
		var lines = (this.global_extra_code + vs_code).split("\n");
		for(var i in lines)
			console.log(i + ": " + lines[i]);
		console.groupEnd();

		console.groupCollapsed("Fragment Shader Code");
		//console.log("FS CODE\n************");
		lines = (this.global_extra_code + fs_code).split("\n");
		for(var i in lines)
			console.log(i + ": " + lines[i]);
		console.groupEnd();
	},

	/**
	* Register a code snippet ready to be used by the #import clause in the shader
	*
	* @method registerSnippet
	* @param {string} id
	* @param {string} code
	*/
	registerSnippet: function(id, code)
	{
		this.snippets[ id ] = { id: id, code: code };
	},

	/**
	* Returns the code of a snipper
	*
	* @method getSnippet
	* @param {string} id
	* @return {string} code
	*/
	getSnippet: function(id)
	{
		return this.snippets[ id ];
	},

	/**
	* register a shaderblock in the global container so it can be used by shadermaterials
	*
	* @method registerShaderBlock
	* @param {string} id
	* @param {LS.ShaderBlock} shader_block
	*/
	registerShaderBlock: function( id, shader_block )
	{
		var block_id = -1;

		if( this.shader_blocks_by_id.get( id ) )
		{
			console.warn("There is already a ShaderBlock with that name, replacing it: ", id);
			block_id = this.shader_blocks_by_id.get(id).flag_id;
			this.clearShaderCodeCache();
		}
		else
			block_id = this.num_shaderblocks++;
		if(block_id >= 64)
			console.warn("Too many shaderblocks registered, not enought bits in a 64bits variable");

		shader_block.flag_id = block_id;
		shader_block.flag_mask = 1<<block_id;
		this.shader_blocks_by_id.set( id, shader_block );
		this.shader_blocks[ block_id ] = shader_block;
	},

	/**
	* register a shaderblock with the given id
	*
	* @method getShaderBlock
	* @param {string|Number} id
	* @return {LS.ShaderBlock} shader_block
	*/
	getShaderBlock: function( id )
	{
		if(id.constructor === String)
			return this.shader_blocks_by_id.get( id );
		return this.shader_blocks[id];
	},

	//this is global code for default shaders
	common_vscode: "\n\
		precision mediump float;\n\
		attribute vec3 a_vertex;\n\
		attribute vec3 a_normal;\n\
		attribute vec2 a_coord;\n\
		uniform mat4 u_model;\n\
		uniform mat4 u_viewprojection;\n\
	",
	common_fscode: "\n\
		precision mediump float;\n\
	"

};

LS.Shaders = Shaders;

/**
* A ShaderBlock represents a block of GLSL code that could be requested by a shader in order to obtain a functionality.
* SBs are registered and given a number, then if a shader wants that functionality it could use #pragma shaderblock "sb_name"
* it will be inserted in the material in the line of the pragma
*
* @class ShaderBlock
* @namespace LS
* @constructor
*/
function ShaderBlock( name )
{
	this.dependency_blocks = []; //blocks referenced by this block
	this.flag_id = -1;
	this.flag_mask = 0;
	this.events = null; //{};
	if(!name)
		throw("ShaderBlock must have a name");
	if(name.indexOf(" ") != -1)
		throw("ShaderBlock name cannot have spaces: " + name);
	this.name = name;
	this.code_map = new Map();
	this.context_macros = null;
}

ShaderBlock.prototype.defineContextMacros = function( macros )
{
	this.context_macros = macros;
}

/**
* register a shaderblock with the given id
* shader_type: vertex or fragment shader
*
* @method addCode
* @param {enum} shader_type could be  GL.VERTEX_SHADER or  GL.FRAGMENT_SHADER
* @param {string} enabled_code the code to insert if the shaderblock is enabled
* @param {string} disabled_code the code to insert if the shaderblock is disabled
* @param {Object} macros [optional] a set of macros to use when compiling this shader codes
*/
ShaderBlock.prototype.addCode = function( shader_type, enabled_code, disabled_code, macros )
{
	enabled_code  = enabled_code || "";
	disabled_code  = disabled_code || "";

	//this.checkDependencies( enabled_code );
	//this.checkDependencies( disabled_code );

	var info = { 
		enabled: new LS.GLSLCode( enabled_code ),
		disabled: new LS.GLSLCode( disabled_code ),
		macros: macros
	};
	this.code_map.set( shader_type, info );
}

ShaderBlock.prototype.bindEvent = function( event, code )  //priority?
{
	if(!this.events)
		this.events = {};
	this.events[ event ] = code;
}

/**
* Returns the full code of a shaderblock resolving all includes, shaderblocks, etc
* shadertype: GL.VERTEX_SHADER = 35633, GL.FRAGMENT_SHADER = 35632
*
* @method getFinalCode
* @param {enum} shader_type could be GL.VERTEX_SHADER or  GL.FRAGMENT_SHADER
* @param {number} block_flags a number containing the mask (every bit is a flag for a shaderblock) with all the enabled shader blocks
* @param {string} context an object with variable that could be fetched by the shaderblocks
* @return {String} the final code ready to be compiled
*/
ShaderBlock.prototype.getFinalCode = function( shader_type, block_flags, context )
{
	block_flags = block_flags || 0;
	var code = this.code_map.get( shader_type );
	if(!code)
		return null;
	var glslcode = (block_flags & this.flag_mask) ? code.enabled : code.disabled;
	var finalcode = glslcode.getFinalCode( shader_type, block_flags, context );

	if( code.macros )
	{
		var macros_code = "";
		for(var i in code.macros)
			macros_code += "#define " + i + code.macros[i] + "\n";
		finalcode = macros_code + finalcode;
	}
	return finalcode;
}

/**
* Registers this shaderblock in the global LS.Shaders container
*
* @method register
**/
ShaderBlock.prototype.register = function()
{
	LS.Shaders.registerShaderBlock(this.name, this);
}

ShaderBlock.prototype.checkDependencies = function( code )
{
//TODO
}


LS.ShaderBlock = ShaderBlock;



/**
* Used for parsing GLSL code and precompute info (mostly preprocessor macros)
* @class GLSLCode
* @constructor
* @param {String} code
*/
function GLSLCode( code )
{
	this.code = code;

	this.blocks = [];
	this.pragmas = {};
	this.uniforms = {};
	this.attributes = {};
	this.includes = {};
	this.snippets = {};
	this.shader_blocks = {}; //warning: this not always contain which shaderblocks are in use, because they could be dynamic using pragma define
	this.is_dynamic = false; //means this shader has no variations using pragmas or macros
	if(code)
		this.parse();
}

LS.GLSLCode = GLSLCode;

GLSLCode.pragma_methods = {};

//block types
GLSLCode.CODE = 1;
GLSLCode.PRAGMA = 2;

//pargma types
GLSLCode.INCLUDE = 1;
GLSLCode.SHADERBLOCK = 2;
GLSLCode.SNIPPET = 3;
GLSLCode.EVENT = 4;

//given a code with some pragmas, it separates them
GLSLCode.prototype.parse = function()
{
	//remove comments
	var code = this.code.replace(/(\/\*([\s\S]*?)\*\/)|(\/\/(.*)$)/gm, '');

	this.fragments = [];
	this.pragmas = {};
	this.uniforms = {};
	this.streams = {};
	this.includes = {};
	this.snippets = {};
	this.shader_blocks = {};
	this.is_dynamic = false; //means this shader has no variations using pragmas or macros

	var current_fragment = [];
	var lines = code.split("\n");

	//parse
	for(var i = 0; i < lines.length; i++)
	{
		var line = lines[i].trim();
		if(!line.length)
			continue;//empty line

		if(line[0] != "#")
		{
			var words = line.split(" ");
			if( words[0] == "uniform" ) //store which uniforms we found in the code (not used yet)
			{
				var uniform_name = words[2].split(";");
				this.uniforms[ uniform_name[0] ] = words[1];
			}
			else if( words[0] == "attribute" ) //store which streams we found in the code (not used yet)
			{
				var uniform_name = words[2].split(";");
				this.attributes[ uniform_name[0] ] = words[1];
			}
			current_fragment.push(line);
			continue;
		}

		var t = line.split(" ");
		if(t[0] == "#pragma")
		{
			//merge lines and add previous fragment
			var current_fragment_code = current_fragment.join("\n");
			if(current_fragment_code.trim()) //in case is empty this code fragment
				this.fragments.push( { type: GLSLCode.CODE, code: current_fragment_code } ); 

			this.is_dynamic = true;
			this.pragmas[ t[2] ] = true;
			var action = t[1];
			current_fragment.length = 0;
			var pragma_info = { type: GLSLCode.PRAGMA, line: line, action: action, param: t[2] };

			var method = LS.GLSLCode.pragma_methods[ action ];
			if( !method || !method.parse )
			{
				console.warn("#pragma action unknown: ", action );
				continue;
			}
			if( method.parse.call( this, pragma_info, t ) === false )
				continue;
			this.fragments.push( pragma_info ); //add pragma fragment
		}
		else
			current_fragment.push( line ); //add line to current fragment lines
	}

	if(current_fragment.length)
	{
		var current_fragment_code = current_fragment.join("\n");
		if(current_fragment_code.trim()) //in case is empty this code fragment
			this.fragments.push( { type: GLSLCode.CODE, code: current_fragment_code } ); //merge lines and add as fragment
	}

	//done
	return true;
}

GLSLCode.prototype.getFinalCode = function( shader_type, block_flags, context )
{
	if( !this.is_dynamic )
		return this.code;

	var code = "";
	context = context || {};
	var fragments = this.fragments;

	for(var i = 0; i < fragments.length; ++i)
	{
		var fragment = fragments[i];
		if( fragment.type === GLSLCode.CODE ) //regular code
		{
			code += fragment.code;
			continue;
		}

		var pragma_method = GLSLCode.pragma_methods[ fragment.action ];
		if(!pragma_method || !pragma_method.getCode )
			continue;

		var r = pragma_method.getCode.call( this, shader_type, fragment, block_flags, context );
		if( r )
			code += r;
	}

	return code;
}

// PRAGMA METHODS ****************************

GLSLCode.pragma_methods["include"] = {
	parse: function( pragma_info, t )
	{
		if(!t[2])
		{
			console.error("shader include without path");
			return false;
		}

		pragma_info.action_type = GLSLCode.INCLUDE;
		//resolve include
		var include = t[2].substr(1, t[2].length - 2); //safer than JSON.parse
		var fullname = include.split(":");
		var filename = fullname[0];
		var subfile = fullname[1];
		pragma_info.include = filename;
		pragma_info.include_subfile = subfile;
		this.includes[ pragma_info.include ] = true;
	},
	getCode: function( shader_type, fragment, block_flags, context )
	{
		var extra_code = "";

		var filename = fragment.include;
		var ext = LS.ResourcesManager.getExtension( filename );
		if(ext)
		{
			var extra_shadercode = LS.ResourcesManager.getResource( filename, LS.ShaderCode );
			if(!extra_shadercode)
			{
				LS.ResourcesManager.load( filename ); //force load
				return null;
			}
			if(!fragment.include_subfile)
				extra_code = "\n" + extra_shadercode._subfiles[""] + "\n";
			else
			{
				var extra = extra_shadercode._subfiles[ fragment.include_subfile ];
				if(extra === undefined)
					return null;
				extra_code = "\n" + extra + "\n";
			}
		}
		else
		{
			var snippet_code = LS.Shaders.getSnippet( filename );
			if( !snippet_code )
				return null; //snippet not found
			extra_code = "\n" + snippet_code.code + "\n";
		}

		return extra_code;
	}
};

GLSLCode.pragma_methods["define"] = {
	parse: function( pragma_info, t )
	{
		var param1 = t[2];
		var param2 = t[3];
		if(!param1 || !param2)
		{
			console.error("#pragma define missing parameters");
			return false;
		}
		pragma_info.define = [ param1, param2.substr(1, param2.length - 2) ];
	},
	getCode: function( shader_type, fragment, block_flags, context )
	{
		context[ fragment.define[0] ] = fragment.define[1];
	}
}

GLSLCode.pragma_methods["shaderblock"] = {
	parse: function( pragma_info, t )
	{
		if(!t[2])
		{
			console.error("#pragma shaderblock without name");
			return false;
		}
		pragma_info.action_type = GLSLCode.SHADERBLOCK;

		var param = t[2];
		if(param[0] == '"') //one means "shaderblock_name", two means shaderblock_var
		{
			pragma_info.shader_block = [1, param.substr(1, param.length - 2)]; //safer than JSON.parse
			this.shader_blocks[ pragma_info.shader_block[1] ] = true;
		}
		else
		{
			pragma_info.shader_block = [2, param];
			if(t[3]) //thirth parameter for default
			{
				pragma_info.shader_block.push( t[3].substr(1, t[3].length - 2) );
				this.shader_blocks[ pragma_info.shader_block[2] ] = true;
			}
		}
	},
	getCode: function( shader_type, fragment, block_flags, context )
	{
		var shader_block_name = fragment.shader_block[1];
		if( fragment.shader_block[0] == 2 ) //is dynamic shaderblock name
		{
			//dynamic shaderblock name
			if( context[ shader_block_name ] ) //search for the name in the context
				shader_block_name = context[ shader_block_name ];
			else 
				shader_block_name = fragment.shader_block[2]; //if not found use the default

			if(!shader_block_name)
			{
				console.error("ShaderBlock: no context var found: " + shader_block_name );
				return null;
			}
		}
		
		var shader_block = LS.Shaders.getShaderBlock( shader_block_name );
		if(!shader_block)
		{
			//console.error("ShaderCode uses unknown ShaderBlock: ", fragment.shader_block);
			return null;
		}

		var block_code = shader_block.getFinalCode( shader_type, block_flags, context );
		if( !block_code )
			return null;

		//add the define BLOCK_name only if enabled
		if( shader_block.flag_mask & block_flags )
			return "\n#define BLOCK_" + ( shader_block.name.toUpperCase() ) +"\n" + block_code + "\n";
		return block_code + "\n";
	}
};

GLSLCode.pragma_methods["snippet"] = { 
	parse: function( pragma_info, t )
	{
		if(!t[2])
		{
			console.error("#pragma snippet without name");
			return false;
		}
		pragma_info.action_type = GLSLCode.SNIPPET;
		var snippet_name = t[2].substr(1, t[2].length - 2); //safer than JSON.parse
		pragma_info.snippet = snippet_name;
		this.snippets[ snippet_name ] = true;
	},
	getCode: function( shader_type, fragment, block_flags, context )
	{
		var snippet = LS.Shaders.getSnippet( fragment.snippet );
		if(!snippet)
		{
			console.error("ShaderCode uses unknown Snippet: ", fragment.snippet);
			return null;
		}

		return "\n" + snippet.code + "\n";
	}
};

GLSLCode.pragma_methods["event"] = { 
	parse: function( pragma_info, t )
	{
		if(!t[2])
		{
			console.error("#pragma event without name");
			return false;
		}
		pragma_info.action_type = GLSLCode.EVENT;
		var name = t[2].substr(1, t[2].length - 2); //safer than JSON.parse
		pragma_info.event = name;
	},
	getCode: function( shader_type, fragment, block_flags, context )
	{
		//dispatch event
		var code = "\n";
		var mask = 1;
		for(var i = 0, l = LS.Shaders.shader_blocks.length; i < l; ++i)
		{
			var block = LS.Shaders.shader_blocks[i];
			if(block_flags & block.flag_mask)
			{
				if(!block.events)
					continue;
				var block_code = block.events[ fragment.event ];
				if(!block_code)
					continue;
				code += block_code + "\n";
			}
		}
		//catch results
		return code;
	}
};

//not used
GLSLCode.breakLines = function(lines)
{
	//clean (this helps in case a line contains two instructions, like "uniform float a; uniform float b;"
	var clean_lines = [];
	for(var i = 0; i < lines.length; i++)
	{
		var line = lines[i].trim();
		if(!line)
			continue;
		var pos = line.lastIndexOf(";");
		if(pos == -1 || pos == lines.length - 1)
			clean_lines.push(line);
		else
		{
			var sublines = line.split(";");
			for(var j = 0; j < sublines.length; ++j)
			{
				if(sublines[j])
					clean_lines.push( sublines[j] + ";" );
			}
		}
	}
	return clean_lines;
}


// shaders

LS.Shaders.registerSnippet("input","\n\
			//used to store topology input information\n\
			struct Input {\n\
				vec4 color;\n\
				vec3 vertex;\n\
				vec3 normal;\n\
				vec2 uv;\n\
				vec2 uv1;\n\
				\n\
				vec3 camPos;\n\
				vec3 viewDir;\n\
				vec3 worldPos;\n\
				vec3 worldNormal;\n\
				vec4 screenPos;\n\
			};\n\
			\n\
			Input getInput()\n\
			{\n\
				Input IN;\n\
				IN.color = vec4(1.0);\n\
				IN.vertex = v_pos;\n\
				IN.normal = v_normal;\n\
				IN.uv = v_uvs;\n\
				IN.uv1 = IN.uv;\n\
				\n\
				IN.camPos = u_camera_eye;\n\
				IN.viewDir = normalize(u_camera_eye - v_pos);\n\
				IN.worldPos = v_pos;\n\
				IN.worldNormal = normalize(v_normal);\n\
				//IN.screenPos = vec4( (v_screenpos.xy / v_screenpos.w) * 0.5 + vec2(0.5), v_screenpos.zw );  //sometimes we need also z and w, thats why we pass all\n\
				IN.screenPos = vec4( (gl_FragCoord.xy / gl_FragCoord.w) * 0.5 + vec2(0.5), gl_FragCoord.zw );  //sometimes we need also z and w, thats why we pass all\n\
				return IN;\n\
			}\n\
	");

LS.Shaders.registerSnippet("structs","\n\
			//used to store topology input information\n\
			struct Input {\n\
				vec4 color;\n\
				vec3 vertex;\n\
				vec3 normal;\n\
				vec2 uv;\n\
				vec2 uv1;\n\
				\n\
				vec3 camPos;\n\
				vec3 viewDir;\n\
				vec3 worldPos;\n\
				vec3 worldNormal;\n\
				vec4 screenPos;\n\
			};\n\
			\n\
			//used to store surface shading properties\n\
			struct SurfaceOutput {\n\
				vec3 Albedo;\n\
				vec3 Normal; //separated in case there is a normal map\n\
				vec3 Emission;\n\
				vec3 Ambient;\n\
				float Specular;\n\
				float Gloss;\n\
				float Alpha;\n\
				float Reflectivity;\n\
				vec4 Extra; //for special purposes\n\
			};\n\
			\n\
			//used to store light contribution\n\
			//CAREFUL: this one is different than \n\
			struct FinalLight {\n\
				vec3 Color;\n\
				vec3 Ambient;\n\
				float Diffuse; //NdotL\n\
				float Specular; //RdotL\n\
				vec3 Emission;\n\
				vec3 Reflection;\n\
				float Attenuation;\n\
				float Shadow; //1.0 means fully lit\n\
			};\n\
	");

LS.Shaders.registerSnippet("spotFalloff","\n\
			float spotFalloff(vec3 spotDir, vec3 lightDir, float angle_phi, float angle_theta)\n\
			{\n\
				float sqlen = dot(lightDir,lightDir);\n\
				float atten = 1.0;\n\
				\n\
				vec4 spotParams = vec4( angle_phi, angle_theta, 1.0, 0.0 );\n\
				spotParams.w = 1.0 / (spotParams.x-spotParams.y);\n\
				\n\
				vec3 dirUnit = lightDir * sqrt(sqlen); //we asume they are normalized\n\
				float spotDot = dot(spotDir, -dirUnit);\n\
				if (spotDot <= spotParams.y)// spotDot <= cos phi/2\n\
					return 0.0;\n\
				else if (spotDot > spotParams.x) // spotDot > cos theta/2\n\
					return 1.0;\n\
				\n\
				// vertex lies somewhere beyond the two regions\n\
				float ifallof = pow( (spotDot-spotParams.y)*spotParams.w,spotParams.z );\n\
				return ifallof;\n\
			}\n\
	");

LS.Shaders.registerSnippet("getFlatNormal","\n\
			#ifdef STANDARD_DERIVATIVES\n\
				#extension GL_OES_standard_derivatives : enable \n\
				vec3 getFlatNormal(vec3 pos)\n\
				{\n\
					vec3 A = dFdx( pos );\n\
					vec3 B = dFdy( pos );\n\
					return normalize( cross(A,B) );\n\
				}\n\
			#else\n\
				vec3 getFlatNormal(vec3 pos)\n\
				{\n\
					return vec3(0.0);\n\
				}\n\
			#endif\n\
	");

LS.Shaders.registerSnippet("perturbNormal","\n\
			#ifdef STANDARD_DERIVATIVES\n\
				#extension GL_OES_standard_derivatives : enable \n\
			#endif\n\
			\n\
				mat3 cotangent_frame(vec3 N, vec3 p, vec2 uv)\n\
				{\n\
					// get edge vectors of the pixel triangle\n\
					#ifdef STANDARD_DERIVATIVES\n\
					\n\
					vec3 dp1 = dFdx( p );\n\
					vec3 dp2 = dFdy( p );\n\
					vec2 duv1 = dFdx( uv );\n\
					vec2 duv2 = dFdy( uv );\n\
					\n\
					// solve the linear system\n\
					vec3 dp2perp = cross( dp2, N );\n\
					vec3 dp1perp = cross( N, dp1 );\n\
					vec3 T = dp2perp * duv1.x + dp1perp * duv2.x;\n\
					vec3 B = dp2perp * duv1.y + dp1perp * duv2.y;\n\
					#else\n\
					vec3 T = vec3(1.0,0.0,0.0); //this is wrong but its a fake solution\n\
					vec3 B = cross(N,T);\n\
					T = cross(B,N);\n\
					#endif\n\
					 \n\
					// construct a scale-invariant frame \n\
					float invmax = inversesqrt( max( dot(T,T), dot(B,B) ) );\n\
					return mat3( T * invmax, B * invmax, N );\n\
				}\n\
				\n\
				vec3 perturbNormal( vec3 N, vec3 V, vec2 texcoord, vec3 normal_pixel )\n\
				{\n\
					#ifdef USE_POINTS\n\
						return N;\n\
					#endif\n\
					\n\
					// assume N, the interpolated vertex normal and \n\
					// V, the view vector (vertex to eye)\n\
					//vec3 normal_pixel = texture2D(normalmap, texcoord ).xyz;\n\
					normal_pixel = normal_pixel * 255./127. - 128./127.;\n\
					mat3 TBN = cotangent_frame(N, V, texcoord);\n\
					return normalize(TBN * normal_pixel);\n\
				}\n\
		");

LS.Shaders.registerSnippet("bumpNormal","\n\
			#ifdef STANDARD_DERIVATIVES\n\
				#extension GL_OES_standard_derivatives : enable \n\
			#endif\n\
				\n\
				// Calculate the surface normal using screen-space partial derivatives of the height field\n\
				vec3 bumpNormal(vec3 position, vec3 normal, sampler2D texture, vec2 uvs, float factor)\n\
				{\n\
				#ifdef STANDARD_DERIVATIVES\n\
			        vec3 dpdx = dFdx(position);\n\
			        vec3 dpdy = dFdy(position);\n\
					vec3 r1 = cross(dpdy, normal);\n\
					vec3 r2 = cross(normal, dpdx);\n\
					\n\
					vec2 dtdx = dFdx(uvs) * factor;\n\
					vec2 dtdy = dFdy(uvs) * factor;\n\
					\n\
			        float h = texture2D( texture,  uvs ).r;\n\
			        float hdx = texture2D( texture,  uvs + dtdx ).r;\n\
			        float hdy = texture2D( texture,  uvs + dtdy ).r;\n\
					\n\
					return normalize(normal + (r1 * (hdx - h) - r2 * (hdy - h)) / dot(dpdx, r1));\n\
				#else\n\
					return normal;\n\
				#endif\n\
				}\n\
		");

LS.Shaders.registerSnippet("computePointSize","\n\
			float computePointSize(float radius, float w)\n\
			{\n\
				if(radius < 0.0)\n\
					return -radius;\n\
				return u_viewport.w * u_camera_perspective.z * radius / w;\n\
			}\n\
	");