diff --git a/modules/base/rendering/renderabletube.cpp b/modules/base/rendering/renderabletube.cpp index d11b471567..4dd76ad7a1 100644 --- a/modules/base/rendering/renderabletube.cpp +++ b/modules/base/rendering/renderabletube.cpp @@ -47,9 +47,11 @@ namespace { constexpr std::string_view _loggerCat = "RenderableTube"; constexpr int8_t CurrentMajorVersion = 0; constexpr int8_t CurrentMinorVersion = 1; - constexpr std::array UniformNames = { - "modelViewTransform", "projectionTransform", "normalTransform", "color", - "opacity", "hasTransferFunction", "transferFunction", "performShading", + constexpr std::array UniformNames = { + "modelViewTransform", "projectionTransform", "normalTransform", "opacity", + "color", "nanColor", "useNanColor", "aboveRangeColor", "useAboveRangeColor", + "belowRangeColor", "useBelowRangeColor", "useColorMap", "colorMapTexture", + "cmapRangeMin", "cmapRangeMax", "hideOutsideRange", "performShading", "nLightSources", "lightDirectionsViewSpace", "lightIntensities", "ambientIntensity", "diffuseIntensity", "specularIntensity" }; @@ -61,14 +63,6 @@ namespace { openspace::properties::Property::Visibility::AdvancedUser }; - constexpr openspace::properties::Property::PropertyInfo ColorInfo = { - "Color", - "Color", - "This value determines the RGB color for the tube", - // @VISIBILITY(1.2) - openspace::properties::Property::Visibility::NoviceUser - }; - constexpr openspace::properties::Property::PropertyInfo EnableFaceCullingInfo = { "EnableFaceCulling", "Enable Face Culling", @@ -111,6 +105,14 @@ namespace { openspace::properties::Property::Visibility::AdvancedUser }; + constexpr openspace::properties::Property::PropertyInfo TubeColorInfo = { + "FixedColor", + "Fixed Color", + "This value is used to define the color of the tube when no color map is" + "used", + openspace::properties::Property::Visibility::NoviceUser + }; + constexpr openspace::properties::Property::PropertyInfo AddEdgesInfo = { "AddEdges", "Add Edges", @@ -144,12 +146,6 @@ namespace { // The input file with data for the tube std::string file; - // [[codegen::verbatim(TransferFunctionInfo.description)]] - std::optional transferFunction; - - // [[codegen::verbatim(ColorInfo.description)]] - std::optional color [[codegen::color()]]; - // [[codegen::verbatim(EnableFaceCullingInfo.description)]] std::optional enableFaceCulling; @@ -169,6 +165,18 @@ namespace { std::optional> lightSources [[codegen::reference("core_light_source")]]; + struct ColorSettings { + // [[codegen::verbatim(TubeColorInfo.description)]] + std::optional fixedColor [[codegen::color()]]; + + // Settings related to the choice of color map, parameters, etc. + std::optional colorMapping + [[codegen::reference("colormappingcomponent")]]; + }; + // Settings related to the coloring of the points, such as a fixed color, + // color map, etc. + std::optional coloring; + // [[codegen::verbatim(AddEdgesInfo.description)]] std::optional addEdges; @@ -203,12 +211,32 @@ RenderableTube::Shading::Shading() addProperty(specularIntensity); } +RenderableTube::ColorSettings::ColorSettings(const ghoul::Dictionary& dictionary) + : properties::PropertyOwner({ "Coloring", "Coloring", "" }) + , tubeColor(TubeColorInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(1.f)) +{ + const Parameters p = codegen::bake(dictionary); + + if (p.coloring.has_value()) { + const Parameters::ColorSettings settings = *p.coloring; + tubeColor = settings.fixedColor.value_or(tubeColor); + + if (settings.colorMapping.has_value()) { + colorMapping = std::make_unique( + *settings.colorMapping + ); + addPropertySubOwner(colorMapping.get()); + } + } + tubeColor.setViewOption(properties::Property::ViewOptions::Color); + addProperty(tubeColor); +} + RenderableTube::RenderableTube(const ghoul::Dictionary& dictionary) : Renderable(dictionary) - , _transferFunctionPath(TransferFunctionInfo) - , _color(ColorInfo, glm::vec3(1.f), glm::vec3(0.f), glm::vec3(1.f)) , _enableFaceCulling(EnableFaceCullingInfo, true) , _lightSourcePropertyOwner({ "LightSources", "Light Sources" }) + , _colorSettings(dictionary) , _addEdges(AddEdgesInfo, true) , _drawWireframe(DrawWireframeInfo, false) , _wireLineWidth(WireLineWidthInfo, 1.f, 1.f, 10.f) @@ -218,20 +246,6 @@ RenderableTube::RenderableTube(const ghoul::Dictionary& dictionary) _dataFile = p.file; - if (p.transferFunction.has_value()) { - _hasTransferFunction = true; - _transferFunctionPath = absPath(*p.transferFunction).string(); - _transferFunction = std::make_shared( - _transferFunctionPath, - [](const openspace::TransferFunction&) {} - ); - } - addProperty(_transferFunctionPath); - - _color.setViewOption(properties::Property::ViewOptions::Color); - _color = p.color.value_or(_color); - addProperty(_color); - _enableFaceCulling = p.enableFaceCulling.value_or(_enableFaceCulling); addProperty(_enableFaceCulling); @@ -252,6 +266,22 @@ RenderableTube::RenderableTube(const ghoul::Dictionary& dictionary) } } + if (p.coloring.has_value() && (*p.coloring).colorMapping.has_value()) { + _hasColorMapFile = true; + + _colorSettings.colorMapping->dataColumn.onChange( + [this]() { _tubeIsDirty = true; } + ); + + _colorSettings.colorMapping->setRangeFromData.onChange([this]() { + int parameterIndex = currentColorParameterIndex(); + _colorSettings.colorMapping->valueRange = _colorDataset.findValueRange( + parameterIndex + ); + }); + } + addPropertySubOwner(_colorSettings); + _addEdges.onChange([this]() { _tubeIsDirty = true; }); _addEdges = p.addEdges.value_or(_addEdges); addProperty(_addEdges); @@ -277,6 +307,10 @@ void RenderableTube::initialize() { readDataFile(); updateTubeData(); + if (_hasColorMapFile) { + _colorSettings.colorMapping->initialize(_colorDataset); + } + for (const std::unique_ptr& ls : _lightSources) { ls->initialize(); } @@ -290,6 +324,10 @@ void RenderableTube::initializeGL() { ); ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames); + if (_hasColorMapFile) { + _colorSettings.colorMapping->initializeTexture(); + } + glGenVertexArrays(1, &_vaoId); glGenBuffers(1, &_vboId); glGenBuffers(1, &_iboId); @@ -338,6 +376,17 @@ void RenderableTube::deinitializeGL() { _iboId = 0; } +int RenderableTube::currentColorParameterIndex() const { + const properties::OptionProperty& property = + _colorSettings.colorMapping->dataColumn; + + if (!_hasColorMapFile || property.options().empty()) { + return 0; + } + + return _colorDataset.index(property.option().description); +} + void RenderableTube::readDataFile() { std::filesystem::path file = absPath(_dataFile); if (!std::filesystem::is_regular_file(file)) { @@ -397,6 +446,7 @@ void RenderableTube::readDataFile() { } // Loop throught json object to fill the datastructure for the polygons + bool isFirstPlygonAndPoint = true; for (auto it = polygons->begin(); it < polygons->end(); ++it) { TimePolygon timePolygon; @@ -451,14 +501,23 @@ void RenderableTube::readDataFile() { pt->at("z").get_to(z); timePolygonPoint.coordinate = glm::dvec3(x, y, z); - // Value (optional) - auto v = pt->find("value"); - if (v != pt->end()) { - float value; - pt->at("value").get_to(value); - timePolygonPoint.value = value; - } + // Data values (optional) + auto colorData = pt->find("data"); + if (colorData != pt->end() && _hasColorMapFile) { + int colorDataIndex = 0; + dataloader::Dataset::Entry entry; + for (auto dt : colorData->items()) { + if (isFirstPlygonAndPoint) { + _colorDataset.variables.push_back({ .index = colorDataIndex++, .name = dt.key() }); + } + entry.data.push_back(dt.value()); + } + _colorDataset.entries.push_back(entry); + if (isFirstPlygonAndPoint) { + isFirstPlygonAndPoint = false; + } + } timePolygon.points.push_back(timePolygonPoint); } _data.push_back(timePolygon); @@ -494,6 +553,11 @@ void RenderableTube::updateTubeData() { void RenderableTube::createSmoothTube(size_t nPolygons, size_t nPoints) { // Verticies + int pointCounter = 0; + + // Get the selected color parameter + int colorParamIndex = currentColorParameterIndex(); + // Calciulate the normals for the top and bottom glm::dvec3 bottomCenter = _data.front().center; glm::dvec3 topCenter = _data.back().center; @@ -502,7 +566,7 @@ void RenderableTube::createSmoothTube(size_t nPolygons, size_t nPoints) { // Add the bottom if (_addEdges) { - addBottom(nPoints, bottomCenter, bottomNormal); + addBottom(nPoints, pointCounter, bottomCenter, bottomNormal, colorParamIndex); } // Add all the polygons that will create the sides of the tube @@ -520,20 +584,24 @@ void RenderableTube::createSmoothTube(size_t nPolygons, size_t nPoints) { _data[polyIndex - 1].center - _data[polyIndex].center : _data[polyIndex].center - _data[polyIndex + 1].center; glm::dvec3 normal = _data[polyIndex].points[pointIndex].coordinate - - glm::proj(_data[polyIndex].points[pointIndex].coordinate, centerLine) - centerLine; + glm::proj(_data[polyIndex].points[pointIndex].coordinate, centerLine) - + centerLine; sidePoint.normal[0] = normal.x; sidePoint.normal[1] = normal.y; sidePoint.normal[2] = normal.z; - sidePoint.value = _data[polyIndex].points[pointIndex].value; + if (_hasColorMapFile) { + sidePoint.value = _colorDataset.entries[pointCounter++].data[colorParamIndex]; + } + _verticies.push_back(sidePoint); } } // Add the top if (_addEdges) { - addTop(nPoints, topCenter, topNormal); + addTop(nPoints, pointCounter - nPoints, topCenter, topNormal, colorParamIndex); } // Indicies @@ -597,6 +665,11 @@ void RenderableTube::createSmoothTube(size_t nPolygons, size_t nPoints) { void RenderableTube::createLowPolyTube(size_t nPolygons, size_t nPoints) { // Verticies + int pointCounter = 0; + + // Get the selected color parameter + int colorParamIndex = currentColorParameterIndex(); + // Calciulate the normals for the top and bottom glm::dvec3 bottomCenter = _data.front().center; glm::dvec3 topCenter = _data.back().center; @@ -605,7 +678,7 @@ void RenderableTube::createLowPolyTube(size_t nPolygons, size_t nPoints) { // Add the bottom if (_addEdges) { - addBottom(nPoints, bottomCenter, bottomNormal); + addBottom(nPoints, pointCounter, bottomCenter, bottomNormal, colorParamIndex); } // Add all the polygons that will create the sides of the tube @@ -651,11 +724,18 @@ void RenderableTube::createLowPolyTube(size_t nPolygons, size_t nPoints) { sidePointTriangleV3.position[1] = v3.coordinate.y; sidePointTriangleV3.position[2] = v3.coordinate.z; - // Value - sidePointTriangleV0.value = v0.value; - sidePointTriangleV1.value = v1.value; - sidePointTriangleV2.value = v2.value; - sidePointTriangleV3.value = v3.value; + if (_hasColorMapFile) { + // Value + sidePointTriangleV0.value = + _colorDataset.entries[pointCounter].data[colorParamIndex]; + sidePointTriangleV1.value = + _colorDataset.entries[pointCounter + nPoints].data[colorParamIndex]; + sidePointTriangleV2.value = + _colorDataset.entries[isLast ? pointCounter + 1 : pointCounter + nPoints + 1].data[colorParamIndex]; + sidePointTriangleV3.value = + _colorDataset.entries[isLast ? pointCounter + 1 - nPoints : pointCounter + 1].data[colorParamIndex]; + ++pointCounter; + } // Normal sidePointTriangleV0.normal[0] = normal.x; @@ -684,7 +764,7 @@ void RenderableTube::createLowPolyTube(size_t nPolygons, size_t nPoints) { // Add the top if (_addEdges) { - addTop(nPoints, topCenter, topNormal); + addTop(nPoints, pointCounter, topCenter, topNormal, colorParamIndex); } // Indicies @@ -748,15 +828,20 @@ void RenderableTube::createLowPolyTube(size_t nPolygons, size_t nPoints) { } } -void RenderableTube::addBottom(size_t nPoints, const glm::dvec3& bottomCenter, - const glm::dvec3& bottomNormal) +void RenderableTube::addBottom(size_t nPoints, int pointCounter, + const glm::dvec3& bottomCenter, + const glm::dvec3& bottomNormal, int colorParamIndex) { // Calculate the transfer function value for the center point of the bottom + // Get the color for selected color parameter float bottomCenterValue = 0.f; - for (const TimePolygonPoint& timePolygonPoint : _data.front().points) { - bottomCenterValue += timePolygonPoint.value; + if (_hasColorMapFile) { + int loopPointCounter = pointCounter; + for (size_t pointIndex = 0; pointIndex < _data.front().points.size(); ++pointIndex) { + bottomCenterValue += _colorDataset.entries[loopPointCounter++].data[colorParamIndex]; + } + bottomCenterValue /= nPoints; } - bottomCenterValue /= nPoints; // Add the bottom's center point PolygonVertex bottomCenterPoint; @@ -768,35 +853,44 @@ void RenderableTube::addBottom(size_t nPoints, const glm::dvec3& bottomCenter, bottomCenterPoint.normal[1] = bottomNormal.y; bottomCenterPoint.normal[2] = bottomNormal.z; - bottomCenterPoint.value = bottomCenterValue; + if (_hasColorMapFile) { + bottomCenterPoint.value = bottomCenterValue; + } _verticies.push_back(bottomCenterPoint); // Add the bottom's sides with proper normals // This will ensure a hard shadow on the tube edge - for (const TimePolygonPoint& timePolygonPoint : _data.front().points) { + for (size_t pointIndex = 0; pointIndex < _data.front().points.size(); ++pointIndex) { PolygonVertex bottomSidePoint; - bottomSidePoint.position[0] = timePolygonPoint.coordinate.x; - bottomSidePoint.position[1] = timePolygonPoint.coordinate.y; - bottomSidePoint.position[2] = timePolygonPoint.coordinate.z; + bottomSidePoint.position[0] = _data.front().points[pointIndex].coordinate.x; + bottomSidePoint.position[1] = _data.front().points[pointIndex].coordinate.y; + bottomSidePoint.position[2] = _data.front().points[pointIndex].coordinate.z; bottomSidePoint.normal[0] = bottomNormal.x; bottomSidePoint.normal[1] = bottomNormal.y; bottomSidePoint.normal[2] = bottomNormal.z; - bottomSidePoint.value = timePolygonPoint.value; + if (_hasColorMapFile) { + bottomSidePoint.value = _colorDataset.entries[pointCounter++].data[colorParamIndex]; + } _verticies.push_back(bottomSidePoint); } } -void RenderableTube::addTop(size_t nPoints, const glm::dvec3& topCenter, - const glm::dvec3& topNormal) +void RenderableTube::addTop(size_t nPoints, int pointCounter, + const glm::dvec3& topCenter, + const glm::dvec3& topNormal, int colorParamIndex) { // Calculate the transfer function value for the center point of the top + // Get the color for selected color parameter float topCenterValue = 0.f; - for (const TimePolygonPoint& timePolygonPoint : _data.back().points) { - topCenterValue += timePolygonPoint.value; + if (_hasColorMapFile) { + int loopPointCounter = pointCounter; + for (const TimePolygonPoint& timePolygonPoint : _data.back().points) { + topCenterValue += _colorDataset.entries[loopPointCounter++].data[colorParamIndex]; + } + topCenterValue /= nPoints; } - topCenterValue /= nPoints; // Add the top's sides with proper normals // This will ensure a hard shadow on the tube edge @@ -810,7 +904,9 @@ void RenderableTube::addTop(size_t nPoints, const glm::dvec3& topCenter, topSidePoint.normal[1] = topNormal.y; topSidePoint.normal[2] = topNormal.z; - topSidePoint.value = timePolygonPoint.value; + if (_hasColorMapFile) { + topSidePoint.value = _colorDataset.entries[pointCounter++].data[colorParamIndex]; + } _verticies.push_back(topSidePoint); } @@ -824,7 +920,9 @@ void RenderableTube::addTop(size_t nPoints, const glm::dvec3& topCenter, topCenterPoint.normal[1] = topNormal.y; topCenterPoint.normal[2] = topNormal.z; - topCenterPoint.value = topCenterValue; + if (_hasColorMapFile) { + topCenterPoint.value = topCenterValue; + } _verticies.push_back(topCenterPoint); } @@ -854,6 +952,8 @@ void RenderableTube::render(const RenderData& data, RendererTasks&) { glm::dmat4 normalTransform = glm::transpose(glm::inverse(modelViewTransform)); // Uniforms + _shader->setUniform(_uniformCache.opacity, opacity()); + _shader->setUniform(_uniformCache.modelViewTransform, glm::mat4(modelViewTransform)); _shader->setUniform( _uniformCache.projectionTransform, @@ -861,9 +961,6 @@ void RenderableTube::render(const RenderData& data, RendererTasks&) { ); _shader->setUniform(_uniformCache.normalTransform, glm::mat3(normalTransform)); - _shader->setUniform(_uniformCache.color, _color.value()); - _shader->setUniform(_uniformCache.opacity, opacity()); - // Settings if (!_enableFaceCulling) { glDisable(GL_CULL_FACE); @@ -879,14 +976,7 @@ void RenderableTube::render(const RenderData& data, RendererTasks&) { #endif } - _shader->setUniform(_uniformCache.hasTransferFunction, _hasTransferFunction); - if (_hasTransferFunction) { - ghoul::opengl::TextureUnit transferFunctionUnit; - transferFunctionUnit.activate(); - _transferFunction->texture().bind(); - _shader->setUniform(_uniformCache.transferFunction, transferFunctionUnit); - } - + // Shading and light settings int nLightSources = 0; _lightIntensitiesBuffer.resize(_lightSources.size()); _lightDirectionsViewSpaceBuffer.resize(_lightSources.size()); @@ -918,6 +1008,57 @@ void RenderableTube::render(const RenderData& data, RendererTasks&) { _shader->setUniform(_uniformCache.specularIntensity, _shading.specularIntensity); } + // Colormap settings + bool useColorMap = _hasColorMapFile && _colorSettings.colorMapping->enabled && + _colorSettings.colorMapping->texture(); + _shader->setUniform(_uniformCache.useColorMap, useColorMap); + + _shader->setUniform(_uniformCache.color, _colorSettings.tubeColor); + + ghoul::opengl::TextureUnit colorMapTextureUnit; + _shader->setUniform(_uniformCache.colorMapTexture, colorMapTextureUnit); + + if (useColorMap) { + colorMapTextureUnit.activate(); + _colorSettings.colorMapping->texture()->bind(); + + const glm::vec2 range = _colorSettings.colorMapping->valueRange; + _shader->setUniform(_uniformCache.cmapRangeMin, range.x); + _shader->setUniform(_uniformCache.cmapRangeMax, range.y); + _shader->setUniform( + _uniformCache.hideOutsideRange, + _colorSettings.colorMapping->hideOutsideRange + ); + + _shader->setUniform( + _uniformCache.nanColor, + _colorSettings.colorMapping->nanColor + ); + _shader->setUniform( + _uniformCache.useNanColor, + _colorSettings.colorMapping->useNanColor + ); + + _shader->setUniform( + _uniformCache.aboveRangeColor, + _colorSettings.colorMapping->aboveRangeColor + ); + _shader->setUniform( + _uniformCache.useAboveRangeColor, + _colorSettings.colorMapping->useAboveRangeColor + ); + + _shader->setUniform( + _uniformCache.belowRangeColor, + _colorSettings.colorMapping->belowRangeColor + ); + _shader->setUniform( + _uniformCache.useBelowRangeColor, + _colorSettings.colorMapping->useBelowRangeColor + ); + } + + // Render glBindVertexArray(_vaoId); @@ -945,10 +1086,6 @@ void RenderableTube::render(const RenderData& data, RendererTasks&) { } void RenderableTube::update(const UpdateData& data) { - if (_hasTransferFunction) { - _transferFunction->update(); - } - if (_shader->isDirty()) { _shader->rebuildFromFile(); ghoul::opengl::updateUniformLocations(*_shader, _uniformCache, UniformNames); diff --git a/modules/base/rendering/renderabletube.h b/modules/base/rendering/renderabletube.h index 5ffc37d57c..fb53d9cace 100644 --- a/modules/base/rendering/renderabletube.h +++ b/modules/base/rendering/renderabletube.h @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include @@ -61,14 +61,6 @@ public: static documentation::Documentation Documentation(); private: - struct Shading : properties::PropertyOwner { - Shading(); - properties::BoolProperty enabled; - properties::FloatProperty ambientIntensity; - properties::FloatProperty diffuseIntensity; - properties::FloatProperty specularIntensity; - }; - struct PolygonVertex { GLfloat position[3]; GLfloat normal[3]; @@ -77,7 +69,6 @@ private: struct TimePolygonPoint { glm::dvec3 coordinate = glm::dvec3(0.0); - float value = 0.f; }; struct TimePolygon { @@ -87,33 +78,51 @@ private: std::vector points; }; + /// Find the index of the currently chosen color parameter in the data + int currentColorParameterIndex() const; + void readDataFile(); void updateTubeData(); void createSmoothTube(size_t nPolygons, size_t nPoints); void createLowPolyTube(size_t nPolygons, size_t nPoints); - void addBottom(size_t nPoints, const glm::dvec3& bottomCenter, - const glm::dvec3& bottomNormal); - void addTop(size_t nPoints, const glm::dvec3& bottomCenter, - const glm::dvec3& bottomNormal); + void addBottom(size_t nPoints, int pointCounter, const glm::dvec3& bottomCenter, + const glm::dvec3& bottomNormal, int colorParamIndex); + void addTop(size_t nPoints, int pointCounter, const glm::dvec3& bottomCenter, + const glm::dvec3& bottomNormal, int colorParamIndex); void updateBufferData(); // Properties - properties::StringProperty _transferFunctionPath; - properties::Vec3Property _color; properties::BoolProperty _enableFaceCulling; properties::PropertyOwner _lightSourcePropertyOwner; + + struct Shading : properties::PropertyOwner { + Shading(); + properties::BoolProperty enabled; + properties::FloatProperty ambientIntensity; + properties::FloatProperty diffuseIntensity; + properties::FloatProperty specularIntensity; + }; Shading _shading; + struct ColorSettings : properties::PropertyOwner { + explicit ColorSettings(const ghoul::Dictionary& dictionary); + properties::Vec3Property tubeColor; + std::unique_ptr colorMapping; + }; + ColorSettings _colorSettings; + properties::BoolProperty _addEdges; properties::BoolProperty _drawWireframe; properties::FloatProperty _wireLineWidth; properties::BoolProperty _useSmoothNormals; - UniformCache(modelViewTransform, projectionTransform, normalTransform, color, - opacity, hasTransferFunction, transferFunction, performShading, nLightSources, - lightDirectionsViewSpace, lightIntensities, ambientIntensity, diffuseIntensity, - specularIntensity) _uniformCache; + UniformCache(modelViewTransform, projectionTransform, normalTransform, opacity, color, + nanColor, useNanColor, aboveRangeColor, useAboveRangeColor, belowRangeColor, + useBelowRangeColor, useColorMap, colorMapTexture, cmapRangeMin, cmapRangeMax, + hideOutsideRange, performShading, nLightSources, lightDirectionsViewSpace, + lightIntensities, ambientIntensity, diffuseIntensity, + specularIntensity)_uniformCache; std::vector _lightIntensitiesBuffer; std::vector _lightDirectionsViewSpaceBuffer; @@ -121,8 +130,9 @@ private: std::filesystem::path _dataFile; std::vector _data; + dataloader::Dataset _colorDataset; bool _tubeIsDirty = false; - bool _hasTransferFunction = false; + bool _hasColorMapFile = false; std::unique_ptr _shader; GLuint _vaoId = 0; @@ -131,7 +141,7 @@ private: std::vector _verticies; std::vector _indicies; - std::shared_ptr _transferFunction; + }; } // namespace openspace diff --git a/modules/base/shaders/tube_fs.glsl b/modules/base/shaders/tube_fs.glsl index f6c61c8263..2e87a5714e 100644 --- a/modules/base/shaders/tube_fs.glsl +++ b/modules/base/shaders/tube_fs.glsl @@ -29,10 +29,23 @@ in vec3 vs_normal; in vec4 vs_positionViewSpace; in float vs_value; -uniform vec3 color; uniform float opacity; -uniform bool hasTransferFunction; -uniform sampler1D transferFunction; +uniform vec3 color; + +uniform vec4 nanColor = vec4(0.5); +uniform bool useNanColor = true; + +uniform vec4 aboveRangeColor; +uniform bool useAboveRangeColor; + +uniform vec4 belowRangeColor; +uniform bool useBelowRangeColor; + +uniform bool useColorMap; +uniform sampler1D colorMapTexture; +uniform float cmapRangeMin; +uniform float cmapRangeMax; +uniform bool hideOutsideRange; uniform bool performShading = true; uniform float ambientIntensity; @@ -47,39 +60,61 @@ uniform float lightIntensities[8]; const vec3 LightColor = vec3(1.0); const float SpecularPower = 100.0; +vec4 sampleColorMap(float dataValue) { + if (useNanColor && isnan(dataValue)) { + return nanColor; + } + + bool isOutside = dataValue < cmapRangeMin || dataValue > cmapRangeMax; + if (isnan(dataValue) || (hideOutsideRange && isOutside)) { + discard; + } + + if (useBelowRangeColor && dataValue < cmapRangeMin) { + return belowRangeColor; + } + + if (useAboveRangeColor && dataValue > cmapRangeMax) { + return aboveRangeColor; + } + + float t = (dataValue - cmapRangeMin) / (cmapRangeMax - cmapRangeMin); + t = clamp(t, 0.0, 1.0); + return texture(colorMapTexture, t); +} + Fragment getFragment() { if (opacity == 0.0) { discard; } Fragment frag; - frag.depth = vs_depth; - frag.gPosition = vs_positionViewSpace; - frag.gNormal = vec4(vs_normal, 0.0); - frag.disableLDR2HDR = true; - frag.color.a = opacity; - vec3 objectColor; - if (hasTransferFunction) { - vec4 tfColor = texture(transferFunction, vs_value); - objectColor = tfColor.rgb; - frag.color.a = opacity * tfColor.a; + // Color map + vec4 objectColor = vec4(1.0); + if (useColorMap) { + objectColor = sampleColorMap(vs_value); } else { - objectColor = color; + objectColor.rgb = color; + } + + objectColor.a *= opacity; + if (objectColor.a == 0.0) { + discard; } - //objectColor = color; if (performShading) { // Ambient light - vec3 totalLightColor = ambientIntensity * LightColor * objectColor; + vec3 totalLightColor = ambientIntensity * LightColor * objectColor.rgb; vec3 viewDirection = normalize(vs_positionViewSpace.xyz); + // Light sources for (int i = 0; i < nLightSources; ++i) { // Diffuse light vec3 lightDirection = lightDirectionsViewSpace[i]; float diffuseFactor = max(dot(vs_normal, lightDirection), 0.0); - vec3 diffuseColor = diffuseIntensity * LightColor * diffuseFactor * objectColor; + vec3 diffuseColor = diffuseIntensity * LightColor * diffuseFactor * objectColor.rgb; // Specular light vec3 reflectDirection = reflect(lightDirection, vs_normal); @@ -87,6 +122,7 @@ Fragment getFragment() { pow(max(dot(viewDirection, reflectDirection), 0.0), SpecularPower); vec3 specularColor = specularIntensity * LightColor * specularFactor; + // Total Light totalLightColor += lightIntensities[i] * (diffuseColor + specularColor); } frag.color.rgb = totalLightColor; @@ -95,6 +131,11 @@ Fragment getFragment() { frag.color.rgb = objectColor.rgb; } + frag.depth = vs_depth; + frag.gPosition = vs_positionViewSpace; + frag.gNormal = vec4(vs_normal, 0.0); + frag.disableLDR2HDR = true; + frag.color.a = opacity; return frag; }