diff --git a/modules/advancedraycasting/advancedraycasting.cmake b/modules/advancedraycasting/advancedraycasting.cmake index 9820ab0f51c3bea617e42ecc201c644867c0de1f..b8dc15bca10ddb40dccfcd69e28a42d42ad71429 100644 --- a/modules/advancedraycasting/advancedraycasting.cmake +++ b/modules/advancedraycasting/advancedraycasting.cmake @@ -4,7 +4,8 @@ SET(ThisModStatus EXPERIMENTAL) IF(${ModuleEnabled}) # Source files: - FILE(GLOB ThisModSources RELATIVE ${ModulesDir} + FILE(GLOB ThisModSources RELATIVE ${ModulesDir} + modules/advancedraycasting/decorators/*.cpp modules/advancedraycasting/pipelines/*.cpp modules/advancedraycasting/processors/*.cpp modules/advancedraycasting/tools/*.cpp @@ -17,7 +18,8 @@ IF(${ModuleEnabled}) modules/advancedraycasting/glsl/*.geom modules/advancedraycasting/glsl/*.vert modules/advancedraycasting/glsl/*.comp - modules/advancedraycasting/pipelines/*.h + modules/advancedraycasting/decorators/*.h + modules/advancedraycasting/pipelines/*.h modules/advancedraycasting/processors/*.h modules/advancedraycasting/tools/*.h ) diff --git a/modules/advancedraycasting/advancedraycasting.cpp b/modules/advancedraycasting/advancedraycasting.cpp index 9be2380793775f5c84c1fb00c67317c94662ad9e..7f9e40a5f979dea9651de8f5fa231e1661a9b055 100644 --- a/modules/advancedraycasting/advancedraycasting.cpp +++ b/modules/advancedraycasting/advancedraycasting.cpp @@ -25,13 +25,21 @@ #include "core/pipeline/pipelinefactory.h" #include "core/pipeline/processorfactory.h" +#include "modules/advancedraycasting/pipelines/preintegratedraycasterdemo.h" + +#include "modules/advancedraycasting/processors/ambientvolumegenerator.h" #include "modules/advancedraycasting/processors/laoraycaster.h" +#include "modules/advancedraycasting/processors/preintegratedraycaster.h" +#include "modules/advancedraycasting/processors/tfpreintegrator.h" namespace campvis { // explicitly instantiate templates to register the pipelines - //template class PipelineRegistrar; + template class PipelineRegistrar; + template class SmartProcessorRegistrar; template class SmartProcessorRegistrar; + template class SmartProcessorRegistrar; + template class SmartProcessorRegistrar; } diff --git a/modules/advancedraycasting/decorators/localambientocclusiondecorator.cpp b/modules/advancedraycasting/decorators/localambientocclusiondecorator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3221f857b16bdf66e484ba2180fdd7e478643549 --- /dev/null +++ b/modules/advancedraycasting/decorators/localambientocclusiondecorator.cpp @@ -0,0 +1,103 @@ +// ================================================================================================ +// +// This file is part of the CAMPVis Software Framework. +// +// If not explicitly stated otherwise: Copyright (C) 2012-2015, all rights reserved, +// Christian Schulte zu Berge +// 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 "localambientocclusiondecorator.h" + +#include "cgt/shadermanager.h" +#include "cgt/textureunit.h" +#include "core/properties/propertycollection.h" + +#include "core/datastructures/imagerepresentationgl.h" +#include "core/datastructures/renderdata.h" +#include "core/classification/geometry1dtransferfunction.h" + +namespace campvis { + + LocalAmbientOcclusionDecorator::LocalAmbientOcclusionDecorator() + : AbstractProcessorDecorator() + , p_aoRays("AORays", "Number of directional samples", 8, 0, 50) + , p_aoSamples("AOSamples", "Number of Samples per ray (controls size of AO sphere)", 10, 0, 80) + , p_aoSphereRadius("AOSphereRadius", "AO Sphere Radius [voxels]", 8.0f, 0.1f, 50.0f, 0.25f) + , p_aoOpacityScale("OpacityScale", "Opacity Scaling", 1.0f, 0.0f, 5.f) + , p_aoEffectGamma("AOEffectGamma", "AO Scale Gamma", 1.0f, .0f, 5.f) + , p_aoEmissiveTransferFunction("AOTransferFunction", "Emissive Transfer Function", new Geometry1DTransferFunction(128)) + , p_aoEmissiveScale("EmissiveScale", "Emissive Color Scaling", 1.0f, 0.0f, 10.f) + { + } + + LocalAmbientOcclusionDecorator::~LocalAmbientOcclusionDecorator() { + + } + + void LocalAmbientOcclusionDecorator::addProperties(AbstractProcessor* propCollection) { + propCollection->addProperty(p_aoRays, AbstractProcessor::INVALID_RESULT | AbstractProcessor::INVALID_PROPERTIES | AbstractProcessor::INVALID_SHADER); + propCollection->addProperty(p_aoSamples, AbstractProcessor::INVALID_RESULT | AbstractProcessor::INVALID_PROPERTIES | AbstractProcessor::INVALID_SHADER); + propCollection->addProperty(p_aoSphereRadius); + propCollection->addProperty(p_aoOpacityScale); + propCollection->addProperty(p_aoEffectGamma); + propCollection->addProperty(p_aoEmissiveTransferFunction); + propCollection->addProperty(p_aoEmissiveScale); + } + + void LocalAmbientOcclusionDecorator::renderProlog(const DataContainer& dataContainer, cgt::Shader* shader) { + auto aotf = p_aoEmissiveTransferFunction.getTF(); + _aoTFUnit = std::make_unique(); + aotf->bind(shader, *_aoTFUnit, "_aoEmissiveTF", "_aoEmissiveTFParams"); + + shader->setUniform("_aoSphereRadius", p_aoSphereRadius.getValue()); + shader->setUniform("_aoGamma", p_aoEffectGamma.getValue()); + shader->setUniform("_aoEmissiveScale", p_aoEmissiveScale.getValue()); + shader->setUniform("_aoOpacityScale", p_aoOpacityScale.getValue()); + } + + void LocalAmbientOcclusionDecorator::renderEpilog(cgt::Shader * shader) + { + // delete the TF texture unit + _aoTFUnit = nullptr; + } + + std::string LocalAmbientOcclusionDecorator::generateHeader() const + { + std::string toReturn; + // the defines need to exist before the include + toReturn += + "#define NUM_AO_RAYS " + std::to_string(p_aoRays.getValue()) + "\n" + "#define NUM_AO_RAY_STEPS " + std::to_string(p_aoSamples.getValue()) + "\n" + "\n" + "uniform float _aoSphereRadius;\n" + "uniform float _aoGamma;\n" + "uniform float _aoOpacityScale;\n" + "uniform float _aoEmissiveScale;\n" + "\n" + "#define AO_GAMMA _aoGamma\n" + "#define AO_OPACITY_SCALE _aoOpacityScale\n" + "#define AO_EMISSIVE_SCALE _aoEmissiveScale\n"; + + // the include has the actual functions + toReturn += "#include \"modules/advancedraycasting/glsl/localambientocclusion.frag\"\n"; + + return toReturn; + } + +} diff --git a/modules/advancedraycasting/decorators/localambientocclusiondecorator.h b/modules/advancedraycasting/decorators/localambientocclusiondecorator.h new file mode 100644 index 0000000000000000000000000000000000000000..767b29799a8b42a199777eb4dfe50b786fce7b49 --- /dev/null +++ b/modules/advancedraycasting/decorators/localambientocclusiondecorator.h @@ -0,0 +1,74 @@ +// ================================================================================================ +// +// This file is part of the CAMPVis Software Framework. +// +// If not explicitly stated otherwise: Copyright (C) 2012-2015, all rights reserved, +// Christian Schulte zu Berge +// 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. +// +// ================================================================================================ + +#ifndef LOCALAMBIENTOCCLUSIONDECORATOR_H__ +#define LOCALAMBIENTOCCLUSIONDECORATOR_H__ + +#include "core/pipeline/abstractprocessordecorator.h" +#include "core/properties/floatingpointproperty.h" +#include "core/properties/datanameproperty.h" +#include "core/properties/colorproperty.h" +#include "core/properties/optionproperty.h" +#include "core/properties/transferfunctionproperty.h" + +#include "modules/modulesapi.h" + +namespace cgt { + class TextureUnit; +} + +namespace campvis { + /** + * \class RaycastingGridDecorator + * Provides a fragment shader function that can test samples and modify their opacity valued to + */ + class CAMPVIS_MODULES_API LocalAmbientOcclusionDecorator : public AbstractProcessorDecorator { + public: + LocalAmbientOcclusionDecorator(); + virtual ~LocalAmbientOcclusionDecorator(); + + protected: + void addProperties(AbstractProcessor* propCollection) override; + + virtual void renderProlog(const DataContainer& dataContainer, cgt::Shader* shader) override; + + virtual void renderEpilog(cgt::Shader* shader) override; + + virtual std::string generateHeader() const override; + + IntProperty p_aoRays; ///< Number of directional AO samples + IntProperty p_aoSamples; ///< Number of steps per directional sample + FloatProperty p_aoSphereRadius; ///< The AO Sphere radius in voxels + FloatProperty p_aoEffectGamma; ///< Gamma controls the strength of the AO effect + FloatProperty p_aoOpacityScale; ///< Scales opacity when sampling for AO rays + + TransferFunctionProperty p_aoEmissiveTransferFunction; + FloatProperty p_aoEmissiveScale; ///< Scales the emissive color to increase/decrease the emissive effect + + std::unique_ptr _aoTFUnit; + }; + +} + +#endif // LOCALAMBIENTOCCLUSIONDECORATOR_H__ diff --git a/modules/advancedraycasting/glsl/ambientvolumegenerator.comp b/modules/advancedraycasting/glsl/ambientvolumegenerator.comp new file mode 100644 index 0000000000000000000000000000000000000000..4f68d26ee7be46763cfeae0fd1778a9caf008970 --- /dev/null +++ b/modules/advancedraycasting/glsl/ambientvolumegenerator.comp @@ -0,0 +1,92 @@ +// ================================================================================================ +// +// This file is part of the CAMPVis Software Framework. +// +// If not explicitly stated otherwise: Copyright (C) 2012-2015, all rights reserved, +// Christian Schulte zu Berge +// 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. +// +// ================================================================================================ + +// (c) 2017 Jakob Weiss + +// ==================== Shader for median filtering. ==================== +// Expects the following dynamic defines: +// +// #define OUTPUT_TEXTURE_FORMAT +// The texture format of the output texture. Preferrably specified through Texture::calcMatchingWriteFormat() +// +// uniform _outputImage +// dynamically defined (i|u)image(1|2|3)D uniform to allow imageStore operation of the output +// #define TEXTURE_DIMENSIONALITY +// The dimensionality of the texture [1,2,3] + +// The work group size can be overridden by dynamic defines +#ifndef WORK_GROUP_SIZE_X +#define WORK_GROUP_SIZE_X 1 +#endif +#ifndef WORK_GROUP_SIZE_Y +#define WORK_GROUP_SIZE_Y 1 +#endif +#ifndef WORK_GROUP_SIZE_Z +#define WORK_GROUP_SIZE_Z 1 +#endif + +#include "tools/transferfunction.frag" + + +// volume +uniform sampler3D _volume; +uniform TextureParameters3D _volumeTextureParams; + +// Transfer function +uniform sampler1D _transferFunction; +uniform TFParameters1D _transferFunctionParams; + +// Transfer function +uniform sampler1D _aoEmissiveTF; +uniform TFParameters1D _aoEmissiveTFParams; + +layout(local_size_x = WORK_GROUP_SIZE_X, local_size_y = WORK_GROUP_SIZE_Y, local_size_z = WORK_GROUP_SIZE_Z) in; + + +void main() { + // get index in global work group i.e x,y position + ivec3 pixel_coords = ivec3(gl_GlobalInvocationID.xyz); + vec3 samplePosition = vec3(pixel_coords)*_volumeTextureParams._sizeRCP; + + float sampleIntensity = texture(_volume, samplePosition).r; + vec4 sampleTFColor = lookupTF(_transferFunction, _transferFunctionParams, sampleIntensity); + + // precompute the LAO ray directions + vec4 aoRayDirs[NUM_AO_RAYS]; + initLAODirs(aoRayDirs, _aoSphereRadius, _volumeTextureParams); + + vec3 ambientOcclusion = computeLAO(samplePosition, aoRayDirs, _volume, _transferFunction, _transferFunctionParams, _aoEmissiveTF, _aoEmissiveTFParams); + + vec4 result = vec4(ambientOcclusion, sampleTFColor.a); + + // output to a specific pixel in the image + #if TEXTURE_DIMENSIONALITY == 1 + imageStore(_outputImage, pixel_coords.x, vec4(result)); + #elif TEXTURE_DIMENSIONALITY == 2 + imageStore(_outputImage, pixel_coords.xy, vec4(result)); + #else + imageStore(_outputImage, pixel_coords, vec4(result)); + #endif + +} diff --git a/modules/advancedraycasting/glsl/genericraycastingloop.frag b/modules/advancedraycasting/glsl/genericraycastingloop.frag new file mode 100644 index 0000000000000000000000000000000000000000..f730b866ece3586385aeb3d917b0b15438dcdf99 --- /dev/null +++ b/modules/advancedraycasting/glsl/genericraycastingloop.frag @@ -0,0 +1,124 @@ + + +// (c) 2017 Jakob Weiss + +/** + ** Prerequisites: ** + + - _samplingStepSize + - _jitterStepSizeMultiplier + - _lightSource + + - vec4 getSampleColor(vec3 sampleTexPos) + - float getSampleAlpha(vec3 sampleTexPos) + - vec3 getSampleGradient(vec3 sampleTexPos) + + If ENABLE_SHADOWING is set: + - _lightSource + - _shadowIntensity + + If ENABLE_SHADING is set: + - _lightSource + - _cameraPosition + + */ + +/** + * Performs the raycasting and returns the final fragment color. + */ +vec4 performRaycasting(in vec3 entryPoint, in vec3 exitPoint, in vec2 texCoords) { + vec4 result = vec4(0.0); + float firstHitT = -1.0; + + // calculate ray parameters + vec3 direction = exitPoint.rgb - entryPoint.rgb; + float t = 0.0; + float tend = length(direction); + direction = normalize(direction); + + jitterEntryPoint(entryPoint, direction, _samplingStepSize * _jitterStepSizeMultiplier); + +#ifdef ENABLE_SHADOWING + vec3 lightSourcePositionInTex = worldToTexture(_volumeTextureParams, _lightSource._position); +#endif + + while (t < tend) { + // compute sample position + vec3 samplePosition = entryPoint.rgb + t * direction; + + // lookup intensity and TF + vec4 color = getSampleColor(samplePosition); + +#ifdef ENABLE_SHADOWING + float shadowSamplingStepSize = 2 * _samplingStepSize; + // compute direction from sample to light + vec3 L = normalize(lightSourcePositionInTex - samplePosition) * shadowSamplingStepSize; + + // simple and expensive implementation of hard shadows + if (color.a > 0.01) { + bool finished = false; + + vec3 shadowSamplePosition = samplePosition + L; + jitterEntryPoint(shadowSamplePosition, L, _jitterStepSizeMultiplier); + + int lightSamples = SHADOW_STEPS; + + float shadowFactor = 0.0; + + // traverse ray from sample to light + while (! finished) { + // grab transparency for the shadow sample + shadowFactor += getSampleAlpha(shadowSamplePosition); + + shadowSamplePosition += L; + finished = //(shadowFactor > 0.95) + --lightSamples < 0 + || any(lessThan(shadowSamplePosition, vec3(0.0, 0.0, 0.0))) + || any(greaterThan(shadowSamplePosition, vec3(1.0, 1.0, 1.0))); + } + // apply shadow to color + color.rgb *= exp(- shadowFactor * shadowSamplingStepSize * _shadowIntensity * SAMPLING_BASE_INTERVAL_RCP); + } +#endif // ENABLE_SHADOWS + + // perform compositing + if (color.a > 0.0) { +#ifdef ENABLE_SHADING + // compute gradient (needed for shading and normals) + vec3 gradient = getSampleGradient(samplePosition); + vec4 worldPos = _volumeTextureParams._textureToWorldMatrix * vec4(samplePosition, 1.0); // calling textureToWorld here crashes Intel HD driver and nVidia driver in debug mode, hence, let's calc it manually... + vec3 normal = -normalize(vec4(gradient, 0.0)).xyz; // negated because gradient is along increasing intensity but we want a surface normal + color.rgb = calculatePhongShading(worldPos.xyz / worldPos.w, _lightSource, _cameraPosition, normal, color.rgb); +#endif + + // accomodate for variable sampling rates + color.a = 1.0 - pow(1.0 - color.a, _samplingStepSize * SAMPLING_BASE_INTERVAL_RCP); + blendUnder(result, color); + } + + // save first hit ray parameter for depth value calculation + if (firstHitT < 0.0 && result.a > 0.3) { + firstHitT = t; + out_FHP = vec4(samplePosition, 1.0); + out_FHN = vec4(-normalize(getSampleGradient(samplePosition)), 1.0); + } + + // early ray termination + if (result.a > 0.975) { + result.a = 1.0; + t = tend; + } + + // advance to the next evaluation point along the ray + t += _samplingStepSize; + } + + // calculate depth value from ray parameter + gl_FragDepth = 1.0; + if (firstHitT >= 0.0) { + float depthEntry = texture(_entryPointsDepth, texCoords).z; + float depthExit = texture(_exitPointsDepth, texCoords).z; + gl_FragDepth = calculateDepthValue(firstHitT/tend, depthEntry, depthExit); + } + return result; +} diff --git a/modules/advancedraycasting/glsl/laoraycaster.frag b/modules/advancedraycasting/glsl/laoraycaster.frag index d92db738107c054af38989e3094079aaa96cabbc..d7f231e5ea04dbbe693f7298cbafe3a8c7856e84 100644 --- a/modules/advancedraycasting/glsl/laoraycaster.frag +++ b/modules/advancedraycasting/glsl/laoraycaster.frag @@ -34,17 +34,6 @@ layout(location = 2) out vec4 out_FHN; ///< outgoing fragment first hit no #include "tools/transferfunction.frag" -uniform float _aoSphereRadius; -uniform float _aoGamma; -uniform float _aoOpacityScale; -uniform float _aoEmissiveScale; - -#define AO_GAMMA _aoGamma -#define AO_OPACITY_SCALE _aoOpacityScale -#define AO_EMISSIVE_SCALE _aoEmissiveScale - -#include "../../modules/advancedraycasting/glsl/localambientocclusion.frag" - uniform vec2 _viewportSizeRCP; uniform float _jitterStepSizeMultiplier; @@ -78,6 +67,7 @@ uniform float _samplingStepSize; const float SAMPLING_BASE_INTERVAL_RCP = 200.0; +//#define DEBUG_AO_DIRECTIONS /** * Performs the raycasting and returns the final fragment color. @@ -106,10 +96,39 @@ vec4 performRaycasting(in vec3 entryPoint, in vec3 exitPoint, in vec2 texCoords) float intensity = texture(_volume, samplePosition).r; vec4 color = lookupTF(_transferFunction, _transferFunctionParams, intensity); +#ifdef DEBUG_AO_DIRECTIONS + // allows to quickly check if the ray directions are uniformly distributed even for non-uniform volume/voxel sizes + color = vec4(0); + for(int i=0; i < NUM_AO_RAYS; ++i) { + //vec3 dir = nthSphereSample(1.0, i, NUM_AO_RAYS); + vec4 dir = aoRayDirs[i]; + vec3 p = samplePosition - vec3(0.5);// + dir.xyz*200; + float d = 1. - clamp(dot(normalize(p), normalize(dir.xyz)), 0, 1); + float r = length(p) - 10*length(dir.xyz); + //r *= smoothstep(0.4, 0.5, length(p)); + float a= clamp(exp(-d*d/0.01) * exp(-r*r/0.001), 0, 1); + + if(r > 0 && color.a < a) { + color.rgb = abs(normalize(dir.xyz)); + color.a = a; + } + } + + // accomodate for variable sampling rates + color.a = 1.0 - pow(1.0 - color.a, _samplingStepSize * SAMPLING_BASE_INTERVAL_RCP); + blendUnder(result, color); + +#else + // perform compositing if (color.a > 0.0) { vec3 n; vec3 ambientOcclusion = computeLAO(samplePosition, aoRayDirs, _volume, _transferFunction, _transferFunctionParams, _aoEmissiveTF, _aoEmissiveTFParams, n); + vec4 aoSampleColor = lookupTF(_aoEmissiveTF, _aoEmissiveTFParams, intensity); + // emissive transfer function overrides the original TF color + if(aoSampleColor.a > 0) { + color.rgb = aoSampleColor.rgb; + } #ifdef ENABLE_SHADING // compute gradient (needed for shading and normals) @@ -119,15 +138,19 @@ vec4 performRaycasting(in vec3 entryPoint, in vec3 exitPoint, in vec2 texCoords) vec3 normal = -normalize(_volumeTextureParams._textureToWorldMatrixInvTransp * vec4(n, 0.0)).xyz; // negated because gradient is along increasing intensity but we want a surface normal color.rgb = calculatePhongShading(worldPos.xyz / worldPos.w, ambientOcclusion, _lightSource, _cameraPosition, normal, color.rgb); #else - color.rgb *= ambientOcclusion; + color.rgb *= ambientOcclusion;//*normalize(abs(n)); #endif - +// With this enabled, raycaster will show the influence of the AO comptuation. useful for parameter tuning and debugging +#ifdef SHOW_AO_ONLY + color.rgb = ambientOcclusion; +#endif // accomodate for variable sampling rates color.a = 1.0 - pow(1.0 - color.a, _samplingStepSize * SAMPLING_BASE_INTERVAL_RCP); blendUnder(result, color); } +#endif // save first hit ray parameter for depth value calculation if (firstHitT < 0.0 && result.a > 0.0) { diff --git a/modules/advancedraycasting/glsl/localambientocclusion.frag b/modules/advancedraycasting/glsl/localambientocclusion.frag index 36e9608bddbfa3328f12204f71adbad3a1ba699e..866295476a5b9fa18b69ed71285b3b29cecd93cd 100644 --- a/modules/advancedraycasting/glsl/localambientocclusion.frag +++ b/modules/advancedraycasting/glsl/localambientocclusion.frag @@ -25,6 +25,7 @@ // (c) 2017 Jakob Weiss #include "tools/transferfunction.frag" +#include "tools/texture3d.frag" // The number of rays in each direction #ifndef NUM_AO_RAYS @@ -77,16 +78,12 @@ vec4 computeLAORay(vec3 pos, vec3 dir, float aoStepSize, sampler3D tex, sampler1 float aoSampleOpacity = aoSampleColor.a * AO_OPACITY_SCALE; // Volume tf sample is used for it's opacity value vec4 aoSampleEmissive = lookupTF(aoEmissiveTF, aoEmissiveTFParams, aoIntensity); // AO sample is used for the material's emissive color - //testing - aoSampleEmissive.rgb = aoSampleColor.rgb*aoSampleEmissive.a; - // compensate step size aoSampleOpacity = 1.0 - pow(1.0 - aoSampleOpacity, AO_GAMMA * aoStepSize * AO_SAMPLING_BASE_INTERVAL_RCP); - //aoSampleEmissive.rgb *= aoStepSize * AO_SAMPLING_BASE_INTERVAL_RCP * aoSampleEmissive.a; // ao emissive color is multiplied by alpha for controlling the intensity + aoSampleEmissive.rgb *= aoStepSize * AO_SAMPLING_BASE_INTERVAL_RCP * aoSampleEmissive.a; // ao emissive color is multiplied by alpha for controlling the intensity - // accumulate + // accumulate aoColor.rgb += (vec3(1) + AO_EMISSIVE_SCALE * aoSampleEmissive.rgb) * aoColor.a; - aoColor.a *= (1 - aoSampleOpacity); aoRayPos += aoStep; @@ -95,13 +92,22 @@ vec4 computeLAORay(vec3 pos, vec3 dir, float aoStepSize, sampler3D tex, sampler1 return aoColor; } -void initLAODirs(inout vec4 aoRayDirs[NUM_AO_RAYS], float aoSphereRadius, TextureParameters3D volumeParameters) { +void initLAODirs(inout vec4 aoRayDirs[NUM_AO_RAYS], float aoSphereRadius, TextureParameters3D volumeParameters) +{ //Normalize for anisotropic voxel sizes - vec3 anisotropicVoxel = volumeParameters._sizeRCP;// * volumeParameters._voxelSizeRCP; + vec3 anisotropicVoxel = volumeParameters._sizeRCP * volumeParameters._voxelSizeRCP; vec3 anisotropicRayStep = anisotropicVoxel / NUM_AO_RAY_STEPS; + float voxelSize = length(volumeParameters._sizeRCP) / sqrt(3); + float scale = aoSphereRadius * voxelSize / NUM_AO_RAY_STEPS; + for(int i=0; i < NUM_AO_RAYS; ++i) { - aoRayDirs[i].rgb = anisotropicRayStep * nthSphereSample(aoSphereRadius, i, NUM_AO_RAYS); - aoRayDirs[i].a = length(aoRayDirs[i].rgb); + vec4 dir = vec4( nthSphereSample(1.0, i, NUM_AO_RAYS), 0.0); + aoRayDirs[i].xyz = scale * normalize((volumeParameters._worldToTextureMatrixInvTransp * dir).xyz); + + //this is somehow slower to compute... + //aoRayDirs[i].xyz = anisotropicRayStep * nthSphereSample(aoSphereRadius, i, NUM_AO_RAYS); + + aoRayDirs[i].w = length(aoRayDirs[i].xyz); } } @@ -114,10 +120,10 @@ vec3 computeLAO(vec3 position, in vec4 aoRayDirs[NUM_AO_RAYS], sampler3D tex, sa for(int i=0; i < NUM_AO_RAYS; ++i) { //vec3 dir = nthSphereSample(1.0, i, NUM_AO_RAYS); vec4 dir = aoRayDirs[i]; - vec4 aoRayColor = computeLAORay(position, dir.rgb, dir.a, tex, volumeTF, volumeTFParams, aoEmissiveTF, aoEmissiveTFParams); + vec4 aoRayColor = computeLAORay(position, dir.xyz, dir.w, tex, volumeTF, volumeTFParams, aoEmissiveTF, aoEmissiveTFParams); ambient += aoRayColor.rgb; - minOcclusionDir += (1.- aoRayColor.a) * dir.rgb; + minOcclusionDir += (1.- aoRayColor.a) * dir.xyz; } minOcclusionDir = normalize(minOcclusionDir); diff --git a/modules/advancedraycasting/glsl/preintegratedraycaster.frag b/modules/advancedraycasting/glsl/preintegratedraycaster.frag new file mode 100644 index 0000000000000000000000000000000000000000..e7f20d5562ad7351e4575312a23322d380f890a4 --- /dev/null +++ b/modules/advancedraycasting/glsl/preintegratedraycaster.frag @@ -0,0 +1,106 @@ +// ================================================================================================ +// +// This file is part of the CAMPVis Software Framework. +// +// If not explicitly stated otherwise: Copyright (C) 2012-2015, all rights reserved, +// Christian Schulte zu Berge +// 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. +// +// ================================================================================================ + +layout(location = 0) out vec4 out_Color; ///< outgoing fragment color +layout(location = 1) out vec4 out_FHP; ///< outgoing fragment first hitpoint +layout(location = 2) out vec4 out_FHN; ///< outgoing fragment first hit normal + +#include "tools/gradient.frag" +#include "tools/raycasting.frag" +#include "tools/shading.frag" +#include "tools/texture2d.frag" +#include "tools/texture3d.frag" + +uniform vec2 _viewportSizeRCP; +uniform float _jitterStepSizeMultiplier; + + +// ray entry points +uniform sampler2D _entryPoints; +uniform sampler2D _entryPointsDepth; +uniform TextureParameters2D _entryParams; + +// ray exit points +uniform sampler2D _exitPoints; +uniform sampler2D _exitPointsDepth; +uniform TextureParameters2D _exitParams; + +// pre-integrated volume +uniform sampler3D _volume; +uniform TextureParameters3D _volumeTextureParams; + + +uniform LightSource _lightSource; +uniform vec3 _cameraPosition; + +uniform float _samplingStepSize; + +#ifdef ENABLE_SHADOWING +uniform float _shadowIntensity; +#endif + +const float SAMPLING_BASE_INTERVAL_RCP = 200.0; + +float getSampleIntensity(vec3 pos) { + return texture(_volume, pos).r; +} + +vec4 getSampleColor(vec3 pos) { + return texture(_volume, pos); +} + +float getSampleAlpha(vec3 pos) { + return getSampleColor(pos).a; +} + +vec3 getSampleGradient(vec3 pos) { + + float dx = textureOffset(_volume, pos, ivec3(1, 0, 0)).a; + float dy = textureOffset(_volume, pos, ivec3(0, 1, 0)).a; + float dz = textureOffset(_volume, pos, ivec3(0, 0, 1)).a; + float mdx = textureOffset(_volume, pos, ivec3(-1, 0, 0)).a; + float mdy = textureOffset(_volume, pos, ivec3(0, -1, 0)).a; + float mdz = textureOffset(_volume, pos, ivec3(0, 0, -1)).a; + return vec3(dx - mdx, dy - mdy, dz - mdz) * _volumeTextureParams._voxelSizeRCP * 0.5; +} + +#include "modules/advancedraycasting/glsl/genericraycastingloop.frag" + +/*** + * The main method. + ***/ +void main() { + vec2 p = gl_FragCoord.xy * _viewportSizeRCP; + vec3 frontPos = texture(_entryPoints, p).rgb; + vec3 backPos = texture(_exitPoints, p).rgb; + + //determine whether the ray has to be casted + if (frontPos == backPos) { + //background need no raycasting + discard; + } else { + //fragCoords are lying inside the boundingbox + out_Color = performRaycasting(frontPos, backPos, p); + } +} diff --git a/modules/advancedraycasting/glsl/tfpreintegrator.comp b/modules/advancedraycasting/glsl/tfpreintegrator.comp new file mode 100644 index 0000000000000000000000000000000000000000..b420c107c3d9a2c5e917a3179c22a644d83865b4 --- /dev/null +++ b/modules/advancedraycasting/glsl/tfpreintegrator.comp @@ -0,0 +1,77 @@ +// ================================================================================================ +// +// This file is part of the CAMPVis Software Framework. +// +// If not explicitly stated otherwise: Copyright (C) 2012-2015, all rights reserved, +// Christian Schulte zu Berge +// 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. +// +// ================================================================================================ + +// (c) 2017 Jakob Weiss + +// ==================== Shader for median filtering. ==================== +// Expects the following dynamic defines: +// +// #define OUTPUT_TEXTURE_FORMAT +// The texture format of the output texture. Preferrably specified through Texture::calcMatchingWriteFormat() +// +// uniform _outputImage +// dynamically defined (i|u)image(1|2|3)D uniform to allow imageStore operation of the output +// #define TEXTURE_DIMENSIONALITY +// The dimensionality of the texture [1,2,3] + +// The work group size can be overridden by dynamic defines +#ifndef WORK_GROUP_SIZE_X +#define WORK_GROUP_SIZE_X 1 +#endif +#ifndef WORK_GROUP_SIZE_Y +#define WORK_GROUP_SIZE_Y 1 +#endif +#ifndef WORK_GROUP_SIZE_Z +#define WORK_GROUP_SIZE_Z 1 +#endif + + +layout(local_size_x = WORK_GROUP_SIZE_X, local_size_y = WORK_GROUP_SIZE_Y, local_size_z = WORK_GROUP_SIZE_Z) in; + + +uniform sampler3D _texture; + +#include "tools/transferfunction.frag" +// Transfer function +uniform sampler1D _transferFunction; +uniform TFParameters1D _transferFunctionParams; + + +void main() { + // get index in global work group i.e x,y position + ivec3 pixel_coords = ivec3(gl_GlobalInvocationID.xyz); + vec4 pixel = texelFetch(_texture, pixel_coords, 0); + + vec4 result = lookupTF(_transferFunction, _transferFunctionParams, pixel.r); + + // output to a specific pixel in the image + #if TEXTURE_DIMENSIONALITY == 1 + imageStore(_outputImage, pixel_coords.x, vec4(result)); + #elif TEXTURE_DIMENSIONALITY == 2 + imageStore(_outputImage, pixel_coords.xy, vec4(result)); + #else + imageStore(_outputImage, pixel_coords, vec4(result)); + #endif + +} diff --git a/modules/advancedraycasting/pipelines/preintegratedraycasterdemo.cpp b/modules/advancedraycasting/pipelines/preintegratedraycasterdemo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d8b2f42ad2d5277f5e88417615f1017bd53979e5 --- /dev/null +++ b/modules/advancedraycasting/pipelines/preintegratedraycasterdemo.cpp @@ -0,0 +1,85 @@ +// ================================================================================================ +// +// This file is part of the CAMPVis Software Framework. +// +// If not explicitly stated otherwise: Copyright (C) 2012-2015, all rights reserved, +// Christian Schulte zu Berge +// 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 "preintegratedraycasterdemo.h" + +#include "cgt/event/keyevent.h" +#include "core/datastructures/imagedata.h" + +#include "core/classification/geometry1dtransferfunction.h" +#include "core/classification/tfgeometry1d.h" + +namespace campvis { + + PreintegratedRayCasterDemo::PreintegratedRayCasterDemo(DataContainer& dc) + : AutoEvaluationPipeline(dc, getId()) + , _tcp(&_canvasSize) + , _lsp() + , _imageReader() + , _pgg() + , _eep(&_canvasSize) + , _integrator() + , _raycaster(&_canvasSize) + { + _tcp.addLqModeProcessor(&_raycaster); + addEventListenerToBack(&_tcp); + + addProcessor(&_tcp); + addProcessor(&_lsp); + addProcessor(&_imageReader); + addProcessor(&_pgg); + addProcessor(&_eep); + addProcessor(&_integrator); + addProcessor(&_raycaster); + } + + PreintegratedRayCasterDemo::~PreintegratedRayCasterDemo() { + } + + void PreintegratedRayCasterDemo::init() { + AutoEvaluationPipeline::init(); + + _imageReader.p_url.setValue(ShdrMgr.completePath("/modules/vis/sampledata/smallHeart.mhd")); + _imageReader.p_targetImageID.setValue("reader.output"); + _imageReader.p_targetImageID.addSharedProperty(&_tcp.p_image); + _imageReader.p_targetImageID.addSharedProperty(&_pgg.p_sourceImageID); + _imageReader.p_targetImageID.addSharedProperty(&_eep.p_sourceImageID); + _imageReader.p_targetImageID.addSharedProperty(&_integrator.p_inputImage); + + _integrator.p_outputImage.addSharedProperty(&_raycaster.p_sourceImageID); + + _eep.p_entryImageID.addSharedProperty(&_raycaster.p_entryImageID); + _eep.p_exitImageID.addSharedProperty(&_raycaster.p_exitImageID); + + _raycaster.p_targetImageID.addSharedProperty(&_renderTargetID); + _raycaster.p_targetImageID.setValue("raycaster.final"); + + Geometry1DTransferFunction* dvrTF = new Geometry1DTransferFunction(128, cgt::vec2(0.f, .05f)); + dvrTF->addGeometry(TFGeometry1D::createQuad(cgt::vec2(.12f, .15f), cgt::col4(85, 0, 0, 128), cgt::col4(255, 0, 0, 128))); + dvrTF->addGeometry(TFGeometry1D::createQuad(cgt::vec2(.19f, .28f), cgt::col4(89, 89, 89, 155), cgt::col4(89, 89, 89, 155))); + dvrTF->addGeometry(TFGeometry1D::createQuad(cgt::vec2(.41f, .51f), cgt::col4(170, 170, 128, 64), cgt::col4(192, 192, 128, 64))); + _integrator.p_transferFunction.replaceTF(dvrTF); + } + +} diff --git a/modules/advancedraycasting/pipelines/preintegratedraycasterdemo.h b/modules/advancedraycasting/pipelines/preintegratedraycasterdemo.h new file mode 100644 index 0000000000000000000000000000000000000000..13104acc3759082dd056956410e37e5b1a1ef6b5 --- /dev/null +++ b/modules/advancedraycasting/pipelines/preintegratedraycasterdemo.h @@ -0,0 +1,72 @@ +// ================================================================================================ +// +// This file is part of the CAMPVis Software Framework. +// +// If not explicitly stated otherwise: Copyright (C) 2012-2015, all rights reserved, +// Christian Schulte zu Berge +// 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. +// +// ================================================================================================ + +#ifndef PREINTEGRATEDRAYCASTERDEMO_H__ +#define PREINTEGRATEDRAYCASTERDEMO_H__ + +#include "core/pipeline/autoevaluationpipeline.h" +#include "modules/base/processors/trackballcameraprovider.h" + +#include "modules/modulesapi.h" +#include "modules/base/processors/lightsourceprovider.h" +#include "modules/io/processors/mhdimagereader.h" +#include "modules/vis/processors/proxygeometrygenerator.h" +#include "modules/vis/processors/eepgenerator.h" +#include "modules/advancedraycasting/processors/tfpreintegrator.h" +#include "modules/advancedraycasting/processors/preintegratedraycaster.h" + +namespace campvis { + class CAMPVIS_MODULES_API PreintegratedRayCasterDemo : public AutoEvaluationPipeline { + public: + /** + * Creates a VolumeRendererDemopipeline. + * \param dataContainer Reference to the DataContainer containing local working set of data + * for this pipeline, must be valid the whole lifetime of this pipeline. + */ + explicit PreintegratedRayCasterDemo(DataContainer& dc); + + /** + * Virtual Destructor + **/ + virtual ~PreintegratedRayCasterDemo(); + + /// \see AutoEvaluationPipeline::init() + virtual void init(); + + /// \see AbstractPipeline::getId() + static const std::string getId() { return "PreintegratedRCDemo"; }; + + protected: + + TrackballCameraProvider _tcp; + LightSourceProvider _lsp; + MhdImageReader _imageReader; + ProxyGeometryGenerator _pgg; + EEPGenerator _eep; + TFPreIntegrator _integrator; + PreintegratedRaycaster _raycaster; + }; +} + +#endif // PREINTEGRATEDRAYCASTERDEMO_H__ diff --git a/modules/advancedraycasting/processors/ambientvolumegenerator.cpp b/modules/advancedraycasting/processors/ambientvolumegenerator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..170c2fb74e00c495acc61562e57320e3a2282347 --- /dev/null +++ b/modules/advancedraycasting/processors/ambientvolumegenerator.cpp @@ -0,0 +1,182 @@ +// ================================================================================================ +// +// This file is part of the CAMPVis Software Framework. +// +// If not explicitly stated otherwise: Copyright (C) 2012-2015, all rights reserved, +// Christian Schulte zu Berge +// 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 "ambientvolumegenerator.h" + +#include "cgt/buffer.h" +#include "cgt/imageunit.h" +#include "cgt/logmanager.h" +#include "cgt/shadermanager.h" +#include "cgt/textureunit.h" +#include "cgt/texture.h" + +#include "core/datastructures/imagedata.h" +#include "core/datastructures/imagerepresentationgl.h" +#include "core/datastructures/renderdata.h" + +#include "core/tools/cshelper.h" +#include "core/tools/quadrenderer.h" +#include "core/tools/stringutils.h" + +#include "core/classification/geometry1dtransferfunction.h" + +#include "modules/advancedraycasting/decorators/localambientocclusiondecorator.h" + +namespace campvis { + + const std::string AmbientVolumeGenerator::loggerCat_ = "CAMPVis.modules.advancedraycasting.AmbientVolumeGenerator"; + + + AmbientVolumeGenerator::AmbientVolumeGenerator() + : AbstractProcessor() + , p_inputImage("InputImage", "Input Image", "", DataNameProperty::READ) + , p_outputImage("OutputImage", "Output Image", "AmbientIllinationVolume", DataNameProperty::WRITE) + , p_workgroupSize("WorkgroupSize", "Workgroup Size", cgt::ivec3(4, 4, 2), cgt::ivec3(1), cgt::ivec3(16)) + , p_transferFunction("TransferFunction", "Transfer Function", new Geometry1DTransferFunction(256, cgt::vec2(0, 1))) + , _shader(nullptr) + { + addProperty(p_inputImage, INVALID_RESULT | INVALID_PROPERTIES); + addProperty(p_outputImage); + addProperty(p_workgroupSize); + addProperty(p_transferFunction); + + addDecorator(new LocalAmbientOcclusionDecorator()); + + decoratePropertyCollection(this); + } + + AmbientVolumeGenerator::~AmbientVolumeGenerator() { + + } + + void AmbientVolumeGenerator::init() { + AbstractProcessor::init(); + + LGL_ERROR; + } + + void AmbientVolumeGenerator::deinit() { + ShdrMgr.dispose(_shader); + + AbstractProcessor::deinit(); + } + + void AmbientVolumeGenerator::updateResult(DataContainer& data) { + ImageRepresentationGL::ScopedRepresentation img(data, p_inputImage.getValue()); + + if (img != 0) { + if (img->getParent()->getDimensionality() > 1) { + + cgt::ImageUnit outputUnit; + generateShader(img, outputUnit); + + if (_shader) { + cgt::ivec3 size = img->getSize(); + cgt::ivec3 wgSize = p_workgroupSize.getValue(); + cgt::ivec3 groups(cgt::ceil(cgt::vec3(size) / cgt::vec3(wgSize))); + + cgt::TextureUnit inputUnit; + inputUnit.activate(); + + cgt::TextureUnit tfUnit; + + _shader->activate(); + + decorateRenderProlog(data, _shader); + + p_transferFunction.getTF()->bind(_shader, tfUnit); + + // create texture for result + std::unique_ptr resultTexture(new cgt::Texture(img->getTexture()->getType(), size, GL_RGBA8, cgt::Texture::LINEAR)); + resultTexture->setWrapping(cgt::Texture::CLAMP_TO_EDGE); + img->bind(_shader, inputUnit, "_volume", "_volumeTextureParams"); + LGL_ERROR; + resultTexture->bindImage(outputUnit, GL_WRITE_ONLY); + LGL_ERROR; + _shader->setUniform("_outputImage", outputUnit.getUnitNumber()); + LGL_ERROR; + + // make sure the textures have been uploaded completely + glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); + + glDispatchCompute(groups.x, groups.y, groups.z); + + LGL_ERROR; + + decorateRenderEpilog(_shader); + + _shader->deactivate(); + + // Make sure the result is actually finished + glFinish(); + + // put resulting image into DataContainer + std::unique_ptr id(new ImageData(img->getParent()->getDimensionality(), size, 4)); + ImageRepresentationGL::create(id.get(), resultTexture.release()); + id->setMappingInformation(img->getParent()->getMappingInformation()); + data.addData(p_outputImage.getValue(), id.release()); + + cgt::TextureUnit::setZeroUnit(); + LGL_ERROR; + + } + } + else { + LERROR("Supports only 2D and 3D Median Filtering."); + } + } + else { + LDEBUG("No suitable input image found."); + } + } + + void AmbientVolumeGenerator::updateProperties(DataContainer& dc) { + ScopedTypedData img(dc, p_inputImage.getValue()); + + if(img) + p_transferFunction.setImageHandle(img.getDataHandle()); + } + + void AmbientVolumeGenerator::generateShader(const ImageRepresentationGL::ScopedRepresentation& img, const cgt::ImageUnit& imgUnit) + { + auto wg = p_workgroupSize.getValue(); + std::stringstream ss; + ss << getDecoratedHeader(); + ss << "#define WORK_GROUP_SIZE_X " << wg.x << std::endl; + ss << "#define WORK_GROUP_SIZE_Y " << wg.y << std::endl; + ss << "#define WORK_GROUP_SIZE_Z " << wg.z << std::endl; + ss << "#define OUTPUT_TEXTURE_FORMAT " << cgt::Texture::calcMatchingWriteFormat(img->getTexture()->getInternalFormat()) << std::endl; + ss << "#define TEXTURE_DIMENSIONALITY " << img->getDimensionality() << std::endl; + ss << CSHelper::generateGLSLImageDefinition(*(img->getTexture()), "_outputImage", imgUnit) << std::endl; + + // very simple caching to eliminate the glsl compiler as a bottleneck + static std::string headers; + if (!_shader || headers != ss.str()) { + headers = ss.str(); + //_shader->setHeaders(headers); + //_shader->rebuild(); + _shader = ShdrMgr.loadCompute("modules/advancedraycasting/glsl/ambientvolumegenerator.comp", headers); + } + } +} diff --git a/modules/advancedraycasting/processors/ambientvolumegenerator.h b/modules/advancedraycasting/processors/ambientvolumegenerator.h new file mode 100644 index 0000000000000000000000000000000000000000..064a3046386b9190cb34dd8361b8c9366428e94c --- /dev/null +++ b/modules/advancedraycasting/processors/ambientvolumegenerator.h @@ -0,0 +1,107 @@ +// ================================================================================================ +// +// This file is part of the CAMPVis Software Framework. +// +// If not explicitly stated otherwise: Copyright (C) 2012-2015, all rights reserved, +// Christian Schulte zu Berge +// 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. +// +// ================================================================================================ + +#ifndef AMBIENTVOLUMEGENERATOR_H__ +#define AMBIENTVOLUMEGENERATOR_H__ + +#include + +#include "core/datastructures/imagerepresentationgl.h" +#include "core/pipeline/abstractprocessordecorator.h" +#include "core/pipeline/visualizationprocessor.h" +#include "core/properties/datanameproperty.h" +#include "core/properties/floatingpointproperty.h" +#include "core/properties/optionproperty.h" +#include "core/properties/transferfunctionproperty.h" +#include "core/properties/colorproperty.h" + +#include "modules/modulesapi.h" + +namespace cgt { + class BufferObject; + class Shader; +} + + +namespace campvis { + /** + * transforms an intensity image with a transfer function, outputting an RGBA volume using OpenGL Compute Shaders. + */ + class CAMPVIS_MODULES_API AmbientVolumeGenerator : public AbstractProcessor, public HasProcessorDecorators { + public: + /** + * Constructs a new GlGaussianFilter Processor + **/ + AmbientVolumeGenerator(); + + /** + * Destructor + **/ + virtual ~AmbientVolumeGenerator(); + + /// \see AbstractProcessor::init + virtual void init(); + /// \see AbstractProcessor::deinit + virtual void deinit(); + + /** + * To be used in ProcessorFactory static methods + */ + static const std::string getId() { return "AmbientVolumeGenerator"; }; + /// \see AbstractProcessor::getName() + virtual const std::string getName() const { return getId(); }; + /// \see AbstractProcessor::getDescription() + virtual const std::string getDescription() const { return "Precomputes an local ambient occlusion volume which stores ambient color + emission for each voxel."; }; + /// \see AbstractProcessor::getAuthor() + virtual const std::string getAuthor() const { return "Jakob Weiss "; }; + /// \see AbstractProcessor::getProcessorState() + virtual ProcessorState getProcessorState() const { return AbstractProcessor::TESTING; }; + + DataNameProperty p_inputImage; ///< ID for input volume + DataNameProperty p_outputImage; ///< ID for output gradient volume + + TransferFunctionProperty p_transferFunction; + + IVec3Property p_workgroupSize; ///< Size of the workgroup + + protected: + /// \see AbstractProcessor::updateResult + virtual void updateResult(DataContainer& dataContainer) override; + + /// \see AbstractProcessor::updateProperties + virtual void updateProperties(DataContainer & dataContainer) override; + + /** + * Generates the shader tailored for the current input image. Simple caching is applied so as not to re-compile the shader + * on every image update if the image metadata does not change + */ + void generateShader(const ImageRepresentationGL::ScopedRepresentation& img, const cgt::ImageUnit& imgUnit); + + cgt::Shader* _shader; ///< Shader for performing median filtering + + static const std::string loggerCat_; + }; +} + +#endif // AMBIENTVOLUMEGENERATOR_H__ diff --git a/modules/advancedraycasting/processors/laoraycaster.cpp b/modules/advancedraycasting/processors/laoraycaster.cpp index e0b0746c82265aa75cfeab582c9b1c0c94168536..d59a00198ef02880c6ede0837a12429ef202220e 100644 --- a/modules/advancedraycasting/processors/laoraycaster.cpp +++ b/modules/advancedraycasting/processors/laoraycaster.cpp @@ -33,6 +33,7 @@ #include "core/pipeline/processordecoratorgradient.h" #include "core/classification/geometry1dtransferfunction.h" +#include "modules/advancedraycasting/decorators/localambientocclusiondecorator.h" #include @@ -42,30 +43,21 @@ namespace campvis { LAORaycaster::LAORaycaster(IVec2Property* viewportSizeProp) : RaycastingProcessor(viewportSizeProp, "modules/advancedraycasting/glsl/laoraycaster.frag", true) , p_enableShading("EnableShading", "Enable Shading", false) - , p_aoRays("AORays", "Number of directional samples", 8, 0, 50) - , p_aoSamples("AOSamples", "Number of Samples per ray (controls size of AO sphere)", 10, 0, 80) - , p_aoSphereRadius("AOSphereRadius", "AO Sphere Radius [voxels]", 8.0f, 0.1f, 50.0f, 0.25f) - , p_aoOpacityScale("OpacityScale", "Opacity Scaling", 1.0f, 0.0f, 5.f) - , p_aoEffectGamma("AOEffectGamma", "AO Scale Gamma", 1.0f, .0f, 5.f) - , p_aoEmissiveTransferFunction("AOTransferFunction", "Emissive Transfer Function", new Geometry1DTransferFunction(128)) - , p_aoEmissiveScale("EmissiveScale", "Emissive Color Scaling", 1.0f, 0.0f, 10.f) + , p_lightId("LightId", "Input Light Source", "lightsource", DataNameProperty::READ) , p_quadGeometry("QuadGeometry", "Quad Geometry", "", DataNameProperty::READ) + , p_showAOnly("showAOOnly", "Show AO Only", false) { addDecorator(new ProcessorDecoratorGradient()); + addDecorator(new LocalAmbientOcclusionDecorator()); addProperty(p_enableShading, INVALID_RESULT | INVALID_PROPERTIES | INVALID_SHADER); - addProperty(p_aoRays, INVALID_RESULT | INVALID_PROPERTIES | INVALID_SHADER); - addProperty(p_aoSamples, INVALID_RESULT | INVALID_PROPERTIES | INVALID_SHADER); - addProperty(p_aoSphereRadius); - addProperty(p_aoOpacityScale); - addProperty(p_aoEffectGamma); - addProperty(p_aoEmissiveTransferFunction); - addProperty(p_aoEmissiveScale); + addProperty(p_lightId); addProperty(p_quadGeometry); - + addProperty(p_showAOnly, INVALID_RESULT | INVALID_SHADER); + decoratePropertyCollection(this); } @@ -87,9 +79,7 @@ namespace campvis { ScopedTypedData cam(data, p_camera.getValue()); if (p_enableShading.getValue() == false || light != nullptr) { - auto aotf = p_aoEmissiveTransferFunction.getTF(); - cgt::TextureUnit aoTFUnit; - aotf->bind(_shader, aoTFUnit, "_aoEmissiveTF", "_aoEmissiveTFParams"); + FramebufferActivationGuard fag(this); @@ -106,11 +96,6 @@ namespace campvis { light->bind(_shader, "_lightSource"); } - _shader->setUniform("_aoSphereRadius", p_aoSphereRadius.getValue()); - _shader->setUniform("_aoGamma", p_aoEffectGamma.getValue()); - _shader->setUniform("_aoEmissiveScale", p_aoEmissiveScale.getValue()); - _shader->setUniform("_aoOpacityScale", p_aoOpacityScale.getValue()); - glEnable(GL_DEPTH_TEST); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -142,8 +127,8 @@ namespace campvis { if (p_enableShading.getValue()) toReturn += "#define ENABLE_SHADING\n"; - toReturn += "#define NUM_AO_RAYS " + std::to_string(p_aoRays.getValue()) + "\n"; - toReturn += "#define NUM_AO_RAY_STEPS " + std::to_string(p_aoSamples.getValue()) + "\n"; + if(p_showAOnly.getValue()) + toReturn += "#define SHOW_AO_ONLY\n"; return toReturn; } @@ -151,7 +136,6 @@ namespace campvis { void LAORaycaster::updateProperties(DataContainer& dataContainer) { RaycastingProcessor::updateProperties(dataContainer); p_lightId.setVisible(p_enableShading.getValue()); - p_aoEmissiveTransferFunction.setVisible(p_aoEmissiveScale.getValue() > 0); } diff --git a/modules/advancedraycasting/processors/laoraycaster.h b/modules/advancedraycasting/processors/laoraycaster.h index 0b8e7dabbcbc2124e0abfab716c199f0baa5225c..ff124d353733490d1907f8b099d478fe8081d785 100644 --- a/modules/advancedraycasting/processors/laoraycaster.h +++ b/modules/advancedraycasting/processors/laoraycaster.h @@ -73,19 +73,13 @@ namespace campvis { virtual void deinit(); BoolProperty p_enableShading; ///< Flag whether to enable shading - IntProperty p_aoRays; ///< Number of directional AO samples - IntProperty p_aoSamples; ///< Number of steps per directional sample - FloatProperty p_aoSphereRadius; ///< The AO Sphere radius in voxels - FloatProperty p_aoEffectGamma; ///< Gamma controls the strength of the AO effect - FloatProperty p_aoOpacityScale; ///< Scales opacity when sampling for AO rays - - TransferFunctionProperty p_aoEmissiveTransferFunction; - FloatProperty p_aoEmissiveScale; ///< Scales the emissive color to increase/decrease the emissive effect DataNameProperty p_lightId; ///< Name/ID for the LightSource to use DataNameProperty p_quadGeometry; + BoolProperty p_showAOnly; ///< show only AO effect + protected: /// \see AbstractProcessor::updateProperties() virtual void updateProperties(DataContainer& dataContainer); diff --git a/modules/advancedraycasting/processors/preintegratedraycaster.cpp b/modules/advancedraycasting/processors/preintegratedraycaster.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1dab6cb3fbbcc4353a17f62ff5d321b867146b00 --- /dev/null +++ b/modules/advancedraycasting/processors/preintegratedraycaster.cpp @@ -0,0 +1,151 @@ +// ================================================================================================ +// +// This file is part of the CAMPVis Software Framework. +// +// If not explicitly stated otherwise: Copyright (C) 2012-2015, all rights reserved, +// Christian Schulte zu Berge +// 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 "preintegratedraycaster.h" + +#include "core/tools/quadrenderer.h" +#include "core/datastructures/lightsourcedata.h" +#include "core/datastructures/cameradata.h" +#include "core/datastructures/renderdata.h" +#include "core/datastructures/genericimagerepresentationlocal.h" +#include "core/datastructures/facegeometry.h" +#include "core/pipeline/processordecoratorgradient.h" + + +#include + +namespace campvis { + const std::string PreintegratedRaycaster::loggerCat_ = "CAMPVis.modules.advancedrc.PreintegratedRaycaster"; + + PreintegratedRaycaster::PreintegratedRaycaster(IVec2Property* viewportSizeProp) + : RaycastingProcessor(viewportSizeProp, "modules/advancedraycasting/glsl/preintegratedraycaster.frag", true) + , p_enableShading("EnableShading", "Enable Shading", true) + , p_shadowSteps("ShadowSteps", "Shadow Steps (0 to turn shadowing off)", 0, 0, 150) + , p_enableColoredShadowing("EnableColoredShadowing", "Enable Colored Shadows (broken)", false) + , p_shadowIntensity("ShadowIntensity", "Shadow Intensity", .15f, .0f, 1.f) + , p_lightId("LightId", "Input Light Source", "lightsource", DataNameProperty::READ) + , p_quadGeometry("QuadGeometry", "Quad Geometry", "", DataNameProperty::READ) + { + addDecorator(new ProcessorDecoratorGradient()); + + addProperty(p_enableShading, INVALID_RESULT | INVALID_PROPERTIES | INVALID_SHADER); + + addProperty(p_shadowSteps, INVALID_RESULT | INVALID_PROPERTIES | INVALID_SHADER); + addProperty(p_enableColoredShadowing, INVALID_RESULT | INVALID_PROPERTIES | INVALID_SHADER); + addProperty(p_shadowIntensity); + addProperty(p_lightId); + addProperty(p_quadGeometry); + + p_shadowIntensity.setVisible(false); + + decoratePropertyCollection(this); + } + + PreintegratedRaycaster::~PreintegratedRaycaster() { + } + + void PreintegratedRaycaster::init() { + RaycastingProcessor::init(); + } + + void PreintegratedRaycaster::deinit() { + RaycastingProcessor::deinit(); + } + + void PreintegratedRaycaster::processImpl(DataContainer& data, ImageRepresentationGL::ScopedRepresentation& image) { + ScopedTypedData light(data, p_lightId.getValue()); + ScopedTypedData outputImage(data, p_targetImageID.getValue()); + ScopedTypedData quadGeometry(data, p_quadGeometry.getValue(), true); + ScopedTypedData cam(data, p_camera.getValue()); + + if (image->getTexture()->getNumChannels() != 4) { + LERROR("PreintegratedRaycaster expects a 4-channel RGBA volume!"); + return; + } + + + if (p_enableShading.getValue() == false || light != nullptr) { + FramebufferActivationGuard fag(this); + + createAndAttachTexture(GL_RGBA8, &outputImage); + createAndAttachTexture(GL_RGBA32F, &outputImage); + createAndAttachTexture(GL_RGBA32F, &outputImage); + createAndAttachDepthTexture(&outputImage); + + static const GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 , GL_COLOR_ATTACHMENT2 }; + glDrawBuffers(3, buffers); + + if ((p_enableShading.getValue() || p_shadowSteps.getValue() > 0) && light != nullptr) { + light->bind(_shader, "_lightSource"); + } + if (p_shadowSteps.getValue() > 0) { + _shader->setUniform("_shadowIntensity", p_shadowIntensity.getValue()); + } + + glEnable(GL_DEPTH_TEST); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if (quadGeometry && cam) { + _shader->setUniform("_viewMatrix", cam->getCamera().getViewMatrix()); + _shader->setUniform("_projectionMatrix", cam->getCamera().getProjectionMatrix()); + quadGeometry->render(GL_TRIANGLE_FAN); + } + else { + QuadRdr.renderQuad(); + } + + // restore state + glDrawBuffers(1, buffers); + glDisable(GL_DEPTH_TEST); + LGL_ERROR; + + data.addData(p_targetImageID.getValue(), new RenderData(_fbo)); + + } + else { + LDEBUG("Could not load light source from DataContainer."); + } + } + + std::string PreintegratedRaycaster::generateHeader() const { + std::string toReturn = RaycastingProcessor::generateHeader(); + if (p_enableShading.getValue()) + toReturn += "#define ENABLE_SHADING\n"; + if (p_shadowSteps.getValue() > 0) { + toReturn += "#define ENABLE_SHADOWING\n"; + toReturn += "#define SHADOW_STEPS " + std::to_string(p_shadowSteps.getValue()) + "\n"; + } + if (p_enableColoredShadowing.getValue()) + toReturn += "#define ENABLE_COLORED_SHADOWING\n"; + return toReturn; + } + + void PreintegratedRaycaster::updateProperties(DataContainer& dataContainer) { + RaycastingProcessor::updateProperties(dataContainer); + p_lightId.setVisible(p_enableShading.getValue() || p_shadowSteps.getValue() > 0); + p_shadowIntensity.setVisible(p_shadowSteps.getValue() > 0); + p_enableColoredShadowing.setVisible(p_shadowSteps.getValue() > 0); + } + +} diff --git a/modules/advancedraycasting/processors/preintegratedraycaster.h b/modules/advancedraycasting/processors/preintegratedraycaster.h new file mode 100644 index 0000000000000000000000000000000000000000..6c7267c06c5cb3eacc5c9457bc9f345741a6499f --- /dev/null +++ b/modules/advancedraycasting/processors/preintegratedraycaster.h @@ -0,0 +1,99 @@ +// ================================================================================================ +// +// This file is part of the CAMPVis Software Framework. +// +// If not explicitly stated otherwise: Copyright (C) 2012-2015, all rights reserved, +// Christian Schulte zu Berge +// 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. +// +// ================================================================================================ + +#ifndef PREINTEGRATEDRAYCASTER_H__ +#define PREINTEGRATEDRAYCASTER_H__ + +#include "core/pipeline/raycastingprocessor.h" +#include "core/properties/floatingpointproperty.h" +#include "core/properties/genericproperty.h" +#include "core/properties/transferfunctionproperty.h" + +#include "modules/modulesapi.h" + +#include + +namespace cgt { + class Shader; +} + +namespace campvis { + /** + * Performs a simple volume ray casting. + */ + class CAMPVIS_MODULES_API PreintegratedRaycaster : public RaycastingProcessor { + public: + /** + * Constructs a new SimpleRaycaster Processor + **/ + explicit PreintegratedRaycaster(IVec2Property* viewportSizeProp); + + /** + * Destructor + **/ + virtual ~PreintegratedRaycaster(); + + /** + * To be used in ProcessorFactory static methods + */ + static const std::string getId() { return "PreintegratedRaycaster"; }; + /// \see AbstractProcessor::getName() + virtual const std::string getName() const { return getId(); }; + /// \see AbstractProcessor::getDescription() + virtual const std::string getDescription() const { return "Performs a simple volume ray casting given a pre-integrated RGBA volume"; }; + /// \see AbstractProcessor::getAuthor() + virtual const std::string getAuthor() const { return "Jakob Weiss "; }; + /// \see AbstractProcessor::getProcessorState() + virtual ProcessorState getProcessorState() const { return AbstractProcessor::TESTING; }; + + /// \see AbstractProcessor::init + virtual void init(); + /// \see AbstractProcessor::deinit + virtual void deinit(); + + BoolProperty p_enableShading; ///< Flag whether to enable shading + IntProperty p_shadowSteps; ///< Number of shadow steps. Zero to turn shadows off + BoolProperty p_enableColoredShadowing; ///< Flag to indicate whether to compute colored shadows + + + FloatProperty p_shadowIntensity; ///< Shadow intensity + DataNameProperty p_lightId; ///< Name/ID for the LightSource to use + + DataNameProperty p_quadGeometry; + + protected: + /// \see AbstractProcessor::updateProperties() + virtual void updateProperties(DataContainer& dataContainer); + + /// \see RaycastingProcessor::processImpl() + virtual void processImpl(DataContainer& data, ImageRepresentationGL::ScopedRepresentation& image); + + /// \see RaycastingProcessor::generateHeader() + virtual std::string generateHeader() const; + + static const std::string loggerCat_; + }; +} + +#endif // PREINTEGRATEDRAYCASTER_H__ diff --git a/modules/advancedraycasting/processors/tfpreintegrator.cpp b/modules/advancedraycasting/processors/tfpreintegrator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2d8af1568b8b60c67e676c5bb1beb24d7915039f --- /dev/null +++ b/modules/advancedraycasting/processors/tfpreintegrator.cpp @@ -0,0 +1,171 @@ +// ================================================================================================ +// +// This file is part of the CAMPVis Software Framework. +// +// If not explicitly stated otherwise: Copyright (C) 2012-2015, all rights reserved, +// Christian Schulte zu Berge +// 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 "tfpreintegrator.h" + +#include "cgt/buffer.h" +#include "cgt/imageunit.h" +#include "cgt/logmanager.h" +#include "cgt/shadermanager.h" +#include "cgt/textureunit.h" +#include "cgt/texture.h" + +#include "core/datastructures/imagedata.h" +#include "core/datastructures/imagerepresentationgl.h" +#include "core/datastructures/renderdata.h" + +#include "core/tools/cshelper.h" +#include "core/tools/quadrenderer.h" +#include "core/tools/stringutils.h" + +#include "core/classification/geometry1dtransferfunction.h" + +namespace campvis { + + const std::string TFPreIntegrator::loggerCat_ = "CAMPVis.modules.advancedraycasting"; + + + TFPreIntegrator::TFPreIntegrator() + : AbstractProcessor() + , p_inputImage("InputImage", "Input Image", "", DataNameProperty::READ) + , p_outputImage("OutputImage", "Output Image", "PreIntegratedVolume", DataNameProperty::WRITE) + , p_workgroupSize("WorkgroupSize", "Workgroup Size", cgt::ivec3(4, 4, 2), cgt::ivec3(1), cgt::ivec3(16)) + , p_transferFunction("TransferFunction", "Transfer Function", new Geometry1DTransferFunction(256, cgt::vec2(0, 1))) + , _shader(nullptr) + { + addProperty(p_inputImage, INVALID_RESULT | INVALID_PROPERTIES); + addProperty(p_outputImage); + addProperty(p_workgroupSize); + addProperty(p_transferFunction); + } + + TFPreIntegrator::~TFPreIntegrator() { + + } + + void TFPreIntegrator::init() { + AbstractProcessor::init(); + + LGL_ERROR; + } + + void TFPreIntegrator::deinit() { + ShdrMgr.dispose(_shader); + + AbstractProcessor::deinit(); + } + + void TFPreIntegrator::updateResult(DataContainer& data) { + ImageRepresentationGL::ScopedRepresentation img(data, p_inputImage.getValue()); + + if (img != 0) { + if (img->getParent()->getDimensionality() > 1) { + + cgt::ImageUnit outputUnit; + generateShader(img, outputUnit); + + if (_shader) { + cgt::ivec3 size = img->getSize(); + cgt::ivec3 wgSize = p_workgroupSize.getValue(); + cgt::ivec3 groups(cgt::ceil(cgt::vec3(size) / cgt::vec3(wgSize))); + + cgt::TextureUnit inputUnit; + inputUnit.activate(); + + cgt::TextureUnit tfUnit; + + _shader->activate(); + + p_transferFunction.getTF()->bind(_shader, tfUnit); + + // create texture for result + std::unique_ptr resultTexture(new cgt::Texture(img->getTexture()->getType(), size, GL_RGBA8, cgt::Texture::LINEAR)); + resultTexture->setWrapping(cgt::Texture::CLAMP_TO_EDGE); + img->bind(_shader, inputUnit); + LGL_ERROR; + resultTexture->bindImage(outputUnit, GL_WRITE_ONLY); + LGL_ERROR; + _shader->setUniform("_outputImage", outputUnit.getUnitNumber()); + LGL_ERROR; + + // make sure the textures have been uploaded completely + glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); + + glDispatchCompute(groups.x, groups.y, groups.z); + + LGL_ERROR; + + + _shader->deactivate(); + + // Make sure the result is actually finished + glFinish(); + + // put resulting image into DataContainer + std::unique_ptr id(new ImageData(img->getParent()->getDimensionality(), size, 4)); + ImageRepresentationGL::create(id.get(), resultTexture.release()); + id->setMappingInformation(img->getParent()->getMappingInformation()); + data.addData(p_outputImage.getValue(), id.release()); + + cgt::TextureUnit::setZeroUnit(); + LGL_ERROR; + } + } + else { + LERROR("Supports only 2D and 3D Median Filtering."); + } + } + else { + LDEBUG("No suitable input image found."); + } + } + + void TFPreIntegrator::updateProperties(DataContainer& dc) { + ScopedTypedData img(dc, p_inputImage.getValue()); + + if(img) + p_transferFunction.setImageHandle(img.getDataHandle()); + } + + void TFPreIntegrator::generateShader(const ImageRepresentationGL::ScopedRepresentation& img, const cgt::ImageUnit& imgUnit) + { + auto wg = p_workgroupSize.getValue(); + std::stringstream ss; + ss << "#define WORK_GROUP_SIZE_X " << wg.x << std::endl; + ss << "#define WORK_GROUP_SIZE_Y " << wg.y << std::endl; + ss << "#define WORK_GROUP_SIZE_Z " << wg.z << std::endl; + ss << "#define OUTPUT_TEXTURE_FORMAT " << cgt::Texture::calcMatchingWriteFormat(img->getTexture()->getInternalFormat()) << std::endl; + ss << "#define TEXTURE_DIMENSIONALITY " << img->getDimensionality() << std::endl; + ss << CSHelper::generateGLSLImageDefinition(*(img->getTexture()), "_outputImage", imgUnit) << std::endl; + + // very simple caching to eliminate the glsl compiler as a bottleneck + static std::string headers; + if (!_shader || headers != ss.str()) { + headers = ss.str(); + //_shader->setHeaders(headers); + //_shader->rebuild(); + _shader = ShdrMgr.loadCompute("modules/advancedraycasting/glsl/tfpreintegrator.comp", headers); + } + } +} diff --git a/modules/advancedraycasting/processors/tfpreintegrator.h b/modules/advancedraycasting/processors/tfpreintegrator.h new file mode 100644 index 0000000000000000000000000000000000000000..9258a349e0a5d45186c08c4fb77f657faf1c5140 --- /dev/null +++ b/modules/advancedraycasting/processors/tfpreintegrator.h @@ -0,0 +1,107 @@ +// ================================================================================================ +// +// This file is part of the CAMPVis Software Framework. +// +// If not explicitly stated otherwise: Copyright (C) 2012-2015, all rights reserved, +// Christian Schulte zu Berge +// 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. +// +// ================================================================================================ + +#ifndef TFPREINTEGRATOR_H__ +#define TFPREINTEGRATOR_H__ + +#include + +#include "core/datastructures/imagerepresentationgl.h" +#include "core/pipeline/abstractprocessordecorator.h" +#include "core/pipeline/visualizationprocessor.h" +#include "core/properties/datanameproperty.h" +#include "core/properties/floatingpointproperty.h" +#include "core/properties/optionproperty.h" +#include "core/properties/transferfunctionproperty.h" +#include "core/properties/colorproperty.h" + +#include "modules/modulesapi.h" + +namespace cgt { + class BufferObject; + class Shader; +} + + +namespace campvis { + /** + * transforms an intensity image with a transfer function, outputting an RGBA volume using OpenGL Compute Shaders. + */ + class CAMPVIS_MODULES_API TFPreIntegrator : public AbstractProcessor { + public: + /** + * Constructs a new GlGaussianFilter Processor + **/ + TFPreIntegrator(); + + /** + * Destructor + **/ + virtual ~TFPreIntegrator(); + + /// \see AbstractProcessor::init + virtual void init(); + /// \see AbstractProcessor::deinit + virtual void deinit(); + + /** + * To be used in ProcessorFactory static methods + */ + static const std::string getId() { return "TFPreIntegrator"; }; + /// \see AbstractProcessor::getName() + virtual const std::string getName() const { return getId(); }; + /// \see AbstractProcessor::getDescription() + virtual const std::string getDescription() const { return "Transforms an intensity image with a transfer function, outputting an RGBA volume using OpenGL Compute Shaders."; }; + /// \see AbstractProcessor::getAuthor() + virtual const std::string getAuthor() const { return "Jakob Weiss "; }; + /// \see AbstractProcessor::getProcessorState() + virtual ProcessorState getProcessorState() const { return AbstractProcessor::TESTING; }; + + DataNameProperty p_inputImage; ///< ID for input volume + DataNameProperty p_outputImage; ///< ID for output gradient volume + + TransferFunctionProperty p_transferFunction; + + IVec3Property p_workgroupSize; ///< Size of the workgroup + + protected: + /// \see AbstractProcessor::updateResult + virtual void updateResult(DataContainer& dataContainer) override; + + /// \see AbstractProcessor::updateProperties + virtual void updateProperties(DataContainer & dataContainer) override; + + /** + * Generates the shader tailored for the current input image. Simple caching is applied so as not to re-compile the shader + * on every image update if the image metadata does not change + */ + void generateShader(const ImageRepresentationGL::ScopedRepresentation& img, const cgt::ImageUnit& imgUnit); + + cgt::Shader* _shader; ///< Shader for performing median filtering + + static const std::string loggerCat_; + }; +} + +#endif // TFPREINTEGRATOR_H__