diff --git a/data/shaders/pointlight.vert b/data/shaders/pointlight.vert index c85231b84..9a1ef7daa 100644 --- a/data/shaders/pointlight.vert +++ b/data/shaders/pointlight.vert @@ -18,35 +18,104 @@ in vec3 Position; in float Energy; in vec3 Color; -in vec2 Corner; - flat out vec3 center; flat out float energy; flat out vec3 col; const float zNear = 1.; -void main(void) +// 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) */) { - // Beyond that value, light is too attenuated - float r = 30 * Energy; - center = Position; - energy = Energy; - vec4 Center = ViewMatrix * vec4(Position, 1.); - vec4 ProjectedCornerPosition = ProjectionMatrix * (Center + r * vec4(Corner, 0., 0.)); - float adjustedDepth = ProjectedCornerPosition.z; - if (Center.z > zNear) // Light is in front of the cam - { - adjustedDepth = max(Center.z - r, zNear); + float nz = (lightRadius - nc * lc) / lz; + float pz = (lc * lc + lz * lz - lightRadius * lightRadius) / + (lz - (nz / nc) * lc); + + if (pz > 0.0f) { + float c = -nz * cameraScale / nc; + if (nc > 0.0f) // Left side boundary + return vec2(c, 1.); + else // Right side boundary + return vec2(-1., c); } - else if (Center.z + r > zNear) // Light is behind the cam but in range - { - adjustedDepth = zNear; + 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); + + if (d > 0) { + 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)); + } + return vec2(1., -1.); +} + +// 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); } - ProjectedCornerPosition /= ProjectedCornerPosition.w; - ProjectedCornerPosition.zw = (ProjectionMatrix * vec4(0., 0., adjustedDepth, 1.)).zw; - ProjectedCornerPosition.xy *= ProjectedCornerPosition.w; - col = Color; - gl_Position = ProjectedCornerPosition; + return vec4(0.); +} + + +void main(void) +{ + float radius = 20. * Energy; + 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; } diff --git a/src/graphics/shaders.cpp b/src/graphics/shaders.cpp index c4c3a36ff..ad1969187 100644 --- a/src/graphics/shaders.cpp +++ b/src/graphics/shaders.cpp @@ -1754,7 +1754,6 @@ namespace LightShader GLuint PointLightShader::attrib_Position; GLuint PointLightShader::attrib_Color; GLuint PointLightShader::attrib_Energy; - GLuint PointLightShader::attrib_Corner; GLuint PointLightShader::uniform_ntex; GLuint PointLightShader::uniform_dtex; GLuint PointLightShader::uniform_spec; @@ -1773,7 +1772,6 @@ namespace LightShader attrib_Position = glGetAttribLocation(Program, "Position"); attrib_Color = glGetAttribLocation(Program, "Color"); attrib_Energy = glGetAttribLocation(Program, "Energy"); - attrib_Corner = glGetAttribLocation(Program, "Corner"); uniform_ntex = glGetUniformLocation(Program, "ntex"); uniform_dtex = glGetUniformLocation(Program, "dtex"); uniform_spec = glGetUniformLocation(Program, "spec"); @@ -1782,10 +1780,6 @@ namespace LightShader glGenVertexArrays(1, &vao); glBindVertexArray(vao); - glBindBuffer(GL_ARRAY_BUFFER, SharedObject::billboardvbo); - glEnableVertexAttribArray(attrib_Corner); - glVertexAttribPointer(attrib_Corner, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0); - glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, MAXLIGHT * sizeof(PointLightInfo), 0, GL_DYNAMIC_DRAW); diff --git a/src/graphics/shaders.hpp b/src/graphics/shaders.hpp index 914eb323d..8b0c0a54f 100644 --- a/src/graphics/shaders.hpp +++ b/src/graphics/shaders.hpp @@ -434,7 +434,6 @@ namespace LightShader public: static GLuint Program; static GLuint attrib_Position, attrib_Energy, attrib_Color; - static GLuint attrib_Corner; static GLuint uniform_ntex, uniform_dtex, uniform_spec, uniform_screen; static GLuint vbo; static GLuint vao;