From c3f6cab19913921dc2668cf1a40dedff5d177bb9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 28 May 2014 15:20:09 -0700 Subject: [PATCH] Working on cascading shadow maps. --- interface/resources/shaders/shadow_map.frag | 12 +- interface/resources/shaders/shadow_map.vert | 10 +- interface/src/Application.cpp | 150 +++++++++++--------- interface/src/Application.h | 6 +- interface/src/renderer/Model.cpp | 9 +- interface/src/voxels/VoxelSystem.cpp | 10 +- 6 files changed, 108 insertions(+), 89 deletions(-) diff --git a/interface/resources/shaders/shadow_map.frag b/interface/resources/shaders/shadow_map.frag index fb3474d9ef..f53c66b095 100644 --- a/interface/resources/shaders/shadow_map.frag +++ b/interface/resources/shaders/shadow_map.frag @@ -13,15 +13,11 @@ uniform sampler2DShadow shadowMap; -// the inverse of the size of the shadow map -const float shadowScale = 1.0 / 2048.0; - varying vec4 shadowColor; void main(void) { - gl_FragColor = mix(shadowColor, gl_Color, 0.25 * - (shadow2D(shadowMap, gl_TexCoord[0].stp + vec3(-shadowScale, -shadowScale, 0.0)).r + - shadow2D(shadowMap, gl_TexCoord[0].stp + vec3(-shadowScale, shadowScale, 0.0)).r + - shadow2D(shadowMap, gl_TexCoord[0].stp + vec3(shadowScale, -shadowScale, 0.0)).r + - shadow2D(shadowMap, gl_TexCoord[0].stp + vec3(shadowScale, shadowScale, 0.0)).r)); + gl_FragColor = mix(shadowColor, gl_Color, shadow2D(shadowMap, gl_TexCoord[0].stp).r * + shadow2D(shadowMap, gl_TexCoord[1].stp).r * + shadow2D(shadowMap, gl_TexCoord[2].stp).r * + shadow2D(shadowMap, gl_TexCoord[3].stp).r); } diff --git a/interface/resources/shaders/shadow_map.vert b/interface/resources/shaders/shadow_map.vert index abe5f99b44..e7efcd7c0f 100644 --- a/interface/resources/shaders/shadow_map.vert +++ b/interface/resources/shaders/shadow_map.vert @@ -21,10 +21,16 @@ void main(void) { vec4 normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0)); gl_FrontColor = shadowColor + gl_Color * (gl_LightSource[0].diffuse * max(0.0, dot(normal, gl_LightSource[0].position))); - // generate the shadow texture coordinate using the eye position + // generate the shadow texture coordinates using the eye position vec4 eyePosition = gl_ModelViewMatrix * gl_Vertex; gl_TexCoord[0] = vec4(dot(gl_EyePlaneS[0], eyePosition), dot(gl_EyePlaneT[0], eyePosition), - dot(gl_EyePlaneR[0], eyePosition), 1.0); + dot(gl_EyePlaneR[0], eyePosition), 1.0); + gl_TexCoord[1] = vec4(dot(gl_EyePlaneS[1], eyePosition), dot(gl_EyePlaneT[1], eyePosition), + dot(gl_EyePlaneR[1], eyePosition), 1.0); + gl_TexCoord[2] = vec4(dot(gl_EyePlaneS[2], eyePosition), dot(gl_EyePlaneT[2], eyePosition), + dot(gl_EyePlaneR[2], eyePosition), 1.0); + gl_TexCoord[3] = vec4(dot(gl_EyePlaneS[3], eyePosition), dot(gl_EyePlaneT[3], eyePosition), + dot(gl_EyePlaneR[3], eyePosition), 1.0); // use the fixed function transform gl_Position = ftransform(); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fac0ef154f..a1c2507822 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2256,94 +2256,104 @@ void Application::updateShadowMap() { glEnable(GL_DEPTH_TEST); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glViewport(0, 0, fbo->width(), fbo->height()); - glm::vec3 lightDirection = -getSunDirection(); glm::quat rotation = rotationBetween(IDENTITY_FRONT, lightDirection); glm::quat inverseRotation = glm::inverse(rotation); - float nearScale = 0.0f; - const float MAX_SHADOW_DISTANCE = 2.0f; - float farScale = (MAX_SHADOW_DISTANCE - _viewFrustum.getNearClip()) / - (_viewFrustum.getFarClip() - _viewFrustum.getNearClip()); - loadViewFrustum(_myCamera, _viewFrustum); - glm::vec3 points[] = { - glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), nearScale), - glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), nearScale), - glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), nearScale), - glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), nearScale), - glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), farScale), - glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), farScale), - glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), farScale), - glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), farScale) }; - glm::vec3 center; - for (size_t i = 0; i < sizeof(points) / sizeof(points[0]); i++) { - center += points[i]; - } - center /= (float)(sizeof(points) / sizeof(points[0])); - float radius = 0.0f; - for (size_t i = 0; i < sizeof(points) / sizeof(points[0]); i++) { - radius = qMax(radius, glm::distance(points[i], center)); - } - center = inverseRotation * center; - // to reduce texture "shimmer," move in texel increments - float texelSize = (2.0f * radius) / fbo->width(); - center = glm::vec3(roundf(center.x / texelSize) * texelSize, roundf(center.y / texelSize) * texelSize, - roundf(center.z / texelSize) * texelSize); + const float MAP_DISTANCES[] = { 0.0f, 2.0f, 6.0f, 14.0f, 30.0f }; + const glm::vec2 MAP_COORDS[] = { glm::vec2(0.0f, 0.0f), glm::vec2(0.5f, 0.0f), + glm::vec2(0.0f, 0.5f), glm::vec2(0.5f, 0.5f) }; - glm::vec3 minima(center.x - radius, center.y - radius, center.z - radius); - glm::vec3 maxima(center.x + radius, center.y + radius, center.z + radius); + int halfSize = fbo->width() / 2; + float frustumScale = 1.0f / (_viewFrustum.getFarClip() - _viewFrustum.getNearClip()); + + for (int i = 0; i < SHADOW_MATRIX_COUNT; i++) { + const glm::vec2& coord = MAP_COORDS[i]; + glViewport(coord.s * halfSize, coord.t * halfSize, halfSize, halfSize); - // stretch out our extents in z so that we get all of the avatars - minima.z -= _viewFrustum.getFarClip() * 0.5f; - maxima.z += _viewFrustum.getFarClip() * 0.5f; + float nearScale = MAP_DISTANCES[i] * frustumScale; + float farScale = MAP_DISTANCES[i + 1] * frustumScale; + loadViewFrustum(_myCamera, _viewFrustum); + glm::vec3 points[] = { + glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), nearScale), + glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), nearScale), + glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), nearScale), + glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), nearScale), + glm::mix(_viewFrustum.getNearTopLeft(), _viewFrustum.getFarTopLeft(), farScale), + glm::mix(_viewFrustum.getNearTopRight(), _viewFrustum.getFarTopRight(), farScale), + glm::mix(_viewFrustum.getNearBottomLeft(), _viewFrustum.getFarBottomLeft(), farScale), + glm::mix(_viewFrustum.getNearBottomRight(), _viewFrustum.getFarBottomRight(), farScale) }; + glm::vec3 center; + for (size_t j = 0; j < sizeof(points) / sizeof(points[0]); j++) { + center += points[j]; + } + center /= (float)(sizeof(points) / sizeof(points[0])); + float radius = 0.0f; + for (size_t j = 0; j < sizeof(points) / sizeof(points[0]); j++) { + radius = qMax(radius, glm::distance(points[j], center)); + } + center = inverseRotation * center; + + // to reduce texture "shimmer," move in texel increments + float texelSize = (2.0f * radius) / halfSize; + center = glm::vec3(roundf(center.x / texelSize) * texelSize, roundf(center.y / texelSize) * texelSize, + roundf(center.z / texelSize) * texelSize); + + glm::vec3 minima(center.x - radius, center.y - radius, center.z - radius); + glm::vec3 maxima(center.x + radius, center.y + radius, center.z + radius); - // save the combined matrix for rendering - _shadowMatrix = glm::transpose(glm::translate(glm::vec3(0.5f, 0.5f, 0.5f)) * glm::scale(glm::vec3(0.5f, 0.5f, 0.5f)) * - glm::ortho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z) * glm::mat4_cast(inverseRotation)); + // stretch out our extents in z so that we get all of the avatars + minima.z -= _viewFrustum.getFarClip() * 0.5f; + maxima.z += _viewFrustum.getFarClip() * 0.5f; - // update the shadow view frustum - _shadowViewFrustum.setPosition(rotation * ((minima + maxima) * 0.5f)); - _shadowViewFrustum.setOrientation(rotation); - _shadowViewFrustum.setOrthographic(true); - _shadowViewFrustum.setWidth(maxima.x - minima.x); - _shadowViewFrustum.setHeight(maxima.y - minima.y); - _shadowViewFrustum.setNearClip(minima.z); - _shadowViewFrustum.setFarClip(maxima.z); - _shadowViewFrustum.setEyeOffsetPosition(glm::vec3()); - _shadowViewFrustum.setEyeOffsetOrientation(glm::quat()); - _shadowViewFrustum.calculate(); + // save the combined matrix for rendering + _shadowMatrices[i] = glm::transpose(glm::translate(glm::vec3(coord, 0.0f)) * glm::scale(glm::vec3(0.5f, 0.5f, 1.0f)) * + glm::translate(glm::vec3(0.5f, 0.5f, 0.5f)) * glm::scale(glm::vec3(0.5f, 0.5f, 0.5f)) * + glm::ortho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z) * glm::mat4_cast(inverseRotation)); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z); + // update the shadow view frustum + _shadowViewFrustum.setPosition(rotation * ((minima + maxima) * 0.5f)); + _shadowViewFrustum.setOrientation(rotation); + _shadowViewFrustum.setOrthographic(true); + _shadowViewFrustum.setWidth(maxima.x - minima.x); + _shadowViewFrustum.setHeight(maxima.y - minima.y); + _shadowViewFrustum.setNearClip(minima.z); + _shadowViewFrustum.setFarClip(maxima.z); + _shadowViewFrustum.setEyeOffsetPosition(glm::vec3()); + _shadowViewFrustum.setEyeOffsetOrientation(glm::quat()); + _shadowViewFrustum.calculate(); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glm::vec3 axis = glm::axis(inverseRotation); - glRotatef(glm::degrees(glm::angle(inverseRotation)), axis.x, axis.y, axis.z); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(minima.x, maxima.x, minima.y, maxima.y, -maxima.z, -minima.z); - // store view matrix without translation, which we'll use for precision-sensitive objects - updateUntranslatedViewMatrix(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glm::vec3 axis = glm::axis(inverseRotation); + glRotatef(glm::degrees(glm::angle(inverseRotation)), axis.x, axis.y, axis.z); - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(1.1f, 4.0f); // magic numbers courtesy http://www.eecs.berkeley.edu/~ravir/6160/papers/shadowmaps.ppt + // store view matrix without translation, which we'll use for precision-sensitive objects + updateUntranslatedViewMatrix(); - _avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE); - _particles.render(OctreeRenderer::SHADOW_RENDER_MODE); - _models.render(OctreeRenderer::SHADOW_RENDER_MODE); + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(1.1f, 4.0f); // magic numbers courtesy http://www.eecs.berkeley.edu/~ravir/6160/papers/shadowmaps.ppt - glDisable(GL_POLYGON_OFFSET_FILL); + _avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE); + _particles.render(OctreeRenderer::SHADOW_RENDER_MODE); + _models.render(OctreeRenderer::SHADOW_RENDER_MODE); - glPopMatrix(); + glDisable(GL_POLYGON_OFFSET_FILL); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); + glPopMatrix(); - glMatrixMode(GL_MODELVIEW); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + } + fbo->release(); glViewport(0, 0, _glWidget->width(), _glWidget->height()); diff --git a/interface/src/Application.h b/interface/src/Application.h index 0065944611..5d29a9057a 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -126,6 +126,8 @@ static const float MIRROR_REARVIEW_DISTANCE = 0.65f; static const float MIRROR_REARVIEW_BODY_DISTANCE = 2.3f; static const float MIRROR_FIELD_OF_VIEW = 30.0f; +static const int SHADOW_MATRIX_COUNT = 4; + class Application : public QApplication { Q_OBJECT @@ -261,7 +263,7 @@ public: /// result from matrix multiplication at high translation magnitudes. void loadTranslatedViewMatrix(const glm::vec3& translation); - const glm::mat4& getShadowMatrix() const { return _shadowMatrix; } + const glm::mat4* getShadowMatrices() const { return _shadowMatrices; } void getModelViewMatrix(glm::dmat4* modelViewMatrix); void getProjectionMatrix(glm::dmat4* projectionMatrix); @@ -491,7 +493,7 @@ private: float _rotateMirror; float _raiseMirror; - glm::mat4 _shadowMatrix; + glm::mat4 _shadowMatrices[SHADOW_MATRIX_COUNT]; Environment _environment; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 678a4b09c0..7c9ba50346 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -1509,9 +1509,12 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re const QVector& networkMeshes = _geometry->getMeshes(); if (receiveShadows) { - glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[0]); - glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[1]); - glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[2]); + for (int i = SHADOW_MATRIX_COUNT - 1; i >= 0; i--) { + glActiveTexture(GL_TEXTURE0 + i); + glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrices()[i][0]); + glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrices()[i][1]); + glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrices()[i][2]); + } } for (int i = 0; i < networkMeshes.size(); i++) { // exit early if the translucency doesn't match what we're drawing diff --git a/interface/src/voxels/VoxelSystem.cpp b/interface/src/voxels/VoxelSystem.cpp index 8937cef7dd..4f1b322455 100644 --- a/interface/src/voxels/VoxelSystem.cpp +++ b/interface/src/voxels/VoxelSystem.cpp @@ -1490,10 +1490,12 @@ void VoxelSystem::applyScaleAndBindProgram(bool texture) { _shadowMapProgram.bind(); glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getShadowDepthTextureID()); - glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[0]); - glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[1]); - glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[2]); - + for (int i = SHADOW_MATRIX_COUNT - 1; i >= 0; i--) { + glActiveTexture(GL_TEXTURE0 + i); + glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrices()[i][0]); + glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrices()[i][1]); + glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrices()[i][2]); + } } else if (texture) { _perlinModulateProgram.bind(); glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPermutationNormalTextureID());