/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * Copyright (C) 2003-2005 3Dlabs Inc. Ltd. * * This application is open source and may be redistributed and/or modified * freely and without restriction, both in commericial and non commericial applications, * as long as this copyright notice is maintained. * * This application is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ /* file: examples/osgshaders/GL2Scene.cpp * author: Mike Weiblen 2005-05-01 * * Compose a scene of several instances of a model, with a different * OpenGL Shading Language shader applied to each. * * See http://www.3dlabs.com/opengl2/ for more information regarding * the OpenGL Shading Language. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "GL2Scene.h" #include "Noise.h" /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// static osg::Image* make3DNoiseImage(int texSize) { osg::Image* image = new osg::Image; image->setImage(texSize, texSize, texSize, 4, GL_RGBA, GL_UNSIGNED_BYTE, new unsigned char[4 * texSize * texSize * texSize], osg::Image::USE_NEW_DELETE); const int startFrequency = 4; const int numOctaves = 4; int f, i, j, k, inc; double ni[3]; double inci, incj, inck; int frequency = startFrequency; GLubyte *ptr; double amp = 0.5; osg::notify(osg::INFO) << "creating 3D noise texture... "; for (f = 0, inc = 0; f < numOctaves; ++f, frequency *= 2, ++inc, amp *= 0.5) { SetNoiseFrequency(frequency); ptr = image->data(); ni[0] = ni[1] = ni[2] = 0; inci = 1.0 / (texSize / frequency); for (i = 0; i < texSize; ++i, ni[0] += inci) { incj = 1.0 / (texSize / frequency); for (j = 0; j < texSize; ++j, ni[1] += incj) { inck = 1.0 / (texSize / frequency); for (k = 0; k < texSize; ++k, ni[2] += inck, ptr += 4) { *(ptr+inc) = (GLubyte) (((noise3(ni) + 1.0) * amp) * 128.0); } } } } osg::notify(osg::INFO) << "DONE" << std::endl; return image; } static osg::Texture3D* make3DNoiseTexture(int texSize ) { osg::Texture3D* noiseTexture = new osg::Texture3D; noiseTexture->setFilter(osg::Texture3D::MIN_FILTER, osg::Texture3D::LINEAR); noiseTexture->setFilter(osg::Texture3D::MAG_FILTER, osg::Texture3D::LINEAR); noiseTexture->setWrap(osg::Texture3D::WRAP_S, osg::Texture3D::REPEAT); noiseTexture->setWrap(osg::Texture3D::WRAP_T, osg::Texture3D::REPEAT); noiseTexture->setWrap(osg::Texture3D::WRAP_R, osg::Texture3D::REPEAT); noiseTexture->setImage( make3DNoiseImage(texSize) ); return noiseTexture; } /////////////////////////////////////////////////////////////////////////// static osg::Image* make1DSineImage( int texSize ) { const float PI = 3.1415927; osg::Image* image = new osg::Image; image->setImage(texSize, 1, 1, 4, GL_RGBA, GL_UNSIGNED_BYTE, new unsigned char[4 * texSize], osg::Image::USE_NEW_DELETE); GLubyte* ptr = image->data(); float inc = 2. * PI / (float)texSize; for(int i = 0; i < texSize; i++) { *ptr++ = (GLubyte)((sinf(i * inc) * 0.5 + 0.5) * 255.); *ptr++ = 0; *ptr++ = 0; *ptr++ = 1; } return image; } static osg::Texture1D* make1DSineTexture( int texSize ) { osg::Texture1D* sineTexture = new osg::Texture1D; sineTexture->setWrap(osg::Texture1D::WRAP_S, osg::Texture1D::REPEAT); sineTexture->setFilter(osg::Texture1D::MIN_FILTER, osg::Texture1D::LINEAR); sineTexture->setFilter(osg::Texture1D::MAG_FILTER, osg::Texture1D::LINEAR); sineTexture->setImage( make1DSineImage(texSize) ); return sineTexture; } /////////////////////////////////////////////////////////////////////////// // in-line GLSL source code for the "microshader" example static const char *microshaderVertSource = { "// microshader - colors a fragment based on its position\n" "varying vec4 color;\n" "void main(void)\n" "{\n" " color = gl_Vertex;\n" " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" "}\n" }; static const char *microshaderFragSource = { "varying vec4 color;\n" "void main(void)\n" "{\n" " gl_FragColor = clamp( color, 0.0, 1.0 );\n" "}\n" }; /////////////////////////////////////////////////////////////////////////// static osg::ref_ptr rootNode; // Create some geometry upon which to render GLSL shaders. static osg::Geode* CreateModel() { osg::Geode* geode = new osg::Geode(); geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0.0f,0.0f,0.0f),1.0f))); geode->addDrawable(new osg::ShapeDrawable(new osg::Cone(osg::Vec3(2.2f,0.0f,-0.4f),0.9f,1.8f))); geode->addDrawable(new osg::ShapeDrawable(new osg::Cylinder(osg::Vec3(4.4f,0.0f,0.0f),1.0f,1.4f))); return geode; } // Add a reference to the masterModel at the specified translation, and // return its StateSet so we can easily attach StateAttributes. static osg::StateSet* ModelInstance() { static float zvalue = 0.0f; static osg::Node* masterModel = CreateModel(); osg::PositionAttitudeTransform* xform = new osg::PositionAttitudeTransform(); xform->setPosition(osg::Vec3( 0.0f, -1.0f, zvalue )); zvalue = zvalue + 2.2f; xform->addChild(masterModel); rootNode->addChild(xform); return xform->getOrCreateStateSet(); } // load source from a file. static void LoadShaderSource( osg::Shader* shader, const std::string& fileName ) { std::string fqFileName = osgDB::findDataFile(fileName); if( fqFileName.length() != 0 ) { shader->loadShaderSourceFromFile( fqFileName.c_str() ); } else { osg::notify(osg::WARN) << "File \"" << fileName << "\" not found." << std::endl; } } /////////////////////////////////////////////////////////////////////////// // rude but convenient globals static osg::Program* BlockyProgram; static osg::Shader* BlockyVertObj; static osg::Shader* BlockyFragObj; static osg::Program* ErodedProgram; static osg::Shader* ErodedVertObj; static osg::Shader* ErodedFragObj; static osg::Program* MarbleProgram; static osg::Shader* MarbleVertObj; static osg::Shader* MarbleFragObj; /////////////////////////////////////////////////////////////////////////// // for demo simplicity, this one callback animates all the shaders, instancing // for each uniform but with a specific operation each time. class AnimateCallback: public osg::Uniform::Callback { public: enum Operation { OFFSET, SIN, COLOR1, COLOR2 }; AnimateCallback(Operation op) : _enabled(true),_operation(op) {} virtual void operator() ( osg::Uniform* uniform, osg::NodeVisitor* nv ) { if( _enabled ) { float angle = 2.0 * nv->getFrameStamp()->getSimulationTime(); float sine = sinf( angle ); // -1 -> 1 float v01 = 0.5f * sine + 0.5f; // 0 -> 1 float v10 = 1.0f - v01; // 1 -> 0 switch(_operation) { case OFFSET : uniform->set( osg::Vec3(0.505f, 0.8f*v01, 0.0f) ); break; case SIN : uniform->set( sine ); break; case COLOR1 : uniform->set( osg::Vec3(v10, 0.0f, 0.0f) ); break; case COLOR2 : uniform->set( osg::Vec3(v01, v01, v10) ); break; } } } private: bool _enabled; Operation _operation; }; /////////////////////////////////////////////////////////////////////////// // Compose a scenegraph with examples of GLSL shaders #define TEXUNIT_SINE 1 #define TEXUNIT_NOISE 2 osg::ref_ptr GL2Scene::buildScene() { osg::Texture3D* noiseTexture = make3DNoiseTexture( 32 /*128*/ ); osg::Texture1D* sineTexture = make1DSineTexture( 32 /*1024*/ ); // the root of our scenegraph. rootNode = new osg::Group; // attach some Uniforms to the root, to be inherited by Programs. { osg::Uniform* OffsetUniform = new osg::Uniform( "Offset", osg::Vec3(0.0f, 0.0f, 0.0f) ); osg::Uniform* SineUniform = new osg::Uniform( "Sine", 0.0f ); osg::Uniform* Color1Uniform = new osg::Uniform( "Color1", osg::Vec3(0.0f, 0.0f, 0.0f) ); osg::Uniform* Color2Uniform = new osg::Uniform( "Color2", osg::Vec3(0.0f, 0.0f, 0.0f) ); OffsetUniform->setUpdateCallback(new AnimateCallback(AnimateCallback::OFFSET)); SineUniform->setUpdateCallback(new AnimateCallback(AnimateCallback::SIN)); Color1Uniform->setUpdateCallback(new AnimateCallback(AnimateCallback::COLOR1)); Color2Uniform->setUpdateCallback(new AnimateCallback(AnimateCallback::COLOR2)); osg::StateSet* ss = rootNode->getOrCreateStateSet(); ss->addUniform( OffsetUniform ); ss->addUniform( SineUniform ); ss->addUniform( Color1Uniform ); ss->addUniform( Color2Uniform ); } // the simple Microshader (its source appears earlier in this file) { osg::StateSet* ss = ModelInstance(); osg::Program* program = new osg::Program; program->setName( "microshader" ); _programList.push_back( program ); program->addShader( new osg::Shader( osg::Shader::VERTEX, microshaderVertSource ) ); program->addShader( new osg::Shader( osg::Shader::FRAGMENT, microshaderFragSource ) ); ss->setAttributeAndModes( program, osg::StateAttribute::ON ); } // the "blocky" shader, a simple animation test { osg::StateSet* ss = ModelInstance(); BlockyProgram = new osg::Program; BlockyProgram->setName( "blocky" ); _programList.push_back( BlockyProgram ); BlockyVertObj = new osg::Shader( osg::Shader::VERTEX ); BlockyFragObj = new osg::Shader( osg::Shader::FRAGMENT ); BlockyProgram->addShader( BlockyFragObj ); BlockyProgram->addShader( BlockyVertObj ); ss->setAttributeAndModes(BlockyProgram, osg::StateAttribute::ON); } // the "eroded" shader, uses a noise texture to discard fragments { osg::StateSet* ss = ModelInstance(); ss->setTextureAttribute(TEXUNIT_NOISE, noiseTexture); ErodedProgram = new osg::Program; ErodedProgram->setName( "eroded" ); _programList.push_back( ErodedProgram ); ErodedVertObj = new osg::Shader( osg::Shader::VERTEX ); ErodedFragObj = new osg::Shader( osg::Shader::FRAGMENT ); ErodedProgram->addShader( ErodedFragObj ); ErodedProgram->addShader( ErodedVertObj ); ss->setAttributeAndModes(ErodedProgram, osg::StateAttribute::ON); ss->addUniform( new osg::Uniform("LightPosition", osg::Vec3(0.0f, 0.0f, 4.0f)) ); ss->addUniform( new osg::Uniform("Scale", 1.0f) ); ss->addUniform( new osg::Uniform("sampler3d", TEXUNIT_NOISE) ); } // the "marble" shader, uses two textures { osg::StateSet* ss = ModelInstance(); ss->setTextureAttribute(TEXUNIT_NOISE, noiseTexture); ss->setTextureAttribute(TEXUNIT_SINE, sineTexture); MarbleProgram = new osg::Program; MarbleProgram->setName( "marble" ); _programList.push_back( MarbleProgram ); MarbleVertObj = new osg::Shader( osg::Shader::VERTEX ); MarbleFragObj = new osg::Shader( osg::Shader::FRAGMENT ); MarbleProgram->addShader( MarbleFragObj ); MarbleProgram->addShader( MarbleVertObj ); ss->setAttributeAndModes(MarbleProgram, osg::StateAttribute::ON); ss->addUniform( new osg::Uniform("NoiseTex", TEXUNIT_NOISE) ); ss->addUniform( new osg::Uniform("SineTex", TEXUNIT_SINE) ); } #ifdef INTERNAL_3DLABS //[ // regular GL 1.x texturing for comparison. osg::StateSet* ss = ModelInstance(); osg::Texture2D* tex0 = new osg::Texture2D; tex0->setImage( osgDB::readImageFile( "images/3dl-ge100.png" ) ); ss->setTextureAttributeAndModes(0, tex0, osg::StateAttribute::ON); #endif //] reloadShaderSource(); #ifdef INTERNAL_3DLABS //[ // add logo overlays rootNode->addChild( osgDB::readNodeFile( "3dl_ogl.logo" ) ); #endif //] return rootNode; } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// GL2Scene::GL2Scene() { _rootNode = buildScene(); _shadersEnabled = true; } GL2Scene::~GL2Scene() { } void GL2Scene::reloadShaderSource() { osg::notify(osg::INFO) << "reloadShaderSource()" << std::endl; LoadShaderSource( BlockyVertObj, "shaders/blocky.vert" ); LoadShaderSource( BlockyFragObj, "shaders/blocky.frag" ); LoadShaderSource( ErodedVertObj, "shaders/eroded.vert" ); LoadShaderSource( ErodedFragObj, "shaders/eroded.frag" ); LoadShaderSource( MarbleVertObj, "shaders/marble.vert" ); LoadShaderSource( MarbleFragObj, "shaders/marble.frag" ); } // mew 2003-09-19 : TODO Need to revisit how to better control // osg::Program enable state in OSG core. glProgram are // different enough from other GL state that StateSet::setAttributeAndModes() // doesn't fit well, so came up with a local implementation. void GL2Scene::toggleShaderEnable() { _shadersEnabled = ! _shadersEnabled; osg::notify(osg::WARN) << "shader enable = " << ((_shadersEnabled) ? "ON" : "OFF") << std::endl; for( unsigned int i = 0; i < _programList.size(); i++ ) { //_programList[i]->enable( _shadersEnabled ); } } /*EOF*/