Commit 2b581529 authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge

Merge branch 'particle-visualization' into 'development'

Particle visualization

See merge request !95
parents fedbc913 f5cda4fc
......@@ -1322,6 +1322,16 @@ void Shader::setNormalizedAttribute(GLint index, const Vector4<GLuint>& v) {
glVertexAttrib4Nuiv(index, v.elem);
}
bool Shader::selectSubroutine(ShaderObject::ShaderType type, const std::string& subroutineName) {
GLuint index = glGetSubroutineIndex(id_, type, subroutineName.c_str());
if (index == GL_INVALID_INDEX) {
LWARNING("Failed to locate subroutine Location: " << subroutineName);
return false;
}
glUniformSubroutinesuiv(type, 1, &index);
return true;
}
//------------------------------------------------------------------------------
const string ShaderManager::loggerCat_("tgt.Shader.Manager");
......
......@@ -258,6 +258,10 @@ public:
std::string getLinkerLog() const;
// Subroutines
bool selectSubroutine(ShaderObject::ShaderType type, const std::string& subroutineName);
//
// Uniform stuff
//
......
......@@ -80,4 +80,8 @@ namespace tgt {
tgtAssert(false, "Could not find Vertex Attribute location for this BufferObject. You have to add it first using setVertexAttributePointer()!");
}
GLuint VertexArrayObject::getId() const {
return _id;
}
}
\ No newline at end of file
......@@ -30,6 +30,12 @@ namespace tgt {
*/
~VertexArrayObject();
/**
* Returns the OpneGL object ID
* \return _id
*/
GLuint getId() const;
/**
* Binds this VertexArrayObject to the current OpenGL context.
*/
......
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2014, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitaet Muenchen
// Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
//
// ================================================================================================
#include "tools/shading.frag"
#include "tools/transferfunction.frag"
in vec3 ex_Velocity;
in float ex_Transparency;
out vec4 out_Color; ///< outgoing fragment color
uniform sampler1D _transferFunction;
uniform TFParameters1D _transferFunctionParams;
uniform int _coloringMode;
uniform float _scale;
uniform vec2 _threshold;
void main() {
if (length(gl_PointCoord - vec2(0.5)) > 0.5) {
discard;
}
switch (_coloringMode) {
case 0: // age
out_Color = lookupTF(_transferFunction, _transferFunctionParams, ex_Transparency);
break;
case 1: // velocity
float f = length(ex_Velocity);
f -= _threshold.x;
f /= (_threshold.y - _threshold.x);
f *= _scale;
out_Color = lookupTF(_transferFunction, _transferFunctionParams, f);
break;
case 2: // direction
out_Color = vec4(normalize(abs(ex_Velocity)), 1.0);
break;
default:
discard;
}
#ifdef ENABLE_SHADING___
// compute gradient (needed for shading and normals)
vec3 gradient = ex_Normal;
out_Color.rgb = calculatePhongShading(ex_Position, _lightSource, _cameraPosition, gradient, _color.rgb, _color.rgb, vec3(1.0, 1.0, 1.0));
#endif
}
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2014, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitaet Muenchen
// Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
//
// ================================================================================================
#include "tools/texture3d.frag"
subroutine void RenderPassType();
subroutine uniform RenderPassType RenderPass;
layout(location = 0) in vec3 in_Position;
layout(location = 1) in vec3 in_Velocity;
layout(location = 2) in float in_StartTime;
layout(location = 3) in vec3 in_InitialPosition;
out vec3 ex_Position;
out vec3 ex_Velocity;
out float ex_StartTime;
out float ex_Transparency;
/// Matrix defining model-to-world transformation
uniform mat4 _modelMatrix = mat4(
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
/// Matrix defining view transformation
uniform mat4 _viewMatrix = mat4(
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
/// Matrix defining projection transformation
uniform mat4 _projectionMatrix = mat4(
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
uniform float _time;
uniform float _frameLength;
uniform float _lifetime;
uniform float _scale;
uniform vec2 _threshold;
uniform sampler3D _volume;
uniform TextureParameters3D _volumeTextureParams;
subroutine (RenderPassType)
void update() {
if (_time > in_StartTime) {
float age = _time - in_StartTime;
if (age > _lifetime || length(in_Velocity) < _threshold.x || length(in_Velocity) > _threshold.y) {
// particle expired, recycle
ex_Position = in_InitialPosition;
ex_Velocity = texture(_volume, (ex_Position / _volumeTextureParams._size).xyz).xyz;
ex_StartTime = _time;
}
else {
// particle alive, advance
ex_Position = in_Position + (in_Velocity * _frameLength * 0.05 * _scale);
// compute new velocity by mixture of old velocity and flow at current location, model some inertia
vec3 v = texture(_volume, (ex_Position / _volumeTextureParams._size).xyz).xyz;
ex_Velocity = mix(in_Velocity, v, 0.5);//smoothstep(0.5, 2.0, v/in_Velocity));
ex_StartTime = in_StartTime;
}
}
else {
ex_Position = in_Position;
ex_Velocity = in_Velocity;
ex_StartTime = in_StartTime;
}
}
subroutine (RenderPassType)
void render() {
float age = _time - in_StartTime;
ex_Transparency = (age >= 0.0) ? 1.0 - (age / _lifetime) : 0.0;
gl_Position = _projectionMatrix * (_viewMatrix * (_modelMatrix * vec4(in_Position, 1.0)));
//ex_Position = in_Position;
mat4 normalMatrix = transpose(inverse(_modelMatrix));
//vec4 normalTmp = (normalMatrix * vec4(in_Normal, 0.0));
// ex_Normal = normalize((normalTmp).xyz);
}
void main() {
// call the current subroutine
RenderPass();
}
......@@ -28,24 +28,35 @@
#include "core/classification/geometry1dtransferfunction.h"
#include "core/classification/tfgeometry1d.h"
#include "core/datastructures/imagedata.h"
namespace campvis {
static const GenericOption<std::string> viewSelectionOptions[2] = {
GenericOption<std::string>("arrows", "Arrows"),
GenericOption<std::string>("particles", "Particle Simulation"),
};
VectorFieldDemo::VectorFieldDemo(DataContainer* dc)
: AutoEvaluationPipeline(dc)
, _lsp()
, _imageReader()
, _vectorFieldReader()
, _pfr(&_canvasSize)
, _vectorFieldRenderer(&_canvasSize)
, _sliceRenderer(&_canvasSize)
, _rtc(&_canvasSize)
, p_camera("Camera", "Camera", tgt::Camera())
, p_sliceNumber("SliceNuber", "Slice Number", 0, 0, 1024)
, p_viewSelection("ViewSelection", "Select 3D View", viewSelectionOptions, 2)
, p_time("Time", "Time", 0, 0, 100)
, _trackballEH(0)
{
addProperty(p_camera);
addProperty(p_sliceNumber);
addProperty(p_viewSelection);
addProperty(p_time);
_trackballEH = new TrackballNavigationEventListener(&p_camera, &_canvasSize);
addEventListenerToBack(_trackballEH);
......@@ -53,6 +64,7 @@ namespace campvis {
addProcessor(&_lsp);
addProcessor(&_imageReader);
addProcessor(&_vectorFieldReader);
addProcessor(&_pfr);
addProcessor(&_vectorFieldRenderer);
addProcessor(&_sliceRenderer);
addProcessor(&_rtc);
......@@ -65,6 +77,7 @@ namespace campvis {
AutoEvaluationPipeline::init();
p_camera.addSharedProperty(&_vectorFieldRenderer.p_camera);
p_camera.addSharedProperty(&_pfr.p_camera);
p_camera.addSharedProperty(&_sliceRenderer.p_camera);
p_sliceNumber.addSharedProperty(&_vectorFieldRenderer.p_sliceNumber);
......@@ -78,9 +91,10 @@ namespace campvis {
_vectorFieldReader.p_url.setValue(CAMPVIS_SOURCE_DIR "/modules/vectorfield/sampledata/result_vec.mhd");
_vectorFieldReader.p_targetImageID.setValue("vectors");
_vectorFieldReader.p_targetImageID.addSharedProperty(&_pfr.p_inputVectors);
_vectorFieldReader.p_targetImageID.addSharedProperty(&_vectorFieldRenderer.p_inputVectors);
_vectorFieldRenderer.p_renderOutput.addSharedProperty(&_rtc.p_firstImageId);
_vectorFieldRenderer.p_renderOutput.setValue("arrows");
_vectorFieldRenderer.p_arrowSize.setValue(0.03f);
_vectorFieldRenderer.p_lenThresholdMin.setValue(100.f);
_vectorFieldRenderer.p_flowProfile1.setValue(0.4716088614374652f);
......@@ -90,24 +104,53 @@ namespace campvis {
_vectorFieldRenderer.p_lenThresholdMax.setValue(400.f);
_vectorFieldRenderer.p_sliceOrientation.setValue(3);
_pfr.p_lenThresholdMin.setValue(100.f);
_pfr.p_flowProfile1.setValue(0.4716088614374652f);
_pfr.p_flowProfile2.setValue(0.0638348311845516f);
_pfr.p_flowProfile3.setValue(0.1713471562960614f);
_pfr.p_flowProfile4.setValue(0.1019371804834016f);
_pfr.p_lenThresholdMax.setValue(400.f);
_pfr.p_renderOutput.setValue("particles");
_pfr.setEnabled(false);
Geometry1DTransferFunction* tf = new Geometry1DTransferFunction(128, tgt::vec2(0.f, 1.f));
tf->addGeometry(TFGeometry1D::createQuad(tgt::vec2(0.f, 1.f), tgt::col4(0, 0, 0, 255), tgt::col4(255, 255, 255, 255)));
_sliceRenderer.p_transferFunction.replaceTF(tf);
_sliceRenderer.p_targetImageID.setValue("slice");
_sliceRenderer.p_targetImageID.addSharedProperty(&_rtc.p_secondImageId);
_rtc.p_firstImageId.setValue("arrows");
_rtc.p_secondImageId.setValue("slice");
_rtc.p_compositingMethod.selectById("depth");
_rtc.p_targetImageId.setValue("composed");
_renderTargetID.setValue("composed");
p_time.addSharedProperty(&_vectorFieldRenderer.p_Time);
p_time.addSharedProperty(&_pfr.p_Time);
}
void VectorFieldDemo::onProcessorValidated(AbstractProcessor* processor) {
if (processor == &_imageReader) {
// update camera
ScopedTypedData<IHasWorldBounds> img(*_data, _sliceRenderer.p_sourceImageID.getValue());
ScopedTypedData<ImageData> img(*_data, _sliceRenderer.p_sourceImageID.getValue());
if (img) {
_trackballEH->reinitializeCamera(img);
p_sliceNumber.setMaxValue(static_cast<int>(img->getSize().z));
}
}
}
void VectorFieldDemo::onPropertyChanged(const AbstractProperty* prop) {
if (prop == &p_viewSelection) {
if (p_viewSelection.getOptionValue() == "arrows") {
_rtc.p_firstImageId.setValue("arrows");
_vectorFieldRenderer.setEnabled(true);
_pfr.setEnabled(false);
}
else if (p_viewSelection.getOptionValue() == "particles") {
_rtc.p_firstImageId.setValue("particles");
_vectorFieldRenderer.setEnabled(false);
_pfr.setEnabled(true);
}
}
}
......
......@@ -32,6 +32,7 @@
#include "modules/base/processors/lightsourceprovider.h"
#include "modules/io/processors/mhdimagereader.h"
#include "modules/vectorfield/processors/particleflowrenderer.h"
#include "modules/vectorfield/processors/vectorfieldrenderer.h"
#include "modules/vis/processors/slicerenderer3d.h"
#include "modules/vis/processors/rendertargetcompositor.h"
......@@ -65,15 +66,22 @@ namespace campvis {
*/
virtual void onProcessorValidated(AbstractProcessor* processor);
/// \see HasPropertyCollection::onPropertyChanged
virtual void onPropertyChanged(const AbstractProperty* prop);
LightSourceProvider _lsp;
MhdImageReader _imageReader;
MhdImageReader _vectorFieldReader;
ParticleFlowRenderer _pfr;
VectorFieldRenderer _vectorFieldRenderer;
SliceRenderer3D _sliceRenderer;
RenderTargetCompositor _rtc;
CameraProperty p_camera;
IntProperty p_sliceNumber;
GenericOptionProperty<std::string> p_viewSelection;
IntProperty p_time;
TrackballNavigationEventListener* _trackballEH;
};
......
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2014, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitaet Muenchen
// Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
//
// ================================================================================================
#include "particleflowrenderer.h"
#include "tgt/buffer.h"
#include "tgt/tgt_math.h"
#include "tgt/logmanager.h"
#include "tgt/shadermanager.h"
#include "tgt/textureunit.h"
#include "tgt/vertexarrayobject.h"
#include "core/classification/geometry1dtransferfunction.h"
#include "core/classification/tfgeometry1d.h"
#include "core/datastructures/imagedata.h"
#include "core/datastructures/imagerepresentationgl.h"
#include "core/datastructures/lightsourcedata.h"
#include "core/datastructures/geometrydatafactory.h"
#include "core/datastructures/renderdata.h"
namespace campvis {
static const GenericOption<ParticleFlowRenderer::ColoringMode> coloringModeOptions[3] = {
GenericOption<ParticleFlowRenderer::ColoringMode>("age", "Coloring by Age", ParticleFlowRenderer::COLORING_AGE),
GenericOption<ParticleFlowRenderer::ColoringMode>("velocity", "Coloring by Velocity", ParticleFlowRenderer::COLORING_VELOCITY),
GenericOption<ParticleFlowRenderer::ColoringMode>("direction", "Coloring by Direction", ParticleFlowRenderer::COLORING_DIRECTION)
};
const std::string ParticleFlowRenderer::loggerCat_ = "CAMPVis.modules.classification.ParticleFlowRenderer";
ParticleFlowRenderer::ParticleFlowRenderer(IVec2Property* viewportSizeProp)
: VisualizationProcessor(viewportSizeProp)
, p_resetButton("ResetButton", "Reset")
, p_inputVectors("InputImage", "Input Image Vectors", "vectors", DataNameProperty::READ)
, p_renderOutput("RenderOutput", "Output Image", "ParticleFlowRenderer.output", DataNameProperty::WRITE)
, p_lenThresholdMin("LenThresholdMin", "Length Threshold Min", .001f, 0.f, 1000.f, 0.005f)
, p_lenThresholdMax("LenThresholdMax", "Length Threshold Max", 10.f, 0.f, 10000.f, 10.f)
, p_numParticles("NumParticles", "Number of Particles", 2048, 32, 65536)
, p_lifetime("Lifetime", "Particle Lifetime", 10.f, 1.f, 100.f, 1.f, 1)
, p_flowProfile1("FlowSpline1", "Flow Profile - Spline 1", 1.f, .0f, 2.f)
, p_flowProfile2("FlowSpline2", "Flow Profile - Spline 2", 1.f, .0f, 2.f)
, p_flowProfile3("FlowSpline3", "Flow Profile - Spline 3", 1.f, .0f, 2.f)
, p_flowProfile4("FlowSpline4", "Flow Profile - Spline 4", 1.f, .0f, 2.f)
, p_Time("time", "Time", 0, 0, 100)
, p_pointSize("PointSize", "Point Size", 4, 1, 16)
, p_coloring("Coloring", "Color Scheme", coloringModeOptions, 3)
, p_transferFunction("TransferFunction", "Coloring Transfer Function", new Geometry1DTransferFunction(256))
, p_enableShading("EnableShading", "Enable Shading", true)
, p_lightId("LightId", "Input Light Source", "lightsource", DataNameProperty::READ)
, p_camera("Camera", "Camera", tgt::Camera())
, _shader(nullptr)
, _positionBufferA(nullptr)
, _positionBufferB(nullptr)
, _velocityBufferA(nullptr)
, _velocityBufferB(nullptr)
, _startTimeBufferA(nullptr)
, _startTimeBufferB(nullptr)
, _initialPositionBuffer(nullptr)
, _vaoA(nullptr)
, _vaoB(nullptr)
{
addProperty(p_resetButton, INVALID_PROPERTIES);
addProperty(p_Time, INVALID_RESULT | FIRST_FREE_TO_USE_INVALIDATION_LEVEL);
addProperty(p_inputVectors, INVALID_RESULT | INVALID_PROPERTIES);
addProperty(p_renderOutput);
addProperty(p_lenThresholdMin);
addProperty(p_lenThresholdMax);
addProperty(p_numParticles);
addProperty(p_lifetime);
addProperty(p_camera);
addProperty(p_flowProfile1);
addProperty(p_flowProfile2);
addProperty(p_flowProfile3);
addProperty(p_flowProfile4);
addProperty(p_pointSize);
addProperty(p_coloring);
addProperty(p_transferFunction);
addProperty(p_enableShading, INVALID_RESULT | INVALID_PROPERTIES | INVALID_SHADER);
addProperty(p_lightId);
static_cast<Geometry1DTransferFunction*>(p_transferFunction.getTF())->addGeometry(TFGeometry1D::createHeatedBodyColorMap());
}
ParticleFlowRenderer::~ParticleFlowRenderer() {
}
void ParticleFlowRenderer::init() {
VisualizationProcessor::init();
_shader = ShdrMgr.loadWithCustomGlslVersion("modules/vectorfield/glsl/particleflowrenderer.vert", "", "modules/vectorfield/glsl/particleflowrenderer.frag", generateGlslHeader(), "400");
const char* outputNames[] = { "ex_Position", "ex_Velocity", "ex_StartTime" };
glTransformFeedbackVaryings(_shader->getID(), 3, outputNames, GL_SEPARATE_ATTRIBS);
_shader->linkProgram();
tgtAssert(_shader->isLinked(), "Shader not linked!");
LGL_ERROR;
}
void ParticleFlowRenderer::deinit() {
ShdrMgr.dispose(_shader);
delete _positionBufferA;
delete _positionBufferB;
delete _velocityBufferA;
delete _velocityBufferB;
delete _startTimeBufferA;
delete _startTimeBufferB;
delete _initialPositionBuffer;
delete _vaoA;
delete _vaoB;
VisualizationProcessor::deinit();
}
void ParticleFlowRenderer::updateResult(DataContainer& dataContainer) {
const float frameLength = 0.1f;
if (_initialPositionBuffer == 0 ) {
LERROR("Transform-Feedback buffers not initialized.");
return;
}
ImageRepresentationGL::ScopedRepresentation vectors(dataContainer, p_inputVectors.getValue());
if (vectors) {
ScopedTypedData<LightSourceData> light(dataContainer, p_lightId.getValue());
if (p_enableShading.getValue() == false || light != nullptr) {
const tgt::Camera& cam = p_camera.getValue();
float scale = getTemporalFlowScaling((float)p_Time.getValue() / 100.f,
p_flowProfile1.getValue(),
p_flowProfile2.getValue(),
p_flowProfile3.getValue(),
p_flowProfile4.getValue());
glEnable(GL_DEPTH_TEST);
_shader->activate();
_shader->setUniform("_projectionMatrix", cam.getProjectionMatrix());
_shader->setUniform("_viewMatrix", cam.getViewMatrix());
_shader->setUniform("_modelMatrix", vectors->getParent()->getMappingInformation().getVoxelToWorldMatrix());
_shader->setUniform("_scale", scale);
_shader->setUniform("_threshold", tgt::vec2(p_lenThresholdMin.getValue(), p_lenThresholdMax.getValue()));
if (p_enableShading.getValue() && light != nullptr) {
light->bind(_shader, "_lightSource");
}
if (getInvalidationLevel() & FIRST_FREE_TO_USE_INVALIDATION_LEVEL) {
// stage 1: perform 1 step of particle simulation
_shader->selectSubroutine(tgt::ShaderObject::VERTEX_SHADER, "update");
_shader->setUniform("_time", _currentTime);
_shader->setUniform("_frameLength", frameLength);
_shader->setUniform("_lifetime", p_lifetime.getValue());
tgt::TextureUnit flowUnit;
vectors->bind(_shader, flowUnit, "_volume", "_volumeTextureParams");
glEnable(GL_RASTERIZER_DISCARD);
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, _feedback[_drawBuffer]);
glBeginTransformFeedback(GL_POINTS);
glBindVertexArray((_drawBuffer == 1) ? _vaoA->getId() : _vaoB->getId());
glDrawArrays(GL_POINTS, 0, _numParticles);
glEndTransformFeedback();
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
glDisable(GL_RASTERIZER_DISCARD);
_drawBuffer = 1 - _drawBuffer;
_currentTime += frameLength;
validate(FIRST_FREE_TO_USE_INVALIDATION_LEVEL);
LGL_ERROR;
}