/*
    This file is part of Mitsuba, a physically based rendering system.

    Copyright (c) 20072011 by Wenzel Jakob and others.

    Mitsuba is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License Version 3
    as published by the Free Software Foundation.

    Mitsuba is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifdef __GNUC__
	#define MAYBE_UNUSED __attribute__((used))
#else
	#define MAYBE_UNUSED
#endif

static const char *sh_paraboloid_vert MAYBE_UNUSED =
	"void main() {\n"
	"	gl_Position = gl_ModelViewMatrix * gl_Vertex;\n"
	"}\n";

static const char *sh_paraboloid_geom MAYBE_UNUSED =
	"#extension GL_EXT_geometry_shader4 : enable\n"
	"\n"
	"/* -> Fragment shader (triangle data, projected position) */\n"
	"varying vec3 p0, edge1, edge2;\n"
	"varying vec2 pos;\n"
	"\n"
	"vec2 project(vec3 p) {\n"
	"	return p.xy / (length(p) + p.z);\n"
	"}\n"
	"\n"
	"float cosAlpha(vec3 v1, vec3 v2) {\n"
	"	vec3 v3 = cross(v1, v2);\n"
	"	return v3.z / length(v3);\n"
	"}\n"
	"\n"
	"void main() {\n"
	"	vec3 cur = gl_PositionIn[0].xyz;\n"
	"	cur.z = -cur.z;\n"
	"\n"
	"	bool curIsInside = cur.z > 0.0;\n"
	"	vec3 vertices[4];\n"
	"	int vCount = 0;\n"
	"\n"
	"	/* Clip the geometry against the forward hemisphere */\n"
	"	for (int i=0; i<3; ++i) {\n"
	"		vec3 next = gl_PositionIn[i < 2 ? i+1 : 0].xyz;\n"
	"		next.z = -next.z;\n"
	"		bool nextIsInside = next.z > 0.0;\n"
	"\n"
	"		if (curIsInside && nextIsInside) {\n"
	"			vertices[vCount++] = next;\n"
	"		} else if (curIsInside != nextIsInside) {\n"
	"			float t = -cur.z / (next.z-cur.z);\n"
	"\n"
	"			vertices[vCount++] = \n"
	"				vec3((1-t)*cur.xy + t*next.xy, 0.0);\n"
	"\n"
	"			if (nextIsInside)\n"
	"				vertices[vCount++] = next;\n"
	"		}\n"
	"\n"
	"		cur = next;\n"
	"		curIsInside = nextIsInside;\n"
	"	}\n"
	"\n"
	"	if (vCount < 3)\n"
	"		return;\n"
	"\n"
	"	/* Transform into projection space */\n"
	"	vec2 projected[4];\n"
	"	for (int i=0; i<vCount; ++i)\n"
	"		projected[i] = project(vertices[i]);\n"
	"\n"
	"	/* Find the longest edge and construct an OBB rotation matrix */\n"
	"	float largest = 0.0;\n"
	"	mat2 rot;\n"
	"	rot[0] = vec2(0.0);\n"
	"\n"
	"	for (int i=0; i<vCount; i++) {\n"
	"		int next = (i < vCount-1) ? i+1 : 0;\n"
	"		vec2 p0 = projected[i],\n"
	"		     p1 = projected[next],\n"
	"		     d  = p1-p0;\n"
	"\n"
	"		float l2 = dot(d, d);\n"
	"		if (l2 > largest) {\n"
	"			largest = l2;\n"
	"			rot[0] = d;\n"
	"		}\n"
	"	}\n"
	"	rot[0] /= sqrt(largest);\n"
	"	rot[1]  = vec2(rot[0].y, -rot[0].x);\n"
	"\n"
	"	/* Find a bounding rectangle for each edge and expand the OBB */\n"
	"	vec2 bmax = vec2(-1.0, -1.0), bmin = vec2(1.0, 1.0);\n"
	"	for (int i=0; i<vCount; i++) {\n"
	"		int next = (i < vCount-1) ? i+1 : 0;\n"
	"		vec2 a  = projected[i],\n"
	"		     b  = projected[next],\n"
	"		     p0 = a * rot,\n"
	"		     p1 = b * rot,\n"
	"			 p2 = p0, p3 = p1;\n"
	"\n"
	"		float ca = cosAlpha(vertices[i], vertices[next]);\n"
	"		if (ca != 0.0) {\n"
	"			vec2 d = vec2(b.y-a.y, a.x-b.x) * rot;\n"
	"			float l = length(d), ica = 1.0 / ca;\n"
	"			d *= (ica - sign(ica) * sqrt(ica*ica - 0.25 * l*l)) / l;\n"
	"			p2 += d; p3 += d;\n"
	"		}\n"
	"\n"
	"		bmax = max(bmax, max(max(p0, p1), max(p2, p3)));\n"
	"		bmin = min(bmin, min(min(p0, p1), min(p2, p3)));\n"
	"	}\n"
	"\n"
	"	/* Plumb triangle data to the fragment shader */\n"
	"	p0 = gl_PositionIn[0].xyz;\n"
	"	edge1 = gl_PositionIn[1].xyz - gl_PositionIn[0].xyz;\n"
	"	edge2 = gl_PositionIn[2].xyz - gl_PositionIn[0].xyz;\n"
	"	p0.z = -p0.z; edge1.z = -edge1.z; edge2.z = -edge2.z;\n"
	"\n"
	"	/* Emit a quad */\n"
	"	pos = rot[0] * bmin.x + rot[1] * bmin.y;\n"
	"	gl_Position = vec4(pos, 0.5, 1.0);\n"
	"	EmitVertex();\n"
	"	pos = rot[0] * bmin.x + rot[1] * bmax.y;\n"
	"	gl_Position = vec4(pos, 0.5, 1.0);\n"
	"	EmitVertex();\n"
	"	pos = rot[0] * bmax.x + rot[1] * bmin.y;\n"
	"	gl_Position = vec4(pos, 0.5, 1.0);\n"
	"	EmitVertex();\n"
	"	pos = rot[0] * bmax.x + rot[1] * bmax.y;\n"
	"	gl_Position = vec4(pos, 0.5, 1.0);\n"
	"	EmitVertex();\n"
	"	EndPrimitive();\n"
	"}\n";

static const char *sh_paraboloid_frag MAYBE_UNUSED =
	"/* Triangle data (from geometry shader) */\n"
	"varying vec3 p0, edge1, edge2;\n"
	"\n"
	"/* Projection space position */\n"
	"varying vec2 pos;\n"
	"\n"
	"/* Depth range */\n"
	"uniform float minDepth;\n"
	"uniform float invDepthRange;\n"
	"\n"
	"void main() {\n"
	"	float r2 = dot(pos, pos);\n"
	"	if (r2 > 1.0)\n"
	"		discard;\n"
	"\n"
	"	/* Turn into a direction */\n"
	"	float cosTheta = (1.0-r2) / (1.0+r2);\n"
	"	float factor = sqrt((1.0-cosTheta*cosTheta) / r2);\n"
	"	vec3 d = vec3(pos * factor, cosTheta);\n"
	"\n"
	"	vec3 pvec = cross(d, edge2);\n"
	"	float det = dot(edge1, pvec);\n"
	"	if (det == 0.0)\n"
	"		discard;\n"
	"\n"
	"	float inv_det = 1.0 / det;\n"
	"	float u = -dot(p0, pvec) * inv_det;\n"
	"\n"
	"	if (u < 0.0 || u > 1.0)\n"
	"		discard;\n"
	"\n"
	"	vec3 qvec = cross(p0, edge1);\n"
	"	float v = -dot(d, qvec) * inv_det;\n"
	"	if (v < 0.0 || u+v > 1.0)\n"
	"		discard;\n"
	"\n"
	"	float t = -dot(edge2, qvec) * inv_det - minDepth;\n"
	"	if (!(t > 0.0)) // catch NaNs as well \n"
	"		discard;\n"
	"	\n"
	"	float depth = t * invDepthRange;\n"
	"\n"
	"	float dx = dFdx(depth), dy = dFdy(depth);\n"
	"	gl_FragDepth = depth + sqrt(dx*dx+dy*dy);\n"
	"}\n";

