Skip to content

Instantly share code, notes, and snippets.

@vicrucann
Last active December 23, 2021 06:01
Show Gist options
  • Save vicrucann/497fd5839bccba45e58b5ca48feca12f to your computer and use it in GitHub Desktop.
Save vicrucann/497fd5839bccba45e58b5ca48feca12f to your computer and use it in GitHub Desktop.
OpenSceneGraph simple geometry shader example: turns line adjacency into triangle strip (based on cinder line shader)
#include <Windows.h>
#include <osg/Camera>
#include <osg/Drawable>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Group>
#include <osg/Node>
#include <osg/NodeVisitor>
#include <osg/Object>
#include <osg/PrimitiveSet>
#include <osg/Program>
#include <osg/Shader>
#include <osg/StateSet>
#include <osg/Transform>
#include <osg/Uniform>
#include <osgViewer/Viewer>
#include <osg/LineWidth>
#include <osg/Point>
#include <osg/BlendFunc>
#include <osgDB/ReadFile>
struct ModelViewProjectionMatrixCallback: public osg::Uniform::Callback
{
ModelViewProjectionMatrixCallback(osg::Camera* camera) :
_camera(camera) {
}
virtual void operator()(osg::Uniform* uniform, osg::NodeVisitor* nv) {
osg::Matrixd viewMatrix = _camera->getViewMatrix();
osg::Matrixd modelMatrix = osg::computeLocalToWorld(nv->getNodePath());
osg::Matrixd modelViewProjectionMatrix = modelMatrix * viewMatrix * _camera->getProjectionMatrix();
uniform->set(modelViewProjectionMatrix);
}
osg::Camera* _camera;
};
struct ViewportCallback: public osg::Uniform::Callback
{
ViewportCallback(osg::Camera* camera) :
_camera(camera) {
}
virtual void operator()(osg::Uniform* uniform, osg::NodeVisitor* /*nv*/) {
const osg::Viewport* viewport = _camera->getViewport();
osg::Vec2f viewportVector = osg::Vec2f(viewport->width(), viewport->height());
uniform->set(viewportVector);
}
osg::Camera* _camera;
};
const int OSG_WIDTH = 1280;
const int OSG_HEIGHT = 960;
osg::Node* createTestScene()
{
osg::Vec3Array* vertices = new osg::Vec3Array;
osg::Vec4Array* colors = new osg::Vec4Array;
vertices->setName("Vertex");
vertices->push_back(osg::Vec3f(0,1,0));
vertices->push_back(osg::Vec3f(1,1,0));
vertices->push_back(osg::Vec3f(1,1,1));
vertices->push_back(osg::Vec3f(0,1,1));
vertices->push_back(osg::Vec3f(0.5,1,1.5));
vertices->push_back(osg::Vec3f(0.5,1,2));
colors->push_back(osg::Vec4(1,0.3,0,1));
osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
geom->addPrimitiveSet(new osg::DrawArrays(GL_LINE_STRIP, 0, vertices->size()));
geom->setUseDisplayList(false);
geom->setVertexArray(vertices);
geom->setColorArray(colors, osg::Array::BIND_OVERALL);
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode->addDrawable(geom.get());
osg::StateSet* state = geode->getOrCreateStateSet();
state->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
state->setMode(GL_BLEND, osg::StateAttribute::ON);
state->setMode(GL_LINE_SMOOTH, osg::StateAttribute::ON);
osg::LineWidth* lw = new osg::LineWidth;
lw->setWidth(10.f);
state->setAttribute(lw, osg::StateAttribute::ON);
osg::BlendFunc* blendfunc = new osg::BlendFunc();
state->setAttributeAndModes(blendfunc, osg::StateAttribute::ON);
return geode.release();
}
int main(int, char**)
{
::SetProcessDPIAware();
osgViewer::Viewer viewer;
osg::Camera* camera = viewer.getCamera();
osg::ref_ptr<osg::Group> root = new osg::Group();
osg::Vec3Array* vertices = new osg::Vec3Array;
osg::Vec4Array* colors = new osg::Vec4Array;
vertices->setName("Vertex");
vertices->push_back(osg::Vec3f(-1,0,0));
vertices->push_back(osg::Vec3f(0,0,0));
vertices->push_back(osg::Vec3f(1,0,0));
vertices->push_back(osg::Vec3f(1,0,1));
vertices->push_back(osg::Vec3f(0,0,0));
vertices->push_back(osg::Vec3f(1,0,0));
vertices->push_back(osg::Vec3f(1,0,1));
vertices->push_back(osg::Vec3f(0,0,1));
vertices->push_back(osg::Vec3f(1,0,0));
vertices->push_back(osg::Vec3f(1,0,1));
vertices->push_back(osg::Vec3f(0,0,1));
vertices->push_back(osg::Vec3f(0.5,0,1.5));
vertices->push_back(osg::Vec3f(1,0,1));
vertices->push_back(osg::Vec3f(0,0,1));
vertices->push_back(osg::Vec3f(0.5,0,1.5));
vertices->push_back(osg::Vec3f(0.5,0,2));
vertices->push_back(osg::Vec3f(0,0,1));
vertices->push_back(osg::Vec3f(0.5,0,1.5));
vertices->push_back(osg::Vec3f(0.5,0,2));
vertices->push_back(osg::Vec3f(0.5,0,3));
colors->setName("Color");
colors->push_back(osg::Vec4f(0.2, 0.9, 1.0, 1));
colors->push_back(osg::Vec4f(0.8, 0.2, 1.0, 1));
colors->push_back(osg::Vec4f(0.0, 0.7, 1.0, 1));
colors->push_back(osg::Vec4f(0.8, 0.1, 1.0, 1));
colors->push_back(osg::Vec4f(0.8, 0.2, 1.0, 1));
colors->push_back(osg::Vec4f(0.0, 0.7, 1.0, 1));
colors->push_back(osg::Vec4f(0.8, 0.1, 1.0, 1));
colors->push_back(osg::Vec4f(0.2, 0.9, 1.0, 1));
colors->push_back(osg::Vec4f(0.0, 0.7, 1.0, 1));
colors->push_back(osg::Vec4f(0.8, 0.1, 1.0, 1));
colors->push_back(osg::Vec4f(0.2, 0.9, 1.0, 1));
colors->push_back(osg::Vec4f(0.2, 0.9, 1.0, 1));
colors->push_back(osg::Vec4f(0.8, 0.2, 1.0, 1));
colors->push_back(osg::Vec4f(0.0, 0.7, 1.0, 1));
colors->push_back(osg::Vec4f(0.8, 0.1, 1.0, 1));
colors->push_back(osg::Vec4f(0.2, 0.9, 1.0, 1));
colors->push_back(osg::Vec4f(0.8, 0.2, 1.0, 1));
colors->push_back(osg::Vec4f(0.0, 0.7, 1.0, 1));
colors->push_back(osg::Vec4f(0.8, 0.1, 1.0, 1));
colors->push_back(osg::Vec4f(0.2, 0.9, 1.0, 1));
// create geometry
osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
geom->addPrimitiveSet(new osg::DrawArrays(GL_LINES_ADJACENCY_EXT, 0, vertices->size()));
geom->setUseDisplayList(false);
// defaults
geom->setVertexArray(vertices);
geom->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
// set attributes
geom->setVertexAttribArray(0, vertices, osg::Array::BIND_PER_VERTEX);
geom->setVertexAttribArray(1, colors, osg::Array::BIND_PER_VERTEX);
// create shader
osg::ref_ptr<osg::Program> program = new osg::Program;
program->setName("shader");
// program->addShader(new osg::Shader(osg::Shader::VERTEX, vertSource));
osg::ref_ptr<osg::Shader> vertShader = new osg::Shader(osg::Shader::VERTEX);
if (!vertShader->loadShaderSourceFromFile("polyline.vert"))
std::cerr << "Could not read VERTEX shader from file" << std::endl;
program->addShader(vertShader);
osg::ref_ptr<osg::Shader> geomShader = new osg::Shader(osg::Shader::GEOMETRY);
if (!geomShader->loadShaderSourceFromFile("polyline.geom"))
std::cerr << "Could not read GEOMETRY shader from file" << std::endl;
program->addShader(geomShader);
osg::ref_ptr<osg::Shader> fragShader = new osg::Shader(osg::Shader::FRAGMENT);
if (!fragShader->loadShaderSourceFromFile("polyline.frag"))
std::cerr << "Could not read FRAGMENT shader from file" << std::endl;
program->addShader(fragShader);
// program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragSource));
// geode
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode->addDrawable(geom.get());
osg::StateSet* state = geode->getOrCreateStateSet();
state->setAttributeAndModes(program.get(), osg::StateAttribute::ON);
// add uniforms
osg::Uniform* modelViewProjectionMatrix = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "ModelViewProjectionMatrix");
modelViewProjectionMatrix->setUpdateCallback(new ModelViewProjectionMatrixCallback(camera));
state->addUniform(modelViewProjectionMatrix);
osg::Uniform* viewportVector = new osg::Uniform(osg::Uniform::FLOAT_VEC2, "Viewport");
viewportVector->setUpdateCallback(new ViewportCallback(camera));
state->addUniform(viewportVector);
float Thickness = 7.0;
float miterLimit = 0.75;
state->addUniform(new osg::Uniform("Thickness", Thickness));
state->addUniform(new osg::Uniform("MiterLimit", miterLimit));
// state settings
state->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
state->setMode(GL_BLEND, osg::StateAttribute::ON);
state->setMode(GL_LINE_SMOOTH, osg::StateAttribute::ON);
osg::LineWidth* lw = new osg::LineWidth;
lw->setWidth(10.f);
state->setAttribute(lw, osg::StateAttribute::ON);
osg::BlendFunc* blendfunc = new osg::BlendFunc();
state->setAttributeAndModes(blendfunc, osg::StateAttribute::ON);
// graphics context params
osg::DisplaySettings::instance()->setNumMultiSamples(4); // anti aliased shadered lines
// scene state run
root->addChild(geode.get());
root->addChild(createTestScene());
viewer.setSceneData(root.get());
viewer.setUpViewInWindow(100,100,OSG_WIDTH, OSG_HEIGHT);
// viewer.setUpViewOnSingleScreen(0);
return viewer.run();
}
#version 330
in VertexData{
vec2 mTexCoord;
vec4 mColor;
} VertexIn;
const vec4 testColor = vec4(0.3,1,0,1);
void main(void)
{
gl_FragColor = vec4(VertexIn.mColor.xy, 0.2, 1);
}
#version 330
uniform float Thickness;
uniform vec2 Viewport;
uniform float MiterLimit;
layout(lines_adjacency) in;
layout(triangle_strip, max_vertices = 7) out;
in VertexData{
vec4 mColor;
} VertexIn[4];
out VertexData{
vec2 mTexCoord;
vec4 mColor;
} VertexOut;
vec2 toScreenSpace(vec4 vertex)
{
return vec2( vertex.xy / vertex.w ) * Viewport;
}
void main(void)
{
// 4 points
vec4 P0 = gl_in[0].gl_Position;
vec4 P1 = gl_in[1].gl_Position;
vec4 P2 = gl_in[2].gl_Position;
vec4 P3 = gl_in[3].gl_Position;
// get the four vertices passed to the shader:
vec2 p0 = toScreenSpace( P0 ); // start of previous segment
vec2 p1 = toScreenSpace( P1 ); // end of previous segment, start of current segment
vec2 p2 = toScreenSpace( P2 ); // end of current segment, start of next segment
vec2 p3 = toScreenSpace( P3 ); // end of next segment
// perform naive culling
vec2 area = Viewport * 1.8;
if( p1.x < -area.x || p1.x > area.x ) return;
if( p1.y < -area.y || p1.y > area.y ) return;
if( p2.x < -area.x || p2.x > area.x ) return;
if( p2.y < -area.y || p2.y > area.y ) return;
// determine the direction of each of the 3 segments (previous, current, next)
vec2 v0 = normalize( p1 - p0 );
vec2 v1 = normalize( p2 - p1 );
vec2 v2 = normalize( p3 - p2 );
// determine the normal of each of the 3 segments (previous, current, next)
vec2 n0 = vec2( -v0.y, v0.x );
vec2 n1 = vec2( -v1.y, v1.x );
vec2 n2 = vec2( -v2.y, v2.x );
// determine miter lines by averaging the normals of the 2 segments
vec2 miter_a = normalize( n0 + n1 ); // miter at start of current segment
vec2 miter_b = normalize( n1 + n2 ); // miter at end of current segment
// determine the length of the miter by projecting it onto normal and then inverse it
float an1 = dot(miter_a, n1);
float bn1 = dot(miter_b, n2);
if (an1==0) an1 = 1;
if (bn1==0) bn1 = 1;
float length_a = Thickness / an1;
float length_b = Thickness / bn1;
// prevent excessively long miters at sharp corners
if( dot( v0, v1 ) < -MiterLimit ) {
miter_a = n1;
length_a = Thickness;
// close the gap
if( dot( v0, n1 ) > 0 ) {
VertexOut.mTexCoord = vec2( 0, 0 );
VertexOut.mColor = VertexIn[1].mColor;
gl_Position = vec4( ( p1 + Thickness * n0 ) / Viewport, 0.0, 1.0 );
EmitVertex();
VertexOut.mTexCoord = vec2( 0, 0 );
VertexOut.mColor = VertexIn[1].mColor;
gl_Position = vec4( ( p1 + Thickness * n1 ) / Viewport, 0.0, 1.0 );
EmitVertex();
VertexOut.mTexCoord = vec2( 0, 0.5 );
VertexOut.mColor = VertexIn[1].mColor;
gl_Position = vec4( p1 / Viewport, 0.0, 1.0 );
EmitVertex();
EndPrimitive();
}
else {
VertexOut.mTexCoord = vec2( 0, 1 );
VertexOut.mColor = VertexIn[1].mColor;
gl_Position = vec4( ( p1 - Thickness * n1 ) / Viewport, 0.0, 1.0 );
EmitVertex();
VertexOut.mTexCoord = vec2( 0, 1 );
VertexOut.mColor = VertexIn[1].mColor;
gl_Position = vec4( ( p1 - Thickness * n0 ) / Viewport, 0.0, 1.0 );
EmitVertex();
VertexOut.mTexCoord = vec2( 0, 0.5 );
VertexOut.mColor = VertexIn[1].mColor;
gl_Position = vec4( p1 / Viewport, 0.0, 1.0 );
EmitVertex();
EndPrimitive();
}
}
if( dot( v1, v2 ) < -MiterLimit ) {
miter_b = n1;
length_b = Thickness;
}
// generate the triangle strip
VertexOut.mTexCoord = vec2( 0, 0 );
VertexOut.mColor = VertexIn[1].mColor;
gl_Position = vec4( ( p1 + length_a * miter_a ) / Viewport, 0.0, 1.0 );
EmitVertex();
VertexOut.mTexCoord = vec2( 0, 1 );
VertexOut.mColor = VertexIn[1].mColor;
gl_Position = vec4( ( p1 - length_a * miter_a ) / Viewport, 0.0, 1.0 );
EmitVertex();
VertexOut.mTexCoord = vec2( 0, 0 );
VertexOut.mColor = VertexIn[2].mColor;
gl_Position = vec4( ( p2 + length_b * miter_b ) / Viewport, 0.0, 1.0 );
EmitVertex();
VertexOut.mTexCoord = vec2( 0, 1 );
VertexOut.mColor = VertexIn[2].mColor;
gl_Position = vec4( ( p2 - length_b * miter_b ) / Viewport, 0.0, 1.0 );
EmitVertex();
EndPrimitive();
}
#version 330
uniform mat4 ModelViewProjectionMatrix;
layout(location = 0) in vec4 Vertex;
layout(location = 1) in vec4 Color;
out VertexData{
vec4 mColor;
} VertexOut;
void main(void)
{
VertexOut.mColor = Color;
gl_Position = ModelViewProjectionMatrix * Vertex;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment