diff --git a/interface/src/scripting/RenderScriptingInterface.h b/interface/src/scripting/RenderScriptingInterface.h index 2025c71510..73ef077c3c 100644 --- a/interface/src/scripting/RenderScriptingInterface.h +++ b/interface/src/scripting/RenderScriptingInterface.h @@ -233,18 +233,17 @@ private: mutable ReadWriteLockable _renderSettingLock; // Runtime value of each settings - int _renderMethod{ RENDER_FORWARD ? render::Args::RenderMethod::FORWARD : render::Args::RenderMethod::DEFERRED }; - bool _shadowsEnabled{ true }; - bool _ambientOcclusionEnabled{ false }; - AntialiasingConfig::Mode _antialiasingMode{ AntialiasingConfig::Mode::NONE }; - float _viewportResolutionScale{ 1.0f }; + int _renderMethod { RENDER_FORWARD ? render::Args::RenderMethod::FORWARD : render::Args::RenderMethod::DEFERRED }; + bool _shadowsEnabled { true }; + bool _ambientOcclusionEnabled { true }; + AntialiasingConfig::Mode _antialiasingMode { AntialiasingConfig::Mode::NONE }; + float _viewportResolutionScale { 1.0f }; QString _fullScreenScreen; - // Actual settings saved on disk Setting::Handle _renderMethodSetting { "renderMethod", RENDER_FORWARD ? render::Args::RenderMethod::FORWARD : render::Args::RenderMethod::DEFERRED }; Setting::Handle _shadowsEnabledSetting { "shadowsEnabled", true }; - Setting::Handle _ambientOcclusionEnabledSetting { "ambientOcclusionEnabled", false }; + Setting::Handle _ambientOcclusionEnabledSetting { "ambientOcclusionEnabled", true }; //Setting::Handle _antialiasingModeSetting { "antialiasingMode", AntialiasingConfig::Mode::TAA }; Setting::Handle _antialiasingModeSetting { "antialiasingMode", AntialiasingConfig::Mode::NONE }; Setting::Handle _viewportResolutionScaleSetting { "viewportResolutionScale", 1.0f }; diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 52b27cf211..8a6768f235 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -68,6 +68,20 @@ void ZoneEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity _bloomIndex = INVALID_INDEX; } } + + if (_tonemappingStage) { + if (!TonemappingStage::isIndexInvalid(_tonemappingIndex)) { + _tonemappingStage->removeTonemapping(_tonemappingIndex); + _tonemappingIndex = INVALID_INDEX; + } + } + + if (_ambientOcclusionStage) { + if (!AmbientOcclusionStage::isIndexInvalid(_ambientOcclusionIndex)) { + _ambientOcclusionStage->removeAmbientOcclusion(_ambientOcclusionIndex); + _ambientOcclusionIndex = INVALID_INDEX; + } + } } void ZoneEntityRenderer::doRender(RenderArgs* args) { @@ -96,6 +110,16 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { assert(_bloomStage); } + if (!_tonemappingStage) { + _tonemappingStage = args->_scene->getStage(); + assert(_tonemappingStage); + } + + if (!_ambientOcclusionStage) { + _ambientOcclusionStage = args->_scene->getStage(); + assert(_ambientOcclusionStage); + } + { // Sun if (_needSunUpdate) { if (LightStage::isIndexInvalid(_sunIndex)) { @@ -149,6 +173,24 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { } } + { + if (_needTonemappingUpdate) { + if (TonemappingStage::isIndexInvalid(_tonemappingIndex)) { + _tonemappingIndex = _tonemappingStage->addTonemapping(_tonemapping); + } + _needTonemappingUpdate = false; + } + } + + { + if (_needAmbientOcclusionUpdate) { + if (AmbientOcclusionStage::isIndexInvalid(_ambientOcclusionIndex)) { + _ambientOcclusionIndex = _ambientOcclusionStage->addAmbientOcclusion(_ambientOcclusion); + } + _needAmbientOcclusionUpdate = false; + } + } + if (_visible) { // Finally, push the lights visible in the frame // @@ -184,6 +226,18 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { } else if (_bloomMode == COMPONENT_MODE_ENABLED) { _bloomStage->_currentFrame.pushBloom(_bloomIndex); } + + if (_tonemappingMode == COMPONENT_MODE_DISABLED) { + _tonemappingStage->_currentFrame.pushTonemapping(0); // Use the fallback tonemapping for "off" + } else if (_tonemappingMode == COMPONENT_MODE_ENABLED) { + _tonemappingStage->_currentFrame.pushTonemapping(_tonemappingIndex); + } + + if (_ambientOcclusionMode == COMPONENT_MODE_DISABLED) { + _ambientOcclusionStage->_currentFrame.pushAmbientOcclusion(INVALID_INDEX); + } else if (_ambientOcclusionMode == COMPONENT_MODE_ENABLED) { + _ambientOcclusionStage->_currentFrame.pushAmbientOcclusion(_ambientOcclusionIndex); + } } CullTest::_containingZones.insert(_entityID); @@ -216,6 +270,8 @@ void ZoneEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe bool skyboxChanged = entity->skyboxPropertiesChanged() || proceduralUserDataChanged; bool hazeChanged = entity->hazePropertiesChanged(); bool bloomChanged = entity->bloomPropertiesChanged(); + bool tonemappingChanged = entity->tonemappingPropertiesChanged(); + bool ambientOcclusionChanged = entity->ambientOcclusionPropertiesChanged(); entity->resetRenderingPropertiesChanged(); if (transformChanged) { @@ -255,6 +311,16 @@ void ZoneEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe updateBloomFromEntity(entity); } + if (tonemappingChanged) { + _tonemappingProperties = entity->getTonemappingProperties(); + updateTonemappingFromEntity(entity); + } + + if (ambientOcclusionChanged) { + _ambientOcclusionProperties = entity->getAmbientOcclusionProperties(); + updateAmbientOcclusionFromEntity(entity); + } + bool visuallyReady = true; uint32_t skyboxMode = entity->getSkyboxMode(); if (skyboxMode == COMPONENT_MODE_ENABLED && !_skyboxTextureURL.isEmpty()) { @@ -275,7 +341,9 @@ bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint entity->ambientLightPropertiesChanged() || entity->hazePropertiesChanged() || entity->bloomPropertiesChanged() || - entity->skyboxPropertiesChanged()) { + entity->skyboxPropertiesChanged() || + entity->tonemappingPropertiesChanged() || + entity->ambientOcclusionPropertiesChanged()) { return true; } @@ -360,6 +428,32 @@ void ZoneEntityRenderer::updateBloomFromEntity(const TypedEntityPointer& entity) bloom->setBloomSize(_bloomProperties.getBloomSize()); } +void ZoneEntityRenderer::updateTonemappingFromEntity(const TypedEntityPointer& entity) { + _tonemappingMode = (ComponentMode)entity->getTonemappingMode(); + + const auto& tonemapping = editTonemapping(); + + tonemapping->setCurve(_tonemappingProperties.getCurve()); + tonemapping->setExposure(_tonemappingProperties.getExposure()); +} + +void ZoneEntityRenderer::updateAmbientOcclusionFromEntity(const TypedEntityPointer& entity) { + _ambientOcclusionMode = (ComponentMode)entity->getAmbientOcclusionMode(); + + const auto& ambientOcclusion = editAmbientOcclusion(); + + ambientOcclusion->setTechnique(_ambientOcclusionProperties.getTechnique()); + ambientOcclusion->setJitter(_ambientOcclusionProperties.getJitter()); + ambientOcclusion->setResolutionLevel(_ambientOcclusionProperties.getResolutionLevel()); + ambientOcclusion->setEdgeSharpness(_ambientOcclusionProperties.getEdgeSharpness()); + ambientOcclusion->setBlurRadius(_ambientOcclusionProperties.getBlurRadius()); + ambientOcclusion->setAORadius(_ambientOcclusionProperties.getAORadius()); + ambientOcclusion->setAOObscuranceLevel(_ambientOcclusionProperties.getAOObscuranceLevel()); + ambientOcclusion->setAOFalloffAngle(_ambientOcclusionProperties.getAOFalloffAngle()); + ambientOcclusion->setAOSamplingAmount(_ambientOcclusionProperties.getAOSamplingAmount()); + ambientOcclusion->setSSAONumSpiralTurns(_ambientOcclusionProperties.getSSAONumSpiralTurns()); +} + void ZoneEntityRenderer::updateKeyBackgroundFromEntity(const TypedEntityPointer& entity) { _skyboxMode = (ComponentMode)entity->getSkyboxMode(); diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h index d2ee90b1e4..78694514d2 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include "RenderableEntityItem.h" #include @@ -47,6 +49,8 @@ private: void updateHazeFromEntity(const TypedEntityPointer& entity); void updateKeyBackgroundFromEntity(const TypedEntityPointer& entity); void updateBloomFromEntity(const TypedEntityPointer& entity); + void updateTonemappingFromEntity(const TypedEntityPointer& entity); + void updateAmbientOcclusionFromEntity(const TypedEntityPointer& entity); void updateAmbientMap(); void updateSkyboxMap(); void setAmbientURL(const QString& ambientUrl); @@ -61,6 +65,8 @@ private: graphics::SkyboxPointer editSkybox() { return editBackground()->getSkybox(); } graphics::HazePointer editHaze() { _needHazeUpdate = true; return _haze; } graphics::BloomPointer editBloom() { _needBloomUpdate = true; return _bloom; } + graphics::TonemappingPointer editTonemapping() { _needTonemappingUpdate = true; return _tonemapping; } + graphics::AmbientOcclusionPointer editAmbientOcclusion() { _needAmbientOcclusionUpdate = true; return _ambientOcclusion; } glm::vec3 _lastPosition; glm::vec3 _lastDimensions; @@ -73,12 +79,16 @@ private: const graphics::SunSkyStagePointer _background { std::make_shared() }; const graphics::HazePointer _haze { std::make_shared() }; const graphics::BloomPointer _bloom { std::make_shared() }; + const graphics::TonemappingPointer _tonemapping { std::make_shared() }; + const graphics::AmbientOcclusionPointer _ambientOcclusion { std::make_shared() }; ComponentMode _keyLightMode { COMPONENT_MODE_INHERIT }; ComponentMode _ambientLightMode { COMPONENT_MODE_INHERIT }; ComponentMode _skyboxMode { COMPONENT_MODE_INHERIT }; ComponentMode _hazeMode { COMPONENT_MODE_INHERIT }; ComponentMode _bloomMode { COMPONENT_MODE_INHERIT }; + ComponentMode _tonemappingMode { COMPONENT_MODE_INHERIT }; + ComponentMode _ambientOcclusionMode { COMPONENT_MODE_INHERIT }; indexed_container::Index _sunIndex { LightStage::INVALID_INDEX }; indexed_container::Index _ambientIndex { LightStage::INVALID_INDEX }; @@ -92,27 +102,37 @@ private: BloomStagePointer _bloomStage; BloomStage::Index _bloomIndex { BloomStage::INVALID_INDEX }; - bool _needUpdate{ true }; - bool _needSunUpdate{ true }; - bool _needAmbientUpdate{ true }; - bool _needBackgroundUpdate{ true }; - bool _needHazeUpdate{ true }; + TonemappingStagePointer _tonemappingStage; + TonemappingStage::Index _tonemappingIndex { TonemappingStage::INVALID_INDEX }; + + AmbientOcclusionStagePointer _ambientOcclusionStage; + AmbientOcclusionStage::Index _ambientOcclusionIndex { AmbientOcclusionStage::INVALID_INDEX }; + + bool _needUpdate { true }; + bool _needSunUpdate { true }; + bool _needAmbientUpdate { true }; + bool _needBackgroundUpdate { true }; + bool _needHazeUpdate { true }; bool _needBloomUpdate { true }; + bool _needTonemappingUpdate { true }; + bool _needAmbientOcclusionUpdate { true }; KeyLightPropertyGroup _keyLightProperties; AmbientLightPropertyGroup _ambientLightProperties; HazePropertyGroup _hazeProperties; SkyboxPropertyGroup _skyboxProperties; BloomPropertyGroup _bloomProperties; + TonemappingPropertyGroup _tonemappingProperties; + AmbientOcclusionPropertyGroup _ambientOcclusionProperties; // More attributes used for rendering: QString _ambientTextureURL; NetworkTexturePointer _ambientTexture; - bool _pendingAmbientTexture{ false }; + bool _pendingAmbientTexture { false }; QString _skyboxTextureURL; NetworkTexturePointer _skyboxTexture; - bool _pendingSkyboxTexture{ false }; + bool _pendingSkyboxTexture { false }; QString _proceduralUserData; }; diff --git a/libraries/entities/src/AmbientOcclusionPropertyGroup.cpp b/libraries/entities/src/AmbientOcclusionPropertyGroup.cpp new file mode 100644 index 0000000000..c16f2a00f3 --- /dev/null +++ b/libraries/entities/src/AmbientOcclusionPropertyGroup.cpp @@ -0,0 +1,300 @@ +// +// AmbientOcclusionPropertyGroup.cpp +// libraries/entities/src +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// SPDX-License-Identifier: Apache-2.0 +// + +#include "AmbientOcclusionPropertyGroup.h" + +#include + +#include "EntityItemProperties.h" +#include "EntityItemPropertiesMacros.h" + +inline void addAmbientOcclusionTechnique(QHash& lookup, AmbientOcclusionTechnique technique) { lookup[AmbientOcclusionTechniqueHelpers::getNameForAmbientOcclusionTechnique(technique)] = technique; } +const QHash stringToAmbientOcclusionTechniqueLookup = [] { + QHash toReturn; + addAmbientOcclusionTechnique(toReturn, AmbientOcclusionTechnique::SSAO); + addAmbientOcclusionTechnique(toReturn, AmbientOcclusionTechnique::HBAO); + return toReturn; +}(); +QString AmbientOcclusionPropertyGroup::getTechniqueAsString() const { return AmbientOcclusionTechniqueHelpers::getNameForAmbientOcclusionTechnique(_technique); } +void AmbientOcclusionPropertyGroup::setTechniqueFromString(const QString& technique) { + auto techniqueItr = stringToAmbientOcclusionTechniqueLookup.find(technique.toLower()); + if (techniqueItr != stringToAmbientOcclusionTechniqueLookup.end()) { + _technique = techniqueItr.value(); + _techniqueChanged = true; + } +} + +void AmbientOcclusionPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, + bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AMBIENT_OCCLUSION_TECHNIQUE, AmbientOcclusion, ambientOcclusion, Technique, technique, getTechniqueAsString); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_JITTER, AmbientOcclusion, ambientOcclusion, Jitter, jitter); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, AmbientOcclusion, ambientOcclusion, ResolutionLevel, resolutionLevel); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, AmbientOcclusion, ambientOcclusion, EdgeSharpness, edgeSharpness); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, AmbientOcclusion, ambientOcclusion, BlurRadius, blurRadius); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_AO_RADIUS, AmbientOcclusion, ambientOcclusion, AORadius, aoRadius); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, AmbientOcclusion, ambientOcclusion, AOObscuranceLevel, aoObscuranceLevel); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, AmbientOcclusion, ambientOcclusion, AOFalloffAngle, aoFalloffAngle); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, AmbientOcclusion, ambientOcclusion, AOSamplingAmount, aoSamplingAmount); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, AmbientOcclusion, ambientOcclusion, SSAONumSpiralTurns, ssaoNumSpiralTurns); +} + +void AmbientOcclusionPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientOcclusion, technique, Technique); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, jitter, bool, setJitter); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, resolutionLevel, uint8_t, setResolutionLevel); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, edgeSharpness, float, setEdgeSharpness); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, blurRadius, uint8_t, setBlurRadius); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, aoRadius, float, setAORadius); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, aoObscuranceLevel, float, setAOObscuranceLevel); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, aoFalloffAngle, float, setAOFalloffAngle); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, aoSamplingAmount, float, setAOSamplingAmount); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientOcclusion, ssaoNumSpiralTurns, float, setSSAONumSpiralTurns); +} + +void AmbientOcclusionPropertyGroup::merge(const AmbientOcclusionPropertyGroup& other) { + COPY_PROPERTY_IF_CHANGED(technique); + COPY_PROPERTY_IF_CHANGED(jitter); + COPY_PROPERTY_IF_CHANGED(resolutionLevel); + COPY_PROPERTY_IF_CHANGED(edgeSharpness); + COPY_PROPERTY_IF_CHANGED(blurRadius); + COPY_PROPERTY_IF_CHANGED(aoRadius); + COPY_PROPERTY_IF_CHANGED(aoObscuranceLevel); + COPY_PROPERTY_IF_CHANGED(aoFalloffAngle); + COPY_PROPERTY_IF_CHANGED(aoSamplingAmount); + COPY_PROPERTY_IF_CHANGED(ssaoNumSpiralTurns); +} + +void AmbientOcclusionPropertyGroup::debugDump() const { + qCDebug(entities) << " AmbientOcclusionPropertyGroup: ---------------------------------------------"; + qCDebug(entities) << " Technique:" << getTechniqueAsString(); + qCDebug(entities) << " Jitter:" << getJitter(); + qCDebug(entities) << " ResolutionLevel:" << getResolutionLevel(); + qCDebug(entities) << " EdgeSharpness:" << getEdgeSharpness(); + qCDebug(entities) << " BlurRadius:" << getBlurRadius(); + qCDebug(entities) << " AORadius:" << getAORadius(); + qCDebug(entities) << " AOObscuranceLevel:" << getAOObscuranceLevel(); + qCDebug(entities) << " AOFalloffAngle:" << getAOFalloffAngle(); + qCDebug(entities) << " AOSamplingAmount:" << getAOSamplingAmount(); + qCDebug(entities) << " SSAONumSpiralTurns:" << getSSAONumSpiralTurns(); +} + +void AmbientOcclusionPropertyGroup::listChangedProperties(QList& out) { + if (techniqueChanged()) { + out << "ambientOcclusion-technique"; + } + if (jitterChanged()) { + out << "ambientOcclusion-jitter"; + } + if (resolutionLevelChanged()) { + out << "ambientOcclusion-resolutionLevel"; + } + if (edgeSharpnessChanged()) { + out << "ambientOcclusion-edgeSharpness"; + } + if (blurRadiusChanged()) { + out << "ambientOcclusion-blurRadius"; + } + if (aoRadiusChanged()) { + out << "ambientOcclusion-aoRadius"; + } + if (aoObscuranceLevelChanged()) { + out << "ambientOcclusion-aoObscuranceLevel"; + } + if (aoFalloffAngleChanged()) { + out << "ambientOcclusion-aoFalloffAngle"; + } + if (aoSamplingAmountChanged()) { + out << "ambientOcclusion-aoSamplingAmount"; + } + if (ssaoNumSpiralTurnsChanged()) { + out << "ambientOcclusion-ssaoNumSpiralTurns"; + } +} + +bool AmbientOcclusionPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_TECHNIQUE, (uint32_t)getTechnique()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_JITTER, getJitter()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, getResolutionLevel()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, getEdgeSharpness()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, getBlurRadius()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, getAORadius()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, getAOObscuranceLevel()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, getAOFalloffAngle()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, getAOSamplingAmount()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, getSSAONumSpiralTurns()); + + return true; +} + +bool AmbientOcclusionPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_TECHNIQUE, AmbientOcclusionTechnique, setTechnique); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_JITTER, bool, setJitter); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, uint8_t, setResolutionLevel); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, float, setEdgeSharpness); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, uint8_t, setBlurRadius); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, float, setAORadius); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, float, setAOObscuranceLevel); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, float, setAOFalloffAngle); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, float, setAOSamplingAmount); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, float, setSSAONumSpiralTurns); + + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_TECHNIQUE, Technique); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_JITTER, Jitter); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, ResolutionLevel); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, EdgeSharpness); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, BlurRadius); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_AO_RADIUS, AORadius); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, AOObscuranceLevel); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, AOFalloffAngle); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, AOSamplingAmount); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, SSAONumSpiralTurns); + + processedBytes += bytesRead; + + Q_UNUSED(somethingChanged); + + return true; +} + +void AmbientOcclusionPropertyGroup::markAllChanged() { + _techniqueChanged = true; + _jitterChanged = true; + _resolutionLevelChanged = true; + _edgeSharpnessChanged = true; + _blurRadiusChanged = true; + _aoRadiusChanged = true; + _aoObscuranceLevelChanged = true; + _aoFalloffAngleChanged = true; + _aoSamplingAmountChanged = true; + _ssaoNumSpiralTurnsChanged = true; +} + +EntityPropertyFlags AmbientOcclusionPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_TECHNIQUE, technique); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_JITTER, jitter); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, resolutionLevel); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, edgeSharpness); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, blurRadius); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_AO_RADIUS, aoRadius); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, aoObscuranceLevel); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, aoFalloffAngle); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, aoSamplingAmount); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, ssaoNumSpiralTurns); + + return changedProperties; +} + +void AmbientOcclusionPropertyGroup::getProperties(EntityItemProperties& properties) const { + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, Technique, getTechnique); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, Jitter, getJitter); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, ResolutionLevel, getResolutionLevel); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, EdgeSharpness, getEdgeSharpness); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, BlurRadius, getBlurRadius); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, AORadius, getAORadius); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, AOObscuranceLevel, getAOObscuranceLevel); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, AOFalloffAngle, getAOFalloffAngle); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, AOSamplingAmount, getAOSamplingAmount); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientOcclusion, SSAONumSpiralTurns, getSSAONumSpiralTurns); +} + +bool AmbientOcclusionPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, Technique, technique, setTechnique); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, Jitter, jitter, setJitter); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, ResolutionLevel, resolutionLevel, setResolutionLevel); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, EdgeSharpness, edgeSharpness, setEdgeSharpness); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, BlurRadius, blurRadius, setBlurRadius); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, AORadius, aoRadius, setAORadius); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, AOObscuranceLevel, aoObscuranceLevel, setAOObscuranceLevel); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, AOFalloffAngle, aoFalloffAngle, setAOFalloffAngle); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, AOSamplingAmount, aoSamplingAmount, setAOSamplingAmount); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientOcclusion, SSAONumSpiralTurns, ssaoNumSpiralTurns, setSSAONumSpiralTurns); + + return somethingChanged; +} + +EntityPropertyFlags AmbientOcclusionPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + + requestedProperties += PROP_AMBIENT_OCCLUSION_TECHNIQUE; + requestedProperties += PROP_AMBIENT_OCCLUSION_JITTER; + requestedProperties += PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL; + requestedProperties += PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS; + requestedProperties += PROP_AMBIENT_OCCLUSION_BLUR_RADIUS; + requestedProperties += PROP_AMBIENT_OCCLUSION_AO_RADIUS; + requestedProperties += PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL; + requestedProperties += PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE; + requestedProperties += PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT; + requestedProperties += PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS; + + return requestedProperties; +} + +void AmbientOcclusionPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_TECHNIQUE, (uint32_t)getTechnique()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_JITTER, getJitter()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, getResolutionLevel()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, getEdgeSharpness()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, getBlurRadius()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, getAORadius()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, getAOObscuranceLevel()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, getAOFalloffAngle()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, getAOSamplingAmount()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, getSSAONumSpiralTurns()); +} + +int AmbientOcclusionPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_TECHNIQUE, AmbientOcclusionTechnique, setTechnique); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_JITTER, bool, setJitter); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, uint8_t, setResolutionLevel); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, float, setEdgeSharpness); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, uint8_t, setBlurRadius); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, float, setAORadius); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, float, setAOObscuranceLevel); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, float, setAOFalloffAngle); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, float, setAOSamplingAmount); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, float, setSSAONumSpiralTurns); + + return bytesRead; +} diff --git a/libraries/entities/src/AmbientOcclusionPropertyGroup.h b/libraries/entities/src/AmbientOcclusionPropertyGroup.h new file mode 100644 index 0000000000..3f348c4d39 --- /dev/null +++ b/libraries/entities/src/AmbientOcclusionPropertyGroup.h @@ -0,0 +1,108 @@ +// +// AmbientOcclusionPropertyGroup.h +// libraries/entities/src +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef hifi_AmbientOcclusionPropertyGroup_h +#define hifi_AmbientOcclusionPropertyGroup_h + +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class EntityTreeElementExtraEncodeData; +class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; + +/*@jsdoc + * AmbientOcclusion is defined by the following properties: + * @typedef {object} Entities.AmbientOcclusion + * @property {AmbientOcclusionTechnique} technique="ssao" - The ambient occlusion technique used. Different techniques have + * different tradeoffs. + * @property {boolean} jitter=false - Whether or not the ambient occlusion sampling is jittered. + * @property {number} resolutionLevel=2 - How high the resolution of the ambient occlusion buffer should be. Higher levels + * mean lower resolution buffers. + * @property {number} edgeSharpness=1.0 - How much to sharpen the edges during the ambient occlusion blurring. + * @property {number} blurRadius=4 - The radius used for blurring, in pixels. + * @property {number} aoRadius=1.0 - The radius used for ambient occlusion. + * @property {number} aoObscuranceLevel=0.5 - Intensify or dim ambient occlusion. + * @property {number} aoFalloffAngle=0.25 - The falloff angle for the AO calculation. + * @property {number} aoSamplingAmount=0.5 - The fraction of AO samples to use, out of the maximum for each technique. + * @property {number} ssaoNumSpiralTurns=7.0 - The angle span used to distribute the AO samples ray directions. SSAO only. + */ + +class AmbientOcclusionPropertyGroup : public PropertyGroup { +public: + // EntityItemProperty related helpers + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, + ScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const override; + virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; + + void merge(const AmbientOcclusionPropertyGroup& other); + + virtual void debugDump() const override; + virtual void listChangedProperties(QList& out) override; + + virtual bool appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt, int& processedBytes) override; + virtual void markAllChanged() override; + virtual EntityPropertyFlags getChangedProperties() const override; + + // EntityItem related helpers + // methods for getting/setting all properties of an entity + virtual void getProperties(EntityItemProperties& propertiesOut) const override; + + /// returns true if something changed + virtual bool setProperties(const EntityItemProperties& properties) override; + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; + + // FIXME: On some machines, SSAO seems to be causing performance problems. Let's default to HBAO for now and maybe + // revisit when we have Vulkan + DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_OCCLUSION_TECHNIQUE, Technique, technique, AmbientOcclusionTechnique, AmbientOcclusionTechnique::HBAO); + DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_JITTER, Jitter, jitter, bool, false); + DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, ResolutionLevel, resolutionLevel, uint8_t, 2); + DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, EdgeSharpness, edgeSharpness, float, 1.0f); + DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, BlurRadius, blurRadius, uint8_t, 4); + DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_RADIUS, AORadius, aoRadius, float, 1.0f); + DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, AOObscuranceLevel, aoObscuranceLevel, float, 0.5f); + DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, AOFalloffAngle, aoFalloffAngle, float, 0.25f); + DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, AOSamplingAmount, aoSamplingAmount, float, 0.5f); + DEFINE_PROPERTY(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, SSAONumSpiralTurns, ssaoNumSpiralTurns, float, 7.0f); +}; + +#endif // hifi_AmbientOcclusionPropertyGroup_h diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index ee9acdb065..c477a3fcef 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -48,6 +48,8 @@ BloomPropertyGroup EntityItemProperties::_staticBloom; ZoneAudioPropertyGroup EntityItemProperties::_staticAudio; KeyLightPropertyGroup EntityItemProperties::_staticKeyLight; AmbientLightPropertyGroup EntityItemProperties::_staticAmbientLight; +TonemappingPropertyGroup EntityItemProperties::_staticTonemapping; +AmbientOcclusionPropertyGroup EntityItemProperties::_staticAmbientOcclusion; GrabPropertyGroup EntityItemProperties::_staticGrab; PulsePropertyGroup EntityItemProperties::_staticPulse; RingGizmoPropertyGroup EntityItemProperties::_staticRing; @@ -90,6 +92,8 @@ void EntityItemProperties::debugDump() const { getAmbientLight().debugDump(); getBloom().debugDump(); getAudio().debugDump(); + getTonemapping().debugDump(); + getAmbientOcclusion().debugDump(); getGrab().debugDump(); qCDebug(entities) << " changed properties..."; @@ -254,6 +258,8 @@ QString EntityItemProperties::getKeyLightModeAsString() const { return getCompon QString EntityItemProperties::getAmbientLightModeAsString() const { return getComponentModeAsString(_ambientLightMode); } QString EntityItemProperties::getHazeModeAsString() const { return getComponentModeAsString(_hazeMode); } QString EntityItemProperties::getBloomModeAsString() const { return getComponentModeAsString(_bloomMode); } +QString EntityItemProperties::getTonemappingModeAsString() const { return getComponentModeAsString(_tonemappingMode); } +QString EntityItemProperties::getAmbientOcclusionModeAsString() const { return getComponentModeAsString(_ambientOcclusionMode); } void EntityItemProperties::setSkyboxModeFromString(const QString& mode) { auto modeItr = stringToComponentMode.find(mode.toLower()); if (modeItr != stringToComponentMode.end()) { @@ -289,6 +295,20 @@ void EntityItemProperties::setBloomModeFromString(const QString& mode) { _bloomModeChanged = true; } } +void EntityItemProperties::setTonemappingModeFromString(const QString& mode) { + auto modeItr = stringToComponentMode.find(mode.toLower()); + if (modeItr != stringToComponentMode.end()) { + _tonemappingMode = modeItr.value(); + _tonemappingModeChanged = true; + } +} +void EntityItemProperties::setAmbientOcclusionModeFromString(const QString& mode) { + auto modeItr = stringToComponentMode.find(mode.toLower()); + if (modeItr != stringToComponentMode.end()) { + _ambientOcclusionMode = modeItr.value(); + _ambientOcclusionModeChanged = true; + } +} inline void addAvatarPriorityMode(QHash& lookup, AvatarPriorityMode mode) { lookup[AvatarPriorityModeHelpers::getNameForAvatarPriorityMode(mode)] = mode; } const QHash stringToAvatarPriority = [] { @@ -625,6 +645,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { changedProperties += _haze.getChangedProperties(); changedProperties += _bloom.getChangedProperties(); changedProperties += _audio.getChangedProperties(); + changedProperties += _tonemapping.getChangedProperties(); + changedProperties += _ambientOcclusion.getChangedProperties(); CHECK_PROPERTY_CHANGE(PROP_FLYING_ALLOWED, flyingAllowed); CHECK_PROPERTY_CHANGE(PROP_GHOSTING_ALLOWED, ghostingAllowed); CHECK_PROPERTY_CHANGE(PROP_FILTER_URL, filterURL); @@ -635,6 +657,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_BLOOM_MODE, bloomMode); CHECK_PROPERTY_CHANGE(PROP_AVATAR_PRIORITY, avatarPriority); CHECK_PROPERTY_CHANGE(PROP_SCREENSHARE, screenshare); + CHECK_PROPERTY_CHANGE(PROP_TONEMAPPING_MODE, tonemappingMode); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_OCCLUSION_MODE, ambientOcclusionMode); // Polyvox CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize); @@ -1587,6 +1611,12 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * * @property {Entities.ZoneAudio} audio - The audio properties of the zone. * + * @property {Entities.ComponentMode} tonemappingMode="inherit" - Configures the tonemapping in the zone. + * @property {Entities.Tonemapping} tonemapping - The tonemapping properties of the zone. + * + * @property {Entities.ComponentMode} ambientOcclusionMode="inherit" - Configures the ambient occlusion in the zone. + * @property {Entities.AmbientOcclusion} ambientOcclusion - The ambient occlusion properties of the zone. + * * @property {boolean} flyingAllowed=true - true if visitors can fly in the zone; false if they * cannot. Only works for domain entities. * @property {boolean} ghostingAllowed=true - true if visitors with avatar collisions turned off will not @@ -1951,6 +1981,8 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s _haze.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); _bloom.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); _audio.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); + _tonemapping.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); + _ambientOcclusion.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties, returnNothingOnEmptyPropertyFlags, isMyOwnAvatarEntity); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FLYING_ALLOWED, flyingAllowed); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GHOSTING_ALLOWED, ghostingAllowed); @@ -1963,6 +1995,8 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BLOOM_MODE, bloomMode, getBloomModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AVATAR_PRIORITY, avatarPriority, getAvatarPriorityAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SCREENSHARE, screenshare, getScreenshareAsString()); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_TONEMAPPING_MODE, tonemappingMode, getTonemappingModeAsString()); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AMBIENT_OCCLUSION_MODE, ambientOcclusionMode, getAmbientOcclusionModeAsString()); } // Web only @@ -2349,6 +2383,8 @@ void EntityItemProperties::copyFromScriptValue(const ScriptValue& object, bool h _haze.copyFromScriptValue(object, namesSet, _defaultSettings); _bloom.copyFromScriptValue(object, namesSet, _defaultSettings); _audio.copyFromScriptValue(object, namesSet, _defaultSettings); + _tonemapping.copyFromScriptValue(object, namesSet, _defaultSettings); + _ambientOcclusion.copyFromScriptValue(object, namesSet, _defaultSettings); COPY_PROPERTY_FROM_QSCRIPTVALUE(flyingAllowed, bool, setFlyingAllowed); COPY_PROPERTY_FROM_QSCRIPTVALUE(ghostingAllowed, bool, setGhostingAllowed); COPY_PROPERTY_FROM_QSCRIPTVALUE(filterURL, QString, setFilterURL); @@ -2359,6 +2395,8 @@ void EntityItemProperties::copyFromScriptValue(const ScriptValue& object, bool h COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(bloomMode, BloomMode); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(avatarPriority, AvatarPriority); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(screenshare, Screenshare); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(tonemappingMode, TonemappingMode); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientOcclusionMode, AmbientOcclusionMode); // Polyvox COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, vec3, setVoxelVolumeSize); @@ -2653,6 +2691,8 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { _haze.merge(other._haze); _bloom.merge(other._bloom); _audio.merge(other._audio); + _tonemapping.merge(other._tonemapping); + _ambientOcclusion.merge(other._ambientOcclusion); COPY_PROPERTY_IF_CHANGED(flyingAllowed); COPY_PROPERTY_IF_CHANGED(ghostingAllowed); COPY_PROPERTY_IF_CHANGED(filterURL); @@ -2663,6 +2703,8 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(bloomMode); COPY_PROPERTY_IF_CHANGED(avatarPriority); COPY_PROPERTY_IF_CHANGED(screenshare); + COPY_PROPERTY_IF_CHANGED(tonemappingMode); + COPY_PROPERTY_IF_CHANGED(ambientOcclusionMode); // Polyvox COPY_PROPERTY_IF_CHANGED(voxelVolumeSize); @@ -3079,6 +3121,22 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_GROUP_PROPERTY_TO_MAP(PROP_LISTENER_ZONES, Audio, audio, ListenerZones, listenerZones); ADD_GROUP_PROPERTY_TO_MAP(PROP_LISTENER_ATTENUATION_COEFFICIENTS, Audio, audio, ListenerAttenuationCoefficients, listenerAttenuationCoefficients); } + { // Tonemapping + ADD_GROUP_PROPERTY_TO_MAP(PROP_TONEMAPPING_CURVE, Tonemapping, tonemapping, Curve, curve); + ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_TONEMAPPING_EXPOSURE, Tonemapping, tonemapping, Exposure, exposure, -4.0f, -4.0f); + } + { // Ambient Occlusion + ADD_GROUP_PROPERTY_TO_MAP(PROP_AMBIENT_OCCLUSION_TECHNIQUE, AmbientOcclusion, ambientOcclusion, Technique, technique); + ADD_GROUP_PROPERTY_TO_MAP(PROP_AMBIENT_OCCLUSION_JITTER, AmbientOcclusion, ambientOcclusion, Jitter, jitter); + ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL, AmbientOcclusion, ambientOcclusion, ResolutionLevel, resolutionLevel, 0, 4); + ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS, AmbientOcclusion, ambientOcclusion, EdgeSharpness, edgeSharpness, 0.0f, 1.0f); + ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_BLUR_RADIUS, AmbientOcclusion, ambientOcclusion, BlurRadius, blurRadius, 0, 15); + ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_AO_RADIUS, AmbientOcclusion, ambientOcclusion, AORadius, aoRadius, 0.01f, 2.0f); + ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL, AmbientOcclusion, ambientOcclusion, AOObscuranceLevel, aoObscuranceLevel, 0.01f, 1.0f); + ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE, AmbientOcclusion, ambientOcclusion, AOFalloffAngle, aoFalloffAngle, 0.0f, 1.0f); + ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT, AmbientOcclusion, ambientOcclusion, AOSamplingAmount, aoSamplingAmount, 0.0f, 1.0f); + ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS, AmbientOcclusion, ambientOcclusion, SSAONumSpiralTurns, ssaoNumSpiralTurns, 0.0f, 10.0f); + } ADD_PROPERTY_TO_MAP(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool); ADD_PROPERTY_TO_MAP(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool); ADD_PROPERTY_TO_MAP(PROP_FILTER_URL, FilterURL, filterURL, QString); @@ -3089,6 +3147,8 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t); ADD_PROPERTY_TO_MAP(PROP_AVATAR_PRIORITY, AvatarPriority, avatarPriority, uint32_t); ADD_PROPERTY_TO_MAP(PROP_SCREENSHARE, Screenshare, screenshare, uint32_t); + ADD_PROPERTY_TO_MAP(PROP_TONEMAPPING_MODE, TonemappingMode, tonemappingMode, uint32_t); + ADD_PROPERTY_TO_MAP(PROP_AMBIENT_OCCLUSION_MODE, AmbientOcclusionMode, ambientOcclusionMode, uint32_t); // Polyvox ADD_PROPERTY_TO_MAP(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, vec3); @@ -3516,6 +3576,12 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy _staticAudio.setProperties(properties); _staticAudio.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + _staticTonemapping.setProperties(properties); + _staticTonemapping.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + + _staticAmbientOcclusion.setProperties(properties); + _staticAmbientOcclusion.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, properties.getFlyingAllowed()); APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, properties.getGhostingAllowed()); APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, properties.getFilterURL()); @@ -3527,6 +3593,8 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)properties.getBloomMode()); APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, (uint32_t)properties.getAvatarPriority()); APPEND_ENTITY_PROPERTY(PROP_SCREENSHARE, (uint32_t)properties.getScreenshare()); + APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_MODE, (uint32_t)properties.getTonemappingMode()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_MODE, (uint32_t)properties.getAmbientOcclusionMode()); } if (properties.getType() == EntityTypes::PolyVox) { @@ -4006,6 +4074,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int properties.getHaze().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); properties.getBloom().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); properties.getAudio().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + properties.getTonemapping().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + properties.getAmbientOcclusion().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FLYING_ALLOWED, bool, setFlyingAllowed); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed); @@ -4018,6 +4088,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLOOM_MODE, uint32_t, setBloomMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AVATAR_PRIORITY, uint32_t, setAvatarPriority); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCREENSHARE, uint32_t, setScreenshare); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TONEMAPPING_MODE, uint32_t, setTonemappingMode); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AMBIENT_OCCLUSION_MODE, uint32_t, setAmbientOcclusionMode); } if (properties.getType() == EntityTypes::PolyVox) { @@ -4399,6 +4471,8 @@ void EntityItemProperties::markAllChanged() { _haze.markAllChanged(); _bloom.markAllChanged(); _audio.markAllChanged(); + _tonemapping.markAllChanged(); + _ambientOcclusion.markAllChanged(); _flyingAllowedChanged = true; _ghostingAllowedChanged = true; _filterURLChanged = true; @@ -4409,6 +4483,8 @@ void EntityItemProperties::markAllChanged() { _bloomModeChanged = true; _avatarPriorityChanged = true; _screenshareChanged = true; + _tonemappingModeChanged = true; + _ambientOcclusionModeChanged = true; // Polyvox _voxelVolumeSizeChanged = true; @@ -5021,6 +5097,8 @@ QList EntityItemProperties::listChangedProperties() { getHaze().listChangedProperties(out); getBloom().listChangedProperties(out); getAudio().listChangedProperties(out); + getTonemapping().listChangedProperties(out); + getAmbientOcclusion().listChangedProperties(out); if (flyingAllowedChanged()) { out += "flyingAllowed"; } @@ -5051,6 +5129,12 @@ QList EntityItemProperties::listChangedProperties() { if (screenshareChanged()) { out += "screenshare"; } + if (tonemappingModeChanged()) { + out += "tonemappingMode"; + } + if (ambientOcclusionModeChanged()) { + out += "ambientOcclusionMode"; + } // Polyvox if (voxelVolumeSizeChanged()) { diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 2ea0354d46..f8e7377df7 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -64,6 +64,8 @@ #include "PulsePropertyGroup.h" #include "RingGizmoPropertyGroup.h" #include "ZoneAudioPropertyGroup.h" +#include "TonemappingPropertyGroup.h" +#include "AmbientOcclusionPropertyGroup.h" #include "MaterialMappingMode.h" #include "BillboardMode.h" @@ -346,6 +348,8 @@ public: DEFINE_PROPERTY_GROUP(Haze, haze, HazePropertyGroup); DEFINE_PROPERTY_GROUP(Bloom, bloom, BloomPropertyGroup); DEFINE_PROPERTY_GROUP(Audio, audio, ZoneAudioPropertyGroup); + DEFINE_PROPERTY_GROUP(Tonemapping, tonemapping, TonemappingPropertyGroup); + DEFINE_PROPERTY_GROUP(AmbientOcclusion, ambientOcclusion, AmbientOcclusionPropertyGroup); DEFINE_PROPERTY(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool, ZoneEntityItem::DEFAULT_FLYING_ALLOWED); DEFINE_PROPERTY(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool, ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED); DEFINE_PROPERTY(PROP_FILTER_URL, FilterURL, filterURL, QString, ZoneEntityItem::DEFAULT_FILTER_URL); @@ -356,6 +360,8 @@ public: DEFINE_PROPERTY_REF_ENUM(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_AVATAR_PRIORITY, AvatarPriority, avatarPriority, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_SCREENSHARE, Screenshare, screenshare, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); + DEFINE_PROPERTY_REF_ENUM(PROP_TONEMAPPING_MODE, TonemappingMode, tonemappingMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); + DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_OCCLUSION_MODE, AmbientOcclusionMode, ambientOcclusionMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); // Polyvox DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 87d990fe87..2ed634c42c 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -175,6 +175,23 @@ enum EntityPropertyList { PROP_DERIVED_38, PROP_DERIVED_39, PROP_DERIVED_40, + PROP_DERIVED_41, + PROP_DERIVED_42, + PROP_DERIVED_43, + PROP_DERIVED_44, + PROP_DERIVED_45, + PROP_DERIVED_46, + PROP_DERIVED_47, + PROP_DERIVED_48, + PROP_DERIVED_49, + PROP_DERIVED_50, + PROP_DERIVED_51, + PROP_DERIVED_52, + PROP_DERIVED_53, + PROP_DERIVED_54, + PROP_DERIVED_55, + PROP_DERIVED_56, + PROP_DERIVED_57, PROP_AFTER_LAST_ITEM, @@ -276,44 +293,44 @@ enum EntityPropertyList { // Zone // Keylight - PROP_KEYLIGHT_COLOR = PROP_DERIVED_0, - PROP_KEYLIGHT_INTENSITY = PROP_DERIVED_1, - PROP_KEYLIGHT_DIRECTION = PROP_DERIVED_2, - PROP_KEYLIGHT_CAST_SHADOW = PROP_DERIVED_3, - PROP_KEYLIGHT_SHADOW_BIAS = PROP_DERIVED_4, - PROP_KEYLIGHT_SHADOW_MAX_DISTANCE = PROP_DERIVED_5, + PROP_KEY_LIGHT_MODE = PROP_DERIVED_0, + PROP_KEYLIGHT_COLOR = PROP_DERIVED_1, + PROP_KEYLIGHT_INTENSITY = PROP_DERIVED_2, + PROP_KEYLIGHT_DIRECTION = PROP_DERIVED_3, + PROP_KEYLIGHT_CAST_SHADOW = PROP_DERIVED_4, + PROP_KEYLIGHT_SHADOW_BIAS = PROP_DERIVED_5, + PROP_KEYLIGHT_SHADOW_MAX_DISTANCE = PROP_DERIVED_6, // Ambient light - PROP_AMBIENT_LIGHT_INTENSITY = PROP_DERIVED_6, - PROP_AMBIENT_LIGHT_URL = PROP_DERIVED_7, - PROP_AMBIENT_LIGHT_COLOR = PROP_DERIVED_8, + PROP_AMBIENT_LIGHT_MODE = PROP_DERIVED_7, + PROP_AMBIENT_LIGHT_INTENSITY = PROP_DERIVED_8, + PROP_AMBIENT_LIGHT_URL = PROP_DERIVED_9, + PROP_AMBIENT_LIGHT_COLOR = PROP_DERIVED_10, // Skybox - PROP_SKYBOX_COLOR = PROP_DERIVED_9, - PROP_SKYBOX_URL = PROP_DERIVED_10, + PROP_SKYBOX_MODE = PROP_DERIVED_11, + PROP_SKYBOX_COLOR = PROP_DERIVED_12, + PROP_SKYBOX_URL = PROP_DERIVED_13, // Haze - PROP_HAZE_RANGE = PROP_DERIVED_11, - PROP_HAZE_COLOR = PROP_DERIVED_12, - PROP_HAZE_GLARE_COLOR = PROP_DERIVED_13, - PROP_HAZE_ENABLE_GLARE = PROP_DERIVED_14, - PROP_HAZE_GLARE_ANGLE = PROP_DERIVED_15, - PROP_HAZE_ALTITUDE_EFFECT = PROP_DERIVED_16, - PROP_HAZE_CEILING = PROP_DERIVED_17, - PROP_HAZE_BASE_REF = PROP_DERIVED_18, - PROP_HAZE_BACKGROUND_BLEND = PROP_DERIVED_19, - PROP_HAZE_ATTENUATE_KEYLIGHT = PROP_DERIVED_20, - PROP_HAZE_KEYLIGHT_RANGE = PROP_DERIVED_21, - PROP_HAZE_KEYLIGHT_ALTITUDE = PROP_DERIVED_22, + PROP_HAZE_MODE = PROP_DERIVED_14, + PROP_HAZE_RANGE = PROP_DERIVED_15, + PROP_HAZE_COLOR = PROP_DERIVED_16, + PROP_HAZE_GLARE_COLOR = PROP_DERIVED_17, + PROP_HAZE_ENABLE_GLARE = PROP_DERIVED_18, + PROP_HAZE_GLARE_ANGLE = PROP_DERIVED_19, + PROP_HAZE_ALTITUDE_EFFECT = PROP_DERIVED_20, + PROP_HAZE_CEILING = PROP_DERIVED_21, + PROP_HAZE_BASE_REF = PROP_DERIVED_22, + PROP_HAZE_BACKGROUND_BLEND = PROP_DERIVED_23, + PROP_HAZE_ATTENUATE_KEYLIGHT = PROP_DERIVED_24, + PROP_HAZE_KEYLIGHT_RANGE = PROP_DERIVED_25, + PROP_HAZE_KEYLIGHT_ALTITUDE = PROP_DERIVED_26, // Bloom - PROP_BLOOM_INTENSITY = PROP_DERIVED_23, - PROP_BLOOM_THRESHOLD = PROP_DERIVED_24, - PROP_BLOOM_SIZE = PROP_DERIVED_25, - PROP_FLYING_ALLOWED = PROP_DERIVED_26, - PROP_GHOSTING_ALLOWED = PROP_DERIVED_27, - PROP_FILTER_URL = PROP_DERIVED_28, - PROP_KEY_LIGHT_MODE = PROP_DERIVED_29, - PROP_AMBIENT_LIGHT_MODE = PROP_DERIVED_30, - PROP_SKYBOX_MODE = PROP_DERIVED_31, - PROP_HAZE_MODE = PROP_DERIVED_32, - PROP_BLOOM_MODE = PROP_DERIVED_33, + PROP_BLOOM_MODE = PROP_DERIVED_27, + PROP_BLOOM_INTENSITY = PROP_DERIVED_28, + PROP_BLOOM_THRESHOLD = PROP_DERIVED_29, + PROP_BLOOM_SIZE = PROP_DERIVED_30, + PROP_FLYING_ALLOWED = PROP_DERIVED_31, + PROP_GHOSTING_ALLOWED = PROP_DERIVED_32, + PROP_FILTER_URL = PROP_DERIVED_33, // Avatar priority PROP_AVATAR_PRIORITY = PROP_DERIVED_34, // Screen-sharing @@ -324,6 +341,22 @@ enum EntityPropertyList { PROP_REVERB_WET_LEVEL = PROP_DERIVED_38, PROP_LISTENER_ZONES = PROP_DERIVED_39, PROP_LISTENER_ATTENUATION_COEFFICIENTS = PROP_DERIVED_40, + // Tonemapping + PROP_TONEMAPPING_MODE = PROP_DERIVED_41, + PROP_TONEMAPPING_CURVE = PROP_DERIVED_42, + PROP_TONEMAPPING_EXPOSURE = PROP_DERIVED_43, + // Ambient Occlusion + PROP_AMBIENT_OCCLUSION_MODE = PROP_DERIVED_44, + PROP_AMBIENT_OCCLUSION_TECHNIQUE = PROP_DERIVED_45, + PROP_AMBIENT_OCCLUSION_JITTER = PROP_DERIVED_46, + PROP_AMBIENT_OCCLUSION_RESOLUTION_LEVEL = PROP_DERIVED_47, + PROP_AMBIENT_OCCLUSION_EDGE_SHARPNESS = PROP_DERIVED_48, + PROP_AMBIENT_OCCLUSION_BLUR_RADIUS = PROP_DERIVED_49, + PROP_AMBIENT_OCCLUSION_AO_RADIUS = PROP_DERIVED_50, + PROP_AMBIENT_OCCLUSION_AO_OBSCURANCE_LEVEL = PROP_DERIVED_51, + PROP_AMBIENT_OCCLUSION_AO_FALLOFF_ANGLE = PROP_DERIVED_52, + PROP_AMBIENT_OCCLUSION_AO_SAMPLING_AMOUNT = PROP_DERIVED_53, + PROP_AMBIENT_OCCLUSION_SSAO_NUM_SPIRAL_TURNS = PROP_DERIVED_54, // Polyvox PROP_VOXEL_VOLUME_SIZE = PROP_DERIVED_0, diff --git a/libraries/entities/src/TonemappingPropertyGroup.cpp b/libraries/entities/src/TonemappingPropertyGroup.cpp new file mode 100644 index 0000000000..2613e3144d --- /dev/null +++ b/libraries/entities/src/TonemappingPropertyGroup.cpp @@ -0,0 +1,167 @@ +// +// TonemappingPropertyGroup.cpp +// libraries/entities/src +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// SPDX-License-Identifier: Apache-2.0 +// + +#include "TonemappingPropertyGroup.h" + +#include + +#include "EntityItemProperties.h" +#include "EntityItemPropertiesMacros.h" + +inline void addTonemappingCurve(QHash& lookup, TonemappingCurve curve) { lookup[TonemappingCurveHelpers::getNameForTonemappingCurve(curve)] = curve; } +const QHash stringToTonemappingCurveLookup = [] { + QHash toReturn; + addTonemappingCurve(toReturn, TonemappingCurve::RGB); + addTonemappingCurve(toReturn, TonemappingCurve::SRGB); + addTonemappingCurve(toReturn, TonemappingCurve::FILMIC); + addTonemappingCurve(toReturn, TonemappingCurve::REINHARD); + return toReturn; +}(); +QString TonemappingPropertyGroup::getCurveAsString() const { return TonemappingCurveHelpers::getNameForTonemappingCurve(_curve); } +void TonemappingPropertyGroup::setCurveFromString(const QString& curve) { + auto curveItr = stringToTonemappingCurveLookup.find(curve.toLower()); + if (curveItr != stringToTonemappingCurveLookup.end()) { + _curve = curveItr.value(); + _curveChanged = true; + } +} + +void TonemappingPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, + bool skipDefaults, EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, bool isMyOwnAvatarEntity) const { + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_TONEMAPPING_CURVE, Tonemapping, tonemapping, Curve, curve, getCurveAsString); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_TONEMAPPING_EXPOSURE, Tonemapping, tonemapping, Exposure, exposure); +} + +void TonemappingPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) { + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_ENUM(tonemapping, curve, Curve); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(tonemapping, exposure, float, setExposure); +} + +void TonemappingPropertyGroup::merge(const TonemappingPropertyGroup& other) { + COPY_PROPERTY_IF_CHANGED(curve); + COPY_PROPERTY_IF_CHANGED(exposure); +} + +void TonemappingPropertyGroup::debugDump() const { + qCDebug(entities) << " TonemappingPropertyGroup: ---------------------------------------------"; + qCDebug(entities) << " Curve:" << getCurveAsString(); + qCDebug(entities) << " Exposure:" << getExposure(); +} + +void TonemappingPropertyGroup::listChangedProperties(QList& out) { + if (curveChanged()) { + out << "tonemapping-curve"; + } + if (exposureChanged()) { + out << "tonemapping-exposure"; + } +} + +bool TonemappingPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_CURVE, (uint32_t)getCurve()); + APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_EXPOSURE, getExposure()); + + return true; +} + + +bool TonemappingPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + + READ_ENTITY_PROPERTY(PROP_TONEMAPPING_CURVE, TonemappingCurve, setCurve); + READ_ENTITY_PROPERTY(PROP_TONEMAPPING_EXPOSURE, float, setExposure); + + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_TONEMAPPING_CURVE, Curve); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_TONEMAPPING_EXPOSURE, Exposure); + + processedBytes += bytesRead; + + Q_UNUSED(somethingChanged); + + return true; +} + +void TonemappingPropertyGroup::markAllChanged() { + _curveChanged = true; + _exposureChanged = true; +} + +EntityPropertyFlags TonemappingPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + + CHECK_PROPERTY_CHANGE(PROP_TONEMAPPING_CURVE, curve); + CHECK_PROPERTY_CHANGE(PROP_TONEMAPPING_EXPOSURE, exposure); + + return changedProperties; +} + +void TonemappingPropertyGroup::getProperties(EntityItemProperties& properties) const { + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Tonemapping, Curve, getCurve); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Tonemapping, Exposure, getExposure); +} + +bool TonemappingPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Tonemapping, Curve, curve, setCurve); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Tonemapping, Exposure, exposure, setExposure); + + return somethingChanged; +} + +EntityPropertyFlags TonemappingPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + + requestedProperties += PROP_TONEMAPPING_CURVE; + requestedProperties += PROP_TONEMAPPING_EXPOSURE; + + return requestedProperties; +} + +void TonemappingPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_CURVE, (uint32_t)getCurve()); + APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_EXPOSURE, getExposure()); +} + +int TonemappingPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_TONEMAPPING_CURVE, TonemappingCurve, setCurve); + READ_ENTITY_PROPERTY(PROP_TONEMAPPING_EXPOSURE, float, setExposure); + + return bytesRead; +} diff --git a/libraries/entities/src/TonemappingPropertyGroup.h b/libraries/entities/src/TonemappingPropertyGroup.h new file mode 100644 index 0000000000..70db875d8c --- /dev/null +++ b/libraries/entities/src/TonemappingPropertyGroup.h @@ -0,0 +1,87 @@ +// +// TonemappingPropertyGroup.h +// libraries/entities/src +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// SPDX-License-Identifier: Apache-2.0 +// + +#ifndef hifi_TonemappingPropertyGroup_h +#define hifi_TonemappingPropertyGroup_h + +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class EntityTreeElementExtraEncodeData; +class ReadBitstreamToTreeParams; +class ScriptEngine; +class ScriptValue; + +/*@jsdoc + * Tonemapping is defined by the following properties: + * @typedef {object} Entities.Tonemapping + * @property {TonemappingCurve} curve="srgb" - The tonemapping curve used. + * @property {number} exposure=0.0 - The applied exposure. + */ +class TonemappingPropertyGroup : public PropertyGroup { +public: + // EntityItemProperty related helpers + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, + ScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties, bool returnNothingOnEmptyPropertyFlags, + bool isMyOwnAvatarEntity) const override; + virtual void copyFromScriptValue(const ScriptValue& object, const QSet &namesSet, bool& _defaultSettings) override; + + void merge(const TonemappingPropertyGroup& other); + + virtual void debugDump() const override; + virtual void listChangedProperties(QList& out) override; + + virtual bool appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt, int& processedBytes) override; + virtual void markAllChanged() override; + virtual EntityPropertyFlags getChangedProperties() const override; + + // EntityItem related helpers + // methods for getting/setting all properties of an entity + virtual void getProperties(EntityItemProperties& propertiesOut) const override; + + /// returns true if something changed + virtual bool setProperties(const EntityItemProperties& properties) override; + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; + + DEFINE_PROPERTY_REF_ENUM(PROP_TONEMAPPING_CURVE, Curve, curve, TonemappingCurve, TonemappingCurve::SRGB); + DEFINE_PROPERTY(PROP_TONEMAPPING_EXPOSURE, Exposure, exposure, float, 0.0); +}; + +#endif // hifi_TonemappingPropertyGroup_h diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index cefd2ccb26..a8561f49d9 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -61,6 +61,8 @@ EntityItemProperties ZoneEntityItem::getProperties(const EntityPropertyFlags& de _hazeProperties.getProperties(properties); _bloomProperties.getProperties(properties); _audioProperties.getProperties(properties); + _tonemappingProperties.getProperties(properties); + _ambientOcclusionProperties.getProperties(properties); COPY_ENTITY_PROPERTY_TO_PROPERTIES(flyingAllowed, getFlyingAllowed); COPY_ENTITY_PROPERTY_TO_PROPERTIES(ghostingAllowed, getGhostingAllowed); @@ -73,6 +75,8 @@ EntityItemProperties ZoneEntityItem::getProperties(const EntityPropertyFlags& de COPY_ENTITY_PROPERTY_TO_PROPERTIES(bloomMode, getBloomMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(avatarPriority, getAvatarPriority); COPY_ENTITY_PROPERTY_TO_PROPERTIES(screenshare, getScreenshare); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(tonemappingMode, getTonemappingMode); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(ambientOcclusionMode, getAmbientOcclusionMode); return properties; } @@ -92,6 +96,8 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie _hazePropertiesChanged |= _hazeProperties.setProperties(properties); _bloomPropertiesChanged |= _bloomProperties.setProperties(properties); bool audioPropertiesChanged = _audioProperties.setProperties(properties); + _tonemappingPropertiesChanged |= _tonemappingProperties.setProperties(properties); + _ambientOcclusionPropertiesChanged |= _ambientOcclusionProperties.setProperties(properties); SET_ENTITY_PROPERTY_FROM_PROPERTIES(flyingAllowed, setFlyingAllowed); SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed); @@ -104,9 +110,12 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie SET_ENTITY_PROPERTY_FROM_PROPERTIES(bloomMode, setBloomMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(avatarPriority, setAvatarPriority); SET_ENTITY_PROPERTY_FROM_PROPERTIES(screenshare, setScreenshare); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(tonemappingMode, setTonemappingMode); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(ambientOcclusionMode, setAmbientOcclusionMode); somethingChanged |= _keyLightPropertiesChanged || _ambientLightPropertiesChanged || _skyboxPropertiesChanged || - _hazePropertiesChanged || _bloomPropertiesChanged || audioPropertiesChanged; + _hazePropertiesChanged || _bloomPropertiesChanged || audioPropertiesChanged || + _tonemappingPropertiesChanged || _ambientOcclusionPropertiesChanged; return somethingChanged; } @@ -177,6 +186,20 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, dataAt += bytesFromAudio; } + { + int bytesFromTonemapping = _tonemappingProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, somethingChanged); + bytesRead += bytesFromTonemapping; + dataAt += bytesFromTonemapping; + } + + { + int bytesFromAmbientOcclusion = _ambientOcclusionProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, somethingChanged); + bytesRead += bytesFromAmbientOcclusion; + dataAt += bytesFromAmbientOcclusion; + } + READ_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, bool, setFlyingAllowed); READ_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed); READ_ENTITY_PROPERTY(PROP_FILTER_URL, QString, setFilterURL); @@ -188,6 +211,8 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_BLOOM_MODE, uint32_t, setBloomMode); READ_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, uint32_t, setAvatarPriority); READ_ENTITY_PROPERTY(PROP_SCREENSHARE, uint32_t, setScreenshare); + READ_ENTITY_PROPERTY(PROP_TONEMAPPING_MODE, uint32_t, setTonemappingMode); + READ_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_MODE, uint32_t, setAmbientOcclusionMode); return bytesRead; } @@ -204,6 +229,8 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p requestedProperties += _hazeProperties.getEntityProperties(params); requestedProperties += _bloomProperties.getEntityProperties(params); requestedProperties += _audioProperties.getEntityProperties(params); + requestedProperties += _tonemappingProperties.getEntityProperties(params); + requestedProperties += _ambientOcclusionProperties.getEntityProperties(params); requestedProperties += PROP_FLYING_ALLOWED; requestedProperties += PROP_GHOSTING_ALLOWED; @@ -216,6 +243,8 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p requestedProperties += PROP_SKYBOX_MODE; requestedProperties += PROP_HAZE_MODE; requestedProperties += PROP_BLOOM_MODE; + requestedProperties += PROP_TONEMAPPING_MODE; + requestedProperties += PROP_AMBIENT_OCCLUSION_MODE; return requestedProperties; } @@ -247,32 +276,40 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits propertyFlags, propertiesDidntFit, propertyCount, appendState); _audioProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + _tonemappingProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); + _ambientOcclusionProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, getFlyingAllowed()); APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, getGhostingAllowed()); APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, getFilterURL()); - APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)getKeyLightMode()); - APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)getAmbientLightMode()); - APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)getSkyboxMode()); - APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)getHazeMode()); - APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)getBloomMode()); + APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, getKeyLightMode()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, getAmbientLightMode()); + APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, getSkyboxMode()); + APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, getHazeMode()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, getBloomMode()); APPEND_ENTITY_PROPERTY(PROP_AVATAR_PRIORITY, getAvatarPriority()); APPEND_ENTITY_PROPERTY(PROP_SCREENSHARE, getScreenshare()); + APPEND_ENTITY_PROPERTY(PROP_TONEMAPPING_MODE, getTonemappingMode()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_OCCLUSION_MODE, getAmbientOcclusionMode()); } void ZoneEntityItem::debugDump() const { quint64 now = usecTimestampNow(); - qCDebug(entities) << " ZoneEntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); - qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); - qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); - qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getComponentModeAsString(_hazeMode); - qCDebug(entities) << " _keyLightMode:" << EntityItemProperties::getComponentModeAsString(_keyLightMode); - qCDebug(entities) << " _ambientLightMode:" << EntityItemProperties::getComponentModeAsString(_ambientLightMode); - qCDebug(entities) << " _skyboxMode:" << EntityItemProperties::getComponentModeAsString(_skyboxMode); - qCDebug(entities) << " _bloomMode:" << EntityItemProperties::getComponentModeAsString(_bloomMode); - qCDebug(entities) << " _avatarPriority:" << getAvatarPriority(); + qCDebug(entities) << " ZoneEntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); + qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getComponentModeAsString(_hazeMode); + qCDebug(entities) << " _keyLightMode:" << EntityItemProperties::getComponentModeAsString(_keyLightMode); + qCDebug(entities) << " _ambientLightMode:" << EntityItemProperties::getComponentModeAsString(_ambientLightMode); + qCDebug(entities) << " _skyboxMode:" << EntityItemProperties::getComponentModeAsString(_skyboxMode); + qCDebug(entities) << " _bloomMode:" << EntityItemProperties::getComponentModeAsString(_bloomMode); + qCDebug(entities) << " _avatarPriority:" << getAvatarPriority(); + qCDebug(entities) << " _tonemappingMode:" << EntityItemProperties::getComponentModeAsString(_tonemappingMode); + qCDebug(entities) << " _ambientOcclusionMode:" << EntityItemProperties::getComponentModeAsString(_ambientOcclusionMode); _keyLightProperties.debugDump(); _ambientLightProperties.debugDump(); @@ -280,6 +317,8 @@ void ZoneEntityItem::debugDump() const { _hazeProperties.debugDump(); _bloomProperties.debugDump(); _audioProperties.debugDump(); + _tonemappingProperties.debugDump(); + _ambientOcclusionProperties.debugDump(); } void ZoneEntityItem::setShapeType(ShapeType type) { @@ -408,53 +447,11 @@ void ZoneEntityItem::resetRenderingPropertiesChanged() { _skyboxPropertiesChanged = false; _hazePropertiesChanged = false; _bloomPropertiesChanged = false; + _tonemappingPropertiesChanged = false; + _ambientOcclusionPropertiesChanged = false; }); } -void ZoneEntityItem::setHazeMode(const uint32_t value) { - if (value < COMPONENT_MODE_ITEM_COUNT && value != _hazeMode) { - _hazeMode = value; - _hazePropertiesChanged = true; - } -} - -uint32_t ZoneEntityItem::getHazeMode() const { - return _hazeMode; -} - -void ZoneEntityItem::setBloomMode(const uint32_t value) { - if (value < COMPONENT_MODE_ITEM_COUNT && value != _bloomMode) { - _bloomMode = value; - _bloomPropertiesChanged = true; - } -} - -uint32_t ZoneEntityItem::getBloomMode() const { - return _bloomMode; -} - -void ZoneEntityItem::setKeyLightMode(const uint32_t value) { - if (value < COMPONENT_MODE_ITEM_COUNT && value != _keyLightMode) { - _keyLightMode = value; - _keyLightPropertiesChanged = true; - } -} - -uint32_t ZoneEntityItem::getKeyLightMode() const { - return _keyLightMode; -} - -void ZoneEntityItem::setAmbientLightMode(const uint32_t value) { - if (value < COMPONENT_MODE_ITEM_COUNT && value != _ambientLightMode) { - _ambientLightMode = value; - _ambientLightPropertiesChanged = true; - } -} - -uint32_t ZoneEntityItem::getAmbientLightMode() const { - return _ambientLightMode; -} - void ZoneEntityItem::setSkyboxMode(const uint32_t value) { if (value < COMPONENT_MODE_ITEM_COUNT && value != _skyboxMode) { _skyboxMode = value; @@ -462,8 +459,46 @@ void ZoneEntityItem::setSkyboxMode(const uint32_t value) { } } -uint32_t ZoneEntityItem::getSkyboxMode() const { - return _skyboxMode; +void ZoneEntityItem::setKeyLightMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _keyLightMode) { + _keyLightMode = value; + _keyLightPropertiesChanged = true; + } +} + +void ZoneEntityItem::setAmbientLightMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _ambientLightMode) { + _ambientLightMode = value; + _ambientLightPropertiesChanged = true; + } +} + +void ZoneEntityItem::setHazeMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _hazeMode) { + _hazeMode = value; + _hazePropertiesChanged = true; + } +} + +void ZoneEntityItem::setBloomMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _bloomMode) { + _bloomMode = value; + _bloomPropertiesChanged = true; + } +} + +void ZoneEntityItem::setTonemappingMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _tonemappingMode) { + _tonemappingMode = value; + _tonemappingPropertiesChanged = true; + } +} + +void ZoneEntityItem::setAmbientOcclusionMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _ambientOcclusionMode) { + _ambientOcclusionMode = value; + _ambientOcclusionPropertiesChanged = true; + } } void ZoneEntityItem::setUserData(const QString& value) { diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index ccf895ca44..6fb0145df7 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -23,6 +23,8 @@ #include "HazePropertyGroup.h" #include "BloomPropertyGroup.h" #include "ZoneAudioPropertyGroup.h" +#include "TonemappingPropertyGroup.h" +#include "AmbientOcclusionPropertyGroup.h" class ZoneEntityItem : public EntityItem { public: @@ -68,36 +70,20 @@ public: virtual bool matchesJSONFilters(const QJsonObject& jsonFilters) const override; - KeyLightPropertyGroup getKeyLightProperties() const { return resultWithReadLock([&] { return _keyLightProperties; }); } - AmbientLightPropertyGroup getAmbientLightProperties() const { return resultWithReadLock([&] { return _ambientLightProperties; }); } - - void setHazeMode(const uint32_t value); - uint32_t getHazeMode() const; + void setSkyboxMode(uint32_t value); + uint32_t getSkyboxMode() const { return _skyboxMode; } void setKeyLightMode(uint32_t value); - uint32_t getKeyLightMode() const; + uint32_t getKeyLightMode() const { return _keyLightMode; } void setAmbientLightMode(uint32_t value); - uint32_t getAmbientLightMode() const; + uint32_t getAmbientLightMode() const { return _ambientLightMode; } - void setSkyboxMode(uint32_t value); - uint32_t getSkyboxMode() const; + void setHazeMode(const uint32_t value); + uint32_t getHazeMode() const { return _hazeMode; } void setBloomMode(const uint32_t value); - uint32_t getBloomMode() const; - - SkyboxPropertyGroup getSkyboxProperties() const { return resultWithReadLock([&] { return _skyboxProperties; }); } - - const HazePropertyGroup& getHazeProperties() const { return _hazeProperties; } - const BloomPropertyGroup& getBloomProperties() const { return _bloomProperties; } - const ZoneAudioPropertyGroup& getAudioProperties() const { return _audioProperties; } - - bool getFlyingAllowed() const { return _flyingAllowed; } - void setFlyingAllowed(bool value) { _flyingAllowed = value; } - bool getGhostingAllowed() const { return _ghostingAllowed; } - void setGhostingAllowed(bool value) { _ghostingAllowed = value; } - QString getFilterURL() const; - void setFilterURL(const QString url); + uint32_t getBloomMode() const { return _bloomMode; } uint32_t getAvatarPriority() const { return _avatarPriority; } void setAvatarPriority(uint32_t value) { _avatarPriority = value; } @@ -105,6 +91,28 @@ public: uint32_t getScreenshare() const { return _screenshare; } void setScreenshare(uint32_t value) { _screenshare = value; } + void setTonemappingMode(uint32_t value); + uint32_t getTonemappingMode() const { return _tonemappingMode; } + + void setAmbientOcclusionMode(const uint32_t value); + uint32_t getAmbientOcclusionMode() const { return _ambientOcclusionMode; } + + SkyboxPropertyGroup getSkyboxProperties() const { return resultWithReadLock([&] { return _skyboxProperties; }); } + KeyLightPropertyGroup getKeyLightProperties() const { return resultWithReadLock([&] { return _keyLightProperties; }); } + AmbientLightPropertyGroup getAmbientLightProperties() const { return resultWithReadLock([&] { return _ambientLightProperties; }); } + const HazePropertyGroup& getHazeProperties() const { return _hazeProperties; } + const BloomPropertyGroup& getBloomProperties() const { return _bloomProperties; } + const ZoneAudioPropertyGroup& getAudioProperties() const { return _audioProperties; } + const TonemappingPropertyGroup& getTonemappingProperties() const { return _tonemappingProperties; } + const AmbientOcclusionPropertyGroup& getAmbientOcclusionProperties() const { return _ambientOcclusionProperties; } + + bool getFlyingAllowed() const { return _flyingAllowed; } + void setFlyingAllowed(bool value) { _flyingAllowed = value; } + bool getGhostingAllowed() const { return _ghostingAllowed; } + void setGhostingAllowed(bool value) { _ghostingAllowed = value; } + QString getFilterURL() const; + void setFilterURL(const QString url); + void setUserData(const QString& value) override; bool keyLightPropertiesChanged() const { return _keyLightPropertiesChanged; } @@ -112,6 +120,8 @@ public: bool skyboxPropertiesChanged() const { return _skyboxPropertiesChanged; } bool hazePropertiesChanged() const { return _hazePropertiesChanged; } bool bloomPropertiesChanged() const { return _bloomPropertiesChanged; } + bool tonemappingPropertiesChanged() const { return _tonemappingPropertiesChanged; } + bool ambientOcclusionPropertiesChanged() const { return _ambientOcclusionPropertiesChanged; } void resetRenderingPropertiesChanged(); @@ -142,35 +152,35 @@ protected: ShapeType _shapeType { DEFAULT_SHAPE_TYPE }; QString _compoundShapeURL; - // The following 3 values are the defaults for zone creation uint32_t _keyLightMode { COMPONENT_MODE_INHERIT }; uint32_t _skyboxMode { COMPONENT_MODE_INHERIT }; uint32_t _ambientLightMode { COMPONENT_MODE_INHERIT }; - uint32_t _hazeMode { COMPONENT_MODE_INHERIT }; uint32_t _bloomMode { COMPONENT_MODE_INHERIT }; + uint32_t _avatarPriority { COMPONENT_MODE_INHERIT }; + uint32_t _screenshare { COMPONENT_MODE_INHERIT }; + uint32_t _tonemappingMode { COMPONENT_MODE_INHERIT }; + uint32_t _ambientOcclusionMode { COMPONENT_MODE_INHERIT }; SkyboxPropertyGroup _skyboxProperties; HazePropertyGroup _hazeProperties; BloomPropertyGroup _bloomProperties; ZoneAudioPropertyGroup _audioProperties; + TonemappingPropertyGroup _tonemappingProperties; + AmbientOcclusionPropertyGroup _ambientOcclusionProperties; bool _flyingAllowed { DEFAULT_FLYING_ALLOWED }; bool _ghostingAllowed { DEFAULT_GHOSTING_ALLOWED }; QString _filterURL { DEFAULT_FILTER_URL }; - // Avatar-updates priority - uint32_t _avatarPriority { COMPONENT_MODE_INHERIT }; - - // Screen-sharing zone - uint32_t _screenshare { COMPONENT_MODE_INHERIT }; - // Dirty flags turn true when either keylight properties is changing values. bool _keyLightPropertiesChanged { false }; bool _ambientLightPropertiesChanged { false }; bool _skyboxPropertiesChanged { false }; bool _hazePropertiesChanged { false }; bool _bloomPropertiesChanged { false }; + bool _tonemappingPropertiesChanged { false }; + bool _ambientOcclusionPropertiesChanged { false }; static bool _drawZoneBoundaries; static bool _zonesArePickable; diff --git a/libraries/graphics/src/graphics/AmbientOcclusion.h b/libraries/graphics/src/graphics/AmbientOcclusion.h new file mode 100644 index 0000000000..66a1ad0ea7 --- /dev/null +++ b/libraries/graphics/src/graphics/AmbientOcclusion.h @@ -0,0 +1,59 @@ +// +// AmbientOcclusion.h +// libraries/graphics/src/graphics +// +// Created by HifiExperiments 6/24/24 +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_model_AmbientOcclusion_h +#define hifi_model_AmbientOcclusion_h + +#include + +#include + +namespace graphics { + class AmbientOcclusion { + public: + AmbientOcclusion() {} + + void setTechnique(const AmbientOcclusionTechnique technique) { _technique = technique; } + void setJitter(const bool jitter) { _jitter = jitter; } + void setResolutionLevel(const uint8_t resolutionLevel) { _resolutionLevel = resolutionLevel; } + void setEdgeSharpness(const float edgeSharpness) { _edgeSharpness = edgeSharpness; } + void setBlurRadius(const uint8_t blurRadius) { _blurRadius = blurRadius; } + void setAORadius(const float aoRadius) { _aoRadius = aoRadius; } + void setAOObscuranceLevel(const float aoObscuranceLevel) { _aoObscuranceLevel = aoObscuranceLevel; } + void setAOFalloffAngle(const float aoFalloffAngle) { _aoFalloffAngle = aoFalloffAngle; } + void setAOSamplingAmount(const float aoSamplingAmount) { _aoSamplingAmount = aoSamplingAmount; } + void setSSAONumSpiralTurns(const float ssaoNumSpiralTurns) { _ssaoNumSpiralTurns = ssaoNumSpiralTurns; } + + AmbientOcclusionTechnique getTechnique() const { return _technique; } + bool getJitter() const { return _jitter; } + uint8_t getResolutionLevel() const { return _resolutionLevel; } + float getEdgeSharpness() const { return _edgeSharpness; } + uint8_t getBlurRadius() const { return _blurRadius; } + float getAORadius() const { return _aoRadius; } + float getAOObscuranceLevel() const { return _aoObscuranceLevel; } + float getAOFalloffAngle() const { return _aoFalloffAngle; } + float getAOSamplingAmount() const { return _aoSamplingAmount; } + float getSSAONumSpiralTurns() const { return _ssaoNumSpiralTurns; } + + private: + AmbientOcclusionTechnique _technique { AmbientOcclusionTechnique::SSAO }; + bool _jitter { false }; + uint8_t _resolutionLevel { 2 }; + float _edgeSharpness { 1.0f }; + uint8_t _blurRadius { 4 }; + float _aoRadius { 1.0f }; + float _aoObscuranceLevel { 0.5f }; + float _aoFalloffAngle { 0.25f }; + float _aoSamplingAmount { 0.5f }; + float _ssaoNumSpiralTurns { 7.0f }; + }; + using AmbientOcclusionPointer = std::shared_ptr; +} +#endif // hifi_model_AmbientOcclusion_h diff --git a/libraries/graphics/src/graphics/Tonemapping.h b/libraries/graphics/src/graphics/Tonemapping.h new file mode 100644 index 0000000000..ad7467d131 --- /dev/null +++ b/libraries/graphics/src/graphics/Tonemapping.h @@ -0,0 +1,35 @@ +// +// Tonemapping.h +// libraries/graphics/src/graphics +// +// Created by HifiExperiments on 6/24/24 +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_model_Tonemapping_h +#define hifi_model_Tonemapping_h + +#include + +#include + +namespace graphics { + class Tonemapping { + public: + Tonemapping() {} + + void setCurve(const TonemappingCurve curve) { _curve = curve; } + void setExposure(const float exposure) { _exposure = exposure; } + + TonemappingCurve getCurve() const { return _curve; } + float getExposure() const { return _exposure; } + + private: + TonemappingCurve _curve { TonemappingCurve::SRGB }; + float _exposure { 0.0f }; + }; + using TonemappingPointer = std::shared_ptr; +} +#endif // hifi_model_Tonemapping_h diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index ead10ffb82..5c0c54c03e 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -299,6 +299,7 @@ enum class EntityVersion : PacketVersion { ShapeUnlit, AmbientColor, SoundEntities, + TonemappingAndAmbientOcclusion, // Add new versions above here NUM_PACKET_TYPE, diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index e61bef45e7..6d736fe8cf 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -43,6 +43,8 @@ #include "TextEffect.h" #include "TextAlignment.h" #include "MirrorMode.h" +#include "TonemappingCurve.h" +#include "AmbientOcclusionTechnique.h" #include "OctreeConstants.h" #include "OctreeElement.h" @@ -285,6 +287,8 @@ public: static int unpackDataFromBytes(const unsigned char* dataBytes, TextEffect& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, TextAlignment& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, MirrorMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } + static int unpackDataFromBytes(const unsigned char* dataBytes, TonemappingCurve& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } + static int unpackDataFromBytes(const unsigned char* dataBytes, AmbientOcclusionTechnique& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec2& result); static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec3& result); static int unpackDataFromBytes(const unsigned char* dataBytes, glm::u8vec3& result); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 68eb1b5a06..5e7c584e33 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -37,6 +37,9 @@ gpu::PipelinePointer AmbientOcclusionEffect::_mipCreationPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_gatherPipeline; gpu::PipelinePointer AmbientOcclusionEffect::_buildNormalsPipeline; +#define MAX_SSAO_SAMPLES 64.0f +#define MAX_HBAO_SAMPLES 6.0f + AmbientOcclusionFramebuffer::AmbientOcclusionFramebuffer() { } @@ -205,29 +208,7 @@ gpu::TexturePointer AmbientOcclusionFramebuffer::getNormalTexture() { } AmbientOcclusionEffectConfig::AmbientOcclusionEffectConfig() : - render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion"), - perspectiveScale{ 1.0f }, - edgeSharpness{ 1.0f }, - blurRadius{ 4 }, - resolutionLevel{ 2 }, - - ssaoRadius{ 1.0f }, - ssaoObscuranceLevel{ 0.4f }, - ssaoFalloffAngle{ 0.15f }, - ssaoNumSpiralTurns{ 7.0f }, - ssaoNumSamples{ 32 }, - - hbaoRadius{ 0.7f }, - hbaoObscuranceLevel{ 0.75f }, - hbaoFalloffAngle{ 0.3f }, - hbaoNumSamples{ 1 }, - - horizonBased{ false }, - ditheringEnabled{ true }, - borderingEnabled{ true }, - fetchMipsEnabled{ true }, - jitterEnabled{ false }{ -} + render::GPUJobConfig::Persistent(QStringList() << "Render" << "Engine" << "Ambient Occlusion") {} void AmbientOcclusionEffectConfig::setSSAORadius(float newRadius) { ssaoRadius = std::max(0.01f, newRadius); emit dirty(); @@ -288,89 +269,105 @@ void AmbientOcclusionEffectConfig::setBlurRadius(int radius) { } AmbientOcclusionEffect::AOParameters::AOParameters() { - _resolutionInfo = glm::vec4{ 0.0f }; - _radiusInfo = glm::vec4{ 0.0f }; - _ditheringInfo = glm::vec4{ 0.0f }; - _sampleInfo = glm::vec4{ 0.0f }; - _falloffInfo = glm::vec4{ 0.0f }; + _resolutionInfo = glm::vec4(0.0f); + _radiusInfo = glm::vec4(0.0f); + _ditheringInfo = glm::vec4(0.0f); + _sampleInfo = glm::vec4(0.0f); + _falloffInfo = glm::vec4(0.0f); } AmbientOcclusionEffect::BlurParameters::BlurParameters() { _blurInfo = { 1.0f, 2.0f, 0.0f, 3.0f }; } -AmbientOcclusionEffect::AmbientOcclusionEffect() { +void AmbientOcclusionEffect::configure(const Config& config) { + _debug = config.debug; + _debugAmbientOcclusion->setTechnique(config.horizonBased ? AmbientOcclusionTechnique::HBAO : AmbientOcclusionTechnique::SSAO); + _debugAmbientOcclusion->setJitter(config.jitterEnabled); + _debugAmbientOcclusion->setResolutionLevel(config.resolutionLevel); + _debugAmbientOcclusion->setEdgeSharpness(config.edgeSharpness); + _debugAmbientOcclusion->setBlurRadius(config.blurRadius); + _debugAmbientOcclusion->setAORadius(config.horizonBased ? config.hbaoRadius : config.ssaoRadius); + _debugAmbientOcclusion->setAOObscuranceLevel(config.horizonBased ? config.hbaoObscuranceLevel : config.ssaoObscuranceLevel); + _debugAmbientOcclusion->setAOFalloffAngle(config.horizonBased ? config.hbaoFalloffAngle : config.ssaoFalloffAngle); + _debugAmbientOcclusion->setAOSamplingAmount(config.horizonBased ? (config.hbaoNumSamples / MAX_HBAO_SAMPLES) : + (config.ssaoNumSamples / MAX_SSAO_SAMPLES)); + _debugAmbientOcclusion->setSSAONumSpiralTurns(config.ssaoNumSpiralTurns); + + _perspectiveScale = config.perspectiveScale; + _ditheringEnabled = config.ditheringEnabled; + _borderingEnabled = config.borderingEnabled; + _fetchMipsEnabled = config.fetchMipsEnabled; } -void AmbientOcclusionEffect::configure(const Config& config) { +void AmbientOcclusionEffect::updateParameters(const graphics::AmbientOcclusionPointer ambientOcclusion) { bool shouldUpdateBlurs = false; bool shouldUpdateTechnique = false; - _isJitterEnabled = config.jitterEnabled; - if (!_framebuffer) { _framebuffer = std::make_shared(); shouldUpdateBlurs = true; } // Update bilateral blur - if (config.blurRadius != _hblurParametersBuffer->getBlurRadius() || _blurEdgeSharpness != config.edgeSharpness) { + if (ambientOcclusion->getBlurRadius() != _hblurParametersBuffer->getBlurRadius() || _blurEdgeSharpness != ambientOcclusion->getEdgeSharpness()) { const float BLUR_EDGE_DISTANCE_SCALE = float(10000 * SSAO_DEPTH_KEY_SCALE); const float BLUR_EDGE_NORMAL_SCALE = 2.0f; auto& hblur = _hblurParametersBuffer.edit()._blurInfo; auto& vblur = _vblurParametersBuffer.edit()._blurInfo; - float blurRadialSigma = float(config.blurRadius) * 0.5f; + float blurRadialSigma = float(ambientOcclusion->getBlurRadius()) * 0.5f; float blurRadialScale = 1.0f / (2.0f*blurRadialSigma*blurRadialSigma); - glm::vec3 blurScales = -glm::vec3(blurRadialScale, glm::vec2(BLUR_EDGE_DISTANCE_SCALE, BLUR_EDGE_NORMAL_SCALE) * config.edgeSharpness); + glm::vec3 blurScales = -glm::vec3(blurRadialScale, glm::vec2(BLUR_EDGE_DISTANCE_SCALE, BLUR_EDGE_NORMAL_SCALE) * ambientOcclusion->getEdgeSharpness()); - _blurEdgeSharpness = config.edgeSharpness; + _blurEdgeSharpness = ambientOcclusion->getEdgeSharpness(); hblur.x = blurScales.x; hblur.y = blurScales.y; hblur.z = blurScales.z; - hblur.w = (float)config.blurRadius; + hblur.w = (float)ambientOcclusion->getBlurRadius(); vblur.x = blurScales.x; vblur.y = blurScales.y; vblur.z = blurScales.z; - vblur.w = (float)config.blurRadius; + vblur.w = (float)ambientOcclusion->getBlurRadius(); } - if (_aoParametersBuffer->isHorizonBased() != config.horizonBased) { + if (_perspectiveScale != _aoParametersBuffer->getPerspectiveScale()) { + _aoParametersBuffer.edit()._resolutionInfo.z = _perspectiveScale; + } + + if (_ditheringEnabled != _aoParametersBuffer->isDitheringEnabled()) { + auto& current = _aoParametersBuffer.edit()._ditheringInfo; + current.x = (float)_ditheringEnabled; + } + + if (_borderingEnabled != _aoParametersBuffer->isBorderingEnabled()) { + auto& current = _aoParametersBuffer.edit()._ditheringInfo; + current.w = (float)_borderingEnabled; + } + + if (_fetchMipsEnabled != _aoParametersBuffer->isFetchMipsEnabled()) { + auto& current = _aoParametersBuffer.edit()._sampleInfo; + current.w = (float)_fetchMipsEnabled; + } + + bool horizonBased = ambientOcclusion->getTechnique() == AmbientOcclusionTechnique::HBAO; + if (_aoParametersBuffer->isHorizonBased() != horizonBased) { auto& current = _aoParametersBuffer.edit()._resolutionInfo; - current.y = config.horizonBased & 1; + current.y = horizonBased & 1; shouldUpdateTechnique = true; } - if (config.fetchMipsEnabled != _aoParametersBuffer->isFetchMipsEnabled()) { - auto& current = _aoParametersBuffer.edit()._sampleInfo; - current.w = (float)config.fetchMipsEnabled; - } - - if (config.perspectiveScale != _aoParametersBuffer->getPerspectiveScale()) { - _aoParametersBuffer.edit()._resolutionInfo.z = config.perspectiveScale; - } - - if (config.resolutionLevel != _aoParametersBuffer->getResolutionLevel()) { + if (ambientOcclusion->getResolutionLevel() != _aoParametersBuffer->getResolutionLevel()) { auto& current = _aoParametersBuffer.edit()._resolutionInfo; - current.x = (float)config.resolutionLevel; + current.x = (float)ambientOcclusion->getResolutionLevel(); shouldUpdateBlurs = true; } - if (config.ditheringEnabled != _aoParametersBuffer->isDitheringEnabled()) { - auto& current = _aoParametersBuffer.edit()._ditheringInfo; - current.x = (float)config.ditheringEnabled; - } - - if (config.borderingEnabled != _aoParametersBuffer->isBorderingEnabled()) { - auto& current = _aoParametersBuffer.edit()._ditheringInfo; - current.w = (float)config.borderingEnabled; - } - - if (config.horizonBased) { + if (horizonBased) { // Configure for HBAO - const auto& radius = config.hbaoRadius; + const auto& radius = ambientOcclusion->getAORadius(); if (shouldUpdateTechnique || radius != _aoParametersBuffer->getRadius()) { auto& current = _aoParametersBuffer.edit()._radiusInfo; current.x = radius; @@ -378,31 +375,33 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.z = 1.0f / current.y; } - if (shouldUpdateTechnique || config.hbaoObscuranceLevel != _aoParametersBuffer->getObscuranceLevel()) { + if (shouldUpdateTechnique || ambientOcclusion->getAOObscuranceLevel() != _aoParametersBuffer->getObscuranceLevel()) { auto& current = _aoParametersBuffer.edit()._radiusInfo; - current.w = config.hbaoObscuranceLevel; + current.w = ambientOcclusion->getAOObscuranceLevel(); } - if (shouldUpdateTechnique || config.hbaoFalloffAngle != _aoParametersBuffer->getFalloffAngle()) { + const float falloffAngle = std::min(1.0f - EPSILON, ambientOcclusion->getAOFalloffAngle()); + if (shouldUpdateTechnique || falloffAngle != _aoParametersBuffer->getFalloffAngle()) { auto& current = _aoParametersBuffer.edit()._falloffInfo; - current.x = config.hbaoFalloffAngle; + current.x = falloffAngle; current.y = 1.0f / (1.0f - current.x); // Compute sin from cos - current.z = sqrtf(1.0f - config.hbaoFalloffAngle * config.hbaoFalloffAngle); + current.z = sqrtf(1.0f - current.x * current.x); current.w = 1.0f / current.z; } - if (shouldUpdateTechnique || config.hbaoNumSamples != _aoParametersBuffer->getNumSamples()) { + const int numSamples = std::max(1, (int)(ambientOcclusion->getAOSamplingAmount() * MAX_HBAO_SAMPLES)); + if (shouldUpdateTechnique || numSamples != _aoParametersBuffer->getNumSamples()) { auto& current = _aoParametersBuffer.edit()._sampleInfo; - current.x = config.hbaoNumSamples; - current.y = 1.0f / config.hbaoNumSamples; + current.x = numSamples; + current.y = 1.0f / numSamples; updateRandomSamples(); updateJitterSamples(); } } else { // Configure for SSAO const double RADIUS_POWER = 6.0; - const auto& radius = config.ssaoRadius; + const auto& radius = ambientOcclusion->getAORadius(); if (shouldUpdateTechnique || radius != _aoParametersBuffer->getRadius()) { auto& current = _aoParametersBuffer.edit()._radiusInfo; current.x = radius; @@ -410,25 +409,26 @@ void AmbientOcclusionEffect::configure(const Config& config) { current.z = (float)(10.0 / pow((double)radius, RADIUS_POWER)); } - if (shouldUpdateTechnique || config.ssaoObscuranceLevel != _aoParametersBuffer->getObscuranceLevel()) { + if (shouldUpdateTechnique || ambientOcclusion->getAOObscuranceLevel() != _aoParametersBuffer->getObscuranceLevel()) { auto& current = _aoParametersBuffer.edit()._radiusInfo; - current.w = config.ssaoObscuranceLevel; + current.w = ambientOcclusion->getAOObscuranceLevel(); } - if (shouldUpdateTechnique || config.ssaoFalloffAngle != _aoParametersBuffer->getFalloffAngle()) { + if (shouldUpdateTechnique || ambientOcclusion->getAOFalloffAngle() != _aoParametersBuffer->getFalloffAngle()) { auto& current = _aoParametersBuffer.edit()._falloffInfo; - current.x = config.ssaoFalloffAngle; + current.x = ambientOcclusion->getAOFalloffAngle(); } - if (shouldUpdateTechnique || config.ssaoNumSpiralTurns != _aoParametersBuffer->getNumSpiralTurns()) { + if (shouldUpdateTechnique || ambientOcclusion->getSSAONumSpiralTurns() != _aoParametersBuffer->getNumSpiralTurns()) { auto& current = _aoParametersBuffer.edit()._sampleInfo; - current.z = config.ssaoNumSpiralTurns; + current.z = ambientOcclusion->getSSAONumSpiralTurns(); } - if (shouldUpdateTechnique || config.ssaoNumSamples != _aoParametersBuffer->getNumSamples()) { + const int numSamples = std::max(1, (int)(ambientOcclusion->getAOSamplingAmount() * MAX_SSAO_SAMPLES)); + if (shouldUpdateTechnique || numSamples != _aoParametersBuffer->getNumSamples()) { auto& current = _aoParametersBuffer.edit()._sampleInfo; - current.x = config.ssaoNumSamples; - current.y = 1.0f / config.ssaoNumSamples; + current.x = numSamples; + current.y = 1.0f / numSamples; updateRandomSamples(); updateJitterSamples(); } @@ -600,12 +600,23 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte RenderArgs* args = renderContext->args; const auto& lightingModel = input.get0(); + const auto ambientOcclusionFrame = input.get4(); - if (!lightingModel->isAmbientOcclusionEnabled()) { + const auto& ambientOcclusionStage = renderContext->_scene->getStage(); + graphics::AmbientOcclusionPointer ambientOcclusion; + if (_debug) { + ambientOcclusion = _debugAmbientOcclusion; + } else if (ambientOcclusionStage && ambientOcclusionFrame->_ambientOcclusions.size()) { + ambientOcclusion = ambientOcclusionStage->getAmbientOcclusion(ambientOcclusionFrame->_ambientOcclusions.front()); + } + + if (!ambientOcclusion || !lightingModel->isAmbientOcclusionEnabled()) { output.edit0().reset(); return; } + updateParameters(ambientOcclusion); + const auto& frameTransform = input.get1(); const auto& linearDepthFramebuffer = input.get3(); @@ -657,7 +668,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte #endif // Update sample rotation - if (_isJitterEnabled) { + if (ambientOcclusion->getJitter()) { updateJitterSamples(); _frameId = (_frameId + 1) % (SSAO_RANDOM_SAMPLE_COUNT); } @@ -831,11 +842,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte config->setGPUBatchRunTime(_gpuTimer->getGPUAverage(), _gpuTimer->getBatchAverage()); } -DebugAmbientOcclusion::DebugAmbientOcclusion() { -} - void DebugAmbientOcclusion::configure(const Config& config) { - _showCursorPixel = config.showCursorPixel; auto cursorPos = glm::vec2(_parametersBuffer->pixelInfo); @@ -916,9 +923,6 @@ void DebugAmbientOcclusion::run(const render::RenderContextPointer& renderContex batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, fullResDepthTexture); batch.draw(gpu::TRIANGLE_STRIP, 4); - batch.setResourceTexture(render_utils::slot::texture::SsaoDepth, nullptr); }); - } - diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 65fc09a814..3c49dba3d4 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -21,6 +21,7 @@ #include "DeferredFrameTransform.h" #include "DeferredFramebuffer.h" #include "SurfaceGeometryPass.h" +#include "AmbientOcclusionStage.h" #include "ssao_shared.h" @@ -70,15 +71,16 @@ protected: #endif glm::ivec2 _frameSize; - int _resolutionLevel{ 0 }; - int _depthResolutionLevel{ 0 }; - bool _isStereo{ false }; + int _resolutionLevel { 0 }; + int _depthResolutionLevel { 0 }; + bool _isStereo { false }; }; using AmbientOcclusionFramebufferPointer = std::shared_ptr; class AmbientOcclusionEffectConfig : public render::GPUJobConfig::Persistent { Q_OBJECT + Q_PROPERTY(bool debug MEMBER debug NOTIFY dirty) Q_PROPERTY(bool horizonBased MEMBER horizonBased NOTIFY dirty) Q_PROPERTY(bool ditheringEnabled MEMBER ditheringEnabled NOTIFY dirty) Q_PROPERTY(bool borderingEnabled MEMBER borderingEnabled NOTIFY dirty) @@ -108,42 +110,44 @@ public: const int MAX_RESOLUTION_LEVEL = 4; const int MAX_BLUR_RADIUS = 15; - void setEdgeSharpness(float sharpness); void setResolutionLevel(int level); + void setEdgeSharpness(float sharpness); void setBlurRadius(int radius); void setSSAORadius(float newRadius); void setSSAOObscuranceLevel(float level); void setSSAOFalloffAngle(float bias); - void setSSAONumSpiralTurns(float turns); void setSSAONumSamples(int samples); + void setSSAONumSpiralTurns(float turns); void setHBAORadius(float newRadius); void setHBAOObscuranceLevel(float level); void setHBAOFalloffAngle(float bias); void setHBAONumSamples(int samples); - float perspectiveScale; - float edgeSharpness; - int blurRadius; // 0 means no blurring - int resolutionLevel; + bool debug { false }; - float ssaoRadius; - float ssaoObscuranceLevel; // intensify or dim down the obscurance effect - float ssaoFalloffAngle; - float ssaoNumSpiralTurns; // defining an angle span to distribute the samples ray directions - int ssaoNumSamples; + bool jitterEnabled { false }; // Add small jittering to AO samples at each frame + bool horizonBased { false }; // Use horizon based AO + int resolutionLevel { 2 }; + float edgeSharpness { 1.0f }; + int blurRadius { 4 }; // 0 means no blurring - float hbaoRadius; - float hbaoObscuranceLevel; // intensify or dim down the obscurance effect - float hbaoFalloffAngle; - int hbaoNumSamples; + float ssaoRadius { 1.0f }; + float ssaoObscuranceLevel { 0.4f }; // intensify or dim down the obscurance effect + float ssaoFalloffAngle { 0.15f }; + int ssaoNumSamples { 32 }; + float ssaoNumSpiralTurns { 7.0f }; // defining an angle span to distribute the samples ray directions - bool horizonBased; // Use horizon based AO - bool ditheringEnabled; // randomize the distribution of taps per pixel, should always be true - bool borderingEnabled; // avoid evaluating information from non existing pixels out of the frame, should always be true - bool fetchMipsEnabled; // fetch taps in sub mips to otpimize cache, should always be true - bool jitterEnabled; // Add small jittering to AO samples at each frame + float hbaoRadius { 0.7f }; + float hbaoObscuranceLevel { 0.75f }; // intensify or dim down the obscurance effect + float hbaoFalloffAngle { 0.3f }; + int hbaoNumSamples { 1 }; + + float perspectiveScale { 1.0f }; + bool ditheringEnabled { true }; // randomize the distribution of taps per pixel, should always be true + bool borderingEnabled { true }; // avoid evaluating information from non existing pixels out of the frame, should always be true + bool fetchMipsEnabled { true }; // fetch taps in sub mips to otpimize cache, should always be true signals: void dirty(); @@ -153,12 +157,13 @@ signals: class AmbientOcclusionEffect { public: - using Input = render::VaryingSet4; + using Input = render::VaryingSet5; using Output = render::VaryingSet2; using Config = AmbientOcclusionEffectConfig; using JobModel = render::Job::ModelIO; - AmbientOcclusionEffect(); + AmbientOcclusionEffect() {} void configure(const Config& config); void run(const render::RenderContextPointer& renderContext, const Input& input, Output& output); @@ -166,7 +171,6 @@ public: // Class describing the uniform buffer with all the parameters common to the AO shaders class AOParameters : public AmbientOcclusionParams { public: - AOParameters(); int getResolutionLevel() const { return _resolutionInfo.x; } @@ -182,7 +186,6 @@ public: bool isDitheringEnabled() const { return _ditheringInfo.x != 0.0f; } bool isBorderingEnabled() const { return _ditheringInfo.w != 0.0f; } bool isHorizonBased() const { return _resolutionInfo.y != 0.0f; } - }; using AOParametersBuffer = gpu::StructBuffer; @@ -191,17 +194,15 @@ private: // Class describing the uniform buffer with all the parameters common to the bilateral blur shaders class BlurParameters : public AmbientOcclusionBlurParams { public: - BlurParameters(); float getEdgeSharpness() const { return (float)_blurInfo.x; } int getBlurRadius() const { return (int)_blurInfo.w; } - }; using BlurParametersBuffer = gpu::StructBuffer; - using FrameParametersBuffer = gpu::StructBuffer< AmbientOcclusionFrameParams>; + void updateParameters(const graphics::AmbientOcclusionPointer ambientOcclusion); void updateBlurParameters(); void updateFramebufferSizes(); void updateRandomSamples(); @@ -213,7 +214,7 @@ private: FrameParametersBuffer _aoFrameParametersBuffer[SSAO_SPLIT_COUNT*SSAO_SPLIT_COUNT]; BlurParametersBuffer _vblurParametersBuffer; BlurParametersBuffer _hblurParametersBuffer; - float _blurEdgeSharpness{ 0.0f }; + float _blurEdgeSharpness { 0.0f }; static const gpu::PipelinePointer& getOcclusionPipeline(); static const gpu::PipelinePointer& getBilateralBlurPipeline(); @@ -229,9 +230,14 @@ private: AmbientOcclusionFramebufferPointer _framebuffer; std::array _randomSamples; - int _frameId{ 0 }; - bool _isJitterEnabled{ true }; - + int _frameId { 0 }; + bool _debug { false }; + float _perspectiveScale { 1.0f }; + bool _ditheringEnabled { true }; + bool _borderingEnabled { true }; + bool _fetchMipsEnabled { true }; + graphics::AmbientOcclusionPointer _debugAmbientOcclusion { std::make_shared() }; + gpu::RangeTimerPointer _gpuTimer; friend class DebugAmbientOcclusion; @@ -246,8 +252,8 @@ class DebugAmbientOcclusionConfig : public render::Job::Config { public: DebugAmbientOcclusionConfig() : render::Job::Config(false) {} - bool showCursorPixel{ false }; - glm::vec2 debugCursorTexcoord{ 0.5f, 0.5f }; + bool showCursorPixel { false }; + glm::vec2 debugCursorTexcoord { 0.5f, 0.5f }; signals: void dirty(); @@ -260,7 +266,7 @@ public: using Config = DebugAmbientOcclusionConfig; using JobModel = render::Job::ModelI; - DebugAmbientOcclusion(); + DebugAmbientOcclusion() {} void configure(const Config& config); void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); @@ -281,7 +287,7 @@ private: gpu::PipelinePointer _debugPipeline; - bool _showCursorPixel{ false }; + bool _showCursorPixel { false }; }; #endif // hifi_AmbientOcclusionEffect_h diff --git a/libraries/render-utils/src/AmbientOcclusionStage.cpp b/libraries/render-utils/src/AmbientOcclusionStage.cpp new file mode 100644 index 0000000000..6b3763a39c --- /dev/null +++ b/libraries/render-utils/src/AmbientOcclusionStage.cpp @@ -0,0 +1,56 @@ +// +// AmbientOcclusionStage.cpp +// +// Created by HifiExperiments on 6/24/24 +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "AmbientOcclusionStage.h" + +#include + +std::string AmbientOcclusionStage::_stageName { "AMBIENT_OCCLUSION_STAGE" }; +const AmbientOcclusionStage::Index AmbientOcclusionStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; + +AmbientOcclusionStage::Index AmbientOcclusionStage::findAmbientOcclusion(const AmbientOcclusionPointer& ambientOcclusion) const { + auto found = _ambientOcclusionMap.find(ambientOcclusion); + if (found != _ambientOcclusionMap.end()) { + return INVALID_INDEX; + } else { + return (*found).second; + } +} + +AmbientOcclusionStage::Index AmbientOcclusionStage::addAmbientOcclusion(const AmbientOcclusionPointer& ambientOcclusion) { + auto found = _ambientOcclusionMap.find(ambientOcclusion); + if (found == _ambientOcclusionMap.end()) { + auto ambientOcclusionId = _ambientOcclusions.newElement(ambientOcclusion); + // Avoid failing to allocate a ambientOcclusion, just pass + if (ambientOcclusionId != INVALID_INDEX) { + // Insert the ambientOcclusion and its index in the reverse map + _ambientOcclusionMap.insert(AmbientOcclusionMap::value_type(ambientOcclusion, ambientOcclusionId)); + } + return ambientOcclusionId; + } else { + return (*found).second; + } +} + +AmbientOcclusionStage::AmbientOcclusionPointer AmbientOcclusionStage::removeAmbientOcclusion(Index index) { + AmbientOcclusionPointer removed = _ambientOcclusions.freeElement(index); + if (removed) { + _ambientOcclusionMap.erase(removed); + } + return removed; +} + +AmbientOcclusionStageSetup::AmbientOcclusionStageSetup() {} + +void AmbientOcclusionStageSetup::run(const render::RenderContextPointer& renderContext) { + auto stage = renderContext->_scene->getStage(AmbientOcclusionStage::getName()); + if (!stage) { + renderContext->_scene->resetStage(AmbientOcclusionStage::getName(), std::make_shared()); + } +} diff --git a/libraries/render-utils/src/AmbientOcclusionStage.h b/libraries/render-utils/src/AmbientOcclusionStage.h new file mode 100644 index 0000000000..d5dee344ba --- /dev/null +++ b/libraries/render-utils/src/AmbientOcclusionStage.h @@ -0,0 +1,84 @@ +// +// AmbientOcclusionStage.h +// +// Created by HifiExperiments on 6/24/24 +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_render_utils_AmbientOcclusionStage_h +#define hifi_render_utils_AmbientOcclusionStage_h + +#include +#include +#include +#include +#include + +#include +#include +#include + +// AmbientOcclusion stage to set up ambientOcclusion-related rendering tasks +class AmbientOcclusionStage : public render::Stage { +public: + static std::string _stageName; + static const std::string& getName() { return _stageName; } + + using Index = render::indexed_container::Index; + static const Index INVALID_INDEX; + static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } + + using AmbientOcclusionPointer = graphics::AmbientOcclusionPointer; + using AmbientOcclusions = render::indexed_container::IndexedPointerVector; + using AmbientOcclusionMap = std::unordered_map; + + using AmbientOcclusionIndices = std::vector; + + Index findAmbientOcclusion(const AmbientOcclusionPointer& ambientOcclusion) const; + Index addAmbientOcclusion(const AmbientOcclusionPointer& ambientOcclusion); + + AmbientOcclusionPointer removeAmbientOcclusion(Index index); + + bool checkAmbientOcclusionId(Index index) const { return _ambientOcclusions.checkIndex(index); } + + Index getNumAmbientOcclusions() const { return _ambientOcclusions.getNumElements(); } + Index getNumFreeAmbientOcclusions() const { return _ambientOcclusions.getNumFreeIndices(); } + Index getNumAllocatedAmbientOcclusions() const { return _ambientOcclusions.getNumAllocatedIndices(); } + + AmbientOcclusionPointer getAmbientOcclusion(Index ambientOcclusionId) const { + return _ambientOcclusions.get(ambientOcclusionId); + } + + AmbientOcclusions _ambientOcclusions; + AmbientOcclusionMap _ambientOcclusionMap; + + class Frame { + public: + Frame() {} + + void clear() { _ambientOcclusions.clear(); } + + void pushAmbientOcclusion(AmbientOcclusionStage::Index index) { _ambientOcclusions.emplace_back(index); } + + AmbientOcclusionStage::AmbientOcclusionIndices _ambientOcclusions; + }; + using FramePointer = std::shared_ptr; + + Frame _currentFrame; +}; +using AmbientOcclusionStagePointer = std::shared_ptr; + +class AmbientOcclusionStageSetup { +public: + using JobModel = render::Job::Model; + + AmbientOcclusionStageSetup(); + void run(const render::RenderContextPointer& renderContext); + +protected: +}; + +#endif diff --git a/libraries/render-utils/src/AssembleLightingStageTask.cpp b/libraries/render-utils/src/AssembleLightingStageTask.cpp index 589cdb31ea..bc040582bd 100644 --- a/libraries/render-utils/src/AssembleLightingStageTask.cpp +++ b/libraries/render-utils/src/AssembleLightingStageTask.cpp @@ -25,6 +25,14 @@ void FetchCurrentFrames::run(const render::RenderContextPointer& renderContext, auto bloomStage = renderContext->_scene->getStage(); assert(bloomStage); output.edit3() = std::make_shared(bloomStage->_currentFrame); + + auto tonemappingStage = renderContext->_scene->getStage(); + assert(tonemappingStage); + output.edit4() = std::make_shared(tonemappingStage->_currentFrame); + + auto ambientOcclusionStage = renderContext->_scene->getStage(); + assert(ambientOcclusionStage); + output.edit5() = std::make_shared(ambientOcclusionStage->_currentFrame); } @@ -47,4 +55,3 @@ void AssembleLightingStageTask::build(JobModel& task, const render::Varying& inp output = Output(currentStageFrames, zones); } - diff --git a/libraries/render-utils/src/AssembleLightingStageTask.h b/libraries/render-utils/src/AssembleLightingStageTask.h index 9770af473c..17add988ef 100644 --- a/libraries/render-utils/src/AssembleLightingStageTask.h +++ b/libraries/render-utils/src/AssembleLightingStageTask.h @@ -16,13 +16,16 @@ #include "BackgroundStage.h" #include "HazeStage.h" #include "BloomStage.h" +#include "TonemappingStage.h" +#include "AmbientOcclusionStage.h" #include "ZoneRenderer.h" class FetchCurrentFrames { public: - using Output = render::VaryingSet4; + using Output = render::VaryingSet6; using JobModel = render::Job::ModelO; FetchCurrentFrames() {} diff --git a/libraries/render-utils/src/BackgroundStage.cpp b/libraries/render-utils/src/BackgroundStage.cpp index 91b766d90b..455f356a45 100644 --- a/libraries/render-utils/src/BackgroundStage.cpp +++ b/libraries/render-utils/src/BackgroundStage.cpp @@ -15,7 +15,7 @@ #include -std::string BackgroundStage::_stageName { "BACKGROUND_STAGE"}; +std::string BackgroundStage::_stageName { "BACKGROUND_STAGE" }; const BackgroundStage::Index BackgroundStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; BackgroundStage::Index BackgroundStage::findBackground(const BackgroundPointer& background) const { diff --git a/libraries/render-utils/src/BloomStage.cpp b/libraries/render-utils/src/BloomStage.cpp index b3ba5f9565..2bedfeea96 100644 --- a/libraries/render-utils/src/BloomStage.cpp +++ b/libraries/render-utils/src/BloomStage.cpp @@ -13,7 +13,7 @@ #include -std::string BloomStage::_stageName { "BLOOM_STAGE"}; +std::string BloomStage::_stageName { "BLOOM_STAGE" }; const BloomStage::Index BloomStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; BloomStage::Index BloomStage::findBloom(const BloomPointer& bloom) const { diff --git a/libraries/render-utils/src/BloomStage.h b/libraries/render-utils/src/BloomStage.h index 7ba7e56035..bb03e181af 100644 --- a/libraries/render-utils/src/BloomStage.h +++ b/libraries/render-utils/src/BloomStage.h @@ -81,26 +81,4 @@ public: protected: }; -class FetchBloomConfig : public render::Job::Config { - Q_OBJECT - Q_PROPERTY(float bloomIntensity MEMBER bloomIntensity WRITE setBloomIntensity NOTIFY dirty); - Q_PROPERTY(float bloomThreshold MEMBER bloomThreshold WRITE setBloomThreshold NOTIFY dirty); - Q_PROPERTY(float bloomSize MEMBER bloomSize WRITE setBloomSize NOTIFY dirty); - -public: - FetchBloomConfig() : render::Job::Config() {} - - float bloomIntensity { graphics::Bloom::INITIAL_BLOOM_INTENSITY }; - float bloomThreshold { graphics::Bloom::INITIAL_BLOOM_THRESHOLD }; - float bloomSize { graphics::Bloom::INITIAL_BLOOM_SIZE }; - -public slots: - void setBloomIntensity(const float value) { bloomIntensity = value; emit dirty(); } - void setBloomThreshold(const float value) { bloomThreshold = value; emit dirty(); } - void setBloomSize(const float value) { bloomSize = value; emit dirty(); } - -signals: - void dirty(); -}; - #endif diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 3eb5924d19..0ff1c435e1 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -662,5 +662,14 @@ void DefaultLightingSetup::run(const RenderContextPointer& renderContext) { _defaultHazeID = hazeStage->addHaze(_defaultHaze); } } -} + if (!_defaultTonemapping) { + auto tonemappingStage = renderContext->_scene->getStage(); + if (tonemappingStage) { + auto tonemapping = std::make_shared(); + + _defaultTonemapping = tonemapping; + _defaultTonemappingID = tonemappingStage->addTonemapping(_defaultTonemapping); + } + } +} diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index 058e0a4cbf..73c43c52a3 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -32,6 +32,7 @@ #include "LightClusters.h" #include "BackgroundStage.h" #include "HazeStage.h" +#include "TonemappingStage.h" #include "SurfaceGeometryPass.h" #include "SubsurfaceScattering.h" @@ -162,11 +163,13 @@ public: protected: graphics::LightPointer _defaultLight; - LightStage::Index _defaultLightID{ LightStage::INVALID_INDEX }; + LightStage::Index _defaultLightID { LightStage::INVALID_INDEX }; graphics::SunSkyStagePointer _defaultBackground; - BackgroundStage::Index _defaultBackgroundID{ BackgroundStage::INVALID_INDEX }; - graphics::HazePointer _defaultHaze{ nullptr }; - HazeStage::Index _defaultHazeID{ HazeStage::INVALID_INDEX }; + BackgroundStage::Index _defaultBackgroundID { BackgroundStage::INVALID_INDEX }; + graphics::HazePointer _defaultHaze { nullptr }; + HazeStage::Index _defaultHazeID { HazeStage::INVALID_INDEX }; + graphics::TonemappingPointer _defaultTonemapping { nullptr }; + TonemappingStage::Index _defaultTonemappingID { TonemappingStage::INVALID_INDEX }; graphics::SkyboxPointer _defaultSkybox { new ProceduralSkybox() }; NetworkTexturePointer _defaultSkyboxNetworkTexture; NetworkTexturePointer _defaultAmbientNetworkTexture; diff --git a/libraries/render-utils/src/HazeStage.cpp b/libraries/render-utils/src/HazeStage.cpp index c850828be5..9251e1e2f9 100644 --- a/libraries/render-utils/src/HazeStage.cpp +++ b/libraries/render-utils/src/HazeStage.cpp @@ -13,7 +13,7 @@ #include -std::string HazeStage::_stageName { "HAZE_STAGE"}; +std::string HazeStage::_stageName { "HAZE_STAGE" }; const HazeStage::Index HazeStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; HazeStage::Index HazeStage::findHaze(const HazePointer& haze) const { diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index ccdf45cedc..58f32cdc4e 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -15,7 +15,7 @@ #include "ViewFrustum.h" -std::string LightStage::_stageName { "LIGHT_STAGE"}; +std::string LightStage::_stageName { "LIGHT_STAGE" }; // The bias matrix goes from homogeneous coordinates to UV coords (see http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/#basic-shader) const glm::mat4 LightStage::Shadow::_biasMatrix { 0.5, 0.0, 0.0, 0.0, diff --git a/libraries/render-utils/src/LightingModel.h b/libraries/render-utils/src/LightingModel.h index a488abcb09..5298fb2ceb 100644 --- a/libraries/render-utils/src/LightingModel.h +++ b/libraries/render-utils/src/LightingModel.h @@ -4,6 +4,7 @@ // // Created by Sam Gateau 7/1/2016. // Copyright 2016 High Fidelity, Inc. +// Copyright 2024 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -17,14 +18,13 @@ #include #include -// LightingModel is a helper class gathering in one place the flags to enable the lighting contributions +// LightingModel is a helper class gathering in one place the flags to enable the lighting contributions class LightingModel { public: using UniformBufferView = gpu::BufferView; LightingModel(); - void setUnlit(bool enable); bool isUnlitEnabled() const; @@ -76,7 +76,6 @@ public: void setBlendshape(bool enable); bool isBlendshapeEnabled() const; - void setAmbientOcclusion(bool enable); bool isAmbientOcclusionEnabled() const; void setShadow(bool enable); @@ -86,55 +85,49 @@ public: gpu::TexturePointer getAmbientFresnelLUT() const { return _ambientFresnelLUT; } protected: - - // Class describing the uniform buffer with the transform info common to the AO shaders // It s changing every frame class Parameters { public: - float enableUnlit{ 1.0f }; - float enableEmissive{ 1.0f }; - float enableLightmap{ 1.0f }; - float enableBackground{ 1.0f }; + float enableUnlit { 1.0f }; + float enableEmissive { 1.0f }; + float enableLightmap { 1.0f }; + float enableBackground { 1.0f }; - float enableScattering{ 1.0f }; - float enableDiffuse{ 1.0f }; - float enableSpecular{ 1.0f }; - float enableAlbedo{ 1.0f }; + float enableScattering { 1.0f }; + float enableDiffuse { 1.0f }; + float enableSpecular { 1.0f }; + float enableAlbedo { 1.0f }; - float enableAmbientLight{ 1.0f }; - float enableDirectionalLight{ 1.0f }; - float enablePointLight{ 1.0f }; - float enableSpotLight{ 1.0f }; + float enableAmbientLight { 1.0f }; + float enableDirectionalLight { 1.0f }; + float enablePointLight { 1.0f }; + float enableSpotLight { 1.0f }; float showLightContour { 0.0f }; // false by default - float enableObscurance{ 1.0f }; + float enableObscurance { 1.0f }; float enableMaterialTexturing { 1.0f }; float enableWireframe { 0.0f }; // false by default - float enableHaze{ 1.0f }; - float enableBloom{ 1.0f }; - float enableSkinning{ 1.0f }; - float enableBlendshape{ 1.0f }; + float enableHaze { 1.0f }; + float enableBloom { 1.0f }; + float enableSkinning { 1.0f }; + float enableBlendshape { 1.0f }; - float enableAmbientOcclusion{ 0.0f }; // false by default - float enableShadow{ 1.0f }; - float spare1{ 1.0f }; - float spare2{ 1.0f }; + float enableAmbientOcclusion { 1.0f }; + float enableShadow { 1.0f }; + float spare1 { 1.0f }; + float spare2 { 1.0f }; Parameters() {} }; UniformBufferView _parametersBuffer; static gpu::TexturePointer _ambientFresnelLUT; }; - using LightingModelPointer = std::shared_ptr; - - - class MakeLightingModelConfig : public render::Job::Config { Q_OBJECT @@ -168,39 +161,37 @@ class MakeLightingModelConfig : public render::Job::Config { Q_PROPERTY(bool enableAmbientOcclusion READ isAmbientOcclusionEnabled WRITE setAmbientOcclusion NOTIFY dirty) Q_PROPERTY(bool enableShadow READ isShadowEnabled WRITE setShadow NOTIFY dirty) - public: MakeLightingModelConfig() : render::Job::Config() {} // Make Lighting Model is always on - bool enableUnlit{ true }; - bool enableEmissive{ true }; - bool enableLightmap{ true }; - bool enableBackground{ true }; - bool enableObscurance{ true }; + bool enableUnlit { true }; + bool enableEmissive { true }; + bool enableLightmap { true }; + bool enableBackground { true }; + bool enableObscurance { true }; - bool enableScattering{ true }; - bool enableDiffuse{ true }; - bool enableSpecular{ true }; + bool enableScattering { true }; + bool enableDiffuse { true }; + bool enableSpecular { true }; - bool enableAlbedo{ true }; + bool enableAlbedo { true }; bool enableMaterialTexturing { true }; - bool enableAmbientLight{ true }; - bool enableDirectionalLight{ true }; - bool enablePointLight{ true }; - bool enableSpotLight{ true }; + bool enableAmbientLight { true }; + bool enableDirectionalLight { true }; + bool enablePointLight { true }; + bool enableSpotLight { true }; bool showLightContour { false }; // false by default bool enableWireframe { false }; // false by default - bool enableHaze{ true }; - bool enableBloom{ true }; - bool enableSkinning{ true }; - bool enableBlendshape{ true }; - - bool enableAmbientOcclusion{ false }; // false by default - bool enableShadow{ true }; + bool enableHaze { true }; + bool enableBloom { true }; + bool enableSkinning { true }; + bool enableBlendshape { true }; + bool enableAmbientOcclusion { true }; + bool enableShadow { true }; void setAmbientOcclusion(bool enable) { enableAmbientOcclusion = enable; emit dirty();} bool isAmbientOcclusionEnabled() const { return enableAmbientOcclusion; } diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index aa80b5ec6e..4bcfc9663b 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -137,6 +137,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto backgroundFrame = currentStageFrames[1]; const auto& hazeFrame = currentStageFrames[2]; const auto& bloomFrame = currentStageFrames[3]; + const auto& tonemappingFrame = currentStageFrames[4]; + const auto& ambientOcclusionFrame = currentStageFrames[5]; // Shadow Task Outputs const auto& shadowTaskOutputs = inputs.get3(); @@ -198,7 +200,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto scatteringResource = task.addJob("Scattering"); // AO job - const auto ambientOcclusionInputs = AmbientOcclusionEffect::Input(lightingModel, deferredFrameTransform, deferredFramebuffer, linearDepthTarget).asVarying(); + const auto ambientOcclusionInputs = AmbientOcclusionEffect::Input(lightingModel, deferredFrameTransform, deferredFramebuffer, linearDepthTarget, ambientOcclusionFrame).asVarying(); const auto ambientOcclusionOutputs = task.addJob("AmbientOcclusion", ambientOcclusionInputs); const auto ambientOcclusionFramebuffer = ambientOcclusionOutputs.getN(0); const auto ambientOcclusionUniforms = ambientOcclusionOutputs.getN(1); @@ -250,8 +252,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto destFramebuffer = static_cast(nullptr); // Lighting Buffer ready for tone mapping - const auto toneMappingInputs = ToneMapAndResample::Input(lightingFramebuffer, destFramebuffer).asVarying(); - const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs, depth); + const auto toneMappingInputs = ToneMapAndResample::Input(lightingFramebuffer, destFramebuffer, tonemappingFrame).asVarying(); + const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs); // Debugging task is happening in the "over" layer after tone mapping and just before HUD { // Debug the bounds of the rendered items, still look at the zbuffer diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 9b483c16c2..cba6f4920f 100644 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -108,6 +108,7 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend const auto lightFrame = currentStageFrames[0]; const auto backgroundFrame = currentStageFrames[1]; const auto hazeFrame = currentStageFrames[2]; + const auto tonemappingFrame = currentStageFrames[4]; const auto& zones = lightingStageInputs[1]; @@ -178,8 +179,8 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend const auto destFramebuffer = static_cast(nullptr); - const auto toneMappingInputs = ToneMapAndResample::Input(resolvedFramebuffer, destFramebuffer).asVarying(); - const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs, depth); + const auto toneMappingInputs = ToneMapAndResample::Input(resolvedFramebuffer, destFramebuffer, tonemappingFrame).asVarying(); + const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs); // HUD Layer const auto renderHUDLayerInputs = RenderHUDLayerTask::Input(toneMappedBuffer, lightingModel, hudOpaque, hudTransparent, hazeFrame).asVarying(); task.addJob("RenderHUDLayer", renderHUDLayerInputs); diff --git a/libraries/render-utils/src/ToneMapAndResampleTask.cpp b/libraries/render-utils/src/ToneMapAndResampleTask.cpp index 1ea9deb1fa..d906d82aa7 100644 --- a/libraries/render-utils/src/ToneMapAndResampleTask.cpp +++ b/libraries/render-utils/src/ToneMapAndResampleTask.cpp @@ -25,10 +25,9 @@ using namespace shader::render_utils::program; gpu::PipelinePointer ToneMapAndResample::_pipeline; gpu::PipelinePointer ToneMapAndResample::_mirrorPipeline; -ToneMapAndResample::ToneMapAndResample(size_t depth) { +ToneMapAndResample::ToneMapAndResample() { Parameters parameters; _parametersBuffer = gpu::BufferView(std::make_shared(sizeof(Parameters), (const gpu::Byte*) ¶meters)); - _depth = depth; } void ToneMapAndResample::init() { @@ -44,13 +43,13 @@ void ToneMapAndResample::init() { void ToneMapAndResample::setExposure(float exposure) { auto& params = _parametersBuffer.get(); - if (params._exposure != exposure) { - _parametersBuffer.edit()._exposure = exposure; + if (_exposure != exposure) { + _exposure = exposure; _parametersBuffer.edit()._twoPowExposure = pow(2.0, exposure); } } -void ToneMapAndResample::setToneCurve(ToneCurve curve) { +void ToneMapAndResample::setCurve(TonemappingCurve curve) { auto& params = _parametersBuffer.get(); if (params._toneCurve != (int)curve) { _parametersBuffer.edit()._toneCurve = (int)curve; @@ -58,8 +57,9 @@ void ToneMapAndResample::setToneCurve(ToneCurve curve) { } void ToneMapAndResample::configure(const Config& config) { - setExposure(config.exposure); - setToneCurve((ToneCurve)config.curve); + _debug = config.debug; + _debugExposure = config.exposure; + _debugCurve = (TonemappingCurve)config.curve; } void ToneMapAndResample::run(const RenderContextPointer& renderContext, const Input& input, Output& output) { @@ -70,6 +70,21 @@ void ToneMapAndResample::run(const RenderContextPointer& renderContext, const In auto lightingBuffer = input.get0()->getRenderBuffer(0); auto destinationFramebuffer = input.get1(); + const auto tonemappingFrame = input.get2(); + + const auto& tonemappingStage = renderContext->_scene->getStage(); + graphics::TonemappingPointer tonemapping; + if (tonemappingStage && tonemappingFrame->_tonemappings.size()) { + tonemapping = tonemappingStage->getTonemapping(tonemappingFrame->_tonemappings.front()); + } + + if (_debug) { + setCurve(_debugCurve); + setExposure(_debugExposure); + } else if (tonemapping) { + setCurve(tonemapping->getCurve()); + setExposure(tonemapping->getExposure()); + } if (!destinationFramebuffer) { destinationFramebuffer = args->_blitFramebuffer; diff --git a/libraries/render-utils/src/ToneMapAndResampleTask.h b/libraries/render-utils/src/ToneMapAndResampleTask.h index 3a812cf445..50a35b2fe4 100644 --- a/libraries/render-utils/src/ToneMapAndResampleTask.h +++ b/libraries/render-utils/src/ToneMapAndResampleTask.h @@ -20,28 +20,24 @@ #include #include -enum class ToneCurve { - // Different tone curve available - None, - Gamma22, - Reinhard, - Filmic, -}; +#include "TonemappingStage.h" class ToneMappingConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(float exposure MEMBER exposure WRITE setExposure); - Q_PROPERTY(int curve MEMBER curve WRITE setCurve); + Q_PROPERTY(bool debug MEMBER debug WRITE setDebug NOTIFY dirty); + Q_PROPERTY(int curve MEMBER curve WRITE setCurve NOTIFY dirty); + Q_PROPERTY(float exposure MEMBER exposure WRITE setExposure NOTIFY dirty); public: ToneMappingConfig() : render::Job::Config(true) {} + void setDebug(bool newDebug) { debug = newDebug; emit dirty(); } + void setCurve(int newCurve) { curve = std::max(0, std::min((int)TonemappingCurve::FILMIC, newCurve)); emit dirty(); } void setExposure(float newExposure) { exposure = newExposure; emit dirty(); } - void setCurve(int newCurve) { curve = std::max((int)ToneCurve::None, std::min((int)ToneCurve::Filmic, newCurve)); emit dirty(); } - - float exposure{ 0.0f }; - int curve{ (int)ToneCurve::Gamma22 }; + bool debug { false }; + int curve { (int)TonemappingCurve::SRGB }; + float exposure { 0.0f }; signals: void dirty(); @@ -49,17 +45,14 @@ signals: class ToneMapAndResample { public: - ToneMapAndResample(size_t depth); + ToneMapAndResample(); virtual ~ToneMapAndResample() {} + void setCurve(TonemappingCurve curve); void setExposure(float exposure); - float getExposure() const { return _parametersBuffer.get()._exposure; } - void setToneCurve(ToneCurve curve); - ToneCurve getToneCurve() const { return (ToneCurve)_parametersBuffer.get()._toneCurve; } - - // Inputs: lightingFramebuffer, destinationFramebuffer - using Input = render::VaryingSet2; + // Inputs: lightingFramebuffer, destinationFramebuffer, tonemappingFrame + using Input = render::VaryingSet3; using Output = gpu::FramebufferPointer; using Config = ToneMappingConfig; using JobModel = render::Job::ModelIO; @@ -73,22 +66,19 @@ protected: gpu::FramebufferPointer _destinationFrameBuffer; - float _factor { 2.0f }; - size_t _depth { 0 }; - - gpu::FramebufferPointer getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer); - private: gpu::PipelinePointer _blitLightBuffer; + float _exposure { 0.0f }; + + bool _debug { false }; + TonemappingCurve _debugCurve { TonemappingCurve::SRGB }; + float _debugExposure { 0.0f }; // Class describing the uniform buffer with all the parameters common to the tone mapping shaders class Parameters { public: - float _exposure = 0.0f; float _twoPowExposure = 1.0f; - glm::vec2 spareA; - int _toneCurve = (int)ToneCurve::Gamma22; - glm::vec3 spareB; + int _toneCurve = (int)TonemappingCurve::SRGB; Parameters() {} }; diff --git a/libraries/render-utils/src/TonemappingStage.cpp b/libraries/render-utils/src/TonemappingStage.cpp new file mode 100644 index 0000000000..9b6029ca1b --- /dev/null +++ b/libraries/render-utils/src/TonemappingStage.cpp @@ -0,0 +1,56 @@ +// +// TonemappingStage.cpp +// +// Created by HifiExperiments on 6/24/24 +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "TonemappingStage.h" + +#include + +std::string TonemappingStage::_stageName { "TONEMAPPING_STAGE" }; +const TonemappingStage::Index TonemappingStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; + +TonemappingStage::Index TonemappingStage::findTonemapping(const TonemappingPointer& tonemapping) const { + auto found = _tonemappingMap.find(tonemapping); + if (found != _tonemappingMap.end()) { + return INVALID_INDEX; + } else { + return (*found).second; + } +} + +TonemappingStage::Index TonemappingStage::addTonemapping(const TonemappingPointer& tonemapping) { + auto found = _tonemappingMap.find(tonemapping); + if (found == _tonemappingMap.end()) { + auto tonemappingId = _tonemappings.newElement(tonemapping); + // Avoid failing to allocate a tonemapping, just pass + if (tonemappingId != INVALID_INDEX) { + // Insert the tonemapping and its index in the reverse map + _tonemappingMap.insert(TonemappingMap::value_type(tonemapping, tonemappingId)); + } + return tonemappingId; + } else { + return (*found).second; + } +} + +TonemappingStage::TonemappingPointer TonemappingStage::removeTonemapping(Index index) { + TonemappingPointer removed = _tonemappings.freeElement(index); + if (removed) { + _tonemappingMap.erase(removed); + } + return removed; +} + +TonemappingStageSetup::TonemappingStageSetup() {} + +void TonemappingStageSetup::run(const render::RenderContextPointer& renderContext) { + auto stage = renderContext->_scene->getStage(TonemappingStage::getName()); + if (!stage) { + renderContext->_scene->resetStage(TonemappingStage::getName(), std::make_shared()); + } +} diff --git a/libraries/render-utils/src/TonemappingStage.h b/libraries/render-utils/src/TonemappingStage.h new file mode 100644 index 0000000000..15161094a2 --- /dev/null +++ b/libraries/render-utils/src/TonemappingStage.h @@ -0,0 +1,84 @@ +// +// TonemappingStage.h +// +// Created by HifiExperiments on 6/24/24 +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_render_utils_TonemappingStage_h +#define hifi_render_utils_TonemappingStage_h + +#include +#include +#include +#include +#include + +#include +#include +#include + +// Tonemapping stage to set up tonemapping-related rendering tasks +class TonemappingStage : public render::Stage { +public: + static std::string _stageName; + static const std::string& getName() { return _stageName; } + + using Index = render::indexed_container::Index; + static const Index INVALID_INDEX; + static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } + + using TonemappingPointer = graphics::TonemappingPointer; + using Tonemappings = render::indexed_container::IndexedPointerVector; + using TonemappingMap = std::unordered_map; + + using TonemappingIndices = std::vector; + + Index findTonemapping(const TonemappingPointer& tonemapping) const; + Index addTonemapping(const TonemappingPointer& tonemapping); + + TonemappingPointer removeTonemapping(Index index); + + bool checkTonemappingId(Index index) const { return _tonemappings.checkIndex(index); } + + Index getNumTonemappings() const { return _tonemappings.getNumElements(); } + Index getNumFreeTonemappings() const { return _tonemappings.getNumFreeIndices(); } + Index getNumAllocatedTonemappings() const { return _tonemappings.getNumAllocatedIndices(); } + + TonemappingPointer getTonemapping(Index tonemappingId) const { + return _tonemappings.get(tonemappingId); + } + + Tonemappings _tonemappings; + TonemappingMap _tonemappingMap; + + class Frame { + public: + Frame() {} + + void clear() { _tonemappings.clear(); } + + void pushTonemapping(TonemappingStage::Index index) { _tonemappings.emplace_back(index); } + + TonemappingStage::TonemappingIndices _tonemappings; + }; + using FramePointer = std::shared_ptr; + + Frame _currentFrame; +}; +using TonemappingStagePointer = std::shared_ptr; + +class TonemappingStageSetup { +public: + using JobModel = render::Job::Model; + + TonemappingStageSetup(); + void run(const render::RenderContextPointer& renderContext); + +protected: +}; + +#endif diff --git a/libraries/render-utils/src/UpdateSceneTask.cpp b/libraries/render-utils/src/UpdateSceneTask.cpp index be61953073..3fca91ccb9 100644 --- a/libraries/render-utils/src/UpdateSceneTask.cpp +++ b/libraries/render-utils/src/UpdateSceneTask.cpp @@ -15,6 +15,8 @@ #include "BackgroundStage.h" #include "HazeStage.h" #include "BloomStage.h" +#include "TonemappingStage.h" +#include "AmbientOcclusionStage.h" #include #include #include "DeferredLightingEffect.h" @@ -24,6 +26,8 @@ void UpdateSceneTask::build(JobModel& task, const render::Varying& input, render task.addJob("BackgroundStageSetup"); task.addJob("HazeStageSetup"); task.addJob("BloomStageSetup"); + task.addJob("TonemappingStageSetup"); + task.addJob("AmbientOcclusionStageSetup"); task.addJob("TransitionStageSetup"); task.addJob("HighlightStageSetup"); diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp index 5332e13816..d9c0de9934 100644 --- a/libraries/render-utils/src/ZoneRenderer.cpp +++ b/libraries/render-utils/src/ZoneRenderer.cpp @@ -26,6 +26,7 @@ #include "DeferredLightingEffect.h" #include "BloomStage.h" +#include "TonemappingStage.h" namespace ru { using render_utils::slot::texture::Texture; @@ -71,6 +72,14 @@ void SetupZones::run(const RenderContextPointer& context, const Input& input) { assert(bloomStage); bloomStage->_currentFrame.clear(); + auto tonemappingStage = context->_scene->getStage(); + assert(tonemappingStage); + tonemappingStage->_currentFrame.clear(); + + auto ambientOcclusionStage = context->_scene->getStage(); + assert(ambientOcclusionStage); + ambientOcclusionStage->_currentFrame.clear(); + // call render over the zones to grab their components in the correct order first... render::renderItems(context, input); @@ -80,6 +89,8 @@ void SetupZones::run(const RenderContextPointer& context, const Input& input) { backgroundStage->_currentFrame.pushBackground(0); hazeStage->_currentFrame.pushHaze(0); bloomStage->_currentFrame.pushBloom(INVALID_INDEX); + tonemappingStage->_currentFrame.pushTonemapping(0); + ambientOcclusionStage->_currentFrame.pushAmbientOcclusion(INVALID_INDEX); } const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() { @@ -105,6 +116,7 @@ const gpu::PipelinePointer& DebugZoneLighting::getAmbientPipeline() { } return _ambientPipeline; } + const gpu::PipelinePointer& DebugZoneLighting::getBackgroundPipeline() { if (!_backgroundPipeline) { gpu::ShaderPointer program = gpu::Shader::createProgram(shader::render_utils::program::zone_drawSkybox); diff --git a/libraries/render-utils/src/toneMapping.slf b/libraries/render-utils/src/toneMapping.slf index 32aa2b0788..0c336c8563 100644 --- a/libraries/render-utils/src/toneMapping.slf +++ b/libraries/render-utils/src/toneMapping.slf @@ -16,8 +16,8 @@ <@include render-utils/ShaderConstants.h@> struct ToneMappingParams { - vec4 _exp_2powExp_s0_s1; - ivec4 _toneCurve_s0_s1_s2; + float _2powExp; + int _toneCurve; }; const float GAMMA_22 = 2.2; @@ -31,17 +31,17 @@ LAYOUT(binding=RENDER_UTILS_BUFFER_TM_PARAMS) uniform toneMappingParamsBuffer { ToneMappingParams params; }; float getTwoPowExposure() { - return params._exp_2powExp_s0_s1.y; + return params._2powExp; } int getToneCurve() { - return params._toneCurve_s0_s1_s2.x; + return params._toneCurve; } LAYOUT(binding=RENDER_UTILS_TEXTURE_TM_COLOR) uniform sampler2D colorMap; layout(location=0) in vec2 varTexCoord0; layout(location=0) out vec4 outFragColor; - + void main(void) { <@if HIFI_USE_MIRRORED@> vec4 fragColorRaw = texture(colorMap, vec2(1.0 - varTexCoord0.x, varTexCoord0.y)); diff --git a/libraries/shared/src/AmbientOcclusionTechnique.cpp b/libraries/shared/src/AmbientOcclusionTechnique.cpp new file mode 100644 index 0000000000..d34f1e247e --- /dev/null +++ b/libraries/shared/src/AmbientOcclusionTechnique.cpp @@ -0,0 +1,24 @@ +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AmbientOcclusionTechnique.h" + +const char* ambientOcclusionTechniqueNames[] = { + "ssao", + "hbao" +}; + +static const size_t AO_TECHNIQUE_NAMES = (sizeof(ambientOcclusionTechniqueNames) / sizeof(ambientOcclusionTechniqueNames[0])); + +QString AmbientOcclusionTechniqueHelpers::getNameForAmbientOcclusionTechnique(AmbientOcclusionTechnique technique) { + if (((int)technique <= 0) || ((int)technique >= (int)AO_TECHNIQUE_NAMES)) { + technique = (AmbientOcclusionTechnique)0; + } + + return ambientOcclusionTechniqueNames[(int)technique]; +} diff --git a/libraries/shared/src/AmbientOcclusionTechnique.h b/libraries/shared/src/AmbientOcclusionTechnique.h new file mode 100644 index 0000000000..15ce034606 --- /dev/null +++ b/libraries/shared/src/AmbientOcclusionTechnique.h @@ -0,0 +1,38 @@ +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AmbientOcclusionTechnique_h +#define hifi_AmbientOcclusionTechnique_h + +#include "QString" + +/*@jsdoc + *