static const char *sh_directional_vert MAYBE_UNUSED =
	"void main() {\n"
	"	gl_Position = ftransform();\n"
	"}\n";

static const char *sh_directional_frag MAYBE_UNUSED =
	"void main() {\n"
	"	float depth = gl_FragCoord.z,\n"
	"	      dx = dFdx(depth), dy = dFdy(depth);\n"
	"	gl_FragDepth = depth + sqrt(dx*dx+dy*dy);\n"
	"}\n";

static const char *sh_cube_6pass_vert MAYBE_UNUSED =
	"/* Transformation matrix for the current cube map face */\n"
	"uniform mat4 transform;\n"
	"\n"
	"/* Depth projection axis */\n"
	"uniform vec4 projDir;\n"
	"\n"
	"/* -> Fragment shader */\n"
	"varying float depth;\n"
	"\n"
	"void main() {\n"
	"	vec4 pos = gl_ModelViewMatrix * gl_Vertex;\n"
	"	gl_Position = transform * pos;\n"
	"	depth = dot(projDir, pos);\n"
	"}\n";

static const char *sh_cube_6pass_frag MAYBE_UNUSED =
	"/* Linear depth from the vertex shader */\n"
	"varying float depth;\n"
	"\n"
	"void main() {\n"
	"	float dx = dFdx(depth), dy = dFdy(depth);\n"
	"\n"
	"	#ifndef DEPTH_CUBEMAPS_UNSUPPORTED\n"
	"		gl_FragDepth = depth + sqrt(dx*dx+dy*dy);\n"
	"	#else\n"
	"		gl_FragDepth = gl_FragColor.r = depth + sqrt(dx*dx+dy*dy);\n"
	"	#endif\n"
	"}\n";

static const char *sh_cube_1pass_vert MAYBE_UNUSED =
	"void main() {\n"
	"	gl_Position = gl_ModelViewMatrix * gl_Vertex;\n"
	"}\n";

static const char *sh_cube_1pass_geom MAYBE_UNUSED =
	"#extension GL_EXT_geometry_shader4 : enable\n"
	"\n"
	"/* Transformation matrix for each cube map face */\n"
	"uniform mat4 transform[6];\n"
	"\n"
	"/* Depth projection axis */\n"
	"uniform vec4 projDir[6];\n"
	"\n"
	"/* -> Fragment shader */\n"
	"varying float depth;\n"
	"\n"
	"void main() {\n"
	"	depth = 0.0; // avoid an (incorrect?) compiler warning\n"
	"\n"
	"	/* Replicate the geometry six times and rasterize to each cube map layer */\n"
	"	for (int side = 0; side < 6; side++) {\n"
	"		gl_Layer = side;\n"
	"		for (int i = 0; i < gl_VerticesIn; i++) {\n"
	"			gl_Position = transform[side] * gl_PositionIn[i];\n"
	"			depth = dot(projDir[side], gl_PositionIn[i]);\n"
	"			EmitVertex();\n"
	"		}\n"
	"		EndPrimitive();\n"
	"	}\n"
	"}\n";

static const char *sh_cube_1pass_frag MAYBE_UNUSED =
	"/* Linear depth from the vertex shader */\n"
	"varying float depth;\n"
	"\n"
	"void main() {\n"
	"	float dx = dFdx(depth), dy = dFdy(depth);\n"
	"	gl_FragDepth = depth + sqrt(dx*dx+dy*dy);\n"
	"}\n";

static const char *sh_hemicube_1pass_vert MAYBE_UNUSED =
	"void main() {\n"
	"	gl_Position = gl_ModelViewMatrix * gl_Vertex;\n"
	"}\n";

