glreduction.cpp 8.98 KB
Newer Older
1
2
3
4
// ================================================================================================
// 
// This file is part of the CAMPVis Software Framework.
// 
5
// If not explicitly stated otherwise: Copyright (C) 2012-2013, all rights reserved,
6
7
8
9
//      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
10
// 
11
12
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
// 
13
14
15
16
// 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
17
// 
18
19
20
21
// 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.
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 
// ================================================================================================

#include "glreduction.h"

#include "tgt/logmanager.h"
#include "tgt/framebufferobject.h"
#include "tgt/shadermanager.h"
#include "tgt/texture.h"
#include "tgt/textureunit.h"

#include "core/datastructures/facegeometry.h"
#include "core/datastructures/imagedata.h"
#include "core/datastructures/imagerepresentationgl.h"
#include "core/tools/quadrenderer.h"

#include <algorithm>

#define DIV_CEIL(x,y) ((x) > 0) ? (1 + ((x) - 1)/(y)) : ((x) / (y))

namespace campvis {

    const std::string GlReduction::loggerCat_ = "CAMPVis.modules.registration.GlReduction";

46
47
    GlReduction::GlReduction(ReductionOperator reductionOperator)
        : _reductionOperator(reductionOperator)
48
49
        , _shader2d(0)
        , _shader3d(0)
50
51
        , _fbo(0)
    {
52
53
54
        _shader2d = ShdrMgr.loadSeparate("core/glsl/passthrough.vert", "core/glsl/tools/glreduction.frag", generateGlslHeader(_reductionOperator) + "#define REDUCTION_2D\n", false);
        _shader3d = ShdrMgr.loadSeparate("core/glsl/passthrough.vert", "core/glsl/tools/glreduction.frag", generateGlslHeader(_reductionOperator) + "#define REDUCTION_3D\n", false);
        if (_shader2d == 0 || _shader3d == 0) {
55
56
57
58
            LERROR("Could not load Shader for OpenGL reduction. Reduction will not work!");
            return;
        }

59
60
61
62
        _shader2d->setAttributeLocation(0, "in_Position");
        _shader2d->setAttributeLocation(1, "in_TexCoord");
        _shader3d->setAttributeLocation(0, "in_Position");
        _shader3d->setAttributeLocation(1, "in_TexCoord");
63
64
65
    }