The technique used for calculating ambient occlusion. Different techniques have different tradeoffs.

+ * + * + * + * + * + * + * + * + *
ValueDescription
"ssao"A basic screen-space AO effect.
"hbao"A form of SSAO that uses the depth buffer for better AO detail, sometimes at the expense of performance.
+ * @typedef {string} AmbientOcclusionTechnique + */ + +enum class AmbientOcclusionTechnique { + SSAO = 0, + HBAO, +}; + +class AmbientOcclusionTechniqueHelpers { +public: + static QString getNameForAmbientOcclusionTechnique(AmbientOcclusionTechnique curve); +}; + +#endif // hifi_AmbientOcclusionTechnique_h diff --git a/libraries/shared/src/TonemappingCurve.cpp b/libraries/shared/src/TonemappingCurve.cpp new file mode 100644 index 0000000000..e65415af04 --- /dev/null +++ b/libraries/shared/src/TonemappingCurve.cpp @@ -0,0 +1,26 @@ +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "TonemappingCurve.h" + +const char* tonemappingCurveNames[] = { + "rgb", + "srgb", + "reinhard", + "filmic" +}; + +static const size_t TONEMAPPING_CURVE_NAMES = (sizeof(tonemappingCurveNames) / sizeof(tonemappingCurveNames[0])); + +QString TonemappingCurveHelpers::getNameForTonemappingCurve(TonemappingCurve curve) { + if (((int)curve <= 0) || ((int)curve >= (int)TONEMAPPING_CURVE_NAMES)) { + curve = (TonemappingCurve)0; + } + + return tonemappingCurveNames[(int)curve]; +} diff --git a/libraries/shared/src/TonemappingCurve.h b/libraries/shared/src/TonemappingCurve.h new file mode 100644 index 0000000000..f13cb3d437 --- /dev/null +++ b/libraries/shared/src/TonemappingCurve.h @@ -0,0 +1,42 @@ +// +// Created by HifiExperiments on 6/23/24 +// Copyright 2024 Overte e.V. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_TonemappingCurve_h +#define hifi_TonemappingCurve_h + +#include "QString" + +/*@jsdoc + *

