This commit is contained in:
Andreas Engberg
2025-10-03 15:02:20 +02:00
parent 60ebdfcc34
commit d4cdaaeae2
5 changed files with 132 additions and 26 deletions

View File

@@ -140,6 +140,8 @@ void BasicVolumeRaycaster::preRaycast(const RaycastData& data,
program.setUniform("brightness_" + id, brightness());
program.setUniform("rNormalization_" + id, _rNormalization);
program.setUniform("rUpperBound_" + id, _rUpperBound);
program.setUniform("valueRange_" + id, _valueRange);
program.setUniform("hideOutsideRange_" + id, _hideOutsideRange);
}
void BasicVolumeRaycaster::postRaycast(const RaycastData&, ghoul::opengl::ProgramObject&)
@@ -233,4 +235,12 @@ void BasicVolumeRaycaster::setModelTransform(glm::mat4 transform) {
_modelTransform = std::move(transform);
}
void BasicVolumeRaycaster::setValueRange(glm::vec2 range) {
_valueRange = range;
}
void BasicVolumeRaycaster::setHideOutsideRange(bool value) {
_hideOutsideRange = value;
}
} // namespace openspace::volume

View File

@@ -89,6 +89,8 @@ public:
VolumeGridType gridType() const;
void setGridType(VolumeGridType gridType);
void setModelTransform(glm::mat4 transform);
void setValueRange(glm::vec2 range);
void setHideOutsideRange(bool value);
private:
glm::dmat4 modelViewTransform(const RenderData& data);
@@ -102,6 +104,8 @@ private:
float _brightness = 1.f;
float _rNormalization = 0.f;
float _rUpperBound = 1.f;
glm::vec2 _valueRange = glm::vec2(0.f);
bool _hideOutsideRange = false;
std::unique_ptr<ghoul::opengl::TextureUnit> _tfUnit;
std::unique_ptr<ghoul::opengl::TextureUnit> _textureUnit;

View File

