root/OpenSceneGraph/trunk/examples/osgmultiplerendertargets/osgmultiplerendertargets.cpp @ 14259

Revision 13574, 17.3 kB (checked in by robert, 17 minutes ago)

From Sebastian Messerschmidt, "Added setColor function to modify an image based on texture coordinates, parallel to the getColor functionality."

  • Property svn:eol-style set to native
Line 
1/* OpenSceneGraph example, osgmultiplerendertargets.
2*
3*  Permission is hereby granted, free of charge, to any person obtaining a copy
4*  of this software and associated documentation files (the "Software"), to deal
5*  in the Software without restriction, including without limitation the rights
6*  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7*  copies of the Software, and to permit persons to whom the Software is
8*  furnished to do so, subject to the following conditions:
9*
10*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11*  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12*  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13*  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14*  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15*  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
16*  THE SOFTWARE.
17*/
18
19#include <osg/GLExtensions>
20#include <osg/Node>
21#include <osg/Geometry>
22#include <osg/Notify>
23#include <osg/MatrixTransform>
24#include <osg/Texture2D>
25#include <osg/TextureRectangle>
26#include <osg/ColorMask>
27#include <osg/Material>
28
29#include <osgGA/TrackballManipulator>
30#include <osgGA/FlightManipulator>
31#include <osgGA/DriveManipulator>
32
33#include <osgViewer/Viewer>
34
35#include <iostream>
36#include <stdio.h>
37
38//
39// Below is relatively straight forward example of using the OpenGL multiple render targets (MRT) extension
40// to FrameBufferObjects/GLSL shaders.
41//
42// Another, more sophisticated MRT example can be found in the osgstereomatch example.
43//
44
45
46// The callback modifies an input image.
47struct MyCameraPostDrawCallback : public osg::Camera::DrawCallback
48{
49    MyCameraPostDrawCallback(osg::Image* image):
50        _image(image)
51    {
52    }
53
54    virtual void operator () (const osg::Camera& /*camera*/) const
55    {
56        if (_image && _image->getPixelFormat()==GL_RGBA && _image->getDataType()==GL_UNSIGNED_BYTE)
57        {
58            // we'll pick out the center 1/2 of the whole image,
59            int column_start = _image->s()/4;
60            int column_end = 3*column_start;
61
62            int row_start = _image->t()/4;
63            int row_end = 3*row_start;
64
65            // and then halve their contribution
66            for(int r=row_start; r<row_end; ++r)
67            {
68                unsigned char* data = _image->data(column_start, r);
69                for(int c=column_start; c<column_end; ++c)
70                {
71                    (*data) = (*data)/2; ++data;
72                    (*data) = (*data)/2; ++data;
73                    (*data) = (*data)/2; ++data;
74                    (*data) = 255; ++data;
75                }
76            }
77
78            _image->dirty();
79        }
80        else if (_image && _image->getPixelFormat()==GL_RGBA && _image->getDataType()==GL_FLOAT)
81        {
82            // we'll pick out the center 1/2 of the whole image,
83            int column_start = _image->s()/4;
84            int column_end = 3*column_start;
85
86            int row_start = _image->t()/4;
87            int row_end = 3*row_start;
88
89            // and then halve their contribution
90            for(int r=row_start; r<row_end; ++r)
91            {
92                float* data = (float*)_image->data(column_start, r);
93                for(int c=column_start; c<column_end; ++c)
94                {
95                    (*data) = (*data)/2.0; ++data;
96                    (*data) = (*data)/2.0; ++data;
97                    (*data) = (*data)/2.0; ++data;
98                    (*data) = 1.0f; ++data;
99                }
100            }
101
102            _image->dirty();
103
104            //print out the first three values
105            float* data = (float*)_image->data(0, 0);
106            fprintf(stderr,"Float pixel data: r %e g %e b %e\n", data[0], data[1], data[2]);
107        }
108    }
109
110    osg::Image* _image;
111};
112
113#define NUM_TEXTURES 4
114
115// The quad geometry is used by the render to texture camera to generate multiple textures.
116osg::Group* createRTTQuad(unsigned int tex_width, unsigned int tex_height, bool useHDR)
117{
118    osg::Group *top_group = new osg::Group;
119
120    osg::ref_ptr<osg::Geode> quad_geode = new osg::Geode;
121
122    osg::ref_ptr<osg::Vec3Array> quad_coords = new osg::Vec3Array; // vertex coords
123    // counter-clockwise
124    quad_coords->push_back(osg::Vec3d(0, 0, -1));
125    quad_coords->push_back(osg::Vec3d(1, 0, -1));
126    quad_coords->push_back(osg::Vec3d(1, 1, -1));
127    quad_coords->push_back(osg::Vec3d(0, 1, -1));
128
129    osg::ref_ptr<osg::Vec2Array> quad_tcoords = new osg::Vec2Array; // texture coords
130    quad_tcoords->push_back(osg::Vec2(0, 0));
131    quad_tcoords->push_back(osg::Vec2(tex_width, 0));
132    quad_tcoords->push_back(osg::Vec2(tex_width, tex_height));
133    quad_tcoords->push_back(osg::Vec2(0, tex_height));
134
135    osg::ref_ptr<osg::Geometry> quad_geom = new osg::Geometry;
136    osg::ref_ptr<osg::DrawArrays> quad_da = new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4);
137
138    osg::ref_ptr<osg::Vec4Array> quad_colors = new osg::Vec4Array;
139    quad_colors->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
140
141    quad_geom->setVertexArray(quad_coords.get());
142    quad_geom->setTexCoordArray(0, quad_tcoords.get());
143    quad_geom->addPrimitiveSet(quad_da.get());
144    quad_geom->setColorArray(quad_colors.get(), osg::Array::BIND_OVERALL);
145
146    osg::StateSet *stateset = quad_geom->getOrCreateStateSet();
147    stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
148
149    stateset->addUniform(new osg::Uniform("width", (int)tex_width));
150
151    // Attach shader, glFragData is used to create data for multiple render targets
152
153    if (useHDR) {
154        static const char *shaderSource = {
155            "uniform int width;"
156            "void main(void)\n"
157            "{\n"
158            "    gl_FragData[0] = vec4(-1e-12,0,0,1);\n"
159            "    gl_FragData[1] = vec4(0,1e-12,0,1);\n"
160            "    gl_FragData[2] = vec4(0,0,1e-12,1);\n"
161            "    gl_FragData[3] = vec4(0,0,1e-12,1);\n"
162            "}\n"
163        };
164
165        osg::ref_ptr<osg::Shader> fshader = new osg::Shader( osg::Shader::FRAGMENT , shaderSource);
166        osg::ref_ptr<osg::Program> program = new osg::Program;
167        program->addShader(fshader.get());
168        stateset->setAttributeAndModes(program.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
169    } else {
170        static const char *shaderSource = {
171            "uniform int width;"
172            "void main(void)\n"
173            "{\n"
174            "    gl_FragData[0] = vec4(1,0,0,1);\n"
175            "    gl_FragData[1] = vec4(0,1,0,1);\n"
176            "    gl_FragData[2] = vec4(0,0,1,1);\n"
177            "    gl_FragData[3] = vec4(0,0,1,1);\n"
178            "}\n"
179        };
180
181        osg::ref_ptr<osg::Shader> fshader = new osg::Shader( osg::Shader::FRAGMENT , shaderSource);
182        osg::ref_ptr<osg::Program> program = new osg::Program;
183        program->addShader(fshader.get());
184        stateset->setAttributeAndModes(program.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
185    }
186
187    quad_geode->addDrawable(quad_geom.get());
188
189    top_group->addChild(quad_geode.get());
190
191    return top_group;
192}
193
194// Here a scene consisting of a single quad is created. This scene is viewed by the screen camera.
195// The quad is textured using a shader and the multiple textures generated in the RTT stage.
196osg::Node* createScene(osg::Node* cam_subgraph, unsigned int tex_width, unsigned int tex_height, bool useHDR, bool useImage, bool useMultiSample)
197{
198    if (!cam_subgraph) return 0;
199
200    // create a group to contain the quad and the pre render camera.
201    osg::Group* parent = new osg::Group;
202
203    // textures to render to and to use for texturing of the final quad
204    osg::TextureRectangle* textureRect[NUM_TEXTURES] = {0,0,0,0};
205
206    for (int i=0;i<NUM_TEXTURES;i++) {
207        textureRect[i] = new osg::TextureRectangle;
208        textureRect[i]->setTextureSize(tex_width, tex_height);
209        textureRect[i]->setInternalFormat(GL_RGBA);
210        textureRect[i]->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
211        textureRect[i]->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
212
213        if (useHDR)
214        {
215            // Default HDR format
216            textureRect[i]->setInternalFormat(GL_RGBA32F_ARB);
217
218            // GL_FLOAT_RGBA32_NV might be supported on pre 8-series GPUs
219            //textureRect[i]->setInternalFormat(GL_FLOAT_RGBA32_NV);
220
221            // GL_RGBA16F_ARB can be used with this example,
222            // but modify e-12 and e12 in the shaders accordingly
223            //textureRect[i]->setInternalFormat(GL_RGBA16F_ARB);
224
225            textureRect[i]->setSourceFormat(GL_RGBA);
226            textureRect[i]->setSourceType(GL_FLOAT);
227        }
228    }
229
230    // first create the geometry of the quad
231    {
232        osg::Geometry* polyGeom = new osg::Geometry();
233
234        polyGeom->setSupportsDisplayList(false);
235
236        osg::Vec3Array* vertices = new osg::Vec3Array;
237        osg::Vec2Array* texcoords = new osg::Vec2Array;
238
239        vertices->push_back(osg::Vec3d(0,0,0));
240        texcoords->push_back(osg::Vec2(0,0));
241
242        vertices->push_back(osg::Vec3d(1,0,0));
243        texcoords->push_back(osg::Vec2(tex_width,0));
244
245        vertices->push_back(osg::Vec3d(1,0,1));
246        texcoords->push_back(osg::Vec2(tex_width,tex_height));
247
248        vertices->push_back(osg::Vec3d(0,0,1));
249        texcoords->push_back(osg::Vec2(0,tex_height));
250
251        polyGeom->setVertexArray(vertices);
252        polyGeom->setTexCoordArray(0,texcoords);
253
254        osg::Vec4Array* colors = new osg::Vec4Array;
255        colors->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
256        polyGeom->setColorArray(colors, osg::Array::BIND_OVERALL);
257
258        polyGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,vertices->size()));
259
260        // now we need to add the textures (generated by RTT) to the Drawable.
261        osg::StateSet* stateset = new osg::StateSet;
262        for (int i=0;i<NUM_TEXTURES;i++){
263            stateset->setTextureAttributeAndModes(i, textureRect[i], osg::StateAttribute::ON);
264        }
265
266        polyGeom->setStateSet(stateset);
267
268        // Attach a shader to the final quad to combine the input textures.
269        if (useHDR) {
270            static const char *shaderSource = {
271                "uniform sampler2DRect textureID0;\n"
272                "uniform sampler2DRect textureID1;\n"
273                "uniform sampler2DRect textureID2;\n"
274                "uniform sampler2DRect textureID3;\n"
275                "uniform float width;\n"
276                "uniform float height; \n"
277                "void main(void)\n"
278                "{\n"
279                "    gl_FragData[0] = \n"
280                "     vec4(  -1e12 * texture2DRect( textureID0, gl_TexCoord[0].st ).rgb, 1) + \n"
281                "     vec4(   1e12 * texture2DRect( textureID1, gl_TexCoord[0].st ).rgb, 1) + \n"
282                "     vec4(   1e12 * texture2DRect( textureID2, gl_TexCoord[0].st ).rgb, 1) + \n"
283                "     vec4(-0.5e12 * texture2DRect( textureID3, gl_TexCoord[0].st ).rgb, 1);  \n"
284                "}\n"
285            };
286            osg::ref_ptr<osg::Shader> fshader = new osg::Shader( osg::Shader::FRAGMENT , shaderSource);
287            osg::ref_ptr<osg::Program> program = new osg::Program;
288            program->addShader( fshader.get());
289            stateset->setAttributeAndModes( program.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
290        } else {
291            static const char *shaderSource = {
292                "uniform sampler2DRect textureID0;\n"
293                "uniform sampler2DRect textureID1;\n"
294                "uniform sampler2DRect textureID2;\n"
295                "uniform sampler2DRect textureID3;\n"
296                "void main(void)\n"
297                "{\n"
298                "    gl_FragData[0] = \n"
299                "          vec4(texture2DRect( textureID0, gl_TexCoord[0].st ).rgb, 1) + \n"
300                "          vec4(texture2DRect( textureID1, gl_TexCoord[0].st ).rgb, 1) + \n"
301                "          vec4(texture2DRect( textureID2, gl_TexCoord[0].st ).rgb, 1) + \n"
302                "     -0.5*vec4(texture2DRect( textureID3, gl_TexCoord[0].st ).rgb, 1);  \n"
303                "}\n"
304            };
305            osg::ref_ptr<osg::Shader> fshader = new osg::Shader( osg::Shader::FRAGMENT , shaderSource);
306            osg::ref_ptr<osg::Program> program = new osg::Program;
307            program->addShader( fshader.get());
308            stateset->setAttributeAndModes( program.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
309
310        }
311
312        stateset->addUniform(new osg::Uniform("textureID0", 0));
313        stateset->addUniform(new osg::Uniform("textureID1", 1));
314        stateset->addUniform(new osg::Uniform("textureID2", 2));
315        stateset->addUniform(new osg::Uniform("textureID3", 3));
316
317        //stateset->setDataVariance(osg::Object::DYNAMIC);
318
319        osg::Geode* geode = new osg::Geode();
320        geode->addDrawable(polyGeom);
321
322        parent->addChild(geode);
323    }
324
325    // now create the camera to do the multiple render to texture
326    {
327        osg::Camera* camera = new osg::Camera;
328
329        // set up the background color and clear mask.
330        camera->setClearColor(osg::Vec4(0.1f,0.1f,0.3f,1.0f));
331        camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
332
333        // the camera is going to look at our input quad
334        camera->setProjectionMatrix(osg::Matrix::ortho2D(0,1,0,1));
335        camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
336        camera->setViewMatrix(osg::Matrix::identity());
337
338        // set viewport
339        camera->setViewport(0, 0, tex_width, tex_height);
340
341        // set the camera to render before the main camera.
342        camera->setRenderOrder(osg::Camera::PRE_RENDER);
343
344        // tell the camera to use OpenGL frame buffer objects
345        camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
346
347        // attach the textures to use
348        for (int i=0; i<NUM_TEXTURES; i++) {
349            if (useMultiSample)
350                camera->attach(osg::Camera::BufferComponent(osg::Camera::COLOR_BUFFER0+i), textureRect[i], 0, 0, false, 4, 4);
351            else
352                camera->attach(osg::Camera::BufferComponent(osg::Camera::COLOR_BUFFER0+i), textureRect[i]);
353
354
355        }
356
357        // we can also read back any of the targets as an image, modify this image and push it back
358        if (useImage) {
359            // which texture to get the image from
360            const int tex_to_get = 0;
361
362            osg::Image* image = new osg::Image;
363            if (useHDR) {
364                image->allocateImage(tex_width, tex_height, 1, GL_RGBA, GL_FLOAT);
365            } else {
366                image->allocateImage(tex_width, tex_height, 1, GL_RGBA, GL_UNSIGNED_BYTE);
367            }
368
369            // attach the image so its copied on each frame.
370            camera->attach(osg::Camera::BufferComponent(osg::Camera::COLOR_BUFFER0 + tex_to_get), image);
371
372            camera->setPostDrawCallback(new MyCameraPostDrawCallback(image));
373
374            // push back the image to the texture
375            textureRect[tex_to_get]->setImage(0, image);
376        }
377
378        // add the subgraph to render
379        camera->addChild(cam_subgraph);
380
381        parent->addChild(camera);
382    }
383
384    return parent;
385}
386
387int main( int argc, char **argv )
388{
389    // use an ArgumentParser object to manage the program arguments.
390    osg::ArgumentParser arguments(&argc,argv);
391
392    // set up the usage document, in case we need to print out how to use this program.
393    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName() + " demonstrates the use of multiple render targets (MRT) with frame buffer objects (FBOs). A render to texture (RTT) camera is used to render to four textures using a single shader. The four textures are then combined to texture the viewed geometry.");
394    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] ...");
395    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information.");
396    arguments.getApplicationUsage()->addCommandLineOption("--width","Set the width of the render to texture.");
397    arguments.getApplicationUsage()->addCommandLineOption("--height","Set the height of the render to texture.");
398    arguments.getApplicationUsage()->addCommandLineOption("--image","Render one of the targets to an image, then apply a post draw callback to modify it and use this image to update the final texture. Print some texture values when using HDR.");
399    arguments.getApplicationUsage()->addCommandLineOption("--hdr","Use high dynamic range (HDR). Create floating point textures to render to.");
400
401    // construct the viewer.
402    osgViewer::Viewer viewer(arguments);
403
404    // if user request help write it out to cout.
405    if (arguments.read("-h") || arguments.read("--help"))
406    {
407        arguments.getApplicationUsage()->write(std::cout);
408        return 1;
409    }
410
411    unsigned tex_width = 512;
412    unsigned tex_height = 512;
413    while (arguments.read("--width", tex_width)) {}
414    while (arguments.read("--height", tex_height)) {}
415
416    bool useHDR = false;
417    while (arguments.read("--hdr")) { useHDR = true; }
418
419    bool useImage = false;
420    while (arguments.read("--image")) { useImage = true; }
421
422    bool useMultiSample = false;
423    while (arguments.read("--ms")) { useMultiSample = true; }
424
425    osg::Group* subGraph = createRTTQuad(tex_width, tex_height, useHDR);
426
427    osg::Group* rootNode = new osg::Group();
428    rootNode->addChild(createScene(subGraph, tex_width, tex_height, useHDR, useImage, useMultiSample));
429
430    // add model to the viewer.
431    viewer.setSceneData( rootNode );
432
433    return viewer.run();
434}
Note: See TracBrowser for help on using the browser.