diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b59c30930..6d3bce725 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -37,6 +37,7 @@ IF(DYNAMIC_OPENSCENEGRAPH) ADD_SUBDIRECTORY(osgcopy) ADD_SUBDIRECTORY(osgcubemap) ADD_SUBDIRECTORY(osgdelaunay) + ADD_SUBDIRECTORY(osgdeferred) ADD_SUBDIRECTORY(osgcluster) ADD_SUBDIRECTORY(osgdatabaserevisions) ADD_SUBDIRECTORY(osgdepthpartition) diff --git a/examples/osgdeferred/CMakeLists.txt b/examples/osgdeferred/CMakeLists.txt new file mode 100644 index 000000000..cc38a23f4 --- /dev/null +++ b/examples/osgdeferred/CMakeLists.txt @@ -0,0 +1,12 @@ +SET(TARGET_SRC + osgdeferred.cpp +) + +SET(TARGET_H + osgdeferred.h +) + +SET(TARGET_ADDED_LIBRARIES osgShadow) + +#### end var setup ### +SETUP_EXAMPLE(osgdeferred) diff --git a/examples/osgdeferred/osgdeferred.cpp b/examples/osgdeferred/osgdeferred.cpp new file mode 100644 index 000000000..4ace27341 --- /dev/null +++ b/examples/osgdeferred/osgdeferred.cpp @@ -0,0 +1,377 @@ + +/* OpenSceneGraph example, osgdeferred. + * + * Original code by Michael Kapelko, published to osg example with permission + * OSG Deferred Shading ( https://bitbucket.org/kornerr/osg-deferred-shading ) + * Shader cleanup, removal of osgFX EffectCompositor and exchange of textures + * done by Christian Buchner + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "osgdeferred.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef OSG_LIBRARY_STATIC +// in case of a static build... +USE_OSGPLUGIN(osg2) +USE_OSGPLUGIN(png) +USE_OSGPLUGIN(jpeg) +USE_OSGPLUGIN(glsl) +USE_SERIALIZER_WRAPPER_LIBRARY(osg) +USE_GRAPHICSWINDOW() +#endif + + +osg::TextureRectangle *createFloatTextureRectangle(int textureSize) +{ + osg::ref_ptr tex2D = new osg::TextureRectangle; + tex2D->setTextureSize(textureSize, textureSize); + tex2D->setInternalFormat(GL_RGBA16F_ARB); + tex2D->setSourceFormat(GL_RGBA); + tex2D->setSourceType(GL_FLOAT); + return tex2D.release(); +} + +osg::Camera *createHUDCamera(double left, + double right, + double bottom, + double top) +{ + osg::ref_ptr camera = new osg::Camera; + camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); + camera->setClearMask(GL_DEPTH_BUFFER_BIT); + camera->setRenderOrder(osg::Camera::POST_RENDER); + camera->setAllowEventFocus(false); + camera->setProjectionMatrix(osg::Matrix::ortho2D(left, right, bottom, top)); + camera->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + return camera.release(); +} + +osg::ref_ptr createLight(const osg::Vec3 &pos) +{ + osg::ref_ptr light = new osg::LightSource; + light->getLight()->setPosition(osg::Vec4(pos.x(), pos.y(), pos.z(), 1)); + light->getLight()->setAmbient(osg::Vec4(0.2, 0.2, 0.2, 1)); + light->getLight()->setDiffuse(osg::Vec4(0.8, 0.8, 0.8, 1)); + return light; +} + +class CreateTangentSpace : public osg::NodeVisitor +{ +public: + CreateTangentSpace() : NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN), tsg(new osgUtil::TangentSpaceGenerator) {} + virtual void apply(osg::Geode& geode) + { + for (unsigned int i = 0; i < geode.getNumDrawables(); ++i) + { + osg::Geometry *geo = dynamic_cast(geode.getDrawable(i)); + if (geo != NULL) + { + // assume the texture coordinate for normal maps is stored in unit #0 + tsg->generate(geo, 0); + // pass2.vert expects the tangent array to be stored inside gl_MultiTexCoord1 + geo->setTexCoordArray(1, tsg->getTangentArray()); + } + } + traverse(geode); + } +private: + osg::ref_ptr tsg; +}; + +Pipeline createPipelinePlainOSG( + osg::ref_ptr scene, + osg::ref_ptr shadowedScene, + const osg::Vec3 lightPos) +{ + Pipeline p; + p.graph = new osg::Group; + p.textureSize = 1024; + + // Pass 1 (shadow). + p.pass1Shadows = createFloatTextureRectangle(p.textureSize); + osg::ref_ptr pass1 = + createRTTCamera(osg::Camera::COLOR_BUFFER, p.pass1Shadows); + pass1->addChild(shadowedScene.get()); + + // pass2 shades expects tangent vectors to be available as texcoord array for texture #1 + // we use osgUtil::TangentSpaceGenerator to generate these + CreateTangentSpace cts; + scene->accept(cts); + + // Pass 2 (positions, normals, colors). + p.pass2Positions = createFloatTextureRectangle(p.textureSize); + p.pass2Normals = createFloatTextureRectangle(p.textureSize); + p.pass2Colors = createFloatTextureRectangle(p.textureSize); + osg::ref_ptr pass2 = + createRTTCamera(osg::Camera::COLOR_BUFFER0, p.pass2Positions); + pass2->attach(osg::Camera::COLOR_BUFFER1, p.pass2Normals); + pass2->attach(osg::Camera::COLOR_BUFFER2, p.pass2Colors); + pass2->addChild(scene.get()); + osg::StateSet *ss = setShaderProgram(pass2, "shaders/pass2.vert", "shaders/pass2.frag"); + ss->setTextureAttributeAndModes(0, createTexture("Images/whitemetal_diffuse.jpg")); + ss->setTextureAttributeAndModes(1, createTexture("Images/whitemetal_normal.jpg")); + ss->addUniform(new osg::Uniform("diffMap", 0)); + ss->addUniform(new osg::Uniform("bumpMap", 1)); + ss->addUniform(new osg::Uniform("useBumpMap", 1)); + + // Pass 3 (final). + p.pass3Final = createFloatTextureRectangle(p.textureSize); + osg::ref_ptr pass3 = + createRTTCamera(osg::Camera::COLOR_BUFFER, p.pass3Final, true); + ss = setShaderProgram(pass3, "shaders/pass3.vert", "shaders/pass3.frag"); + ss->setTextureAttributeAndModes(0, p.pass2Positions); + ss->setTextureAttributeAndModes(1, p.pass2Normals); + ss->setTextureAttributeAndModes(2, p.pass2Colors); + ss->setTextureAttributeAndModes(3, p.pass1Shadows); + ss->addUniform(new osg::Uniform("posMap", 0)); + ss->addUniform(new osg::Uniform("normalMap", 1)); + ss->addUniform(new osg::Uniform("colorMap", 2)); + ss->addUniform(new osg::Uniform("shadowMap", 3)); + // Light position. + ss->addUniform(new osg::Uniform("lightPos", lightPos)); + // Graph. + p.graph->addChild(pass1); + p.graph->addChild(pass2); + p.graph->addChild(pass3); + return p; +} + +osg::Camera *createRTTCamera(osg::Camera::BufferComponent buffer, + osg::Texture *tex, + bool isAbsolute) +{ + osg::ref_ptr camera = new osg::Camera; + camera->setClearColor(osg::Vec4()); + camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); + camera->setRenderOrder(osg::Camera::PRE_RENDER); + if (tex) + { + tex->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR); + tex->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR); + camera->setViewport(0, 0, tex->getTextureWidth(), tex->getTextureHeight()); + camera->attach(buffer, tex); + } + if (isAbsolute) + { + camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); + camera->setProjectionMatrix(osg::Matrix::ortho2D(0.0, 1.0, 0.0, 1.0)); + camera->setViewMatrix(osg::Matrix::identity()); + camera->addChild(createScreenQuad(1.0f, 1.0f)); + } + return camera.release(); +} + +osg::ref_ptr createSceneRoom() +{ + // Room. + osg::ref_ptr room = new osg::MatrixTransform; + osg::ref_ptr roomModel = osgDB::readNodeFile("simpleroom.osgt"); + room->addChild(roomModel); + room->setMatrix(osg::Matrix::translate(0, 0, 1)); + // Torus. + osg::ref_ptr torus = new osg::MatrixTransform; + osg::ref_ptr torusModel = osgDB::readNodeFile("torus.osgt"); + torus->addChild(torusModel); + setAnimationPath(torus, osg::Vec3(0, 0, 15), 6, 16); + // Torus2. + osg::ref_ptr torus2 = new osg::MatrixTransform; + torus2->addChild(torusModel); + setAnimationPath(torus2, osg::Vec3(-20, 0, 10), 20, 0); + // Torus3. + osg::ref_ptr torus3 = new osg::MatrixTransform; + torus3->addChild(torusModel); + setAnimationPath(torus3, osg::Vec3(0, 0, 40), 3, 25); + // Scene. + osg::ref_ptr scene = new osg::Group; + scene->addChild(room); + scene->addChild(torus); + scene->addChild(torus2); + scene->addChild(torus3); + return scene; +} + +osg::Geode *createScreenQuad(float width, + float height, + float scale, + osg::Vec3 corner) +{ + osg::Geometry* geom = osg::createTexturedQuadGeometry( + corner, + osg::Vec3(width, 0, 0), + osg::Vec3(0, height, 0), + 0, + 0, + scale, + scale); + osg::ref_ptr quad = new osg::Geode; + quad->addDrawable(geom); + int values = osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED; + quad->getOrCreateStateSet()->setAttribute( + new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, + osg::PolygonMode::FILL), + values); + quad->getOrCreateStateSet()->setMode(GL_LIGHTING, values); + return quad.release(); +} + +osg::Texture2D *createTexture(const std::string &fileName) +{ + osg::ref_ptr texture = new osg::Texture2D; + texture->setImage(osgDB::readImageFile(fileName)); + texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::REPEAT); + texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::REPEAT); + texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR); + texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); + texture->setMaxAnisotropy(16.0f); + return texture.release(); +} + +osg::ref_ptr createTextureDisplayQuad( + const osg::Vec3 &pos, + osg::StateAttribute *tex, + float scale, + float width, + float height) +{ + osg::ref_ptr hc = createHUDCamera(); + hc->addChild(createScreenQuad(width, height, scale, pos)); + hc->getOrCreateStateSet()->setTextureAttributeAndModes(0, tex); + return hc; +} + +void setAnimationPath(osg::ref_ptr node, + const osg::Vec3 ¢er, + float time, + float radius) +{ + // Create animation. + osg::ref_ptr path = new osg::AnimationPath; + path->setLoopMode(osg::AnimationPath::LOOP); + unsigned int numSamples = 32; + float delta_yaw = 2.0f * osg::PI / (static_cast(numSamples) - 1.0f); + float delta_time = time / static_cast(numSamples); + for (unsigned int i = 0; i < numSamples; ++i) + { + float yaw = delta_yaw * static_cast(i); + osg::Vec3 pos(center.x() + sinf(yaw)*radius, + center.y() + cosf(yaw)*radius, + center.z()); + osg::Quat rot(-yaw, osg::Z_AXIS); + path->insert(delta_time * static_cast(i), + osg::AnimationPath::ControlPoint(pos, rot)); + } + // Assign it. + node->setUpdateCallback(new osg::AnimationPathCallback(path)); +} + +osg::ref_ptr setShaderProgram(osg::ref_ptr pass, + const std::string& vert, + const std::string& frag) +{ + osg::ref_ptr program = new osg::Program; + program->addShader(osgDB::readShaderFile(vert)); + program->addShader(osgDB::readShaderFile(frag)); + osg::ref_ptr ss = pass->getOrCreateStateSet(); + ss->setAttributeAndModes( + program.get(), + osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + return ss; +} + +int main() +{ + // Useful declaration. + osg::ref_ptr ss; + // Scene. + osg::Vec3 lightPos(0, 0, 80); + osg::ref_ptr scene = createSceneRoom(); + osg::ref_ptr light = createLight(lightPos); + scene->addChild(light.get()); + // Shadowed scene. + osg::ref_ptr shadowMap = new osgShadow::SoftShadowMap; + shadowMap->setJitteringScale(16); + shadowMap->addShader(osgDB::readShaderFile("shaders/pass1Shadow.frag")); + shadowMap->setLight(light); + osg::ref_ptr shadowedScene = new osgShadow::ShadowedScene; + shadowedScene->setShadowTechnique(shadowMap.get()); + shadowedScene->addChild(scene.get()); + Pipeline p = createPipelinePlainOSG(scene, shadowedScene, lightPos); + // Quads to display 1 pass textures. + osg::ref_ptr qTexN = + createTextureDisplayQuad(osg::Vec3(0, 0.7, 0), + p.pass2Normals, + p.textureSize); + osg::ref_ptr qTexP = + createTextureDisplayQuad(osg::Vec3(0, 0.35, 0), + p.pass2Positions, + p.textureSize); + osg::ref_ptr qTexC = + createTextureDisplayQuad(osg::Vec3(0, 0, 0), + p.pass2Colors, + p.textureSize); + // Qaud to display 2 pass shadow texture. + osg::ref_ptr qTexS = + createTextureDisplayQuad(osg::Vec3(0.7, 0.7, 0), + p.pass1Shadows, + p.textureSize); + // Quad to display 3 pass final (screen) texture. + osg::ref_ptr qTexFinal = + createTextureDisplayQuad(osg::Vec3(0, 0, 0), + p.pass3Final, + p.textureSize, + 1, + 1); + // Must be processed before the first pass takes + // the result into pass1Shadows texture. + p.graph->insertChild(0, shadowedScene.get()); + // Quads are displayed in order, so the biggest one (final) must be first, + // otherwise other quads won't be visible. + p.graph->addChild(qTexFinal.get()); + p.graph->addChild(qTexN.get()); + p.graph->addChild(qTexP.get()); + p.graph->addChild(qTexC.get()); + p.graph->addChild(qTexS.get()); + + // Display everything. + osgViewer::Viewer viewer; + + // add the stats handler + viewer.addEventHandler(new osgViewer::StatsHandler); + + // Make screenshots with 'c'. + viewer.addEventHandler( + new osgViewer::ScreenCaptureHandler( + new osgViewer::ScreenCaptureHandler::WriteToFile( + "screenshot", + "png", + osgViewer::ScreenCaptureHandler::WriteToFile::OVERWRITE))); + + viewer.getCamera()->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR); + viewer.setSceneData(p.graph.get()); + + return viewer.run(); +} + diff --git a/examples/osgdeferred/osgdeferred.h b/examples/osgdeferred/osgdeferred.h new file mode 100644 index 000000000..dcfc05142 --- /dev/null +++ b/examples/osgdeferred/osgdeferred.h @@ -0,0 +1,66 @@ + +#include +#include +#include +#include +#include +#include + +struct Pipeline +{ + int textureSize; + osg::ref_ptr graph; + osg::Texture *pass1Shadows; + osg::Texture *pass2Colors; + osg::Texture *pass2Normals; + osg::Texture *pass2Positions; + osg::Texture *pass3Final; +}; + +osg::TextureRectangle *createFloatTextureRectangle(); + +osg::Camera *createHUDCamera(double left = 0, + double right = 1, + double bottom = 0, + double top = 1); + +osg::ref_ptr createLight(const osg::Vec3 &pos); + +Pipeline createPipelineEffectCompositor( + osg::ref_ptr scene, + osg::ref_ptr shadowedScene, + const osg::Vec3 lightPos); + +Pipeline createPipelinePlainOSG( + osg::ref_ptr scene, + osg::ref_ptr shadowedScene, + const osg::Vec3 lightPos); + +osg::Camera *createRTTCamera(osg::Camera::BufferComponent buffer, + osg::Texture *tex, + bool isAbsolute = false); + +osg::ref_ptr createSceneRoom(); + +osg::Geode *createScreenQuad(float width, + float height, + float scale = 1, + osg::Vec3 corner = osg::Vec3()); + +osg::Texture2D *createTexture(const std::string &fileName); + +osg::ref_ptr createTextureDisplayQuad(const osg::Vec3 &pos, + osg::StateAttribute *tex, + float scale, + float width = 0.3, + float height = 0.2); + +void setAnimationPath(osg::ref_ptr node, + const osg::Vec3 ¢er, + float time, + float radius); + +osg::ref_ptr setShaderProgram(osg::ref_ptr pass, + const std::string& vert, + const std::string& frag); + diff --git a/runexamples.bat b/runexamples.bat index 19c454e1d..3ccf98782 100644 --- a/runexamples.bat +++ b/runexamples.bat @@ -51,6 +51,9 @@ osgcubemap echo osgdistortion osgdistortion +echo osgdeferred +osgdeferred + echo osgforest osgforest