static const char *sh_hemicube_1pass_geom MAYBE_UNUSED =
	"#extension GL_EXT_geometry_shader4 : enable\n"
	"\n"
	"/* Transformation matrix for each cube map face */\n"
	"uniform mat4 transform[5];\n"
	"\n"
	"/* Depth projection axis */\n"
	"uniform vec4 projDir[5];\n"
	"\n"
	"/* -> Fragment shader */\n"
	"varying float depth;\n"
	"\n"
	"void main() {\n"
	"	depth = 0.0; // avoid an (incorrect?) compiler warning\n"
	"\n"
	"	/* Replicate the geometry six times and rasterize to each cube map layer */\n"
	"	for (int side = 0; side < 5; side++) {\n"
	"		gl_Layer = side != 4 ? side : 5;\n"
	"		for (int i = 0; i < gl_VerticesIn; i++) {\n"
	"			gl_Position = transform[side] * gl_PositionIn[i];\n"
	"			depth = dot(projDir[side], gl_PositionIn[i]);\n"
	"			EmitVertex();\n"
	"		}\n"
	"		EndPrimitive();\n"
	"	}\n"
	"}\n";

static const char *sh_hemicube_1pass_frag MAYBE_UNUSED =
	"/* Linear depth from the vertex shader */\n"
	"varying float depth;\n"
	"\n"
	"void main() {\n"
	"	float dx = dFdx(depth), dy = dFdy(depth);\n"
	"	gl_FragDepth = depth + sqrt(dx*dx+dy*dy);\n"
	"}\n";

static const char *sh_background_vert MAYBE_UNUSED =
	"uniform mat4 clipToWorld;\n"
	"varying vec3 position;\n"
	"\n"
	"void main() {\n"
	"	vec4 pos = ftransform();\n"
	"	gl_Position = pos;\n"
	"\n"
	"	vec4 tmp = clipToWorld * pos;\n"
	"	position = tmp.xyz / tmp.w;\n"
	"}\n"
	"\n";

static const char *sh_background_frag MAYBE_UNUSED =
	"#extension GL_EXT_gpu_shader4 : enable\n"
	"\n"
	"varying vec3 position;\n"
	"\n"
	"#if defined(DIRECTIONAL_CAMERA)\n"
	"	uniform vec3 camDirection;\n"
	"#else\n"
	"	uniform vec3 camPosition;\n"
	"#endif\n"
	"\n"
	"uniform float emitterScale;\n"
	"\n"
	"{{ SUPPLEMENTAL CODE }}\n"
	"\n"
	"void main() {\n"
	"	vec3 result;\n"
	"\n"
	"	#if !defined(DIRECTIONAL_CAMERA)\n"
	"		result = BACKGROUND_EVAL_NAME(normalize(position - camPosition));\n"
	"	#else\n"
	"		result = BACKGROUND_EVAL_NAME(camDirection);\n"
	"	#endif\n"
	"\n"
	"	gl_FragColor = vec4(result * emitterScale, 1.0);\n"
	"}\n";

static const char *sh_unsupported_vert MAYBE_UNUSED =
	"/* Uniform parameters */\n"
	"uniform mat4 instanceTransform;\n"
	"\n"
	"void main() {\n"
	"	vec4 pos = instanceTransform * gl_Vertex;\n"
	"	gl_Position = gl_ModelViewProjectionMatrix * pos;\n"
	"}\n";

static const char *sh_unsupported_frag MAYBE_UNUSED =
	"void main() {\n"
	"	gl_FragColor = vec4(0.0);\n"
	"}\n";

