From 46ed8382e83bd777082fe935e87e2940e6a7ff78 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Thu, 28 Sep 2023 09:40:51 +0200 Subject: [PATCH] Add the ability to specify a custom shader for the RenderableGalaxy class --- .../examples/galaxyshader/galaxyraycast.glsl | 73 ++++++++++++++++++ .../assets/examples/galaxyshader/volume.asset | 76 +++++++++++++++++++ modules/galaxy/rendering/galaxyraycaster.cpp | 6 +- modules/galaxy/rendering/galaxyraycaster.h | 4 +- modules/galaxy/rendering/renderablegalaxy.cpp | 12 ++- modules/galaxy/rendering/renderablegalaxy.h | 1 + 6 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 data/assets/examples/galaxyshader/galaxyraycast.glsl create mode 100644 data/assets/examples/galaxyshader/volume.asset diff --git a/data/assets/examples/galaxyshader/galaxyraycast.glsl b/data/assets/examples/galaxyshader/galaxyraycast.glsl new file mode 100644 index 0000000000..bc4acd2ffe --- /dev/null +++ b/data/assets/examples/galaxyshader/galaxyraycast.glsl @@ -0,0 +1,73 @@ +/***************************************************************************************** +* * +* OpenSpace * +* * +* Copyright (c) 2014-2020 * +* * +* Permission is hereby granted, free of charge, to any person obtaining a copy of this * +* software and associated documentation files (the "Software"), to deal in the Software * +* without restriction, including without limitation the rights to use, copy, modify, * +* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * +* permit persons to whom the Software is furnished to do so, subject to the following * +* conditions: * +* * +* The above copyright notice and this permission notice shall be included in all copies * +* or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * +* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * +* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * +* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * +* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * +* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * +****************************************************************************************/ + +uniform float maxStepSize#{id} = 0.1; +uniform vec3 aspect#{id} = vec3(1.0); +uniform float opacityCoefficient#{id} = 1.0; +uniform float absorptionMultiply#{id} = 50.0; +uniform float emissionMultiply#{id} = 1500.0; +uniform sampler3D galaxyTexture#{id}; + +void sample#{id}(vec3 samplePos, vec3 dir, inout vec3 accumulatedColor, + inout vec3 accumulatedAlpha, inout float stepSize) +{ + vec3 aspect = aspect#{id}; + stepSize = maxStepSize#{id} / length(dir / aspect); + + // Early ray termination on black parts of the data + vec3 normalizedPos = samplePos * 2.0 - 1.0; + if (normalizedPos.x * normalizedPos.x + normalizedPos.y * normalizedPos.y > 0.7) { + return; + } + + vec4 sampledColor = texture(galaxyTexture#{id}, samplePos.xyz); + + // Source textures currently are square-rooted to avoid dithering in the shadows. + // So square them back + sampledColor = sampledColor*sampledColor; + + // Fudge for the dust "spreading" + sampledColor.a = clamp(sampledColor.a, 0.0, 1.0); + sampledColor.a = pow(sampledColor.a, 0.7); + + // Absorption probability + float scaledDensity = sampledColor.a * stepSize * absorptionMultiply#{id}; + vec3 alphaTint = vec3(-0.7, -0.44, -0.35); + vec3 absorption = alphaTint * scaledDensity; + + // Extinction + vec3 extinction = exp(-absorption); + accumulatedColor.rgb *= extinction; + + // Emission + accumulatedColor.rgb += + sampledColor.rgb * stepSize * emissionMultiply#{id} * opacityCoefficient#{id}; + + vec3 oneMinusFrontAlpha = vec3(1.0) - accumulatedAlpha; + accumulatedAlpha += oneMinusFrontAlpha * sampledColor.rgb * opacityCoefficient#{id}; +} + +float stepSize#{id}(vec3 samplePos, vec3 dir) { + return maxStepSize#{id} * length(dir * 1.0 / aspect#{id}); +} diff --git a/data/assets/examples/galaxyshader/volume.asset b/data/assets/examples/galaxyshader/volume.asset new file mode 100644 index 0000000000..d52b3f2bc0 --- /dev/null +++ b/data/assets/examples/galaxyshader/volume.asset @@ -0,0 +1,76 @@ +local transforms = asset.require("scene/solarsystem/sun/transforms") + + + +local data = asset.syncedResource({ + Name = "Milkyway Volume Data", + Type = "HttpSynchronization", + Identifier = "milkyway_volume_data", + Version = 1 +}) + + +local KiloParsec = 3.086E19 + +local MilkyWayVolume = { + Identifier = "MilkyWayVolume_CustomShader", + Parent = transforms.SolarSystemBarycenter.Identifier, + Transform = { + Translation = { + Type = "StaticTranslation", + -- The center of the Milky Way is approximately 8 kiloparsec from the Sun. + -- The x-axis of galactic coordinates points from the sun towards the center + -- of the galaxy. + Position = { 8 * KiloParsec, 0, 0 } + } + }, + Renderable = { + Type = "RenderableGalaxy", + StepSize = 0.01, + AbsorptionMultiply = 200, + EmissionMultiply = 250, + Rotation = { math.pi, 3.1248, 4.45741 }, + RaycastingShader = asset.localResource("galaxyraycast2.glsl"), + Volume = { + Type = "Volume", + Filename = data .. "MilkyWayRGBAVolume1024x1024x128.raw", + Dimensions = { 1024, 1024, 128 }, + Size = { 1.2E21, 1.2E21, 0.15E21 }, + Downscale = 0.4, + }, + Points = { + Type = "Points", + Filename = data .. "MilkyWayPoints.off", + EnabledPointsRatio = 0.3, + Texture = data .. "halo.png" + } + }, + GUI = { + Path = "/Milky Way", + Name = "Milky Way Volume (Custom Shader)", + Description = "Volumetric rendering of Milky Way galaxy based on simulation from NAOJ" + } +} + + +asset.onInitialize(function() + openspace.addSceneGraphNode(MilkyWayVolume) +end) + +asset.onDeinitialize(function() + openspace.removeSceneGraphNode(MilkyWayVolume) +end) + +asset.export(MilkyWayVolume) + + + +asset.meta = { + Name = "Milky Way Volume", + Version = "1.1", + Description = [[Volumetric rendering of Milky Way galaxy based on simulations from " + "NAOJ with a custom shader]], + Author = "OpenSpace Team", + URL = "http://openspaceproject.com", + License = "MIT License" +} diff --git a/modules/galaxy/rendering/galaxyraycaster.cpp b/modules/galaxy/rendering/galaxyraycaster.cpp index 7aa2334963..623bdb04c1 100644 --- a/modules/galaxy/rendering/galaxyraycaster.cpp +++ b/modules/galaxy/rendering/galaxyraycaster.cpp @@ -43,10 +43,12 @@ namespace { namespace openspace { -GalaxyRaycaster::GalaxyRaycaster(ghoul::opengl::Texture& texture) +GalaxyRaycaster::GalaxyRaycaster(ghoul::opengl::Texture& texture, + std::optional raycastingShader) : _boundingBox(glm::vec3(1.f)) , _texture(texture) , _textureUnit(nullptr) + , _raycastingShader(raycastingShader.value_or(GlslRaycastPath)) {} void GalaxyRaycaster::initialize() { @@ -149,7 +151,7 @@ std::string GalaxyRaycaster::boundsFragmentShaderPath() const { } std::string GalaxyRaycaster::raycasterPath() const { - return std::string(GlslRaycastPath); + return _raycastingShader.string(); } std::string GalaxyRaycaster::helperPath() const { diff --git a/modules/galaxy/rendering/galaxyraycaster.h b/modules/galaxy/rendering/galaxyraycaster.h index 9be21a3f97..5711247f40 100644 --- a/modules/galaxy/rendering/galaxyraycaster.h +++ b/modules/galaxy/rendering/galaxyraycaster.h @@ -45,7 +45,8 @@ struct RaycastData; class GalaxyRaycaster : public VolumeRaycaster { public: - GalaxyRaycaster(ghoul::opengl::Texture& texture); + GalaxyRaycaster(ghoul::opengl::Texture& texture, + std::optional raycastingShader = std::nullopt); ~GalaxyRaycaster() override = default; void initialize(); @@ -87,6 +88,7 @@ private: float _emissionMultiply = 0.f; ghoul::opengl::Texture& _texture; std::unique_ptr _textureUnit; + std::filesystem::path _raycastingShader; }; // GalaxyRaycaster diff --git a/modules/galaxy/rendering/renderablegalaxy.cpp b/modules/galaxy/rendering/renderablegalaxy.cpp index b77ccf1001..b6ca4bbab3 100644 --- a/modules/galaxy/rendering/renderablegalaxy.cpp +++ b/modules/galaxy/rendering/renderablegalaxy.cpp @@ -175,6 +175,10 @@ namespace { // [[codegen::verbatim(EmissionMultiplyInfo.description)]] std::optional emissionMultiply; + // If this value is specified, the default raycasting shader is overwritten and + // the shader found at the provided location is used instead + std::optional raycastingShader; + enum class [[codegen::map(StarRenderingMethod)]] StarRenderingMethod { Points, Billboards @@ -285,6 +289,7 @@ RenderableGalaxy::RenderableGalaxy(const ghoul::Dictionary& dictionary) _stepSize = p.stepSizeInfo.value_or(_stepSize); _absorptionMultiply = p.absorptionMultiply.value_or(_absorptionMultiply); _emissionMultiply = p.emissionMultiply.value_or(_emissionMultiply); + _raycastingShader = p.raycastingShader.value_or(_raycastingShader); _starRenderingMethod.addOptions({ { StarRenderingMethod::Points, "Points" }, @@ -417,7 +422,12 @@ void RenderableGalaxy::initializeGL() { _texture->setDimensions(_volume->dimensions()); _texture->uploadTexture(); - _raycaster = std::make_unique(*_texture); + if (_raycastingShader.empty()) { + _raycaster = std::make_unique(*_texture); + } + else { + _raycaster = std::make_unique(*_texture, _raycastingShader); + } _raycaster->initialize(); // We no longer need the data diff --git a/modules/galaxy/rendering/renderablegalaxy.h b/modules/galaxy/rendering/renderablegalaxy.h index a6b9be02fc..6614529633 100644 --- a/modules/galaxy/rendering/renderablegalaxy.h +++ b/modules/galaxy/rendering/renderablegalaxy.h @@ -88,6 +88,7 @@ private: glm::ivec3 _volumeDimensions = glm::ivec3(0); std::string _pointsFilename; std::string _pointSpreadFunctionTexturePath; + std::filesystem::path _raycastingShader; std::unique_ptr _raycaster; std::unique_ptr>> _volume;