Retain "simple" shadows, supply cascaded as option.

This commit is contained in:
Andrzej Kapolka 2014-05-29 12:48:41 -07:00
parent ff77644caa
commit 09231f01f3
11 changed files with 146 additions and 49 deletions

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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));
}

View file

@ -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();

View file

@ -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...");

View file

@ -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;

View file

@ -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();
}

View file

@ -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";

View file

@ -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<NetworkMesh>& 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);

View file

@ -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);

View file

@ -233,6 +233,7 @@ private:
static ProgramObject _perlinModulateProgram;
static ProgramObject _shadowMapProgram;
static ProgramObject _cascadedShadowMapProgram;
int _hookID;
std::vector<glBufferIndex> _freeIndexes;