in vec3 Position; in float Energy; in vec3 Color; in float Radius; flat out vec3 center; flat out float energy; flat out vec3 col; flat out float radius; const float zNear = 1.; // Code borrowed from https://software.intel.com/en-us/articles/deferred-rendering-for-current-and-future-rendering-pipelines // Maths explanations are found here http://www.gamasutra.com/view/feature/131351/the_mechanics_of_robust_stencil_.php?page=6 vec2 UpdateClipRegionRoot(float nc, /* Tangent plane x/y normal coordinate (view space) */ float lc, /* Light x/y coordinate (view space) */ float lz, /* Light z coordinate (view space) */ float lightRadius, float cameraScale /* Project scale for coordinate (_11 or _22 for x/y respectively) */) { float nz = (lightRadius - nc * lc) / lz; float pz = (lc * lc + lz * lz - lightRadius * lightRadius) / (lz - (nz / nc) * lc); if (pz > 0.) { float c = -nz * cameraScale / nc; if (nc > 0.) // Left side boundary return vec2(c, 1.); else // Right side boundary return vec2(-1., c); } return vec2(-1., 1.); } vec2 UpdateClipRegion(float lc, /* Light x/y coordinate (view space) */ float lz, /* Light z coordinate (view space) */ float lightRadius, float cameraScale /* Project scale for coordinate (_11 or _22 for x/y respectively) */) { float rSq = lightRadius * lightRadius; float lcSqPluslzSq = lc * lc + lz * lz; float d = rSq * lc * lc - lcSqPluslzSq * (rSq - lz * lz); // The camera is inside lignt bounding sphere, quad fits whole screen if (d <= 0.) return vec2(-1., 1.); float a = lightRadius * lc; float b = sqrt(d); float nx0 = (a + b) / lcSqPluslzSq; float nx1 = (a - b) / lcSqPluslzSq; vec2 clip0 = UpdateClipRegionRoot(nx0, lc, lz, lightRadius, cameraScale); vec2 clip1 = UpdateClipRegionRoot(nx1, lc, lz, lightRadius, cameraScale); return vec2(max(clip0.x, clip1.x), min(clip0.y, clip1.y)); } // Returns bounding box [min.x, max.x, min.y, max.y] in clip [-1, 1] space. vec4 ComputeClipRegion(vec3 lightPosView, float lightRadius) { if (lightPosView.z + lightRadius >= zNear) { vec2 clipX = UpdateClipRegion(lightPosView.x, lightPosView.z, lightRadius, ProjectionMatrix[0][0]); vec2 clipY = UpdateClipRegion(lightPosView.y, lightPosView.z, lightRadius, ProjectionMatrix[1][1]); return vec4(clipX, clipY); } return vec4(0.); } void main(void) { vec4 Center = ViewMatrix * vec4(Position, 1.); Center /= Center.w; vec2 ProjectedCornerPosition; vec4 clip = ComputeClipRegion(Center.xyz, Radius); switch (gl_VertexID) { case 0: ProjectedCornerPosition = clip.xz; break; case 1: ProjectedCornerPosition = clip.xw; break; case 2: ProjectedCornerPosition = clip.yz; break; case 3: ProjectedCornerPosition = clip.yw; break; } // Work out nearest depth for quad Z // Clamp to near plane in case this light intersects the near plane... don't want our quad to be clipped float quadDepth = max(zNear, Center.z - Radius); // Project quad depth into clip space vec4 quadClip = ProjectionMatrix * vec4(0., 0., quadDepth, 1.0f); gl_Position = vec4(ProjectedCornerPosition, quadClip.z / quadClip.w, 1.); col = Color; center = Position; energy = Energy; radius = Radius; }