Commit 3572dba4 authored by mostajab's avatar mostajab Committed by Christian Schulte zu Berge

Introducing an advanced optimized ray caster that allows for empty space skipping.

Merged Morteza's changes into one single commit.
parent 71cf711c
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2013, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universität München
// Boltzmannstr. 3, 85748 Garching b. München, 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 "VoxelTextureMipMapping.h"
#include "tgt/shadermanager.h"
#include "tgt/textureunit.h"
#include "tgt/tgt_gl.h"
#include "core/datastructures/geometrydatafactory.h"
#define VOXEL_DEPTH_MIPMAPPING 32
namespace campvis {
VoxelTexMipMapGenerator::VoxelTexMipMapGenerator() {
_quad = GeometryDataFactory::createQuad(tgt::vec3(0.f), tgt::vec3(1.f), tgt::vec3(1.f, 1.f, 0.f), tgt::vec3(0.f, 0.f, 0.f));
_shader = ShdrMgr.loadWithCustomGlslVersion("core/glsl/passthrough.vert", "", "modules/vis/advraycaster/glsl/MipmapVoxelTexture.frag", "", "330");
}
void VoxelTexMipMapGenerator::init() {
}
unsigned int VoxelTexMipMapGenerator::computeMaxLevel(unsigned int resolutionX, unsigned int resolutionY) {
unsigned int count = 0;
unsigned int resolution = std::max(resolutionX, resolutionY);
while(resolution){
resolution /= 2;
count++;
}
return count-1;
}
void VoxelTexMipMapGenerator::attachMipmapsTo(tgt::Texture* texture, unsigned int resolutionX, unsigned int resolutionY) {
unsigned int maxLevel = computeMaxLevel(resolutionX, resolutionY);
LGL_ERROR;
// attach mipmap levels to this voxel texture
glBindTexture(GL_TEXTURE_2D, texture->getId());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
LGL_ERROR;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
LGL_ERROR;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
LGL_ERROR;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, maxLevel);
LGL_ERROR;
int div = 2;
for(unsigned int level = 1; level <= maxLevel; level++){
#if (VOXEL_DEPTH_MIPMAPPING == 8)
glTexImage2D(GL_TEXTURE_2D, level, GL_R32UI, resolutionX/div, resolutionY/div, 0, GL_RED_INTEGER_EXT, GL_UNSIGNED_BYTE, 0);
#else if (VOXEL_DEPTH_MIPMAPPING == 32)
glTexImage2D(GL_TEXTURE_2D, level, GL_R32UI, resolutionX/div, resolutionY/div, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, 0);
#endif
div = div << 1;
LGL_ERROR;
}
}
void VoxelTexMipMapGenerator::renderMipmapsFor(tgt::Texture* texture, unsigned int resolutionX, unsigned int resolutionY, unsigned int voxelSize) {
glPushAttrib(GL_ALL_ATTRIB_BITS);
LGL_ERROR;
unsigned int maxLevel = computeMaxLevel(resolutionX, resolutionY);
tgt::FramebufferObject frameBuffer;
LGL_ERROR;
/// Activate the shader for geometry Rendering.
_shader->activate();
tgt::TextureUnit bbvUnit;
bbvUnit.activate();
texture->bind();
LGL_ERROR;
_shader->setIgnoreUniformLocationError(true);
_shader->setUniform("_voxelTexture", bbvUnit.getUnitNumber());
LGL_ERROR;
_shader->setUniform("_voxelTextureParams._size", tgt::vec2(static_cast<float>(texture->getDimensions().r), static_cast<float>(texture->getDimensions().g)));
LGL_ERROR;
_shader->setUniform("_voxelTextureParams._sizeRCP", tgt::vec3(1.f) / tgt::vec3(texture->getDimensions()));
LGL_ERROR;
_shader->setUniform("_voxelTextureParams._numChannels", static_cast<int>(1));
LGL_ERROR;
_shader->setIgnoreUniformLocationError(false);
LGL_ERROR;
frameBuffer.activate();
// Set OpenGL pixel alignment to 1 to avoid problems with NPOT textures
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
for(unsigned int i = 0; i < maxLevel; i++){
// Set OpenGL pixel alignment to 1 to avoid problems with NPOT textures
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
tgt::mat4 projection = tgt::mat4::createOrtho(0, 1, 0, 1, -1, 1);
_shader->setUniform("_projectionMatrix", projection);
_shader->setUniform("_level", static_cast<int>(i));
float invResXFloat = static_cast<float>( 1.0/(resolutionX/pow(2.0,double(i))) );
float invResYFloat = static_cast<float>( 1.0/(resolutionY/pow(2.0,double(i))) );
_shader->setUniform("_inverseTexSizeX", static_cast<float>(invResXFloat));
_shader->setUniform("_inverseTexSizeY", static_cast<float>(invResYFloat));
LGL_ERROR;
glViewport(0, 0, static_cast<GLsizei>(resolutionX/pow(2.0,double(i+1))), static_cast<GLsizei>(resolutionY/pow(2.0,double(i+1))));
frameBuffer.attachTexture(texture, GL_COLOR_ATTACHMENT0, i+1, 0);
frameBuffer.isComplete();
LGL_ERROR;
_quad->render(GL_POLYGON);
LGL_ERROR;
}
_shader->deactivate();
frameBuffer.deactivate();
glPopAttrib();
LGL_ERROR;
}
VoxelTexMipMapGenerator::~VoxelTexMipMapGenerator() {
ShdrMgr.dispose(_shader);
delete _quad;
}
}
\ No newline at end of file
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2013, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universität München
// Boltzmannstr. 3, 85748 Garching b. München, 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 VOXELTEXTUREMIPMAPPER_H__
#define VOXELTEXTUREMIPMAPPER_H__
#include <string>
#include "ext/glew/include/GL/glew.h"
#include "tgt/framebufferobject.h"
#include "tgt/shadermanager.h"
#include "tgt/texture.h"
#include "core/datastructures/facegeometry.h"
namespace campvis {
/**
* class for generating the mip maps of the voxel texture
*
*/
class VoxelTexMipMapGenerator {
public:
/**
* Creates a VoxelTexMipMapGenerator and prepare the framebuffer and shader for next function calls of the object.
*/
VoxelTexMipMapGenerator();
void init();
/**
* Computes the maximum level of mipmapping.
* \param resolution largest dimension of the voxelized volume.
*/
unsigned int computeMaxLevel(unsigned int resolutionX, unsigned int resolutionY);
/**
* Attaches the mipmaps to the texture with the passed textureID and resolution.
* \param voxelTextureID ID of the texture which the mip maps are going to be attached to.
* \param resolution largest dimension of the voxelized volume.
*/
void attachMipmapsTo(tgt::Texture* texture, unsigned int resolutionX, unsigned int resolutionY);
/**
* renders the mipmaps of the texture with the passed texture object and resolution.
* \param voxelTextureID texture object.
* \param resolution largest dimension of the voxelized volume.
*/
void renderMipmapsFor(tgt::Texture* texture, unsigned int resolution, unsigned int resolutionY, unsigned int voxelSize);
~VoxelTexMipMapGenerator();
private:
tgt::Shader* _shader; ///< Pointer to the shader object
FaceGeometry* _quad;
};
}
#endif // VOXELTEXTUREMIPMAPPER_H__
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2013, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universität München
// Boltzmannstr. 3, 85748 Garching b. München, 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 "AdvOptimizedRaycaster.h"
#include "core/tools/quadrenderer.h"
#include "core/datastructures/renderdata.h"
#include "core/pipeline/processordecoratorshading.h"
#include "core/datastructures/imagedata.h"
#include "core/datastructures/geometrydatafactory.h"
#include "modules/vis/advraycaster/voxeltexturemipmapping.h"
#include <tbb/tbb.h>
namespace campvis {
const std::string AdvOptimizedRaycaster::loggerCat_ = "CAMPVis.modules.vis.AdvOptimizedRaycaster";
AdvOptimizedRaycaster::AdvOptimizedRaycaster(IVec2Property* viewportSizeProp)
: RaycastingProcessor(viewportSizeProp, "modules/vis/advraycaster/glsl/AdvOptimizedRaycaster.frag", true, "400")
, p_targetImageID("targetImageID", "Output Image", "", DataNameProperty::WRITE)
, p_enableShadowing("EnableShadowing", "Enable Hard Shadows (Expensive!)", false, AbstractProcessor::INVALID_RESULT | AbstractProcessor::INVALID_SHADER | AbstractProcessor::INVALID_PROPERTIES)
, p_shadowIntensity("ShadowIntensity", "Shadow Intensity", .5f, .0f, 1.f)
, p_enableIntersectionRefinement("EnableIntersectionRefinement", "Enable Intersection Refinement", false, AbstractProcessor::INVALID_RESULT | AbstractProcessor::INVALID_SHADER)
, p_useEmptySpaceSkipping("EnableEmptySpaceSkipping", "Enable Empty Space Skipping", true, AbstractProcessor::INVALID_RESULT | INVALID_BBV)
, _vv(0)
, _quad(0)
, _voxelGeneratorShdr(0)
, _vvTex(0)
, _mipMapGen(0)
{
addDecorator(new ProcessorDecoratorShading());
addProperty(&p_targetImageID);
addProperty(&p_enableIntersectionRefinement);
addProperty(&p_useEmptySpaceSkipping);
addProperty(&p_enableShadowing);
addProperty(&p_shadowIntensity);
p_shadowIntensity.setVisible(false);
p_transferFunction.setInvalidationLevel(p_transferFunction.getInvalidationLevel() | INVALID_BBV);
p_sourceImageID.setInvalidationLevel(p_sourceImageID.getInvalidationLevel() | INVALID_BBV);
decoratePropertyCollection(this);
}
AdvOptimizedRaycaster::~AdvOptimizedRaycaster() {
}
void AdvOptimizedRaycaster::init() {
RaycastingProcessor::init();
_quad = GeometryDataFactory::createQuad(tgt::vec3(0.f), tgt::vec3(1.f), tgt::vec3(1.f, 1.f, 0.f), tgt::vec3(0.f, 0.f, 0.f));
_voxelGeneratorShdr = ShdrMgr.loadWithCustomGlslVersion("core/glsl/passthrough.vert", "", "modules/vis/advraycaster/glsl/rendervolumevoxelizing.frag", "", "400");
invalidate(INVALID_BBV);
}
void AdvOptimizedRaycaster::deinit() {
delete _vv;
delete _vvTex;
RaycastingProcessor::deinit();
}
void AdvOptimizedRaycaster::processImpl(DataContainer& data, ImageRepresentationGL::ScopedRepresentation& image) {
tgt::TextureUnit bbvUnit;
if (getInvalidationLevel() & INVALID_BBV){
renderVv(data);
validate(INVALID_BBV);
_shader->activate();
}
if (_vvTex != 0 && p_useEmptySpaceSkipping.getValue()){
// bind
bbvUnit.activate();
_vvTex->bind();
_shader->setIgnoreUniformLocationError(true);
_shader->setUniform("_vvTexture", bbvUnit.getUnitNumber());
_shader->setUniform("_vvTextureParams._size", tgt::vec3(_vvTex->getDimensions()));
_shader->setUniform("_vvTextureParams._sizeRCP", tgt::vec3(1.f) / tgt::vec3(_vvTex->getDimensions()));
_shader->setUniform("_vvTextureParams._numChannels", static_cast<int>(1));
_shader->setUniform("_vvVoxelSize", static_cast<int>(_vv->getBrickSize()));
_shader->setUniform("_vvVoxelDepth", static_cast<int>(_vv->getBrickDepth()));
_shader->setUniform("_hasVv", true);
_shader->setUniform("_vvMaxMipMapLevel", _maxMipMapLevel);
_shader->setIgnoreUniformLocationError(false);
} else {
_shader->setUniform("_hasVv", false);
}
FramebufferActivationGuard fag(this);
createAndAttachTexture(GL_RGBA8);
createAndAttachTexture(GL_RGBA32F);
createAndAttachTexture(GL_RGBA32F);
createAndAttachDepthTexture();
static const GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 , GL_COLOR_ATTACHMENT2 };
glDrawBuffers(3, buffers);
if (p_enableShadowing.getValue())
_shader->setUniform("_shadowIntensity", p_shadowIntensity.getValue());
glEnable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
QuadRdr.renderQuad();
glDisable(GL_DEPTH_TEST);
LGL_ERROR;
glDrawBuffers(1, buffers);
data.addData(p_targetImageID.getValue(), new RenderData(_fbo));
}
std::string AdvOptimizedRaycaster::generateHeader() const {
std::string toReturn = RaycastingProcessor::generateHeader();
if (p_enableShadowing.getValue())
toReturn += "#define ENABLE_SHADOWING\n";
if (p_enableIntersectionRefinement.getValue())
toReturn += "#define INTERSECTION_REFINEMENT\n";
return toReturn;
}
void AdvOptimizedRaycaster::updateProperties() {
p_shadowIntensity.setVisible(p_enableShadowing.getValue());
validate(AbstractProcessor::INVALID_PROPERTIES);
}
void AdvOptimizedRaycaster::renderVv(DataContainer& data) {
delete _vv;
_vv = 0;
delete _vvTex;
_vvTex = 0;
delete _mipMapGen;
_mipMapGen = 0;
ImageRepresentationGL::ScopedRepresentation img(data, p_sourceImageID.getValue());
if (img != 0){
_vv = new VoxelizedRenderVolume(img->getParent(), 4);
_vvTex = _vv->createEmptyImageData();
_mipMapGen = new VoxelTexMipMapGenerator();
LDEBUG("Start computing voxel visibilities...");
glPushAttrib(GL_ALL_ATTRIB_BITS);
tgt::FramebufferObject frameBuffer;
LGL_ERROR;
/// Activate the shader to generate the voxelized volume.
_voxelGeneratorShdr->activate();
tgt::TextureUnit volumeUnit, tfUnit;
volumeUnit.activate();
tfUnit.activate();
LGL_ERROR;
_voxelGeneratorShdr->setIgnoreUniformLocationError(true);
img->bind(_voxelGeneratorShdr, volumeUnit, "_volume", "_volumeTextureParams");
p_transferFunction.getTF()->bind(_voxelGeneratorShdr, tfUnit, "_transferFunction", "_transferFunctionParams");
_voxelGeneratorShdr->setUniform("_voxelDepth", static_cast<unsigned int>(_vv->getBrickDepth()));
_voxelGeneratorShdr->setUniform("_voxelSize", static_cast<unsigned int>(_vv->getBrickSize()));
_voxelGeneratorShdr->setIgnoreUniformLocationError(false);
LGL_ERROR;
frameBuffer.activate();
// Set OpenGL pixel alignment to 1 to avoid problems with NPOT textures
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
tgt::mat4 projection = tgt::mat4::createOrtho(0, 1, 0, 1, -1, 1);
_voxelGeneratorShdr->setUniform("_projectionMatrix", projection);
LGL_ERROR;
//write to:
glViewport(0, 0, static_cast<GLsizei>(_vvTex->getWidth()), static_cast<GLsizei>(_vvTex->getHeight()));
frameBuffer.attachTexture(_vvTex, GL_COLOR_ATTACHMENT0, 0, 0);
frameBuffer.isComplete();
LGL_ERROR;
_quad->render(GL_POLYGON);
LGL_ERROR;
_shader->deactivate();
frameBuffer.deactivate();
glPopAttrib();
LDEBUG("...finished computing voxel visibilities.");
LGL_ERROR;
LDEBUG("Start computing the levels of the voxel object.");
_mipMapGen->attachMipmapsTo(_vvTex, _vv->getWidth(), _vv->getHeight());
_mipMapGen->renderMipmapsFor(_vvTex, _vv->getWidth(), _vv->getHeight(), _vv->getBrickSize());
_maxMipMapLevel = _mipMapGen->computeMaxLevel(_vv->getWidth(), _vv->getHeight());
LDEBUG("...finished computing voxel visibilities mip maps.");
}
}
}
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2013, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universität München
// Boltzmannstr. 3, 85748 Garching b. München, 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 AdvOptimizedRaycaster_H__
#define AdvOptimizedRaycaster_H__
#include "core/pipeline/raycastingprocessor.h"
#include "core/properties/floatingpointproperty.h"
#include "core/properties/genericproperty.h"
#include "core/properties/transferfunctionproperty.h"
#include "modules/vis/advraycaster/rendervolumeVoxelizing.h"
#include "modules/vis/advraycaster/VoxelTextureMipMapping.h"
#include <string>
namespace tgt {
class Shader;
}
namespace campvis {
/**
* Performs ray casting using acceleration data structure. Also, the number of control instructions which is used is reduced to increase the execution performance on the gpu.
* The method is similar to [THGM11] paper. First, the data to be rendered is voxelized and stored in a 2D texture (each element of the texture is a 32-bit int. So, each element of the texture
* can store 32 ). Then, a hierarchy is generated to increase the ray-rendering
* [THGM11] Sinje Thiedemann, Niklas Henrich, Thorsten Grosch, and Stefan Müller. 2011. Voxel-based global illumination. In Symposium on Interactive 3D Graphics and Games (I3D '11). ACM, New York, NY, USA, 103-110. DOI=10.1145/1944745.1944763 http://doi.acm.org/10.1145/1944745.1944763
*/
class AdvOptimizedRaycaster : public RaycastingProcessor {
public:
enum AdditionalInvalidationLevels {
INVALID_BBV = AbstractProcessor::FIRST_FREE_TO_USE_INVALIDATION_LEVEL
};
/**
* Constructs a new AdvOptimizedRaycaster Processor
**/
AdvOptimizedRaycaster(IVec2Property* viewportSizeProp);
/**
* Destructor
**/
virtual ~AdvOptimizedRaycaster();
/// \see AbstractProcessor::getName()
virtual const std::string getName() const { return "AdvOptimizedRaycaster"; };
/// \see AbstractProcessor::getDescription()
virtual const std::string getDescription() const { return "Performs advanced ray casting to render sparse and big volumes faster."; };
/// \see AbstractProcessor::getAuthor()
virtual const std::string getAuthor() const { return "Morteza Mostajab <mostajab@in.tum.de>"; };
/// \see AbstractProcessor::getProcessorState()
virtual ProcessorState getProcessorState() const { return AbstractProcessor::EXPERIMENTAL; };
/// \see AbstractProcessor::init
virtual void init();
/// \see AbstractProcessor::deinit
virtual void deinit();
DataNameProperty p_targetImageID; ///< image ID for output image
BoolProperty p_enableShadowing;
FloatProperty p_shadowIntensity;
BoolProperty p_enableIntersectionRefinement;
BoolProperty p_useEmptySpaceSkipping;
protected:
/// \see HasProperyCollection::updateProperties()
virtual void updateProperties();
/// \see RaycastingProcessor::processImpl()
virtual void processImpl(DataContainer& data, ImageRepresentationGL::ScopedRepresentation& image);
/// \see RaycastingProcessor::generateHeader()
virtual std::string generateHeader() const;
/// Renders the voxelized volume
void renderVv(DataContainer& dh);
VoxelTexMipMapGenerator* _mipMapGen; //!< Voxel Texture Mip Map Generator
int _maxMipMapLevel;
VoxelizedRenderVolume* _vv; //!< Voxelized Render volume
tgt::Texture* _vvTex; //!< Generated 2D Texture
FaceGeometry* _quad;
tgt::Shader* _voxelGeneratorShdr;
static const std::string loggerCat_;
};
}
#endif // AdvOptimizedRaycaster_H__
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2013, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitt Mnchen
// Boltzmannstr. 3, 85748 Garching b. Mnchen, 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
//