OpenSceneGraph/examples/osgtessellationshaders/osgtessellationshaders.cpp
2016-06-08 13:25:02 +01:00

304 lines
10 KiB
C++

/* A demonstration of Tessellation Shaders in OpenScenegraph.
*
* Instructions:
* Press plus to increase tesselation and minus to decrease it.
* Press right arrow to increase inner tesselation and left arrow to decrease it.
* Press up arrow to increase outer tesselation and down arrow to decrease it.
*
* Original code by Philip Rideout
* Adapted to OpenScenegraph by John Kaniarz
* Additional work by Michael Mc Donnell
*/
#include <osg/Program>
#include <osg/PatchParameter>
#include <osg/ShapeDrawable>
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include <osgGA/TrackballManipulator>
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<osg::Geode> 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;i<VertexCount;i+=3){
vertices->push_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<osg::Program> 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<osg::Uniform> tessInnerU, osg::ref_ptr<osg::Uniform> 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<osg::Uniform> _tessInnerU;
osg::ref_ptr<osg::Uniform> _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<osg::Program> program = createProgram();
osg::ref_ptr<osg::Geode> geode = CreateIcosahedron(program.get());
osg::ref_ptr<osg::Uniform> tessInnerU = new osg::Uniform("TessLevelInner", 1.0f);
osg::ref_ptr<osg::Uniform> 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();
}