[osg-users] using modern shaders with osg - setting vertex attribute layout

Sebastian Messerschmidt sebastian.messerschmidt at gmx.de
Sun Sep 3 07:23:51 PDT 2017


Hi Antiro,

I've cleaned up the example (removed the image mod, hdr etc.) and 
supplied vertex shaders (once you move towards aliasing, you basically 
need to replace ALL fixed function stuff).

You were right about the viewer.realize before setting up the aliasing 
however. This is due the the context not being available before
realizing it.


(Some additonal notes: you don't need to use texture rectangle. Instead 
in deferred steps simply use texelFetch in conjunction with textureSize 
on Texture2D targets.
)


Below you find the adapted example, giving the desired result:
#include <osg/GLExtensions>
#include <osg/Node>
#include <osg/Geometry>
#include <osg/Notify>
#include <osg/MatrixTransform>
#include <osg/Texture2D>
#include <osg/TextureRectangle>
#include <osg/ColorMask>
#include <osg/Material>
#include <osg/Capability>

#include <osgGA/TrackballManipulator>
#include <osgGA/FlightManipulator>
#include <osgGA/DriveManipulator>

#include <osgViewer/Viewer>

#include <iostream>
#include <stdio.h>

#include <osg/PolygonMode>

static std::string vertex_shader =
{
	"#version 330\n"
	"layout(location = 0) in vec4 osg_Vertex;	\n"
	"layout(location = 1) in vec3 osg_Normal;	\n"
	"layout(location = 2) in vec4 osg_Color;	\n"
	"layout(location = 3) in vec4 osg_MultiTexCoord0; \n"
	"uniform mat4 osg_ModelViewProjectionMatrix; \n"
	"out vec2 texture_coords;"
	"void main(void) \n"
	"{ \n"
		"texture_coords = osg_MultiTexCoord0.st;\n"
	"	gl_Position = osg_ModelViewProjectionMatrix * osg_Vertex; \n"
	"}\n"
};

static std::string def_frag_shader =
{
	"#version 330\n"
	"uniform sampler2DRect textureID0;\n"

	"in vec2 texture_coords;"
	"out vec4 target;"
	"void main(void)\n"
	"{\n"
	"    target = vec4(texture( textureID0, texture_coords.st ).rgb, 1);  \n"
	"}\n"
};

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<osg::Geode> 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::Camera *createHUDCamera(double left = 0,
	double right = 1,
	double bottom = 0,
	double top = 1)
{
	osg::ref_ptr<osg::Camera> 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<osg::Camera> createTextureDisplayQuad(
	const osg::Vec3 &pos,
	osg::StateAttribute *tex,
	float scale,
	float width = 0.3,
	float height = 0.2)
{
	osg::ref_ptr<osg::Camera> hc = createHUDCamera();
	hc->addChild(createScreenQuad(width, height, scale, pos));
	hc->getOrCreateStateSet()->setTextureAttributeAndModes(0, tex);
	//need shader!!
	
	osg::ref_ptr<osg::Program> program = new osg::Program;
	osg::ref_ptr<osg::Shader> vshader = new 
osg::Shader(osg::Shader::VERTEX, vertex_shader);
	osg::ref_ptr<osg::Shader> fshader = new 
osg::Shader(osg::Shader::FRAGMENT, def_frag_shader);
	program->addShader(vshader.get());
	program->addShader(fshader.get());
	hc->getOrCreateStateSet()->setAttributeAndModes(program.get(), 
osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);

	return hc;
}


#define NUM_TEXTURES 2

// The quad geometry is used by the render to texture camera to generate 
multiple textures.
osg::Group* createRTTQuad(unsigned int tex_width, unsigned int 
tex_height, bool useHDR)
{
	osg::Group *top_group = new osg::Group;

	osg::ref_ptr<osg::Geode> quad_geode = new osg::Geode;

	osg::ref_ptr<osg::Vec3Array> quad_coords = new osg::Vec3Array; // 
vertex coords
																   // counter-clockwise
	quad_coords->push_back(osg::Vec3d(0, 0, -1));
	quad_coords->push_back(osg::Vec3d(1, 0, -1));
	quad_coords->push_back(osg::Vec3d(1, 1, -1));
	quad_coords->push_back(osg::Vec3d(0, 1, -1));

	osg::ref_ptr<osg::Vec2Array> quad_tcoords = new osg::Vec2Array; // 
texture coords
	quad_tcoords->push_back(osg::Vec2(0, 0));
	quad_tcoords->push_back(osg::Vec2(tex_width, 0));
	quad_tcoords->push_back(osg::Vec2(tex_width, tex_height));
	quad_tcoords->push_back(osg::Vec2(0, tex_height));

	osg::ref_ptr<osg::Geometry> quad_geom = new osg::Geometry;
	osg::ref_ptr<osg::DrawArrays> quad_da = new 
osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4);

	osg::ref_ptr<osg::Vec4Array> quad_colors = new osg::Vec4Array;
	quad_colors->push_back(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));

	quad_geom->setVertexArray(quad_coords.get());
	quad_geom->setTexCoordArray(0, quad_tcoords.get());
	quad_geom->addPrimitiveSet(quad_da.get());
	quad_geom->setColorArray(quad_colors.get(), osg::Array::BIND_OVERALL);

	osg::StateSet *stateset = quad_geom->getOrCreateStateSet();
	stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);

	stateset->addUniform(new osg::Uniform("width", (int)tex_width));

	// Attach shader, glFragData is used to create data for multiple render 