    GlReduction::~GlReduction() {
66
67
        ShdrMgr.dispose(_shader2d);
        ShdrMgr.dispose(_shader3d);
68
69
    }

70
    std::vector<float> GlReduction::reduce(const ImageData* image) {
71
        tgtAssert(image != 0, "Image must not be 0!");
72
        if (_shader2d == 0 || _shader3d == 0) {
73
            LERROR("Could not load Shader for OpenGL reduction. Reduction will not work!");
74
            return std::vector<float>();
75
76
77
        }
        if (image == 0) {
            LERROR("Empty image received - nothing to reduce!");
78
            return std::vector<float>();
79
80
81
82
83
        }

        const ImageRepresentationGL* repGl = image->getRepresentation<ImageRepresentationGL>();
        if (repGl == 0) {
            LERROR("Could not convert input image to OpenGL texture - no reduction possible!");
84
            return std::vector<float>();
85
86
        }

87
88
89
        return reduce(repGl->getTexture());
    }
    
90
91
92
    std::vector<float> GlReduction::reduce(const tgt::Texture* texture) {
        std::vector<float> toReturn;

93
        tgtAssert(texture != 0, "Image must not be 0!");
94
        if (_shader2d == 0 || _shader3d == 0) {
95
            LERROR("Could not load Shader for OpenGL reduction. Reduction will not work!");
96
            return toReturn;
97
98
99
        }
        if (texture == 0) {
            LERROR("Empty texture received - nothing to reduce!");
100
            return toReturn;
101
        }
102

103
        const tgt::ivec3& size = texture->getDimensions();
104
        tgt::vec2 texCoordShift = tgt::vec2(.5f) / tgt::vec2(size.xy());
105
        tgt::ivec2 currentSize = size.xy();
106
107
        reduceSizes(currentSize, texCoordShift);
        tgt::ivec2 startSize = currentSize;
108
109
110
111
112
113
114
115

        // Set OpenGL pixel alignment to 1 to avoid problems with NPOT textures
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

        // get a free texture unit
        tgt::TextureUnit inputUnit;

        // create temporary textures
116
        tgt::Texture* tempTextures[2];
117
        for (size_t i = 0; i < 2; ++i) {
118
119
            tempTextures[i] = new tgt::Texture(0, tgt::ivec3(currentSize, 1), GL_RGBA, GL_RGBA32F, GL_FLOAT, tgt::Texture::NEAREST);
            tempTextures[i]->uploadTexture();
120
            tempTextures[i]->setWrapping(tgt::Texture::CLAMP_TO_EDGE);
121
        }
122
123
        size_t readTex = 0;
        size_t writeTex = 1;
124
125
126
127
128
129
130

        // create and initialize FBO
        _fbo = new tgt::FramebufferObject();
        _fbo->activate();
        LGL_ERROR;

        // perform first reduction step outside:
131
132
        tgt::Shader* leShader = (texture->getDimensions().z == 1) ? _shader2d : _shader3d;
        leShader->activate();
133
        _fbo->attachTexture(tempTextures[readTex]);
134

135
136
        inputUnit.activate();
        texture->bind();
137
138
139
140
141
        leShader->setUniform("_texture", inputUnit.getUnitNumber());
        leShader->setUniform("_texCoordsShift", texCoordShift);
        if (leShader == _shader3d)
            leShader->setUniform("_textureDepth", texture->getDimensions().z);

142
        glViewport(startSize.x - currentSize.x, startSize.y - currentSize.y, currentSize.x, currentSize.y);
143
        QuadRdr.renderQuad();
144
        leShader->deactivate();
145
146
        LGL_ERROR;

147
148
        _shader2d->activate();
        _shader2d->setUniform("_texture", inputUnit.getUnitNumber());
149
150
151
        reduceSizes(currentSize, texCoordShift);
        glViewport(startSize.x - currentSize.x, startSize.y - currentSize.y, currentSize.x, currentSize.y);

152
153
        // perform reduction until 1x1 texture remains
        while (currentSize.x > 1 || currentSize.y > 1) {
154
155
            _fbo->attachTexture(tempTextures[writeTex]);
            tempTextures[readTex]->bind();
156

157
            _shader2d->setUniform("_texCoordsShift", texCoordShift);
158
159
160
            QuadRdr.renderQuad();
            LGL_ERROR;

161
            reduceSizes(currentSize, texCoordShift);
162
            std::swap(writeTex, readTex);
163
164
        }

165
        _shader2d->deactivate();
166
167
168


        // read back stuff
169
170
        GLenum readBackFormat = tempTextures[readTex]->getFormat();
        size_t channels = tempTextures[readTex]->getNumChannels();
171
        toReturn.resize(currentSize.x * currentSize.y * channels);
172
        glReadBuffer(GL_COLOR_ATTACHMENT0);
173
        glReadPixels(startSize.x - currentSize.x, startSize.y - currentSize.y, currentSize.x, currentSize.y, readBackFormat, GL_FLOAT, &toReturn.front());
174
175
176
177
178
179
180
181
        LGL_ERROR;

        // clean up...
        _fbo->detachAll();
        _fbo->deactivate();
        delete _fbo;
        _fbo = 0;

182
183
        delete tempTextures[0];
        delete tempTextures[1];
184
185
        LGL_ERROR;

186
        return toReturn;
187
188
    }

189
    void GlReduction::reduceSizes(tgt::ivec2& currentSize, tgt::vec2& texCoordShift) {
190
191
        if (currentSize.x > 1) {
            currentSize.x = DIV_CEIL(currentSize.x, 2);
192
193
            if (currentSize.x == 1)
                texCoordShift.x *= -1.f;
194
195
196
        }
        if (currentSize.y > 1) {
            currentSize.y = DIV_CEIL(currentSize.y, 2);
197
198
            if (currentSize.y == 1)
                texCoordShift.y *= -1.f;
199
        }
200

201
202
    }

203
204
    std::string GlReduction::generateGlslHeader(ReductionOperator reductionOperator) {
        switch (reductionOperator) {
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
            case MIN:
                return 
                    "#define REDUCTION_OP_2(a, b) min(a, b)\n"
                    "#define REDUCTION_OP_4(a, b, c, d) min(a, min(b, min(c, d)))\n";
                break;
            case MAX:
                return 
                    "#define REDUCTION_OP_2(a, b) max(a, b)\n"
                    "#define REDUCTION_OP_4(a, b, c, d) max(a, max(b, max(c, d)))\n";
                break;
            case PLUS:
                return 
                    "#define REDUCTION_OP_2(a, b) a+b\n"
                    "#define REDUCTION_OP_4(a, b, c, d) a+b+c+d\n";
                break;
            case MULTIPLICATION:
                return 
                    "#define REDUCTION_OP_2(a, b) a*b\n"
                    "#define REDUCTION_OP_4(a, b, c, d) a*b*c*d\n";
                break;
            case MIN_MAX_DEPTH_ONLY:
                return 
                    "#define REDUCTION_OP_2(a, b) vec4(min(a.r, b.r), max(a.g, b.g), 0.0, 0.0)\n"
                    "#define REDUCTION_OP_4(a, b, c, d) vec4(min(a.r, min(b.r, min(c.r, d.r))), max(a.g, max(b.g, max(c.g, d.g))), 0.0, 0.0)\n";
                break;
            default:
                tgtAssert(false, "Should not reach this, wrong enum value?");
                return "";
                break;
234
235
236
        }
    }

237
}