OpenSceneGraph/examples/osguniformbuffer/osguniformbuffer.cpp

207 lines
6.9 KiB
C++

#include <iostream>
#include <osg/Array>
#include <osg/BoundingSphere>
#include <osg/BufferIndexBinding>
#include <osg/BufferObject>
#include <osg/Group>
#include <osg/Math>
#include <osg/MatrixTransform>
#include <osg/Program>
#include <osg/Shader>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgUtil/Optimizer>
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
using namespace std;
using namespace osg;
// This example is based on the sample code in the
// ARB_uniform_buffer_object extension specification.
GLfloat colors1[] = {
// block
0.45,0.45,1,1,
0.45,0.45,1,1,
0.75,0.75,0.75,1,
0.0,0.0,1.0,1,
0.0,1.0,0.0,1
};
GLfloat colors2[] = {
// block
0.45,0.45,1,1,
0.45,0.45,1,1,
0.75,0.75,0.75,1,
1.0,0.0,0.0,1,
0.0,1.0,0.0,1,
};
char vertexShaderSource[] =
"// Vertex shader for Gooch shading\n"
"// Author: Randi Rost\n"
"// Copyright (c) 2002-2006 3Dlabs Inc. Ltd.\n"
"// See 3Dlabs-License.txt for license information\n"
"vec3 LightPosition = vec3(0.0, 10.0, 4.0); \n"
"varying float NdotL;\n"
"varying vec3 ReflectVec;\n"
"varying vec3 ViewVec;\n"
"void main(void)\n"
"{\n"
"vec3 ecPos = vec3 (gl_ModelViewMatrix * gl_Vertex);\n"
"vec3 tnorm = normalize(gl_NormalMatrix * gl_Normal);\n"
"vec3 lightVec = normalize(LightPosition - ecPos);\n"
"ReflectVec = normalize(reflect(-lightVec, tnorm));\n"
"ViewVec = normalize(-ecPos);\n"
"NdotL = (dot(lightVec, tnorm) + 1.0) * 0.5;\n"
"gl_Position = ftransform();\n"
"}\n";
char fragmentShaderSource[] =
"// Fragment shader for Gooch shading, adapted for ARB_uniform_buffer_object\n"
"#extension GL_ARB_uniform_buffer_object : enable\n"
"layout(std140) uniform colors0\n"
"{\n"
"float DiffuseCool;\n"
"float DiffuseWarm;\n"
"vec3 SurfaceColor;\n"
"vec3 WarmColor;\n"
"vec3 CoolColor;\n"
"};\n"
"varying float NdotL;\n"
"varying vec3 ReflectVec;\n"
"varying vec3 ViewVec;\n"
"void main (void)\n"
"{\n"
"vec3 kcool = min(CoolColor + DiffuseCool * SurfaceColor, 1.0);\n"
"vec3 kwarm = min(WarmColor + DiffuseWarm * SurfaceColor, 1.0); \n"
"vec3 kfinal = mix(kcool, kwarm, NdotL);\n"
"vec3 nreflect = normalize(ReflectVec);\n"
"vec3 nview = normalize(ViewVec);\n"
"float spec = max(dot(nreflect, nview), 0.0);\n"
"spec = pow(spec, 32.0);\n"
"gl_FragColor = vec4 (min(kfinal + spec, 1.0), 1.0);\n"
"}\n";
// Callback for animating the WarmColor
class UniformBufferCallback : public StateAttributeCallback
{
public:
void operator() (StateAttribute* attr, NodeVisitor* nv)
{
UniformBufferBinding* ubb = static_cast<UniformBufferBinding*>(attr);
UniformBufferObject* ubo
= static_cast<UniformBufferObject*>(ubb->getBufferObject());
FloatArray* array = static_cast<FloatArray*>(ubo->getBufferData(0));
double time = nv->getFrameStamp()->getSimulationTime();
double frac = fmod(time, 1.0);
Vec4f warmColor = (Vec4f(0.0, 0.0, 1.0 ,1) * frac
+ Vec4f(1.0, 0.0, 0.0, 1) * (1 - frac));
// Since we're using the std140 layout, we know where the
// warmColor variable is located in the buffer.
for (int i = 0; i < 4; ++i)
(*array)[12 + i] = warmColor[i];
array->dirty();
}
};
int main(int argc, char** argv)
{
osg::ArgumentParser arguments(&argc,argv);
osgViewer::Viewer viewer(arguments);
if (arguments.argc() <= 1) {
cerr << "Need a scene.\n";
return 1;
}
osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments);
if (!loadedModel) {
cerr << "couldn't load " << argv[1] << "\n";
return 1;
}
osgUtil::Optimizer optimizer;
optimizer.optimize(loadedModel.get());
const BoundingSphere bound = loadedModel->getBound();
const float displacement = 2.25 * bound.radius();
Group* scene = new Group;
StateSet* rootSS = scene->getOrCreateStateSet();
Shader* vertexShader = new Shader(Shader::VERTEX);
vertexShader->setShaderSource(vertexShaderSource);
Shader* fragmentShader = new Shader(Shader::FRAGMENT);
fragmentShader->setShaderSource(fragmentShaderSource);
Program* prog = new Program;
prog->addShader(vertexShader);
prog->addShader(fragmentShader);
prog->addBindUniformBlock("colors0", 0);
rootSS->setAttributeAndModes(prog, StateAttribute::ON);
// Place 3 instances of the loaded model with different uniform
// blocks for each.
//
// The blocksize is known because of the std140 format.
const unsigned blockSize = 20 * sizeof(GLfloat);
ref_ptr<FloatArray> colorArray
= new FloatArray(&colors1[0],
&colors1[sizeof(colors1) / sizeof(GLfloat)]);
ref_ptr<UniformBufferObject> ubo = new UniformBufferObject;
colorArray->setBufferObject(ubo.get());
Group* group1 = new Group;
StateSet* ss1 = group1->getOrCreateStateSet();
group1->addChild(loadedModel.get());
scene->addChild(group1);
ref_ptr<UniformBufferBinding> ubb1
= new UniformBufferBinding(0, ubo.get(), 0, blockSize);
ss1->setAttributeAndModes(ubb1.get(), StateAttribute::ON);
ref_ptr<FloatArray> colorArray2
= new FloatArray(&colors2[0],
&colors2[sizeof(colors2) / sizeof(GLfloat)]);
ref_ptr<UniformBufferObject> ubo2 = new UniformBufferObject;
colorArray2->setBufferObject(ubo2.get());
MatrixTransform* group2 = new MatrixTransform;
Matrix mat2 = Matrix::translate(-displacement, 0.0, 0.0);
group2->setMatrix(mat2);
StateSet* ss2 = group2->getOrCreateStateSet();
group2->addChild(loadedModel.get());
scene->addChild(group2);
ref_ptr<UniformBufferBinding> ubb2
= new UniformBufferBinding(0, ubo2.get(), 0, blockSize);
ss2->setAttributeAndModes(ubb2.get(), StateAttribute::ON);
ref_ptr<FloatArray> colorArray3
= new FloatArray(&colors2[0],
&colors2[sizeof(colors2) / sizeof(GLfloat)]);
ref_ptr<UniformBufferObject> ubo3 = new UniformBufferObject;
colorArray3->setBufferObject(ubo3.get());
MatrixTransform* group3 = new MatrixTransform;
Matrix mat3 = Matrix::translate(displacement, 0.0, 0.0);
group3->setMatrix(mat3);
StateSet* ss3 = group3->getOrCreateStateSet();
group3->addChild(loadedModel.get());
scene->addChild(group3);
ref_ptr<UniformBufferBinding> ubb3
= new UniformBufferBinding(0, ubo3.get(), 0, blockSize);
ubb3->setUpdateCallback(new UniformBufferCallback);
ubb3->setDataVariance(Object::DYNAMIC);
ss3->setAttributeAndModes(ubb3.get(), StateAttribute::ON);
viewer.setSceneData(scene);
viewer.realize();
return viewer.run();
}