The tonemapping curve applied to the final rendering.

+ * + * + * + * + * + * + * + * + * + * + *
ValueDescription
"rgb"No tonemapping, colors are kept in RGB.
"srgb"Colors are converted to sRGB.
"reinhard"Reinhard tonemapping is applied.
"filmic"Filmic tonemapping is applied.
+ * @typedef {string} TonemappingCurve + */ + +enum class TonemappingCurve { + RGB = 0, + SRGB, + REINHARD, + FILMIC +}; + +class TonemappingCurveHelpers { +public: + static QString getNameForTonemappingCurve(TonemappingCurve curve); +}; + +#endif // hifi_TonemappingCurve_h diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index 5c20d64345..75b927a2c9 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -55,6 +55,7 @@ Rectangle { Column { Repeater { model: [ + "debugEnabled:debug", "horizonBased:horizonBased", "jitterEnabled:jitterEnabled", "ditheringEnabled:ditheringEnabled", @@ -72,7 +73,7 @@ Rectangle { Column { Repeater { model: [ - "debugEnabled:showCursorPixel" + "showCursorPixel:showCursorPixel" ] HifiControls.CheckBox { boxSize: 20 @@ -100,8 +101,6 @@ Rectangle { ] } - - TabView { anchors.left: parent.left anchors.right: parent.right diff --git a/scripts/developer/utilities/render/luci/ToneMapping.qml b/scripts/developer/utilities/render/luci/ToneMapping.qml index a76990e37c..3995403917 100644 --- a/scripts/developer/utilities/render/luci/ToneMapping.qml +++ b/scripts/developer/utilities/render/luci/ToneMapping.qml @@ -14,15 +14,11 @@ import "../../lib/prop" as Prop Column { anchors.left: parent.left - anchors.right: parent.right - Prop.PropScalar { - label: "Exposure" + anchors.right: parent.right + Prop.PropBool { + label: "Debug" object: Render.getConfig("RenderMainView.ToneMapping") - property: "exposure" - min: -4 - max: 4 - anchors.left: parent.left - anchors.right: parent.right + property: "debug" } Prop.PropEnum { label: "Tone Curve" @@ -35,6 +31,15 @@ Column { "Filmic", ] anchors.left: parent.left - anchors.right: parent.right - } + anchors.right: parent.right + } + Prop.PropScalar { + label: "Exposure" + object: Render.getConfig("RenderMainView.ToneMapping") + property: "exposure" + min: -4 + max: 4 + anchors.left: parent.left + anchors.right: parent.right + } } diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index 254923af63..42dd02230d 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -165,6 +165,48 @@ "bloom.bloomSize": { "tooltip": "The radius of bloom. The higher the value, the larger the bloom." }, + "tonemappingMode": { + "tooltip": "Configures the tonemapping applied to the final render." + }, + "tonemapping.curve": { + "tooltip": "The tonemapping curve used." + }, + "tonemapping.exposure": { + "tooltip": "The exposure used during tonemapping." + }, + "ambientOcclusionMode": { + "tooltip": "Configures the ambient occlusion in this zone." + }, + "ambientOcclusion.technique": { + "tooltip": "The ambient occlusion technique used. Different techniques have different tradeoffs." + }, + "ambientOcclusion.jitter": { + "tooltip": "Whether or not the ambient occlusion sampling is jittered." + }, + "ambientOcclusion.resolutionLevel": { + "tooltip": "How high the resolution of the ambient occlusion buffer should be. Higher levels mean lower resolution buffers." + }, + "ambientOcclusion.edgeSharpness": { + "tooltip": "How much to sharpen the edges during the ambient occlusion blurring." + }, + "ambientOcclusion.blurRadius": { + "tooltip": "The radius used for blurring, in pixels." + }, + "ambientOcclusion.aoRadius": { + "tooltip": "The radius used for ambient occlusion." + }, + "ambientOcclusion.aoObscuranceLevel": { + "tooltip": "Intensify or dim ambient occlusion." + }, + "ambientOcclusion.aoFalloffAngle": { + "tooltip": "The falloff angle for the AO calculation." + }, + "ambientOcclusion.aoSamplingAmount": { + "tooltip": "The fraction of AO samples to use, out of the maximum for each technique." + }, + "ambientOcclusion.ssaoNumSpiralTurns": { + "tooltip": "The angle span used to distribute the AO samples ray directions. SSAO only." + }, "audio.reverbEnabled": { "tooltip": "If reverb should be enabled for listeners in this zone." }, diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index c740830948..4cc870d016 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -618,6 +618,124 @@ const GROUPS = [ } ] }, + { + id: "zone_tonemapping", + label: "ZONE TONEMAPPING", + properties: [ + { + label: "Tonemapping", + type: "dropdown", + options: { inherit: "Inherit", disabled: "Off", enabled: "On" }, + propertyID: "tonemappingMode", + }, + { + label: "Curve", + type: "dropdown", + options: { rgb: "RGB", srgb: "sRGB", reinhard: "Reinhard", filmic: "Filmic" }, + propertyID: "tonemapping.curve", + showPropertyRule: { "tonemappingMode": "enabled" }, + }, + { + label: "Exposure", + type: "number-draggable", + min: -4.0, + max: 4.0, + step: 0.1, + decimals: 1, + propertyID: "tonemapping.exposure", + showPropertyRule: { "tonemappingMode": "enabled" }, + } + ] + }, + { + id: "zone_ambient_occlusion", + label: "ZONE AMBIENT OCCLUSION", + properties: [ + { + label: "Ambient Occlusion", + type: "dropdown", + options: { inherit: "Inherit", disabled: "Off", enabled: "On" }, + propertyID: "ambientOcclusionMode", + }, + //{ + // label: "Technique", + // type: "dropdown", + // options: { ssao: "SSAO", hbao: "HBAO" }, + // propertyID: "ambientOcclusion.technique", + // showPropertyRule: { "ambientOcclusionMode": "enabled" }, + //}, + { + label: "Jitter", + type: "bool", + propertyID: "ambientOcclusion.jitter", + showPropertyRule: { "ambientOcclusionMode": "enabled" }, + }, + { + label: "Resolution Level", + type: "number-draggable", + step: 1, + decimals: 0, + propertyID: "ambientOcclusion.resolutionLevel", + showPropertyRule: { "ambientOcclusionMode": "enabled" }, + }, + { + label: "Edge Sharpness", + type: "number-draggable", + step: 0.01, + decimals: 2, + propertyID: "ambientOcclusion.edgeSharpness", + showPropertyRule: { "ambientOcclusionMode": "enabled" }, + }, + { + label: "Blur Radius (pixels)", + type: "number-draggable", + step: 1, + decimals: 0, + propertyID: "ambientOcclusion.blurRadius", + showPropertyRule: { "ambientOcclusionMode": "enabled" }, + }, + { + label: "AO Radius", + type: "number-draggable", + step: 0.01, + decimals: 2, + propertyID: "ambientOcclusion.aoRadius", + showPropertyRule: { "ambientOcclusionMode": "enabled" }, + }, + { + label: "Intensity", + type: "number-draggable", + step: 0.01, + decimals: 2, + propertyID: "ambientOcclusion.aoObscuranceLevel", + showPropertyRule: { "ambientOcclusionMode": "enabled" }, + }, + { + label: "Falloff Angle", + type: "number-draggable", + step: 0.01, + decimals: 2, + propertyID: "ambientOcclusion.aoFalloffAngle", + showPropertyRule: { "ambientOcclusionMode": "enabled" }, + }, + { + label: "Sampling Amount", + type: "number-draggable", + step: 0.01, + decimals: 2, + propertyID: "ambientOcclusion.aoSamplingAmount", + showPropertyRule: { "ambientOcclusionMode": "enabled" }, + }, + { + label: "Num Spiral Turns", + type: "number-draggable", + step: 0.01, + decimals: 2, + propertyID: "ambientOcclusion.ssaoNumSpiralTurns", + showPropertyRule: { "ambientOcclusionMode": "enabled" }, + } + ] + }, { id: "zone_avatar_priority", label: "ZONE AVATAR PRIORITY", @@ -1938,8 +2056,9 @@ const GROUPS_PER_TYPE = { None: [ 'base', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], Shape: [ 'base', 'shape', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], Text: [ 'base', 'text', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], - Zone: [ 'base', 'zone', 'zone_key_light', 'zone_skybox', 'zone_ambient_light', 'zone_haze', - 'zone_bloom', 'zone_avatar_priority', 'zone_audio', 'spatial', 'behavior', 'scripts', 'physics' ], + Zone: [ 'base', 'zone', 'zone_key_light', 'zone_skybox', 'zone_ambient_light', 'zone_haze', + 'zone_bloom', 'zone_tonemapping', 'zone_ambient_occlusion', 'zone_avatar_priority', + 'zone_audio', 'spatial', 'behavior', 'scripts', 'physics' ], Model: [ 'base', 'model', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], Image: [ 'base', 'image', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], Web: [ 'base', 'web', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], diff --git a/scripts/system/create/entityProperties/html/tabs/zone_ambient_occlusion.png b/scripts/system/create/entityProperties/html/tabs/zone_ambient_occlusion.png new file mode 100644 index 0000000000..ace90dbcbc Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/zone_ambient_occlusion.png differ diff --git a/scripts/system/create/entityProperties/html/tabs/zone_tonemapping.png b/scripts/system/create/entityProperties/html/tabs/zone_tonemapping.png new file mode 100644 index 0000000000..06f93f57e4 Binary files /dev/null and b/scripts/system/create/entityProperties/html/tabs/zone_tonemapping.png differ