static const char *sh_render_vert MAYBE_UNUSED =
	"/* Uniform parameters */\n"
	"uniform mat4 vplTransform;\n"
	"uniform mat4 instanceTransform;\n"
	"\n"
	"#ifndef FACE_NORMALS\n"
	"	/* -> Fragment program */\n"
	"	varying vec3 posInVPLSpace;\n"
	"	varying vec3 posInWorldSpace;\n"
	"	varying vec2 uv;\n"
	"	varying vec3 normal;\n"
	"#else\n"
	"	/* -> Geometry program */\n"
	"	varying vec3 posInVPLSpace_vertex;\n"
	"	varying vec3 posInWorldSpace_vertex;\n"
	"	varying vec2 uv_vertex;\n"
	"#endif\n"
	"\n"
	"#ifdef ANISOTROPIC\n"
	"	varying vec3 tangent;\n"
	"#endif\n"
	"\n"
	"#ifdef VERTEX_COLORS\n"
	"	#ifndef FACE_NORMALS\n"
	"		varying vec3 vertexColor;\n"
	"	#else\n"
	"		varying vec3 vertexColor_vertex;\n"
	"	#endif\n"
	"#endif\n"
	"\n"
	"void main() {\n"
	"	vec4 pos = instanceTransform * gl_Vertex;\n"
	"	gl_Position = gl_ModelViewProjectionMatrix * pos;\n"
	"\n"
	"	#ifndef FACE_NORMALS\n"
	"		posInWorldSpace = pos.xyz;\n"
	"		posInVPLSpace   = (vplTransform * pos).xyz;\n"
	"		uv = gl_MultiTexCoord0.xy;\n"
	"\n"
	"		/* Multiply by instanceTransform (only rigid transformations allowed) */\n"
	"		normal = (instanceTransform * vec4(gl_Normal, 0.0)).xyz;\n"
	"\n"
	"		#ifdef VERTEX_COLORS\n"
	"			vertexColor = gl_Color.rgb;\n"
	"		#endif\n"
	"	#else\n"
	"		posInWorldSpace_vertex = pos.xyz;\n"
	"		posInVPLSpace_vertex   = (vplTransform * pos).xyz;\n"
	"		uv_vertex = gl_MultiTexCoord0.xy;\n"
	"\n"
	"		#ifdef VERTEX_COLORS\n"
	"			vertexColor_vertex = gl_Color.rgb;\n"
	"		#endif\n"
	"	#endif\n"
	"\n"
	"	#ifdef ANISOTROPIC\n"
	"		tangent = gl_MultiTexCoord1.xyz;\n"
	"	#endif\n"
	"}\n";

static const char *sh_render_geom MAYBE_UNUSED =
	"#extension GL_EXT_geometry_shader4 : enable\n"
	"\n"
	"/* From vertex program */\n"
	"varying in vec3 posInVPLSpace_vertex[3];\n"
	"varying in vec3 posInWorldSpace_vertex[3];\n"
	"varying in vec2 uv_vertex[3];\n"
	"\n"
	"/* To fragment program */\n"
	"varying out vec3 posInVPLSpace;\n"
	"varying out vec3 posInWorldSpace;\n"
	"varying out vec3 normal;\n"
	"varying out vec2 uv;\n"
	"\n"
	"#ifdef VERTEX_COLORS\n"
	"	varying in vec3 vertexColor_vertex[3];\n"
	"	varying out vec3 vertexColor;\n"
	"#endif\n"
	"\n"
	"void main() {\n"
	"	vec3 edge1 = posInWorldSpace_vertex[0]-posInWorldSpace_vertex[1];\n"
	"	vec3 edge2 = posInWorldSpace_vertex[0]-posInWorldSpace_vertex[2];\n"
	"\n"
	"	normal = cross(edge1, edge2);\n"
	"	for (int i=0; i<gl_VerticesIn; ++i) {\n"
	"		gl_Position = gl_PositionIn[i];\n"
	"		posInWorldSpace = posInWorldSpace_vertex[i];\n"
	"		posInVPLSpace = posInVPLSpace_vertex[i];\n"
	"		uv = uv_vertex[i];\n"
	"\n"
	"		#ifdef VERTEX_COLORS\n"
	"			vertexColor = vertexColor_vertex[i];\n"
	"		#endif\n"
	"\n"
	"		EmitVertex();\n"
	"	}\n"
	"	EndPrimitive();\n"
	"}\n";

