/* A demonstration of Tessellation Shaders in OpenScenegraph. * * Instructions: * Press plus to increase tessellation and minus to decrease it. * Press right arrow to increase inner tessellation and left arrow to decrease it. * Press up arrow to increase outer tessellation and down arrow to decrease it. * * Original code by Philip Rideout * Adapted to OpenScenegraph by John Kaniarz * Additional work by Michael Mc Donnell */ #include #include #include #include #include #include static const char* vertSource = { "#version 400\n" "in vec4 osg_Vertex;\n" "out vec3 vPosition;\n" "void main(){\n" " vPosition = osg_Vertex.xyz;\n" "}\n" }; static const char* tessControlSource = { "#version 400\n" "layout(vertices = 3) out;\n" "in vec3 vPosition[];\n" "out vec3 tcPosition[];\n" "uniform float TessLevelInner;\n" "uniform float TessLevelOuter;\n" "#define ID gl_InvocationID\n" "void main(){\n" " tcPosition[ID] = vPosition[ID];\n" " if (ID == 0) {\n" " gl_TessLevelInner[0] = TessLevelInner;\n" " gl_TessLevelOuter[0] = TessLevelOuter;\n" " gl_TessLevelOuter[1] = TessLevelOuter;\n" " gl_TessLevelOuter[2] = TessLevelOuter;\n" " }\n" "}\n" }; static const char* tessEvalSource = { "#version 400\n" "layout(triangles, equal_spacing, cw) in;\n" "in vec3 tcPosition[];\n" "out vec3 tePosition;\n" "out vec3 tePatchDistance;\n" "uniform mat4 osg_ProjectionMatrix;\n" "uniform mat4 osg_ModelViewMatrix;\n" "void main(){\n" " vec3 p0 = gl_TessCoord.x * tcPosition[0];\n" " vec3 p1 = gl_TessCoord.y * tcPosition[1];\n" " vec3 p2 = gl_TessCoord.z * tcPosition[2];\n" " tePatchDistance = gl_TessCoord;\n" " tePosition = normalize(p0 + p1 + p2);\n" " gl_Position = osg_ProjectionMatrix * osg_ModelViewMatrix * vec4(tePosition, 1);\n" "}\n" }; static const char* geomSource = { "#version 400\n" "uniform mat4 osg_ModelViewMatrix;\n" "uniform mat3 osg_NormalMatrix;\n" "layout(triangles) in;\n" "layout(triangle_strip, max_vertices = 3) out;\n" "in vec3 tePosition[3];\n" "in vec3 tePatchDistance[3];\n" "out vec3 gFacetNormal;\n" "out vec3 gPatchDistance;\n" "out vec3 gTriDistance;\n" "out vec4 gColor;\n" "void main(){\n" " vec3 A = tePosition[2] - tePosition[0];\n" " vec3 B = tePosition[1] - tePosition[0];\n" " gFacetNormal = osg_NormalMatrix * normalize(cross(A, B));\n" " gPatchDistance = tePatchDistance[0];\n" " gTriDistance = vec3(1, 0, 0);\n" " gColor = osg_ModelViewMatrix[0];\n" " gl_Position = gl_in[0].gl_Position; EmitVertex();\n" " gPatchDistance = tePatchDistance[1];\n" " gTriDistance = vec3(0, 1, 0);\n" " gColor = osg_ModelViewMatrix[1];\n" " gl_Position = gl_in[1].gl_Position; EmitVertex();\n" " gPatchDistance = tePatchDistance[2];\n" " gTriDistance = vec3(0, 0, 1);\n" " gColor = osg_ModelViewMatrix[2];\n" " gl_Position = gl_in[2].gl_Position; EmitVertex();\n" " EndPrimitive();\n" "}\n" }; static const char* fragSource = { "#version 400\n" "out vec4 FragColor;\n" "in vec3 gFacetNormal;\n" "in vec3 gTriDistance;\n" "in vec3 gPatchDistance;\n" "in vec4 gColor;\n" "in float gPrimitive;\n" "uniform vec3 LightPosition;\n" "uniform vec3 DiffuseMaterial;\n" "uniform vec3 AmbientMaterial;\n" "float amplify(float d, float scale, float offset){\n" " d = scale * d + offset;\n" " d = clamp(d, 0, 1);\n" " d = 1 - exp2(-2*d*d);\n" " return d;\n" "}\n" "void main(){\n" " vec3 N = normalize(gFacetNormal);\n" " vec3 L = LightPosition;\n" " float df = abs(dot(N, L));\n" " vec3 color = AmbientMaterial + df * DiffuseMaterial;\n" " float d1 = min(min(gTriDistance.x, gTriDistance.y), gTriDistance.z);\n" " float d2 = min(min(gPatchDistance.x, gPatchDistance.y), gPatchDistance.z);\n" " color = amplify(d1, 40, -0.5) * amplify(d2, 60, -0.5) * color;\n" " FragColor = vec4(color, 1.0);\n" "}\n" }; osg::ref_ptr CreateIcosahedron(osg::Program* /*program*/) { osg::Geode *geode=new osg::Geode(); osg::Geometry *geometry = new osg::Geometry(); const unsigned int Faces[] = { 2, 1, 0, 3, 2, 0, 4, 3, 0, 5, 4, 0, 1, 5, 0, 11, 6, 7, 11, 7, 8, 11, 8, 9, 11, 9, 10, 11, 10, 6, 1, 2, 6, 2, 3, 7, 3, 4, 8, 4, 5, 9, 5, 1, 10, 2, 7, 6, 3, 8, 7, 4, 9, 8, 5, 10, 9, 1, 6, 10 }; int IndexCount = sizeof(Faces) / sizeof(Faces[0]); const float Verts[] = { 0.000f, 0.000f, 1.000f, 0.894f, 0.000f, 0.447f, 0.276f, 0.851f, 0.447f, -0.724f, 0.526f, 0.447f, -0.724f, -0.526f, 0.447f, 0.276f, -0.851f, 0.447f, 0.724f, 0.526f, -0.447f, -0.276f, 0.851f, -0.447f, -0.894f, 0.000f, -0.447f, -0.276f, -0.851f, -0.447f, 0.724f, -0.526f, -0.447f, 0.000f, 0.000f, -1.000f }; int VertexCount = sizeof(Verts)/sizeof(float); osg::Vec3Array* vertices = new osg::Vec3Array(); for(int i=0;ipush_back(osg::Vec3(Verts[i],Verts[i+1],Verts[i+2])); } geometry->setVertexArray(vertices); geometry->addPrimitiveSet(new osg::DrawElementsUInt(osg::PrimitiveSet::PATCHES,IndexCount,Faces)); // Expand the bounding box, otherwise the geometry is clipped in front when tessellating. osg::BoundingBox bbox(osg::Vec3(-1.0f, -1.9f, -1.0f), osg::Vec3(1.0f, 1.0f, 1.0f)); geometry->setInitialBound(bbox); geode->addDrawable(geometry); return geode; } osg::ref_ptr createProgram() { osg::Program *program = new osg::Program(); program->addShader(new osg::Shader(osg::Shader::VERTEX,vertSource)); program->addShader(new osg::Shader(osg::Shader::TESSCONTROL,tessControlSource)); program->addShader(new osg::Shader(osg::Shader::TESSEVALUATION,tessEvalSource)); program->addShader(new osg::Shader(osg::Shader::GEOMETRY,geomSource)); program->addShader(new osg::Shader(osg::Shader::FRAGMENT,fragSource)); program->setParameter(GL_GEOMETRY_VERTICES_OUT_EXT, 3); program->setParameter(GL_GEOMETRY_INPUT_TYPE_EXT, GL_TRIANGLES); program->setParameter(GL_GEOMETRY_OUTPUT_TYPE_EXT, GL_TRIANGLE_STRIP); return program; } class KeyboardEventHandler : public osgGA::GUIEventHandler { public: KeyboardEventHandler(osg::ref_ptr tessInnerU, osg::ref_ptr tessOuterU): _tessInnerU(tessInnerU), _tessOuterU(tessOuterU) { tessInnerU->get(_tessInner); tessOuterU->get(_tessOuter); } virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& gaa) { if(ea.getEventType()==osgGA::GUIEventAdapter::KEYDOWN){ switch (ea.getKey()){ case osgGA::GUIEventAdapter::KEY_Up: increaseOuterTesselation(); return true; case osgGA::GUIEventAdapter::KEY_Down: decreaseOuterTesselation(); return true; case osgGA::GUIEventAdapter::KEY_Left: decreaseInnerTesselation(); return true; case osgGA::GUIEventAdapter::KEY_Right: increaseInnerTesselation(); return true; case osgGA::GUIEventAdapter::KEY_Plus: case osgGA::GUIEventAdapter::KEY_KP_Add: increaseInnerTesselation(); increaseOuterTesselation(); return true; case osgGA::GUIEventAdapter::KEY_Minus: case osgGA::GUIEventAdapter::KEY_KP_Subtract: decreaseInnerTesselation(); decreaseOuterTesselation(); return true; } } return osgGA::GUIEventHandler::handle(ea, gaa); } private: osg::ref_ptr _tessInnerU; osg::ref_ptr _tessOuterU; float _tessInner; float _tessOuter; void increaseInnerTesselation() { _tessInnerU->set(++_tessInner); } void decreaseInnerTesselation() { _tessInner = std::max(1.0f, _tessInner-1.0f); _tessInnerU->set(_tessInner); } void increaseOuterTesselation() { _tessOuterU->set(++_tessOuter); } void decreaseOuterTesselation() { _tessOuter = std::max(1.0f, _tessOuter-1.0f); _tessOuterU->set(_tessOuter); } }; int main(int, char* []) { osgViewer::Viewer viewer; viewer.setUpViewInWindow(100,100,800,600); osg::ref_ptr program = createProgram(); osg::ref_ptr geode = CreateIcosahedron(program.get()); osg::ref_ptr tessInnerU = new osg::Uniform("TessLevelInner", 1.0f); osg::ref_ptr tessOuterU = new osg::Uniform("TessLevelOuter", 1.0f); osg::StateSet *state; state = geode->getOrCreateStateSet(); state->addUniform(new osg::Uniform("AmbientMaterial",osg::Vec3(0.04f, 0.04f, 0.04f))); state->addUniform(new osg::Uniform("DiffuseMaterial",osg::Vec3(0.0f, 0.75f, 0.75f))); state->addUniform(new osg::Uniform("LightPosition",osg::Vec3(0.25f, 0.25f, 1.0f))); state->addUniform(tessInnerU.get()); state->addUniform(tessOuterU.get()); state->setAttribute(new osg::PatchParameter(3)); state->setAttribute(program.get()); // switch on the uniforms that track the modelview and projection matrices osgViewer::Viewer::Windows windows; viewer.getWindows(windows); for(osgViewer::Viewer::Windows::iterator itr = windows.begin(); itr != windows.end(); ++itr) { osg::State *s=(*itr)->getState(); s->setUseModelViewAndProjectionUniforms(true); s->setUseVertexAttributeAliasing(true); } viewer.addEventHandler(new KeyboardEventHandler(tessInnerU, tessOuterU)); viewer.addEventHandler(new osgViewer::StatsHandler); viewer.setSceneData(geode.get()); return viewer.run(); }