From 09231f01f3e96a293ffe6280e200c46b8030a7b8 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 29 May 2014 12:48:41 -0700 Subject: [PATCH] Retain "simple" shadows, supply cascaded as option. --- .../shaders/cascaded_shadow_map.frag | 29 ++++++++++++++ .../shaders/cascaded_shadow_map.vert | 33 ++++++++++++++++ interface/resources/shaders/shadow_map.frag | 15 ++++---- interface/resources/shaders/shadow_map.vert | 7 ++-- interface/src/Application.cpp | 34 ++++++++++++++--- interface/src/Application.h | 6 +-- interface/src/Menu.cpp | 15 ++++++-- interface/src/Menu.h | 7 +++- interface/src/renderer/Model.cpp | 10 +---- interface/src/voxels/VoxelSystem.cpp | 38 ++++++++++++------- interface/src/voxels/VoxelSystem.h | 1 + 11 files changed, 146 insertions(+), 49 deletions(-) create mode 100644 interface/resources/shaders/cascaded_shadow_map.frag create mode 100644 interface/resources/shaders/cascaded_shadow_map.vert diff --git a/interface/resources/shaders/cascaded_shadow_map.frag b/interface/resources/shaders/cascaded_shadow_map.frag new file mode 100644 index 0000000000..9b3e8f7cc7 --- /dev/null +++ b/interface/resources/shaders/cascaded_shadow_map.frag @@ -0,0 +1,29 @@ +#version 120 + +// +// cascaded_shadow_map.frag +// fragment shader +// +// Created by Andrzej Kapolka on 5/29/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +uniform sampler2DShadow shadowMap; + +uniform vec3 shadowDistances; + +// the color in shadow +varying vec4 shadowColor; + +// the interpolated position +varying vec4 position; + +void main(void) { + int shadowIndex = int(dot(step(vec3(position.z), shadowDistances), vec3(1.0, 1.0, 1.0))); + vec3 texCoord = vec3(dot(gl_EyePlaneS[shadowIndex], position), dot(gl_EyePlaneT[shadowIndex], position), + dot(gl_EyePlaneR[shadowIndex], position)); + gl_FragColor = mix(shadowColor, gl_Color, shadow2D(shadowMap, texCoord).r); +} diff --git a/interface/resources/shaders/cascaded_shadow_map.vert b/interface/resources/shaders/cascaded_shadow_map.vert new file mode 100644 index 0000000000..68ff95b28a --- /dev/null +++ b/interface/resources/shaders/cascaded_shadow_map.vert @@ -0,0 +1,33 @@ +#version 120 + +// +// cascaded_shadow_map.vert +// vertex shader +// +// Created by Andrzej Kapolka on 5/29/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the color in shadow +varying vec4 shadowColor; + +// the interpolated position +varying vec4 position; + +void main(void) { + // the shadow color includes only the ambient terms + shadowColor = gl_Color * (gl_LightModel.ambient + gl_LightSource[0].ambient); + + // the normal color includes diffuse + 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 coordinates using the eye position + position = gl_ModelViewMatrix * gl_Vertex; + + // use the fixed function transform + gl_Position = ftransform(); +} diff --git a/interface/resources/shaders/shadow_map.frag b/interface/resources/shaders/shadow_map.frag index f2246fff55..fa79040ce3 100644 --- a/interface/resources/shaders/shadow_map.frag +++ b/interface/resources/shaders/shadow_map.frag @@ -13,17 +13,16 @@ uniform sampler2DShadow shadowMap; -uniform vec3 shadowDistances; +// the inverse of the size of the shadow map +const float shadowScale = 1.0 / 2048.0; // the color in shadow varying vec4 shadowColor; -// the interpolated position -varying vec4 position; - void main(void) { - int shadowIndex = int(dot(step(vec3(position.z), shadowDistances), vec3(1.0, 1.0, 1.0))); - vec3 texCoord = vec3(dot(gl_EyePlaneS[shadowIndex], position), dot(gl_EyePlaneT[shadowIndex], position), - dot(gl_EyePlaneR[shadowIndex], position)); - gl_FragColor = mix(shadowColor, gl_Color, shadow2D(shadowMap, texCoord).r); + 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)); } diff --git a/interface/resources/shaders/shadow_map.vert b/interface/resources/shaders/shadow_map.vert index dcc5324927..5d2affba98 100644 --- a/interface/resources/shaders/shadow_map.vert +++ b/interface/resources/shaders/shadow_map.vert @@ -14,9 +14,6 @@ // the color in shadow varying vec4 shadowColor; -// the interpolated position -varying vec4 position; - void main(void) { // the shadow color includes only the ambient terms shadowColor = gl_Color * (gl_LightModel.ambient + gl_LightSource[0].ambient); @@ -26,7 +23,9 @@ void main(void) { gl_FrontColor = shadowColor + gl_Color * (gl_LightSource[0].diffuse * max(0.0, dot(normal, gl_LightSource[0].position))); // generate the shadow texture coordinates using the eye position - position = gl_ModelViewMatrix * gl_Vertex; + 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); // use the fixed function transform gl_Position = ftransform(); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a2892eb003..af94ddba01 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -616,7 +616,7 @@ void Application::paintGL() { whichCamera = _viewFrustumOffsetCamera; } - if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) { + if (Menu::getInstance()->getShadowsEnabled()) { updateShadowMap(); } @@ -2263,13 +2263,20 @@ void Application::updateShadowMap() { 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) }; - int halfSize = fbo->width() / 2; float frustumScale = 1.0f / (_viewFrustum.getFarClip() - _viewFrustum.getNearClip()); loadViewFrustum(_myCamera, _viewFrustum); - for (int i = 0; i < SHADOW_MATRIX_COUNT; i++) { + int matrixCount = 1; + int targetSize = fbo->width(); + float targetScale = 1.0f; + if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) { + matrixCount = CASCADED_SHADOW_MATRIX_COUNT; + targetSize = fbo->width() / 2; + targetScale = 0.5f; + } + for (int i = 0; i < matrixCount; i++) { const glm::vec2& coord = MAP_COORDS[i]; - glViewport(coord.s * fbo->width(), coord.t * fbo->height(), halfSize, halfSize); + glViewport(coord.s * fbo->width(), coord.t * fbo->height(), targetSize, targetSize); float nearScale = SHADOW_MATRIX_DISTANCES[i] * frustumScale; float farScale = SHADOW_MATRIX_DISTANCES[i + 1] * frustumScale; @@ -2294,7 +2301,7 @@ void Application::updateShadowMap() { center = inverseRotation * center; // to reduce texture "shimmer," move in texel increments - float texelSize = (2.0f * radius) / halfSize; + float texelSize = (2.0f * radius) / targetSize; center = glm::vec3(roundf(center.x / texelSize) * texelSize, roundf(center.y / texelSize) * texelSize, roundf(center.z / texelSize) * texelSize); @@ -2306,7 +2313,8 @@ void Application::updateShadowMap() { maxima.z += _viewFrustum.getFarClip() * 0.5f; // 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)) * + _shadowMatrices[i] = glm::transpose(glm::translate(glm::vec3(coord, 0.0f)) * + glm::scale(glm::vec3(targetScale, targetScale, 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)); @@ -2433,6 +2441,20 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // Setup 3D lights (after the camera transform, so that they are positioned in world space) setupWorldLight(); + // setup shadow matrices (again, after the camera transform) + int shadowMatrixCount = 0; + if (Menu::getInstance()->isOptionChecked(MenuOption::SimpleShadows)) { + shadowMatrixCount = 1; + } else if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) { + shadowMatrixCount = CASCADED_SHADOW_MATRIX_COUNT; + } + for (int i = shadowMatrixCount - 1; i >= 0; i--) { + glActiveTexture(GL_TEXTURE0 + i); + glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&_shadowMatrices[i][0]); + glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&_shadowMatrices[i][1]); + glTexGenfv(GL_R, GL_EYE_PLANE, (const GLfloat*)&_shadowMatrices[i][2]); + } + if (!selfAvatarOnly && Menu::getInstance()->isOptionChecked(MenuOption::Stars)) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... stars..."); diff --git a/interface/src/Application.h b/interface/src/Application.h index 59f9ad4e15..68d437815b 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -126,7 +126,6 @@ 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; static const float SHADOW_MATRIX_DISTANCES[] = { 0.0f, 2.0f, 6.0f, 14.0f, 30.0f }; class Application : public QApplication { @@ -264,8 +263,6 @@ public: /// result from matrix multiplication at high translation magnitudes. void loadTranslatedViewMatrix(const glm::vec3& translation); - const glm::mat4* getShadowMatrices() const { return _shadowMatrices; } - void getModelViewMatrix(glm::dmat4* modelViewMatrix); void getProjectionMatrix(glm::dmat4* projectionMatrix); @@ -494,7 +491,8 @@ private: float _rotateMirror; float _raiseMirror; - glm::mat4 _shadowMatrices[SHADOW_MATRIX_COUNT]; + static const int CASCADED_SHADOW_MATRIX_COUNT = 4; + glm::mat4 _shadowMatrices[CASCADED_SHADOW_MATRIX_COUNT]; Environment _environment; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 45b4089a11..ef5ffeaaf1 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -303,7 +303,12 @@ Menu::Menu() : appInstance->getGlowEffect(), SLOT(cycleRenderMode())); - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Shadows, 0, false); + QMenu* shadowMenu = renderOptionsMenu->addMenu("Shadows"); + QActionGroup* shadowGroup = new QActionGroup(shadowMenu); + shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true)); + shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false)); + shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false)); + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::BuckyBalls, 0, false); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Particles, 0, true); @@ -666,6 +671,10 @@ void Menu::scanMenu(QMenu* menu, settingsAction modifySetting, QSettings* set) { set->endGroup(); } +bool Menu::getShadowsEnabled() const { + return isOptionChecked(MenuOption::SimpleShadows) || isOptionChecked(MenuOption::CascadedShadows); +} + void Menu::handleViewFrustumOffsetKeyModifier(int key) { const float VIEW_FRUSTUM_OFFSET_DELTA = 0.5f; const float VIEW_FRUSTUM_OFFSET_UP_DELTA = 0.05f; @@ -836,8 +845,8 @@ void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) { } } -bool Menu::isOptionChecked(const QString& menuOption) { - QAction* menu = _actionHash.value(menuOption); +bool Menu::isOptionChecked(const QString& menuOption) const { + const QAction* menu = _actionHash.value(menuOption); if (menu) { return menu->isChecked(); } diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 012dc1662c..e973ae88b3 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -102,6 +102,8 @@ public: int getMaxVoxels() const { return _maxVoxels; } QAction* getUseVoxelShader() const { return _useVoxelShader; } + bool getShadowsEnabled() const; + void handleViewFrustumOffsetKeyModifier(int key); // User Tweakable LOD Items @@ -170,7 +172,7 @@ public slots: void removeSeparator(const QString& menuName, const QString& separatorName); void addMenuItem(const MenuItemProperties& properties); void removeMenuItem(const QString& menuName, const QString& menuitem); - bool isOptionChecked(const QString& menuOption); + bool isOptionChecked(const QString& menuOption) const; void setIsOptionChecked(const QString& menuOption, bool isChecked); private slots: @@ -301,6 +303,7 @@ namespace MenuOption { const QString Bandwidth = "Bandwidth Display"; const QString BandwidthDetails = "Bandwidth Details"; const QString BuckyBalls = "Bucky Balls"; + const QString CascadedShadows = "Cascaded"; const QString Chat = "Chat..."; const QString ChatCircling = "Chat Circling"; const QString CollideWithAvatars = "Collide With Avatars"; @@ -376,7 +379,7 @@ namespace MenuOption { const QString ScriptEditor = "Script Editor..."; const QString SettingsExport = "Export Settings"; const QString SettingsImport = "Import Settings"; - const QString Shadows = "Shadows"; + const QString SimpleShadows = "Simple"; const QString ShowBordersVoxelNodes = "Show Voxel Nodes"; const QString ShowBordersModelNodes = "Show Model Nodes"; const QString ShowBordersParticleNodes = "Show Particle Nodes"; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 7c9ba50346..1839e20ef7 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -508,7 +508,7 @@ bool Model::render(float alpha, RenderMode mode, bool receiveShadows) { glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.5f * alpha); - receiveShadows &= Menu::getInstance()->isOptionChecked(MenuOption::Shadows); + receiveShadows &= Menu::getInstance()->getShadowsEnabled(); renderMeshes(alpha, mode, false, receiveShadows); glDisable(GL_ALPHA_TEST); @@ -1508,14 +1508,6 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& networkMeshes = _geometry->getMeshes(); - if (receiveShadows) { - 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 const NetworkMesh& networkMesh = networkMeshes.at(i); diff --git a/interface/src/voxels/VoxelSystem.cpp b/interface/src/voxels/VoxelSystem.cpp index 11bd47b7e4..3525a086b4 100644 --- a/interface/src/voxels/VoxelSystem.cpp +++ b/interface/src/voxels/VoxelSystem.cpp @@ -518,9 +518,19 @@ void VoxelSystem::initVoxelMemory() { _shadowMapProgram.bind(); _shadowMapProgram.setUniformValue("shadowMap", 0); - _shadowMapProgram.setUniformValue("shadowDistances", -SHADOW_MATRIX_DISTANCES[1], - -SHADOW_MATRIX_DISTANCES[2], -SHADOW_MATRIX_DISTANCES[3]); _shadowMapProgram.release(); + + _cascadedShadowMapProgram.addShaderFromSourceFile(QGLShader::Vertex, + Application::resourcesPath() + "shaders/cascaded_shadow_map.vert"); + _cascadedShadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment, + Application::resourcesPath() + "shaders/cascaded_shadow_map.frag"); + _cascadedShadowMapProgram.link(); + + _cascadedShadowMapProgram.bind(); + _cascadedShadowMapProgram.setUniformValue("shadowMap", 0); + _cascadedShadowMapProgram.setUniformValue("shadowDistances", -SHADOW_MATRIX_DISTANCES[1], + -SHADOW_MATRIX_DISTANCES[2], -SHADOW_MATRIX_DISTANCES[3]); + _cascadedShadowMapProgram.release(); } } _renderer = new PrimitiveRenderer(_maxVoxels); @@ -1168,6 +1178,7 @@ glm::vec3 VoxelSystem::computeVoxelVertex(const glm::vec3& startVertex, float vo ProgramObject VoxelSystem::_perlinModulateProgram; ProgramObject VoxelSystem::_shadowMapProgram; +ProgramObject VoxelSystem::_cascadedShadowMapProgram; void VoxelSystem::init() { if (_initialized) { @@ -1488,16 +1499,14 @@ void VoxelSystem::render() { void VoxelSystem::applyScaleAndBindProgram(bool texture) { - if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) { - _shadowMapProgram.bind(); + if (Menu::getInstance()->getShadowsEnabled()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) { + _cascadedShadowMapProgram.bind(); + } else { + _shadowMapProgram.bind(); + } glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getShadowDepthTextureID()); - 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()); @@ -1511,11 +1520,14 @@ void VoxelSystem::removeScaleAndReleaseProgram(bool texture) { // scale back down to 1 so heads aren't massive glPopMatrix(); - if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) { - _shadowMapProgram.release(); + if (Menu::getInstance()->getShadowsEnabled()) { + if (Menu::getInstance()->isOptionChecked(MenuOption::CascadedShadows)) { + _cascadedShadowMapProgram.release(); + } else { + _shadowMapProgram.release(); + } glBindTexture(GL_TEXTURE_2D, 0); - } else if (texture) { _perlinModulateProgram.release(); glBindTexture(GL_TEXTURE_2D, 0); diff --git a/interface/src/voxels/VoxelSystem.h b/interface/src/voxels/VoxelSystem.h index 15e2b20a75..acd0cd170a 100644 --- a/interface/src/voxels/VoxelSystem.h +++ b/interface/src/voxels/VoxelSystem.h @@ -233,6 +233,7 @@ private: static ProgramObject _perlinModulateProgram; static ProgramObject _shadowMapProgram; + static ProgramObject _cascadedShadowMapProgram; int _hookID; std::vector _freeIndexes;