static const char *sh_render_frag MAYBE_UNUSED =
	"#extension GL_EXT_gpu_shader4 : enable\n"
	"\n"
	"#define EPSILON 0.001\n"
	"\n"
	"/* Some helper functions for BSDF implementations */\n"
	"float cosTheta(vec3 v) { return v.z; }\n"
	"float sinTheta2(vec3 v) { return 1.0-v.z*v.z; }\n"
	"float sinTheta(vec3 v) { return sqrt(max(0.0, sinTheta2(v))); }\n"
	"float tanTheta(vec3 v) { return sinTheta(v)/cosTheta(v); }\n"
	"float sinPhi(vec3 v) { return v.y/sinTheta(v); }\n"
	"float cosPhi(vec3 v) { return v.x/sinTheta(v); }\n"
	"const float pi = 3.141592653589;\n"
	"const float inv_pi = 0.318309886183791;\n"
	"const float inv_twopi = 0.159154943091895;\n"
	"const float inv_fourpi = 0.0795774715459477;\n"
	"\n"
	"/* From the vertex program */\n"
	"varying vec3 posInVPLSpace;\n"
	"varying vec3 posInWorldSpace;\n"
	"varying vec3 normal;\n"
	"varying vec2 uv;\n"
	"\n"
	"#ifdef ANISOTROPIC\n"
	"	varying vec3 tangent;\n"
	"#endif\n"
	"\n"
	"#ifdef VERTEX_COLORS\n"
	"	varying vec3 vertexColor;\n"
	"#endif\n"
	"\n"
	"/* Uniform parameters */\n"
	"uniform mat3 vplFrame;\n"
	"uniform vec3 vplPower;\n"
	"uniform vec2 vplUV;\n"
	"uniform vec3 vplWi;\n"
	"uniform float minDistSqr;\n"
	"uniform float emitterScale;\n"
	"\n"
	"uniform mat4 vplTransform;\n"
	"\n"
	"#ifdef CUBEMAP_VPL\n"
	"	uniform samplerCube shadowMap;\n"
	"#else\n"
	"	uniform sampler2D shadowMap;\n"
	"#endif\n"
	"\n"
	"#ifdef DIRECTIONAL_VPL\n"
	"	uniform vec3 vplDirection;\n"
	"#else\n"
	"	uniform vec3 vplPosition;\n"
	"	uniform vec2 depthRange;\n"
	"#endif\n"
	"\n"
	"#ifdef DIRECTIONAL_CAMERA\n"
	"	uniform vec3 camDirection;\n"
	"#else\n"
	"	uniform vec3 camPosition;\n"
	"#endif\n"
	"\n"
	"#if defined(DIRECTIONAL_VPL)\n"
	"	bool isShadowed(vec3 p) {\n"
	"		p = p * 0.5 + 0.5;\n"
	"		return texture2D(shadowMap, p.xy).r * (1 + EPSILON) < p.z;\n"
	"	}\n"
	"#elif defined(PARABOLOIDAL_VPL)\n"
	"	bool isShadowed(vec3 p) {\n"
	"		float depth = texture2D(shadowMap, (p.xy / (-p.z + length(p))) * 0.5 + 0.5).r;\n"
	"		depth = (depth * (depthRange[1]-depthRange[0]) + depthRange[0]) * (1.0+EPSILON);\n"
	"		return dot(p, p) > depth * depth && p.z < 0.0;\n"
	"	}\n"
	"#elif defined(CUBEMAP_VPL)\n"
	"	bool isShadowed(vec3 d) {\n"
	"		float depth = textureCube(shadowMap, vec3(d.x, -d.y, d.z)).r;\n"
	"		float ref_depth = max(max(abs(d.x), abs(d.y)), abs(d.z));\n"
	"		depth = (depth * (depthRange[1]-depthRange[0]) + depthRange[0]) * (1.0+EPSILON);\n"
	"		return depth < ref_depth;\n"
	"	}\n"
	"#endif\n"
	"\n"
	"{{ SUPPLEMENTAL CODE }}\n"
	"\n"
	"void main() {\n"
	"	/* Set up an ONB */\n"
	"	vec3 N = normalize(normal);\n"
	"	mat3 frame;\n"
	"\n"
	"	#ifdef ANISOTROPIC\n"
	"		/* Use the per-vertex tangent information to construct a frame */\n"
	"\n"
	"		frame[0] = normalize(tangent - dot(tangent, N)*N);\n"
	"	#else\n"
	"		/* The material is isotropic -- any frame will do */\n"
	"\n"
	"		if (abs(N.x) > abs(N.y)) {\n"
	"			float invLen = 1.0 / sqrt(N.x*N.x + N.z*N.z);\n"
	"			frame[0] = vec3(-N.z * invLen, 0.0, N.x * invLen);\n"
	"		} else {\n"
	"			float invLen = 1.0 / sqrt(N.y*N.y + N.z*N.z);\n"
	"			frame[0] = vec3(0.0, -N.z * invLen, N.y * invLen);\n"
	"		}\n"
	"	#endif\n"
	"\n"
	"	frame[1] = cross(N, frame[0]);\n"
	"	frame[2] = N;\n"
	"\n"
	"	/* Compute the incident direction in local coordinates (at the point being rendered) */\n"
	"	vec3 wiWorld;\n"
	"	#ifdef DIRECTIONAL_CAMERA\n"
	"		wiWorld = -camDirection;\n"
	"	#else\n"
	"		wiWorld = normalize(camPosition - posInWorldSpace);\n"
	"	#endif\n"
	"	vec3 wi = wiWorld * frame;\n"
	"\n"
	"	vec3 emission;\n"
	"\n"
	"	#if defined(EMITTER_AREA_EVAL_NAME) && defined(EMITTER_DIR_EVAL_NAME)\n"
	"		emission = EMITTER_AREA_EVAL_NAME(uv) *\n"
	"					EMITTER_DIR_EVAL_NAME(wi) * emitterScale;\n"
	"	#else\n"
	"		emission = vec3(0.0);\n"
	"	#endif\n"
	"\n"
	"	if (isShadowed(posInVPLSpace)) {\n"
	"		gl_FragColor = vec4(emission, 1.0);\n"
	"		return;\n"
	"	}\n"
	"\n"
	"	/* Compute the outgoing direction in local coordinates (at the point being rendered) */\n"
	"	vec3 woWorld;\n"
	"\n"
	"	#ifdef DIRECTIONAL_VPL\n"
	"		woWorld = -vplDirection;\n"
	"	#else\n"
	"		woWorld = vplPosition - posInWorldSpace;\n"
	"		float distSqr = dot(woWorld, woWorld);\n"
	"		woWorld /= sqrt(distSqr);\n"
	"	#endif\n"
	"	vec3 wo = woWorld * frame;\n"
	"\n"
	"	/* Compute the outgoing direction in local coordinates (at the VPL) */\n"
	"	vec3 vplWo = -(woWorld * vplFrame); /* The parentheses are required or incorrect code is generated on OSX .. */\n"
	"\n"
	"	vec3 result = vplPower;\n"
	"\n"
	"	#ifdef EMITTER_VPL\n"
	"		#ifdef VPL_ON_SURFACE\n"
	"			result *= VPL_EVAL_NAME(vplWo) * cosTheta(vplWo);\n"
	"		#else\n"
	"			result *= VPL_EVAL_NAME(vplWo);\n"
	"		#endif\n"
	"	#else\n"
	"		result *= VPL_EVAL_NAME(vplUV, vplWi, vplWo);\n"
	"	#endif\n"
	"\n"
	"	result *= BSDF_EVAL_NAME(uv, wi, wo);\n"
	"\n"
	"	#ifndef DIRECTIONAL_VPL\n"
	"		result *= (1.0 / max(distSqr, minDistSqr));\n"
	"	#endif\n"
	"\n"
	"	gl_FragColor = vec4(result + emission, 1.0);\n"
	"}\n";

