Commit 45a42e7b authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Merge branch 'adv-raycaster' into 'development'

Adv raycaster

See merge request !108
parents 699e8ff7 6867e814
...@@ -235,14 +235,8 @@ namespace campvis { ...@@ -235,14 +235,8 @@ namespace campvis {
_mainWindow->deinit(); _mainWindow->deinit();
QuadRenderer::deinit(); QuadRenderer::deinit();
// deinit OpenGL and cgt
cgt::deinitGL();
} }
// MainWindow dtor needs a valid CampVisApplication, so we need to call it here instead of during destruction.
delete _mainWindow;
// now delete everything in the right order: // now delete everything in the right order:
for (std::vector<PipelineRecord>::iterator it = _pipelines.begin(); it != _pipelines.end(); ++it) { for (std::vector<PipelineRecord>::iterator it = _pipelines.begin(); it != _pipelines.end(); ++it) {
delete it->_painter; delete it->_painter;
...@@ -252,6 +246,12 @@ namespace campvis { ...@@ -252,6 +246,12 @@ namespace campvis {
delete *it; delete *it;
} }
{
// Deinit everything OpenGL using the local context.
cgt::GLContextScopedLock lock(_localContext);
cgt::deinitGL();
}
GLJobProc.stop(); GLJobProc.stop();
OpenGLJobProcessor::deinit(); OpenGLJobProcessor::deinit();
SimpleJobProcessor::deinit(); SimpleJobProcessor::deinit();
......
...@@ -90,6 +90,13 @@ namespace campvis { ...@@ -90,6 +90,13 @@ namespace campvis {
*/ */
virtual size_t getDimensionality() const = 0; virtual size_t getDimensionality() const = 0;
/**
* Returns the intensity domain where this TF has it's non-transparent parts.
* I.e. the minimum and the maximum intensity being opaque.
* \return The intensity domain where this TF has it's non-transparent parts.
*/
virtual cgt::vec2 getVisibilityDomain() const = 0;
/** /**
* Binds the transfer function OpenGL texture to the given texture and sets up uniforms. * Binds the transfer function OpenGL texture to the given texture and sets up uniforms.
* \note Calling thread must have a valid OpenGL context. * \note Calling thread must have a valid OpenGL context.
......
...@@ -59,6 +59,13 @@ namespace campvis { ...@@ -59,6 +59,13 @@ namespace campvis {
* Destructor, make sure to delete the OpenGL texture beforehand by calling deinit() with a valid OpenGL context! * Destructor, make sure to delete the OpenGL texture beforehand by calling deinit() with a valid OpenGL context!
*/ */
virtual ~GenericGeometryTransferFunction(); virtual ~GenericGeometryTransferFunction();
/**
* Returns the intensity domain where this TF has it's non-transparent parts.
* I.e. the minimum and the maximum intensity being opaque.
* \return cgt::vec2(0.f, 1.f)
*/
virtual cgt::vec2 getVisibilityDomain() const;
/** /**
* Initializes the Shader, hence, this methods has to be called from a thread with a valid OpenGL context! * Initializes the Shader, hence, this methods has to be called from a thread with a valid OpenGL context!
...@@ -128,6 +135,20 @@ namespace campvis { ...@@ -128,6 +135,20 @@ namespace campvis {
campvis::GenericGeometryTransferFunction<T>::~GenericGeometryTransferFunction() { campvis::GenericGeometryTransferFunction<T>::~GenericGeometryTransferFunction() {
} }
template<class T>
cgt::vec2 campvis::GenericGeometryTransferFunction<T>::getVisibilityDomain() const {
if (_geometries.empty())
return cgt::vec2(-1.f, -1.f);
else {
cgt::vec2 minmax(1.f, 0.f);
for (size_t i = 0; i < _geometries.size(); ++i) {
minmax.x = std::min(minmax.x, _geometries[i]->getIntensityDomain().x);
minmax.y = std::max(minmax.y, _geometries[i]->getIntensityDomain().y);
}
return minmax;
}
}
template<class T> template<class T>
void campvis::GenericGeometryTransferFunction<T>::initShader() { void campvis::GenericGeometryTransferFunction<T>::initShader() {
_shader = ShdrMgr.load("core/glsl/passthrough.vert", "core/glsl/passthrough.frag", ""); _shader = ShdrMgr.load("core/glsl/passthrough.vert", "core/glsl/passthrough.frag", "");
......
...@@ -103,5 +103,9 @@ namespace campvis { ...@@ -103,5 +103,9 @@ namespace campvis {
return _rightColor; return _rightColor;
} }
cgt::vec2 SimpleTransferFunction::getVisibilityDomain() const {
return cgt::vec2(0.f, 1.f);
}
} }
\ No newline at end of file
...@@ -58,6 +58,13 @@ namespace campvis { ...@@ -58,6 +58,13 @@ namespace campvis {
*/ */
virtual size_t getDimensionality() const; virtual size_t getDimensionality() const;
/**
* Returns the intensity domain where this TF has it's non-transparent parts.
* I.e. the minimum and the maximum intensity being opaque.
* \return cgt::vec2(0.f, 1.f)
*/
virtual cgt::vec2 getVisibilityDomain() const;
const cgt::col4& getLeftColor() const; const cgt::col4& getLeftColor() const;
void setLeftColor(const cgt::col4& color); void setLeftColor(const cgt::col4& color);
const cgt::col4& getRightColor() const; const cgt::col4& getRightColor() const;
......
...@@ -157,4 +157,8 @@ namespace campvis { ...@@ -157,4 +157,8 @@ namespace campvis {
_keyPoints.insert(lb, kp); _keyPoints.insert(lb, kp);
} }
cgt::vec2 TFGeometry1D::getIntensityDomain() const {
return cgt::vec2(_keyPoints.front()._position, _keyPoints.back()._position);
}
} }
\ No newline at end of file
...@@ -110,6 +110,11 @@ namespace campvis { ...@@ -110,6 +110,11 @@ namespace campvis {
*/ */
void addKeyPoint(float position, const cgt::col4& color); void addKeyPoint(float position, const cgt::col4& color);
/**
* Returns the intensity domain of this TFGeometry1D.
*/
cgt::vec2 getIntensityDomain() const;
/** /**
* Creates a simple quad geometry for the given interval. * Creates a simple quad geometry for the given interval.
* A quad geometry consists of two KeyPoints. * A quad geometry consists of two KeyPoints.
......
...@@ -129,6 +129,10 @@ namespace campvis { ...@@ -129,6 +129,10 @@ namespace campvis {
std::sort(_keyPoints.begin(), _keyPoints.end(), KeyPointSorter(_center._position)); std::sort(_keyPoints.begin(), _keyPoints.end(), KeyPointSorter(_center._position));
} }
cgt::vec2 TFGeometry2D::getIntensityDomain() const {
return cgt::vec2(_keyPoints.front()._position.x, _keyPoints.back()._position.x);
}
} }
\ No newline at end of file
...@@ -75,6 +75,11 @@ namespace campvis { ...@@ -75,6 +75,11 @@ namespace campvis {
* \return * \return
*/ */
std::vector<KeyPoint>& getKeyPoints(); std::vector<KeyPoint>& getKeyPoints();
/**
* Returns the intensity domain of this TFGeometry1D.
*/
cgt::vec2 getIntensityDomain() const;
/** /**
* Renders this transfer function geometry to the current active OpenGL context. * Renders this transfer function geometry to the current active OpenGL context.
......
...@@ -59,8 +59,9 @@ vec4 textureToWorld(in TextureParameters3D texParams, in vec4 texCoords) { ...@@ -59,8 +59,9 @@ vec4 textureToWorld(in TextureParameters3D texParams, in vec4 texCoords) {
* \param texCoords texture coordinates * \param texCoords texture coordinates
* \return \a texCoords transformes to woorld coordinates. * \return \a texCoords transformes to woorld coordinates.
*/ */
vec4 textureToWorld(in TextureParameters3D texParams, in vec3 texCoords) { vec3 textureToWorld(in TextureParameters3D texParams, in vec3 texCoords) {
return textureToWorld(texParams, vec4(texCoords, 1.0)); vec4 v = textureToWorld(texParams, vec4(texCoords, 1.0));
return v.xyz;
} }
/** /**
......
...@@ -44,11 +44,13 @@ namespace campvis { ...@@ -44,11 +44,13 @@ namespace campvis {
, p_inputImage("InputImage", "Input Image", "", DataNameProperty::READ) , p_inputImage("InputImage", "Input Image", "", DataNameProperty::READ)
, p_outputImage("OutputImage", "Output Image", "GlImageResampler.out", DataNameProperty::WRITE) , p_outputImage("OutputImage", "Output Image", "GlImageResampler.out", DataNameProperty::WRITE)
, p_resampleScale("ResampleScale", "Resampling Scale", .5f, .01f, 10.f) , p_resampleScale("ResampleScale", "Resampling Scale", .5f, .01f, 10.f)
, p_targetSize("TargetSize", "Size of Resampled Image", cgt::ivec3(128), cgt::ivec3(1), cgt::ivec3(1024))
, _shader(0) , _shader(0)
{ {
addProperty(p_inputImage); addProperty(p_inputImage, INVALID_RESULT | INVALID_PROPERTIES);
addProperty(p_outputImage); addProperty(p_outputImage);
addProperty(p_resampleScale); addProperty(p_resampleScale, INVALID_RESULT | INVALID_PROPERTIES);
addProperty(p_targetSize);
} }
GlImageResampler::~GlImageResampler() { GlImageResampler::~GlImageResampler() {
...@@ -73,7 +75,7 @@ namespace campvis { ...@@ -73,7 +75,7 @@ namespace campvis {
if (img != 0) { if (img != 0) {
cgt::vec3 originalSize(img->getSize()); cgt::vec3 originalSize(img->getSize());
cgt::ivec3 resampledSize(cgt::ceil(originalSize * p_resampleScale.getValue())); const cgt::ivec3& resampledSize = p_targetSize.getValue();
cgt::TextureUnit inputUnit; cgt::TextureUnit inputUnit;
inputUnit.activate(); inputUnit.activate();
...@@ -115,4 +117,14 @@ namespace campvis { ...@@ -115,4 +117,14 @@ namespace campvis {
} }
} }
void GlImageResampler::updateProperties(DataContainer& dataContainer) {
ImageRepresentationGL::ScopedRepresentation img(dataContainer, p_inputImage.getValue());
if (img != 0) {
p_targetSize.setMaxValue(cgt::ivec3(img->getSize()) * int(p_resampleScale.getMaxValue()));
p_targetSize.setValue(cgt::ivec3(cgt::vec3(img->getSize()) * p_resampleScale.getValue()));
}
}
} }
...@@ -72,11 +72,13 @@ namespace campvis { ...@@ -72,11 +72,13 @@ namespace campvis {
DataNameProperty p_outputImage; ///< ID for output gradient volume DataNameProperty p_outputImage; ///< ID for output gradient volume
FloatProperty p_resampleScale; ///< Resampling Scale FloatProperty p_resampleScale; ///< Resampling Scale
IVec3Property p_targetSize; ///< Size of resampled image
protected: protected:
/// \see AbstractProcessor::updateResult /// \see AbstractProcessor::updateResult
virtual void updateResult(DataContainer& dataContainer); virtual void updateResult(DataContainer& dataContainer);
virtual void updateProperties(DataContainer& dataContainer);
cgt::Shader* _shader; ///< Shader for resampling cgt::Shader* _shader; ///< Shader for resampling
static const std::string loggerCat_; static const std::string loggerCat_;
...@@ -85,3 +87,4 @@ namespace campvis { ...@@ -85,3 +87,4 @@ namespace campvis {
} }
#endif // GLIMAGERESAMPLER_H__ #endif // GLIMAGERESAMPLER_H__
...@@ -33,6 +33,8 @@ layout(location = 2) out vec4 out_FHN; ///< outgoing fragment first hit no ...@@ -33,6 +33,8 @@ layout(location = 2) out vec4 out_FHN; ///< outgoing fragment first hit no
#include "tools/texture3d.frag" #include "tools/texture3d.frag"
#include "tools/transferfunction.frag" #include "tools/transferfunction.frag"
#include "modules/vis/glsl/voxelhierarchy.frag"
uniform vec2 _viewportSizeRCP; uniform vec2 _viewportSizeRCP;
uniform float _jitterStepSizeMultiplier; uniform float _jitterStepSizeMultiplier;
...@@ -47,7 +49,7 @@ uniform sampler2D _exitPoints; ...@@ -47,7 +49,7 @@ uniform sampler2D _exitPoints;
uniform sampler2D _exitPointsDepth; uniform sampler2D _exitPointsDepth;
uniform TextureParameters2D _exitParams; uniform TextureParameters2D _exitParams;
// DRR volume // Input volume
uniform sampler3D _volume; uniform sampler3D _volume;
uniform TextureParameters3D _volumeTextureParams; uniform TextureParameters3D _volumeTextureParams;
...@@ -55,183 +57,35 @@ uniform TextureParameters3D _volumeTextureParams; ...@@ -55,183 +57,35 @@ uniform TextureParameters3D _volumeTextureParams;
uniform sampler1D _transferFunction; uniform sampler1D _transferFunction;
uniform TFParameters1D _transferFunctionParams; uniform TFParameters1D _transferFunctionParams;
// BBV Lookup volume
uniform usampler2D _vvTexture;
uniform int _vvVoxelSize;
uniform int _vvVoxelDepth;
uniform int _vvMaxMipMapLevel;
uniform LightSource _lightSource; uniform LightSource _lightSource;
uniform vec3 _cameraPosition; uniform vec3 _cameraPosition;
uniform float _samplingStepSize; uniform float _samplingStepSize;
#ifdef INTERSECTION_REFINEMENT
bool _inVoid = false;
#endif
#ifdef ENABLE_SHADOWING
uniform float _shadowIntensity;
#endif
// TODO: copy+paste from Voreen - eliminate or improve.
const float SAMPLING_BASE_INTERVAL_RCP = 200.0; const float SAMPLING_BASE_INTERVAL_RCP = 200.0;
// converting the sample coordinates [0, 1]^3 to integer voxel coordinate in l-level of the voxelized texture.
ivec3 voxelToBrick(in vec3 voxel, int level) {
int res = 1 << level;
return ivec3(floor(voxel.x / (_vvVoxelSize * res)), floor(voxel.y / (_vvVoxelSize * res)), floor(voxel.z / _vvVoxelDepth));
}
// Looks up the l mipmap level of voxelized texture of the render data and returns the value in samplePosition coordinates.
// samplePosition is in texture coordiantes [0, 1]
// level is the level in the hierarchy
uint lookupInBbv(in vec3 samplePosition, in int level) {
ivec3 byte = voxelToBrick(samplePosition * _volumeTextureParams._size, level);
uint texel = uint(texelFetch(_vvTexture, byte.xy, level).r);
return texel;
}
// Converts the ray to bitmask which can be intersect with voxelized volume
void rayMapToMask(in vec3 samplePos, in vec3 entryPoint, in vec3 direction, in int level, out uint mask, inout float tFar) {
int res = 1 << level ; //< the number of voxels from voxelized volume data which are mapped into
//< voxelized volume in l-th level of hierarchy
// calculating the voxel which the ray's origin will start from.
// BoxLlf is the lower-left corner of the voxel.
// BoxUrb is the upper-right corner of the voxel.
// NOTE: the z-direction coordinate is not important. So, we simply put BoxLlf.z = 0 and BoxUrb.z = 1. Because we are
// going to intersect the raybitmask with the bitmask of the voxel in z-direction to find the intersection point.
vec3 brickVoxel = floor((samplePos * _volumeTextureParams._size) / vec3(_vvVoxelSize * res, _vvVoxelSize * res, _vvVoxelDepth)) * vec3(_vvVoxelSize * res, _vvVoxelSize * res, _vvVoxelDepth);
vec3 boxLlf = vec3((brickVoxel * _volumeTextureParams._sizeRCP).xy, 0.0f);
vec3 boxUrb = vec3((boxLlf + (_volumeTextureParams._sizeRCP * vec3(_vvVoxelSize * res, _vvVoxelSize * res, _vvVoxelDepth))).xy, 1.0f);
// here, tmin and tmax for the ray which is intersecting with the voxel is computed.
vec3 tMin = (boxLlf - entryPoint) / direction;
vec3 tMax = (boxUrb - entryPoint) / direction;
// As tmin and tmax values are not necessary tmin and tmax values and value should be resorted, to find the
// exit point of the voxel we just compute minimum value of maximum values in tmin and tmax.
vec3 tFarVec = max(tMin, tMax);
tFar = min(tFar, min(tFarVec.x, min(tFarVec.y, tFarVec.z)));
// Here we are going to calculate the entry and exit point from the voxel by calculated tFar.
int bit1 = clamp(int(floor(samplePos.z * 32)), 0, 31);
int bit2 = clamp(int(floor((entryPoint.z + tFar * direction.z) * 32)), 0, 31);
// setting the bits of the bitmask values.
mask = bitfieldInsert(uint(0x0), uint(0xffffffff), max((min(bit1, bit2)), 0), min(abs(bit2 - bit1) + 1, 32));
// HACK HACK HACK
// right now, the bitmask generation for ray is not working correctly.
mask = 0xffffffff;
}
// intersects the ray bitmask and voxelized data bitmap.
// bitray: ray bitmask,
// texel: the texel coordinate in the voxelized volume data,
// level: the level of the texture,
// intersectionBitmask: the result bitmask of intersecting the data in texel of the voxelized volume data and ray bitmask.
// it returns wherther there was an intersection or not.
bool intersectBits(in uint bitRay, in vec3 texel, in int level, out uint intersectionBitmask) {
// Fetch bitmask from hierarchy and compute intersection via bitwise AND
intersectionBitmask = (bitRay & lookupInBbv(texel, level));
return (intersectionBitmask != uint(0));
}
//
bool intersectHierarchy(in int level, in vec3 samplePos, in vec3 entryPoint, in vec3 direction, inout float tFar, out uint intersectionBitmask) {
uint rayBitmask = uint(0);
// Mapping the ray to a bitmask
rayMapToMask(samplePos, entryPoint, direction, level, rayBitmask, tFar);
// Intersect the ray bitmask with current pixel
return intersectBits(rayBitmask, samplePos, level, intersectionBitmask);
}
/** /**
* Performs the raycasting and returns the final fragment color. * Performs the raycasting and returns the final fragment color.
*/ */
vec4 performRaycasting(in vec3 entryPoint, in vec3 exitPoint, in vec2 texCoords) { vec4 performRaycasting(in vec3 entryPoint, in vec3 exitPoint, in vec2 texCoords) {
vec4 result = vec4(0.0); vec4 result = vec4(0.0);
vec3 direction = exitPoint - entryPoint; vec3 direction = exitPoint - entryPoint;
float tNear = 0.0;
float tFar = length(direction); // Adjust direction a bit to prevent division by zero
direction = normalize(direction); direction.x = (abs(direction.x) < 0.0000001) ? 0.0000001 : direction.x;
direction.y = (abs(direction.y) < 0.0000001) ? 0.0000001 : direction.y;
direction.z = (abs(direction.z) < 0.0000001) ? 0.0000001 : direction.z;
OFFSET = (0.25 / (1 << _vhMaxMipMapLevel)); //< offset value used to avoid self-intersection or previous voxel intersection.
jitterEntryPoint(entryPoint, direction, _samplingStepSize * _jitterStepSizeMultiplier); jitterEntryPoint(entryPoint, direction, _samplingStepSize * _jitterStepSizeMultiplier);
float firstHitT = -1.0f; float firstHitT = -1.0f;
// compute sample position
vec3 samplePosition = entryPoint.rgb + tNear * direction;
bool intersectionFound = false;
uint intersectionBitmask = uint(0);
int level = min(3, _vvMaxMipMapLevel); //< start from a level of hierarchy. We are trying to start from one of the levels in middle (not coarse and not best).
float offset = (0.0025 / int(1 << int(_vvMaxMipMapLevel))) / length(direction); //< offset value used to avoid self-intersection or previous voxel intersection.
int i = 0;
float newTfar = 1.0f; //< tFar calculated for the point where the ray is going out of current texel in voxelized data.
while(!intersectionFound && (tNear <= tFar) && (level >= 0) && (level < _vvMaxMipMapLevel) && (i < 100)) {
newTfar = tFar; //< because it will be compared to calculated tFar in intersectHierarchy function.
// intersects the ray with the hierarchy of voxelized volume texture.
if(intersectHierarchy(level, samplePosition, entryPoint, direction, newTfar, intersectionBitmask)) {
// if the level is 0, the intersection is found, otherwise check the intersection in a higher level of hierarchy.
intersectionFound = (level == 0);
level--;
} else {
// The beginning part of the ray is cut and going to a coarser level of hierarchy.
tNear = newTfar + offset;
samplePosition = entryPoint + tNear * direction;
level++;
}
i++;
}
if(!intersectionFound) {
result = vec4(0, 0, 0, 0);
return result;
}
bool tFarIntersectionFound = false;
uint tFarIntersectionBitmask = uint(0);
int tFarLevel = min(3, _vvMaxMipMapLevel);
int k = 0;
float tFarDec = 0.0;
float newTnear = 1.0f;
vec3 tFarSamplePosition = exitPoint - tFarDec * direction;
while(!tFarIntersectionFound && (tFarDec <= tFar) && (tFarLevel >= 0) && (tFarLevel < _vvMaxMipMapLevel) && (k < 100)) {
newTnear = tFar;
if(intersectHierarchy(tFarLevel, tFarSamplePosition, exitPoint, -direction, newTnear, tFarIntersectionBitmask)) {
tFarIntersectionFound = (tFarLevel == 0);
tFarLevel--;
} else {
tFarDec = newTnear + offset;
tFarSamplePosition = exitPoint - tFarDec * direction;
tFarLevel++;
float tNear = clipFirstHitpoint(entryPoint, direction, 0.0, 1.0);
} float tFar = 1.0 - clipFirstHitpoint(exitPoint, -direction, 0.0, 1.0);
k++; // compute sample position
} vec3 samplePosition = entryPoint.rgb + tNear * direction;
if(!tFarIntersectionFound) {
result = vec4(0, 0, 0, 0);
return result;
}
tFar -= tFarDec;
while (tNear < tFar) { while (tNear < tFar) {
vec3 samplePosition = entryPoint.rgb + tNear * direction; vec3 samplePosition = entryPoint.rgb + tNear * direction;
...@@ -240,46 +94,13 @@ vec4 performRaycasting(in vec3 entryPoint, in vec3 exitPoint, in vec2 texCoords) ...@@ -240,46 +94,13 @@ vec4 performRaycasting(in vec3 entryPoint, in vec3 exitPoint, in vec2 texCoords)
float intensity = texture(_volume, samplePosition).r; float intensity = texture(_volume, samplePosition).r;
vec4 color = lookupTF(_transferFunction, _transferFunctionParams, intensity); vec4 color = lookupTF(_transferFunction, _transferFunctionParams, intensity);
#ifdef INTERSECTION_REFINEMENT
if (color.a <= 0.0) {
// we're within void, make the steps bigger
_inVoid = true;
} else {
if (_inVoid) {
float formerT = t - _samplingStepSize;
// we just left the void, perform intersection refinement
for (float stepPower = 0.5; stepPower > 0.1; stepPower /= 2.0) {
// compute refined sample position
float newT = formerT + _samplingStepSize * stepPower;
vec3 newSamplePosition = entryPoint.rgb + newT * direction;
// lookup refined intensity + TF
float newIntensity = texture(_volume, newSamplePosition).r;
vec4 newColor = lookupTF(_transferFunction, _transferFunctionParams, newIntensity);
if (newColor.a <= 0.0) {
// we're back in the void - look on the right-hand side
formerT = newT;
}
else {
// we're still in the matter - look on the left-hand side
samplePosition = newSamplePosition;
color = newColor;
t -= _samplingStepSize * stepPower;
}
}
_inVoid = false;
}
}
#endif
// perform compositing // perform compositing
if (color.a > 0.0) { if (color.a > 0.0) {
#ifdef ENABLE_SHADING #ifdef ENABLE_SHADING
// compute gradient (needed for shading and normals) // compute gradient (needed for shading and normals)
vec3 gradient = computeGradient(_volume, _volumeTextureParams, samplePosition); vec3 gradient = computeGradient(_volume, _volumeTextureParams, samplePosition);
color.