@@ -157,6 +157,23 @@ namespace {
openspace::properties::Property::Visibility::AdvancedUser
};
constexpr openspace::properties::Property::PropertyInfo RangeInfo = {
"ValueRange",
"Value Range",
"The range of values to use in the color mapping. The lowest value will be "
"mapped to the first color in the color map.",
openspace::properties::Property::Visibility::AdvancedUser
};
constexpr openspace::properties::Property::PropertyInfo HideOutsideInfo = {
"HideValuesOutsideRange",
"Hide values outside range",
"If ture, density values outside the provided range will be hidden, i.e., not "
"rendered at all.",
openspace::properties::Property::Visibility::AdvancedUser
};
struct [[codegen::Dictionary(RenderableTimeVaryingVolume)]] Parameters {
// [[codegen::verbatim(SourceDirectoryInfo.description)]]
std::filesystem::path sourceDirectory [[codegen::directory()]];
@@ -193,6 +210,12 @@ namespace {
// [[codegen::verbatim(ShowVolumeSliceInfo.description)]]
std::optional<bool> showVolumeSlice;
// [[codegen::verbatim(RangeInfo.description)]]
std::optional<glm::vec2> valueRange;
// [[codegen::verbatim(HideOutsideInfo.description)]]
std::optional<bool> hideValuesOutsideRange;
};
#include "renderabletimevaryingvolume_codegen.cpp"
} // namespace
@@ -224,7 +247,7 @@ RenderableTimeVaryingVolume::RenderableTimeVaryingVolume(
const ghoul::Dictionary& dictionary)
: Renderable(dictionary)
, _gridType(GridTypeInfo)
, _stepSize(StepSizeInfo, 0.02f, 0.001f, 0.1f)
, _stepSize(StepSizeInfo, 0.01f, 0.001f, 0.1f, 0.001f)
, _brightness(BrightnessInfo, 0.33f, 0.f, 1.f)
, _rNormalization(rNormalizationInfo, 0.f, 0.f, 2.f)
, _rUpperBound(rUpperBoundInfo, 1.f, 0.f, 2.f)
@@ -235,6 +258,8 @@ RenderableTimeVaryingVolume::RenderableTimeVaryingVolume(
, _triggerTimeJump(TriggerTimeJumpInfo)
, _jumpToTimestep(JumpToTimestepInfo, 0, 0, 256)
, _invertDataAtZ(false)
, _valueRange(RangeInfo, glm::vec2(0.f))
, _hideOutsideRange(HideOutsideInfo)
{
const Parameters p = codegen::bake<Parameters>(dictionary);
@@ -286,6 +311,12 @@ RenderableTimeVaryingVolume::RenderableTimeVaryingVolume(
});
addPropertySubOwner(_volumeSlice);
_valueRange = p.valueRange.value_or(_valueRange);
_hideOutsideRange = p.hideValuesOutsideRange.value_or(false);
addProperty(_valueRange);
addProperty(_hideOutsideRange);
}
RenderableTimeVaryingVolume::~RenderableTimeVaryingVolume() {}
@@ -299,13 +330,23 @@ void RenderableTimeVaryingVolume::initializeGL() {
}
namespace fs = std::filesystem;
double timestep = 0.0;
// VTI specific until we decide on a meta data format that can be read
const int nVtiSamples = 191;
const int VtiSimulationTime = 48; // seconds
double deltaTimeStep = static_cast<double>(VtiSimulationTime) / static_cast<double>(nVtiSamples);
int step = 0;
float globalMin = std::numeric_limits<float>::max();
float globalMax = std::numeric_limits<float>::lowest();
for (const fs::directory_entry& e : fs::recursive_directory_iterator(sequenceDir)) {
if (e.is_regular_file() && e.path().extension() == ".dictionary") {
loadTimestepMetadata(e.path());
loadTimestepMetadata(e.path(), globalMin, globalMax);
}
if (e.is_regular_file() && e.path().extension() == ".vti") {
const auto [metadata, scalars] = readVTIFile(e.path(), timestep++);
double timestep = Time::convertTime(std::format("2023-03-23T20:29:{:02}", deltaTimeStep * step++));
const auto [metadata, scalars] = readVTIFile(e.path(), timestep);
Timestep t;
t.metadata = metadata;
@@ -314,6 +355,10 @@ void RenderableTimeVaryingVolume::initializeGL() {
t.onGpu = false;
t.rawData = scalars;
globalMin = std::min(globalMin, t.metadata.minValue);
globalMax = std::max(globalMax, t.metadata.maxValue);
_volumeTimesteps[t.metadata.time] = std::move(t);
}
}
@@ -329,30 +374,44 @@ void RenderableTimeVaryingVolume::initializeGL() {
);
RawVolumeReader<float> reader(path, t.metadata.dimensions);
t.rawVolume = reader.read(_invertDataAtZ);
for (size_t i = 0; i < t.metadata.dimensions.x * t.metadata.dimensions.y * t.metadata.dimensions.z; i++) {
float d = t.rawVolume->data()[i];
globalMin = std::min(globalMin, d);
globalMax = std::max(globalMax, d);
}
}
const float min = t.metadata.minValue;
const float diff = t.metadata.maxValue - t.metadata.minValue;
float* data;
// We've read data from binary file
if (t.rawVolume) {
data = t.rawVolume->data();
for (size_t i = 0; i < t.rawVolume->nCells(); i++) {
data[i] = glm::clamp((data[i] - min) / diff, 0.f, 1.f);
}
//const float min = t.metadata.minValue;
//const float diff = t.metadata.maxValue - t.metadata.minValue;
const float min = globalMin;
const float diff = globalMax - globalMin;
//_valueRange = glm::vec2(t.metadata.minValue, t.metadata.maxValue);
_valueRange = glm::vec2(globalMin, globalMax);
t.histogram = std::make_shared<Histogram>(0.f, 1.f, 100);
for (size_t i = 0; i < t.rawVolume->nCells(); i++) {
t.histogram->add(data[i]);
}
data = t.rawVolume->data();
//for (size_t i = 0; i < t.rawVolume->nCells(); i++) {
// data[i] = glm::clamp((data[i] - min) / diff, 0.f, 1.f);
//}
//t.histogram = std::make_shared<Histogram>(0.f, 1.f, 100);
//for (size_t i = 0; i < t.rawVolume->nCells(); i++) {
// t.histogram->add(data[i]);
//}
}
// Data came from xml file
else {
const float min = globalMin;
const float diff = globalMax - globalMin;
_valueRange = glm::vec2(globalMin, globalMax);
data = t.rawData.data();
for (size_t i = 0; i < t.rawData.size(); i++) {
data[i] = glm::clamp((data[i] - min) / diff, 0.f, 1.f);
}
//for (size_t i = 0; i < t.rawData.size(); i++) {
// data[i] = glm::clamp((data[i] - min) / diff, 0.f, 1.f);
//}
}
// TODO: handle normalization properly for different timesteps + transfer function
@@ -360,7 +419,7 @@ void RenderableTimeVaryingVolume::initializeGL() {
t.metadata.dimensions,
GL_TEXTURE_3D,
ghoul::opengl::Texture::Format::Red,
GL_RED,
GL_R32F,
GL_FLOAT,
ghoul::opengl::Texture::FilterMode::Linear,
ghoul::opengl::Texture::WrappingMode::Clamp
@@ -441,7 +500,8 @@ void RenderableTimeVaryingVolume::initializeGL() {
ghoul::opengl::updateUniformLocations(*_shader, _uniformCache);
}
void RenderableTimeVaryingVolume::loadTimestepMetadata(const std::filesystem::path& path)
void RenderableTimeVaryingVolume::loadTimestepMetadata(const std::filesystem::path& path,
float& globalMin, float& globalMax)
{
RawVolumeMetadata metadata;
@@ -463,6 +523,9 @@ void RenderableTimeVaryingVolume::loadTimestepMetadata(const std::filesystem::pa
t.inRam = false;
t.onGpu = false;
globalMin = std::min(t.metadata.minValue, globalMin);
globalMax = std::max(t.metadata.maxValue, globalMax);
_volumeTimesteps[t.metadata.time] = std::move(t);
}
@@ -566,6 +629,8 @@ void RenderableTimeVaryingVolume::update(const UpdateData&) {
_raycaster->setBrightness(_brightness * opacity());
_raycaster->setRNormalization(_rNormalization);
_raycaster->setRUpperBound(_rUpperBound);
_raycaster->setValueRange(_valueRange);
_raycaster->setHideOutsideRange(_hideOutsideRange);
}
}
@@ -625,13 +690,13 @@ glm::mat4 RenderableTimeVaryingVolume::calculateModelTransform() {
void RenderableTimeVaryingVolume::createPlane() const {
const std::array<GLfloat, 36> vertexData = {
// x y
// x y
-1.f, -1.f,
1.f, 1.f,
-1.f, 1.f,
-1.f, -1.f,
1.f, -1.f,
1.f, 1.f,
1.f, 1.f,
};
glBindVertexArray(_quad);
@@ -682,7 +747,7 @@ void RenderableTimeVaryingVolume::renderVolumeSlice(const RenderData& data) {
_shader->setUniform(_uniformCache.transferFunction, transferFunctionUnit);
if (_slicePlaneIsDirty) {
// Create basis to convert points on the 2D plane into volume 3D coordinates
// Create basis to convert points on the 2D plane into volume 3D coordinates
glm::vec3 n = glm::normalize(_volumeSlice.normal.value());
glm::vec3 up = glm::abs(n.z) < 0.999f ? glm::vec3(0.f, 0.f, 1.f)
@@ -691,7 +756,7 @@ void RenderableTimeVaryingVolume::renderVolumeSlice(const RenderData& data) {
glm::vec3 tangent = glm::normalize(glm::cross(up, n));
glm::vec3 bitangent = glm::normalize(glm::cross(n, tangent));
_basisTransform = glm::mat3(tangent, bitangent, n);
_basisTransform = glm::mat3(tangent, bitangent, n);
}
_shader->setUniform(_uniformCache.basis, _basisTransform);

View File

@@ -33,6 +33,7 @@
#include <openspace/properties/misc/triggerproperty.h>
#include <openspace/properties/scalar/floatproperty.h>
#include <openspace/properties/scalar/intproperty.h>
#include <openspace/properties/vector/vec2property.h>
#include <openspace/properties/vector/vec3property.h>
#include <openspace/rendering/transferfunction.h>
#include <ghoul/opengl/uniformcache.h>
@@ -83,7 +84,8 @@ private:
Timestep* timestepFromIndex(int target);
void jumpToTimestep(int target);
void loadTimestepMetadata(const std::filesystem::path& path);
void loadTimestepMetadata(const std::filesystem::path& path, float& globalMin,
float& globalMax);
glm::mat4 calculateModelTransform();
void createPlane() const;
@@ -104,6 +106,8 @@ private:
properties::TriggerProperty _triggerTimeJump;
properties::IntProperty _jumpToTimestep;
properties::Vec2Property _valueRange;
properties::BoolProperty _hideOutsideRange;
std::map<double, Timestep> _volumeTimesteps;
std::unique_ptr<BasicVolumeRaycaster> _raycaster;
@@ -112,7 +116,7 @@ private:
struct VolumeSliceSettings : properties::PropertyOwner {
VolumeSliceSettings();
properties::Vec3Property normal;
properties::FloatProperty offset;
properties::BoolProperty shouldRenderSlice;

View File

@@ -31,6 +31,9 @@ uniform int nClips_#{id};
uniform vec3 clipNormals_#{id}[8];
uniform vec2 clipOffsets_#{id}[8];
uniform vec2 valueRange_#{id};
uniform bool hideOutsideRange_#{id};
uniform float brightness_#{id} = 1.0;
// unitless factor that multiplies with the brightness [0,1] to achieve desired visuals.
const float SamplingIntervalReferenceFactor = 500.0;
@@ -70,12 +73,32 @@ void sample#{id}(vec3 samplePos, vec3 dir, inout vec3 accumulatedColor,
if (clipAlpha > 0) {
float val = texture(volumeTexture_#{id}, transformedPos).r;
float minVal = valueRange_#{id}.x;
float diff = valueRange_#{id}.y - valueRange_#{id}.x;
val = (val - minVal) / diff;
if (rNormalization_#{id} > 0 && gridType_#{id} == 1) {
val *= pow(transformedPos.x, rNormalization_#{id});
}
vec4 color = texture(transferFunction_#{id}, val);
// Apply an alpha falloff using eq: e^(-((x*2-1)^(p)+(y*2-1)^(p)) * p), a large value
// of p results in a square shape, a value of 2 resulsts in a circle
if(hideOutsideRange_#{id}) {
const float e = 2.71828;
float p = 400;
float scale = 1.05; // Scaling the cube further to reduce the impact on the edges
float x = (transformedPos.x * 2 - 1) * scale;
float y = (transformedPos.y * 2 - 1) * scale;
float z = (transformedPos.z * 2 -1) * scale;
float v = pow(e, -(pow(abs(x), p) + pow(abs(y), p) + pow(abs(z), p)) * p);
color.a *= v;
// color = vec4(v, 0, 0, 0.5);
}
vec3 backColor = color.rgb;
vec3 backAlpha = color.aaa;