targets

	{
		static const char *shaderSource = {
			"uniform int width;"
			"void main(void)\n"
			"{\n"
			"    gl_FragData[0] = vec4(1,0,0,1);\n"
			"    gl_FragData[1] = vec4(0,1,0,1);\n"
			"    gl_FragData[2] = vec4(0,0,1,1);\n"
			"    gl_FragData[3] = vec4(0,0,1,1);\n"
			"}\n"
		};

		osg::ref_ptr<osg::Shader> fshader = new 
osg::Shader(osg::Shader::FRAGMENT, shaderSource);
		osg::ref_ptr<osg::Program> program = new osg::Program;
		osg::ref_ptr<osg::Shader> vshader = new 
osg::Shader(osg::Shader::VERTEX, vertex_shader);
		program->addShader(vshader.get());

		program->addShader(fshader.get());
		stateset->setAttributeAndModes(program.get(), osg::StateAttribute::ON 
| osg::StateAttribute::OVERRIDE);
	}

	quad_geode->addDrawable(quad_geom.get());

	top_group->addChild(quad_geode.get());

	return top_group;
}


osg::TextureRectangle* textureRect[NUM_TEXTURES] = { 0,0 }; //moved to 
global scope to easily view intermediate textures
															// Here a scene consisting of a single quad is created. 
This scene is viewed by the screen camera.
															// The quad is textured using a shader and the multiple 
textures generated in the RTT stage.
osg::Node* createScene(osg::Node* cam_subgraph, unsigned int tex_width, 
unsigned int tex_height, bool useHDR, bool useImage, bool useMultiSample)
{
	if (!cam_subgraph) return 0;

	// create a group to contain the quad and the pre render camera.
	osg::Group* parent = new osg::Group;

	// textures to render to and to use for texturing of the final quad
	//osg::TextureRectangle* textureRect[NUM_TEXTURES] = {0,0,0,0};

	for (int i = 0; i < NUM_TEXTURES; i++)
	{
		textureRect[i] = new osg::TextureRectangle;
		textureRect[i]->setTextureSize(tex_width, tex_height);
		textureRect[i]->setInternalFormat(GL_RGBA);
		textureRect[i]->setFilter(osg::Texture2D::MIN_FILTER, 
osg::Texture2D::LINEAR);
		textureRect[i]->setFilter(osg::Texture2D::MAG_FILTER, 
osg::Texture2D::LINEAR);
	}

	// first create the geometry of the quad
	{
		osg::Geometry* polyGeom = new osg::Geometry();

		polyGeom->setSupportsDisplayList(false);

		osg::Vec3Array* vertices = new osg::Vec3Array;
		osg::Vec2Array* texcoords = new osg::Vec2Array;

		vertices->push_back(osg::Vec3d(0, 0, 0));
		texcoords->push_back(osg::Vec2(0, 0));

		vertices->push_back(osg::Vec3d(1, 0, 0));
		texcoords->push_back(osg::Vec2(tex_width, 0));

		vertices->push_back(osg::Vec3d(1, 0, 1));
		texcoords->push_back(osg::Vec2(tex_width, tex_height));

		vertices->push_back(osg::Vec3d(0, 0, 1));
		texcoords->push_back(osg::Vec2(0, tex_height));

		polyGeom->setVertexArray(vertices);
		polyGeom->setTexCoordArray(0, texcoords);

		osg::Vec4Array* colors = new osg::Vec4Array;
		colors->push_back(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
		polyGeom->setColorArray(colors, osg::Array::BIND_OVERALL);

		polyGeom->addPrimitiveSet(new 
osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, vertices->size()));

		// now we need to add the textures (generated by RTT) to the Drawable.
		osg::StateSet* stateset = new osg::StateSet;
		for (int i = 0; i < NUM_TEXTURES; i++) {
			stateset->setTextureAttributeAndModes(i, textureRect[i], 
osg::StateAttribute::ON);
		}

		polyGeom->setStateSet(stateset);
	
		// Attach a shader to the final quad to combine the input textures.
		{
			static const char *shaderSource = {
				"#version 330\n"
				"uniform sampler2DRect textureID0;\n"
				"uniform sampler2DRect textureID1;\n"
				"uniform sampler2DRect textureID2;\n"
				"uniform sampler2DRect textureID3;\n"

				"in vec2 texture_coords;"
				"void main(void)\n"
				"{\n"
				"    gl_FragData[0] = \n"
				"          vec4(texture2DRect( textureID0, texture_coords.st ).rgb, 
1) + \n"
				"          vec4(texture2DRect( textureID1, texture_coords.st ).rgb, 
1) + \n"
				"          vec4(texture2DRect( textureID2, texture_coords.st ).rgb, 
1) + \n"
				"     -0.5*vec4(texture2DRect( textureID3, texture_coords.st ).rgb, 
1);  \n"
				"}\n"
			};
			osg::ref_ptr<osg::Shader> fshader = new 
osg::Shader(osg::Shader::FRAGMENT, shaderSource);
			osg::ref_ptr<osg::Program> program = new osg::Program;
			osg::ref_ptr<osg::Shader> vshader = new 
osg::Shader(osg::Shader::VERTEX, vertex_shader);
			program->addShader(vshader.get());

			program->addShader(fshader.get());
			stateset->setAttributeAndModes(program.get(), osg::StateAttribute::ON 
| osg::StateAttribute::OVERRIDE);

		}

		stateset->addUniform(new osg::Uniform("textureID0", 0));
		stateset->addUniform(new osg::Uniform("textureID1", 1));
		stateset->addUniform(new osg::Uniform("textureID2", 2));
		stateset->addUniform(new osg::Uniform("textureID3", 3));

		//stateset->setDataVariance(osg::Object::DYNAMIC);

		osg::Geode* geode = new osg::Geode();
		geode->addDrawable(polyGeom);

		parent->addChild(geode);
	}

	// now create the camera to do the multiple render to texture
	{
		osg::Camera* camera = new osg::Camera;

		// set up the background color and clear mask.
		camera->setClearColor(osg::Vec4(0.1f, 0.1f, 0.3f, 1.0f));
		camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		// the camera is going to look at our input quad
		camera->setProjectionMatrix(osg::Matrix::ortho2D(0, 1, 0, 1));
		camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
		camera->setViewMatrix(osg::Matrix::identity());

		// set viewport
		camera->setViewport(0, 0, tex_width, tex_height);

		// set the camera to render before the main camera.
		camera->setRenderOrder(osg::Camera::PRE_RENDER);

		// tell the camera to use OpenGL frame buffer objects
		camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);

		// attach the textures to use
		for (int i = 0; i < NUM_TEXTURES; i++) {
		 
camera->attach(osg::Camera::BufferComponent(osg::Camera::COLOR_BUFFER0 + 
i), textureRect[i]);
		}




		// add the subgraph to render
		camera->addChild(cam_subgraph);

		parent->addChild(camera);
	}

	return parent;
}

int main(int argc, char **argv)
{
	// use an ArgumentParser object to manage the program arguments.
	osg::ArgumentParser arguments(&argc, argv);
		
	// construct the viewer.
	osgViewer::Viewer viewer(arguments);

	unsigned tex_width = 512;
	unsigned tex_height = 512;
	
	
	osg::Group* subGraph = createRTTQuad(tex_width, tex_height, false);

	osg::Group* rootNode = new osg::Group();
	rootNode->addChild(createScene(subGraph, tex_width, tex_height, false, 
false, false));

	rootNode->addChild(createScene(subGraph, tex_width, tex_height, false, 
false, false));


	//Some code to view the intermediate textures
	osg::ref_ptr<osg::Camera> testTex =
		createTextureDisplayQuad(osg::Vec3(0, 0.7, 0),
			textureRect[0],
			tex_width);
	osg::ref_ptr<osg::Camera> testTex2 =
		createTextureDisplayQuad(osg::Vec3(0, 0.35, 0),
			textureRect[1],
			tex_width);

	rootNode->addChild(testTex);
	rootNode->addChild(testTex2);

	// add model to the viewer.
	viewer.setSceneData(rootNode);
	viewer.realize();
	viewer.getCamera()->getGraphicsContext()->getState()->setUseModelViewAndProjectionUniforms(true);
	viewer.getCamera()->getGraphicsContext()->getState()->setUseVertexAttributeAliasing(true);

	return viewer.run();
}



More information about the osg-users mailing list