1 //info : osgSSBO example,testing ShaderStorageBufferObjects ,Markus Hein, 2014, osg-3.2.1
2 //required hardware and driver must support GL >= GL 4.3 or GL ES 3.1 (GL ES not tested, would be nice if someone will test it on a small device)
3
4 //testing osg support for Shader Storage Buffer Objects
5
6 //version: "first take" from last night session..
7
8
9
10 #include <osg/StateAttributeCallback>
11 #include <osg/Texture2D>
12 #include <osg/Geometry>
13 #include <osg/Geode>
14 #include <osgDB/ReadFile>
15 #include <osgGA/StateSetManipulator>
16 #include <osgViewer/Viewer>
17 #include <osgViewer/ViewerEventHandlers>
18
19 #include <osg/Node>
20 #include <osg/PositionAttitudeTransform>
21 #include <osg/Geometry>
22 #include <osg/Notify>
23 #include <osg/MatrixTransform>
24 #include <osg/Texture2D>
25 #include <osg/TextureRectangle>
26 #include <osg/Stencil>
27 #include <osg/Depth>
28 #include <osg/Billboard>
29 #include <osg/Material>
30 #include <osg/AnimationPath>
31
32 #include <osgGA/TrackballManipulator>
33 #include <osgGA/FlightManipulator>
34 #include <osgGA/DriveManipulator>
35
36 #include <osgUtil/SmoothingVisitor>
37
38 #include <osgDB/Registry>
39 #include <osgDB/ReadFile>
40 #include <osgDB/WriteFile>
41
42 #include <osgViewer/Viewer>
43 #include <osgViewer/ViewerEventHandlers>
44 #include <osgViewer/Renderer>
45
46
47 #include <osg/Array>
48 #include <osg/BoundingSphere>
49 #include <osg/BufferIndexBinding>
50 #include <osg/BufferObject>
51 #include <osg/Group>
52 #include <osg/Math>
53 #include <osg/MatrixTransform>
54 #include <osg/Program>
55 #include <osg/Shader>
56 #include <osg/Drawable>
57 #include <osg/CopyOp>
58 #include <osg/State>
59 #include <osg/Matrix>
60 #include <osg/ShapeDrawable>
61 #include <osg/GL>
62 #include <osg/StateSet>
63 #include <osg/Texture2D>
64 #include <osg/BlendFunc>
65 #include <osg/TexEnv>
66 #include <osg/Material>
67 #include <osg/PointSprite>
68 #include <osg/Program>
69 #include <osg/Notify>
70 #include <osg/Point>
71 #include <osg/io_utils>
72 #include <osg/VertexProgram>
73
74 #include <osgText/Font>
75 #include <osgText/Text>
76
77
78 #include <osgDB/ReadFile>
79 #include <osgDB/WriteFile>
80 #include <osgDB/FileNameUtils>
81 #include <osgUtil/Optimizer>
82 #include <iostream>
83 #include <typeinfo>
84
85
86 using namespace osg;
87
88
89 //todo .. #define COMPUTATION_IN_SEPARATE_THREAD
90
91 #define WORK_GROUP_SIZE 16
92
93
94 #define PRERENDER_ANTIALIASINGMULTISAMPLES 16
95 #define PRERENDER_HIGH_QUALITY_ANTIALIASING
96 #define PRERENDER_WIDTH 1920
97 #define PRERENDER_HEIGHT 1080
98
99
100 #define SUB_PLACEMENT_OFFSET_HORIZONTAL 0.5
101 #define SUB_PLACEMENT_OFFSET_VERTICAL 0.5
102
103 enum BufferOffset
104 {
105 POSITION_NOW_OFFSET,
106 POSITION_OLD_OFFSET,
107 POSITION_INIT_OFFSET,
108
109 VELOCITY_NOW_OFFSET,
110 VELOCITY_OLD_OFFSET,
111 VELOCITY_INIT_OFFSET,
112
113 ACCELERATION_OFFSET,
114 PROPERTIES_OFFSET,
115
116 OFFSET_END
117 };
118
119
120 const int __numDataValuesPerChannel = OFFSET_END;
121 const int __numChannels = 4;
122
123 //512x512x4x7 = 7.340.032 floats in SSBO on GPU
124 const int NUM_ELEMENTS_X = 512;
125 const int NUM_ELEMENTS_Y = 512;
126
random(float min,float max)127 float random(float min, float max) { return min + (max - min)*(float)rand() / (float)RAND_MAX; }
128
129
130 enum Channel
131 {
132 RED_CHANNEL,
133 GREEN_CHANNEL,
134 BLUE_CHANNEL,
135 ALPHA_CHANNEL,
136 RGB_CHANNEL,
137 RGBA_CHANNEL
138 };
139
140
141
142 class ShaderStorageBufferCallback : public osg::StateAttributeCallback
143 {
144 public:
operator ()(osg::StateAttribute * attr,osg::NodeVisitor * nv)145 void operator() (osg::StateAttribute* attr, osg::NodeVisitor* nv)
146 {
147 //if you need to process the data in your app-code , better leaving it on GPU and processing there, uploading per frame will make it slow
148 #if 0
149 osg::ShaderStorageBufferBinding* ssbb = static_cast<osg::ShaderStorageBufferBinding*>(attr);
150 osg::ShaderStorageBufferObject* ssbo
151 = static_cast<osg::ShaderStorageBufferObject*>(ssbb->getBufferObject());
152
153 osg::FloatArray* array = static_cast<osg::FloatArray*>(ssbo->getBufferData(0));
154
155 float someValue = array->at(0);
156 //std::cout << "someValue now: " << someValue << std::endl;
157 //data transfer performance test
158 // array->dirty();
159 #endif
160 }
161 };
162
163
164 //do not forget to set OSG_FILE_PATH to default OSG-Data and make sure the new shaders are copied there under"shaders"
165 class ComputeNode : public osg::PositionAttitudeTransform
166 {
167
168 public:
169
170 osg::ref_ptr<osg::Program> _computeProgram;
171 osg::ref_ptr<osg::Shader> _computeShader; //compute and write position data in SSBO
172
173 osg::ref_ptr<osg::Shader> _vertexShader; //reading position data from SSBO (OBS!: make sure glMemoryBuffer() is syncing this)
174 osg::ref_ptr<osg::Shader> _geometryShader; //building a quad looking to the camera
175 osg::ref_ptr<osg::Shader> _fragmentShader; //use false-colors etc. for making your data visible
176
177 osg::ref_ptr<osg::Node> _helperNode; // coordinate system node
178
179 ref_ptr<osg::ShaderStorageBufferObject> _ssbo;
180 ref_ptr<osg::ShaderStorageBufferBinding> _ssbb;
181
182 GLfloat* _data; // some data we upload to GPU, initialised with random values
183 osg::ref_ptr<FloatArray> _dataArray; //
184
185 osg::ref_ptr<osg::Group> _computationResultsRenderGroup;
186 osg::ref_ptr<osg::Program> _computationResultsRenderProgram;
187 osg::ref_ptr<osg::StateSet> _computationResultsRenderStateSet;
188
189
190 std::string _computeShaderSourcePath;
191 std::string _vertexShaderSourcePath;
192 std::string _geometryShaderSourcePath;
193 std::string _fragmentShaderSourcePath;
194
195
196 void addHelperGeometry();
197 void addDataMonitor(osg::Vec3 placement, osg::Vec3 relativePlacement, float scale, Channel channel, BufferOffset shaderBufferOffset, std::string labelcaption, float minDataRange, float maxDataRange);
198 void addComputationResultsRenderTree();
199 void initComputingSetup();
200
ComputeNode()201 ComputeNode()
202 {
203 const char* envOsgFilePath = getenv("OSG_FILE_PATH");
204 std::stringstream computeshaderpath; computeshaderpath << envOsgFilePath << "/shaders/osgssboComputeShader.cs";
205 _computeShaderSourcePath = computeshaderpath.str();
206 std::stringstream vertexshaderpath; vertexshaderpath << envOsgFilePath << "/shaders/osgssboVertexShader.vs";
207 _vertexShaderSourcePath = vertexshaderpath.str();
208 std::stringstream geometryshaderpath; geometryshaderpath << envOsgFilePath << "/shaders/osgssboGeometryShader.gs";
209 _geometryShaderSourcePath = geometryshaderpath.str();
210 std::stringstream fragmentshaderpath; fragmentshaderpath << envOsgFilePath << "/shaders/osgssboFragmentShader.fs";
211 _fragmentShaderSourcePath = fragmentshaderpath.str();
212 }
213
214 };
215
216
217 class ComputeNodeUpdateCallback : public osg::NodeCallback
218 {
219 public:
220
221 ComputeNode* _computeNode;
222 osg::Timer_t _prevShaderUpdateTime;
223 osg::Timer _timer;
224
ComputeNodeUpdateCallback()225 ComputeNodeUpdateCallback(){}
226
ComputeNodeUpdateCallback(ComputeNode * computeNode)227 ComputeNodeUpdateCallback(ComputeNode* computeNode)
228 {
229 _computeNode = computeNode;
230 _prevShaderUpdateTime = 0;
231 }
232
operator ()(osg::Node * node,osg::NodeVisitor * nv)233 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
234 {
235 osg::Timer_t currTime = _timer.tick();
236
237 if (_timer.delta_s(_prevShaderUpdateTime, currTime) > 1.0) //one second interval for shader-changed-do-reload check
238 {
239 osg::ref_ptr<osg::Shader> reloadedshader;
240 std::string runningSource;
241 std::string reloadedstring;
242
243 if (_computeNode->_computeShader.valid())
244 {
245 runningSource = _computeNode->_computeShader->getShaderSource();
246 reloadedshader = osg::Shader::readShaderFile(osg::Shader::COMPUTE, _computeNode->_computeShaderSourcePath);
247
248 reloadedstring = reloadedshader->getShaderSource();
249 if (!osgDB::equalCaseInsensitive(runningSource.c_str(), reloadedstring.c_str()))
250 {
251 _computeNode->_computeProgram->removeShader(_computeNode->_computeShader.get());
252 _computeNode->_computeShader = reloadedshader.get();
253 _computeNode->_computeProgram->addShader(_computeNode->_computeShader.get());
254 }
255 }
256
257 if (_computeNode->_vertexShader.valid())
258 {
259
260 runningSource = _computeNode->_vertexShader->getShaderSource();
261 reloadedshader = osg::Shader::readShaderFile(osg::Shader::VERTEX, _computeNode->_vertexShaderSourcePath);
262
263 reloadedstring = reloadedshader->getShaderSource();
264 if (!osgDB::equalCaseInsensitive(runningSource.c_str(), reloadedstring.c_str()))
265 {
266 _computeNode->_computationResultsRenderProgram->removeShader(_computeNode->_vertexShader.get());
267 _computeNode->_vertexShader = reloadedshader.get();
268 _computeNode->_computationResultsRenderProgram->addShader(_computeNode->_vertexShader.get());
269 }
270 }
271
272
273
274 if (_computeNode->_geometryShader.valid())
275 {
276 runningSource = _computeNode->_geometryShader->getShaderSource();
277 reloadedshader = osg::Shader::readShaderFile(osg::Shader::GEOMETRY, _computeNode->_geometryShaderSourcePath);
278
279 reloadedstring = reloadedshader->getShaderSource();
280 if (!osgDB::equalCaseInsensitive(runningSource.c_str(), reloadedstring.c_str()))
281 {
282 _computeNode->_computationResultsRenderProgram->removeShader(_computeNode->_geometryShader.get());
283 _computeNode->_geometryShader = reloadedshader.get();
284 _computeNode->_computationResultsRenderProgram->addShader(_computeNode->_geometryShader.get());
285 }
286 }
287
288 if (_computeNode->_fragmentShader.valid())
289 {
290 runningSource = _computeNode->_fragmentShader->getShaderSource();
291 reloadedshader = osg::Shader::readShaderFile(osg::Shader::FRAGMENT, _computeNode->_fragmentShaderSourcePath);
292
293 reloadedstring = reloadedshader->getShaderSource();
294 if (!osgDB::equalCaseInsensitive(runningSource.c_str(), reloadedstring.c_str()))
295 {
296 _computeNode->_computationResultsRenderProgram->removeShader(_computeNode->_fragmentShader.get());
297 _computeNode->_fragmentShader = reloadedshader.get();
298 _computeNode->_computationResultsRenderProgram->addShader(_computeNode->_fragmentShader.get());
299 }
300 }
301
302
303 _prevShaderUpdateTime = _timer.tick();
304 }
305
306 traverse(node, nv);
307
308 }
309 };
310
311 //set OSG_FILE_PATH for loading axes.osgt
addHelperGeometry()312 void ComputeNode::addHelperGeometry()
313 {
314 _helperNode = osgDB::readRefNodeFile("axes.osgt");
315
316 if (_helperNode.valid())
317 {
318 addChild(_helperNode.get());
319 }
320
321 //osg::PositionAttitudeTransform* pat = new osg::PositionAttitudeTransform;
322 //pat->setPosition(osg::Vec3(0.5, 0, 0.5));
323 //osg::Geode *sphereGeode = new osg::Geode;
324 //float radius = 0.5f;
325 //osg::TessellationHints* hints = new osg::TessellationHints;
326 //hints->setDetailRatio(0.9f);
327 //osg::ShapeDrawable* sphere = new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0.0f, 0.0f, 0.0f), radius), hints);
328 //sphereGeode->addDrawable(sphere);
329 //sphere->setColor(osg::Vec4(0, 1, 0, 0.1));
330 //osg::StateSet* stateset = sphereGeode->getOrCreateStateSet();
331 //osg::BlendFunc *blend = new osg::BlendFunc;
332 //blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA);
333 //stateset->setAttributeAndModes(blend, osg::StateAttribute::ON);
334 //pat->addChild(sphereGeode);
335 //addChild(pat);
336 }
337
338
339
340
341
342
addDataMonitor(osg::Vec3 placement,osg::Vec3 relativePlacement,float scale,Channel colorchannel,BufferOffset shaderStorageBufferOffset,std::string labelCaption,float minDataRange,float maxDataRange)343 void ComputeNode::addDataMonitor(osg::Vec3 placement, osg::Vec3 relativePlacement, float scale, Channel colorchannel, BufferOffset shaderStorageBufferOffset, std::string labelCaption, float minDataRange, float maxDataRange)
344 {
345 osg::PositionAttitudeTransform* pat = new osg::PositionAttitudeTransform;
346 pat->setPosition(relativePlacement);
347 addChild(pat);
348 osg::Geometry* geom;
349
350 if (NUM_ELEMENTS_X >= NUM_ELEMENTS_Y)
351 {
352 float ratio = (float)((float)NUM_ELEMENTS_Y / (float)NUM_ELEMENTS_X);
353 geom = osg::createTexturedQuadGeometry(placement, osg::Vec3(1.0f*scale, 0.0f, 0.0f), osg::Vec3(0.0f, 0.0f, ratio*1.0f*scale));
354 }
355 else
356 {
357 float ratio = (float)((float)NUM_ELEMENTS_X / (float)NUM_ELEMENTS_Y);
358 geom = osg::createTexturedQuadGeometry(placement, osg::Vec3(ratio*1.0f*scale, 0.0f, 0.0f), osg::Vec3(0.0f, 0.0f, 1.0f*scale));
359
360 }
361
362 geom->setVertexAttribArray(1, geom->getTexCoordArray(0), osg::Array::BIND_PER_VERTEX);
363
364 osg::ref_ptr<osg::Geode> quad = new osg::Geode;
365 quad->addDrawable(geom);
366 quad->setStateSet(getOrCreateStateSet());
367 pat->addChild(quad.get());
368
369 static const char* vertexShaderSrcChannelMonitor = {
370
371 "#version 430 \n"
372
373 "uniform int numRows;\n"
374 "uniform int numCols;\n"
375 "uniform float osg_FrameTime;\n"
376 "uniform mat4 osg_ProjectionMatrix;\n"
377 "uniform mat4 osg_ModelViewMatrix;\n"
378 "out vec2 texCoordFromVertexShader;\n"
379 "struct particle{ float x; float y; float z; float w;};"
380 "layout (location = 0) in vec3 vertexpos;\n"
381 "attribute vec2 tex_coords;\n"
382 "void main() {\n"
383 "texCoordFromVertexShader.xy = tex_coords.xy; gl_Position = ( osg_ProjectionMatrix * osg_ModelViewMatrix * vec4(vertexpos.x,vertexpos.y,vertexpos.z,1) ); \n"
384 "}\n"
385 };
386
387
388
389
390 std::stringstream fragmentshaderstringstreamChannelMonitor;
391 fragmentshaderstringstreamChannelMonitor << "#version 430\n";
392 fragmentshaderstringstreamChannelMonitor << "uniform int numRows;\n";
393 fragmentshaderstringstreamChannelMonitor << "uniform int numCols;\n";
394 fragmentshaderstringstreamChannelMonitor << "uniform float dataRangeMin;\n";
395 fragmentshaderstringstreamChannelMonitor << "uniform float dataRangeMax;\n";
396 fragmentshaderstringstreamChannelMonitor << "in vec2 texCoordFromVertexShader;\n";
397 fragmentshaderstringstreamChannelMonitor << "struct particle{ float x; float y; float z; float w;};";
398 fragmentshaderstringstreamChannelMonitor << "layout(std140, binding=0) coherent buffer particles{particle p[];}; ";
399 fragmentshaderstringstreamChannelMonitor << "\n";
400 fragmentshaderstringstreamChannelMonitor << "void main(void)\n";
401 fragmentshaderstringstreamChannelMonitor << "{\n";
402 fragmentshaderstringstreamChannelMonitor << "ivec2 storePos = ivec2(numRows*texCoordFromVertexShader.x, numCols*texCoordFromVertexShader.y); particle particleData = p[" << shaderStorageBufferOffset * NUM_ELEMENTS_X*NUM_ELEMENTS_Y << " + (storePos.x*numRows + storePos.y)]; ";
403
404 //fragmentshaderstringstreamChannelMonitor << " memoryBarrierBuffer(); \n";
405 fragmentshaderstringstreamChannelMonitor << " float dataRangeMultiplier = 1.0 / abs(dataRangeMax - dataRangeMin); \n";
406
407 switch (colorchannel)
408 {
409 case RED_CHANNEL:
410 {
411 fragmentshaderstringstreamChannelMonitor << " vec4 color; color.x = 0.5+dataRangeMultiplier*particleData.x; color.y =0.0; color.z = 0.0; color.w = 1.0; gl_FragColor = color;\n";
412
413 break;
414 }
415 case GREEN_CHANNEL:
416 {
417 fragmentshaderstringstreamChannelMonitor << " vec4 color; color.x = 0.0; color.y = 0.5+dataRangeMultiplier*particleData.y; color.z = 0.0; color.w = 1.0; gl_FragColor = color;\n";
418 break;
419 }
420 case BLUE_CHANNEL:
421 {
422 fragmentshaderstringstreamChannelMonitor << " vec4 color; color.x = 0.0; color.y = 0.0; color.z = 0.5+dataRangeMultiplier*particleData.z; color.w = 0.0 ; gl_FragColor = color;\n";
423 break;
424 }
425 case ALPHA_CHANNEL:
426 {
427 fragmentshaderstringstreamChannelMonitor << " vec4 color; color.x = 0.5+dataRangeMultiplier*particleData.w; color.y = 0.5+dataRangeMultiplier*particleData.w; color.z = 0.5+dataRangeMultiplier*particleData.w; color.w = 0.5+0.5*particleData.w; gl_FragColor = color;\n";
428 break;
429 }
430
431 case RGB_CHANNEL:
432 {
433 fragmentshaderstringstreamChannelMonitor << " vec4 color; color.x = 0.5+dataRangeMultiplier*particleData.x; color.y = 0.5+dataRangeMultiplier*particleData.y; color.z = 0.5+dataRangeMultiplier*particleData.z; color.w = 1.0; gl_FragColor = color;\n";
434 break;
435 }
436
437 case RGBA_CHANNEL:
438 {
439 fragmentshaderstringstreamChannelMonitor << " vec4 color; color.x = 0.5+dataRangeMultiplier*particleData.x; color.y = 0.5+dataRangeMultiplier*particleData.y; color.z = 0.5+dataRangeMultiplier*particleData.z; color.w = 0.5+0.5*particleData.w; gl_FragColor = color;\n";
440 break;
441 }
442
443 }
444
445 fragmentshaderstringstreamChannelMonitor << "}\n";
446
447
448
449
450 osg::Program * program = new osg::Program;
451 program->addShader(new osg::Shader(osg::Shader::VERTEX, vertexShaderSrcChannelMonitor));
452 program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragmentshaderstringstreamChannelMonitor.str().c_str()));
453 program->addBindAttribLocation("tex_coords", 1);
454
455 osg::StateSet* ss = geom->getOrCreateStateSet();
456 ss->setAttributeAndModes(program, osg::StateAttribute::ON);
457 ss->addUniform(new osg::Uniform("numRows", (int)NUM_ELEMENTS_X));
458 ss->addUniform(new osg::Uniform("numCols", (int)NUM_ELEMENTS_Y));
459
460 ss->addUniform(new osg::Uniform("dataRangeMin", (float)minDataRange));
461 ss->addUniform(new osg::Uniform("dataRangeMax", (float)maxDataRange));
462
463
464 ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
465
466 //add a label
467 osg::ref_ptr<osgText::Text> text = new osgText::Text;
468 osg::ref_ptr<osgText::Font> font = osgText::readRefFontFile("fonts/arial.ttf");
469 text->setFont(font);
470 text->setColor(osg::Vec4(1, 1, 1, 1));
471 text->setCharacterSize(0.1*scale);
472 text->setPosition(placement + osg::Vec3(0.05, 0.05, 0));
473 pat->setName(labelCaption);
474 text->setText(pat->getName());
475 text->setBackdropType(osgText::Text::OUTLINE);
476 text->setBackdropImplementation(osgText::Text::POLYGON_OFFSET);
477 text->setBackdropOffset(0.05f);
478 text->setBackdropColor(osg::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
479
480 quad->addDrawable(text);
481
482 pat->addChild(quad.get());
483
484 }
485
486 //compute texture image , taken from osgspotlight
createSpotLightImage(const osg::Vec4 & centerColour,const osg::Vec4 & backgroudColour,unsigned int size,float power)487 osg::Image* createSpotLightImage(const osg::Vec4& centerColour, const osg::Vec4& backgroudColour, unsigned int size, float power)
488 {
489 osg::Image* image = new osg::Image;
490 image->allocateImage(size, size, 1,
491 GL_RGBA, GL_UNSIGNED_BYTE);
492
493
494 float mid = (float(size) - 1)*0.5f;
495 float div = 2.0f / float(size);
496 for (unsigned int r = 0; r < size; ++r)
497 {
498 unsigned char* ptr = image->data(0, r, 0);
499 for (unsigned int c = 0; c < size; ++c)
500 {
501 float dx = (float(c) - mid)*div;
502 float dy = (float(r) - mid)*div;
503 float r = powf(1.0f - sqrtf(dx*dx + dy*dy), power);
504 if (r < 0.0f) r = 0.0f;
505 osg::Vec4 color = centerColour*r + backgroudColour*(1.0f - r);
506 *ptr++ = (unsigned char)((color[0])*255.0f);
507 *ptr++ = (unsigned char)((color[1])*255.0f);
508 *ptr++ = (unsigned char)((color[2])*255.0f);
509 *ptr++ = (unsigned char)((color[3])*255.0f);
510 }
511 }
512 return image;
513 }
514
515
addComputationResultsRenderTree()516 void ComputeNode::addComputationResultsRenderTree()
517 {
518
519 _computationResultsRenderProgram = new osg::Program;
520
521 _vertexShader = osg::Shader::readShaderFile(osg::Shader::VERTEX, _vertexShaderSourcePath);
522 _computationResultsRenderProgram->addShader(_vertexShader.get());
523
524 _geometryShader = osg::Shader::readShaderFile(osg::Shader::GEOMETRY, _geometryShaderSourcePath);
525 _computationResultsRenderProgram->addShader(_geometryShader.get());
526
527 _fragmentShader = osg::Shader::readShaderFile(osg::Shader::FRAGMENT, _fragmentShaderSourcePath);
528 _computationResultsRenderProgram->addShader(_fragmentShader.get());
529
530
531 _computationResultsRenderProgram->addBindAttribLocation("tex_coords", 1);
532
533 _computationResultsRenderGroup = new osg::Group;
534 _computationResultsRenderGroup->setDataVariance(osg::Object::DYNAMIC);
535 _computationResultsRenderStateSet = _computationResultsRenderGroup->getOrCreateStateSet();
536 _computationResultsRenderStateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
537
538 osg::PointSprite *sprite = new osg::PointSprite;
539 int texture_unit = 0;
540 _computationResultsRenderStateSet->setTextureAttributeAndModes(texture_unit, sprite, osg::StateAttribute::ON);
541 _computationResultsRenderStateSet->setAttributeAndModes(_computationResultsRenderProgram.get(), osg::StateAttribute::ON);
542 _computationResultsRenderStateSet->addUniform(new osg::Uniform("particleTexture", texture_unit));
543 _computationResultsRenderStateSet->addUniform(new osg::Uniform("numRows", (int)NUM_ELEMENTS_X));
544 _computationResultsRenderStateSet->addUniform(new osg::Uniform("numCols", (int)NUM_ELEMENTS_Y));
545
546
547 _computationResultsRenderStateSet->setMode(GL_POINT_SMOOTH, osg::StateAttribute::ON);
548 _computationResultsRenderStateSet->setMode(GL_VERTEX_PROGRAM_POINT_SIZE_ARB, osg::StateAttribute::ON);
549 _computationResultsRenderStateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
550 _computationResultsRenderStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
551
552 osg::Texture2D *tex = new osg::Texture2D();
553
554 osg::Image* particleImage = createSpotLightImage(osg::Vec4(1, 0, 0, 1), osg::Vec4(0.5, 0, 0, 0.0), 32, 0.7);
555 if (particleImage)
556 {
557 tex->setImage(particleImage);
558 }
559 _computationResultsRenderStateSet->setTextureAttributeAndModes(texture_unit, tex, osg::StateAttribute::ON);
560
561
562 osg::BlendFunc *blend = new osg::BlendFunc;
563 if (false) //emissive particles
564 {
565 blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE);
566 }
567 else
568 {
569 blend->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA);
570 }
571
572 _computationResultsRenderStateSet->setAttributeAndModes(blend, osg::StateAttribute::ON);
573
574
575 osg::Depth* depth = new osg::Depth;
576 depth->setRange(0.0f, 0.0f);
577 depth->setFunction(osg::Depth::ALWAYS);
578 depth->setWriteMask(false);
579 depth->setFunction(osg::Depth::ALWAYS);
580
581 _computationResultsRenderStateSet->setAttributeAndModes(depth, osg::StateAttribute::OFF);
582
583
584 osg::Geode* particleGeode = new osg::Geode;
585 unsigned int numVertices = NUM_ELEMENTS_X*NUM_ELEMENTS_Y;
586
587 osg::Geometry* particleGeometry = new osg::Geometry;
588 particleGeometry->setUseDisplayList(false);
589 particleGeometry->setUseVertexBufferObjects(true);
590
591 osg::Vec3Array* vertexarray = new osg::Vec3Array;
592 osg::Vec2Array* tcoords = new osg::Vec2Array;
593
594 osg::Vec2 bottom_texcoord(0.0f, 0.0f);
595
596 osg::Vec2 dx_texcoord(1.0f / (float)(NUM_ELEMENTS_X), 0.0f);
597 osg::Vec2 dy_texcoord(0.0f, 1.0f / (float)(NUM_ELEMENTS_Y));
598
599
600
601 for (int i = 0; i < NUM_ELEMENTS_X; i++)
602 {
603 osg::Vec2 texcoord = bottom_texcoord + dy_texcoord*(float)i;
604
605 for (int j = 0; j < NUM_ELEMENTS_Y; j++)
606 {
607 vertexarray->push_back(osg::Vec3(texcoord.x(), texcoord.y(), 0.0));
608 tcoords->push_back(osg::Vec2(texcoord.x(), texcoord.y()));
609 texcoord += dx_texcoord;
610 }
611 }
612
613 particleGeometry->setVertexArray(vertexarray);
614 particleGeometry->addPrimitiveSet(new osg::DrawArrays(GL_POINTS, 0, numVertices));
615 particleGeometry->setTexCoordArray(0, tcoords);
616 //this glMemoryBarrier thing... not sure if we could better do instanced drawing? all the data is in Shader Storage Buffer..
617 particleGeometry->setVertexAttribArray(1, particleGeometry->getTexCoordArray(0), osg::Array::BIND_PER_VERTEX);
618
619 _computationResultsRenderGroup->addChild(particleGeode);
620 particleGeode->addDrawable(particleGeometry);
621
622 addChild(_computationResultsRenderGroup.get());
623
624 }
625
626
initComputingSetup()627 void ComputeNode::initComputingSetup()
628 {
629
630 _computeProgram = new osg::Program;
631 _computeProgram->setComputeGroups((NUM_ELEMENTS_X / WORK_GROUP_SIZE) <= 1 ? 1 : (NUM_ELEMENTS_X / WORK_GROUP_SIZE), (NUM_ELEMENTS_Y / WORK_GROUP_SIZE) <= 1 ? 1 : (NUM_ELEMENTS_Y / WORK_GROUP_SIZE), 1);
632 _computeShader = osg::Shader::readShaderFile(osg::Shader::COMPUTE, _computeShaderSourcePath);
633 _computeProgram->addShader(_computeShader.get());
634
635 setDataVariance(osg::Object::DYNAMIC);
636 osg::StateSet* statesetComputation = getOrCreateStateSet();
637 statesetComputation->setAttributeAndModes(_computeProgram.get());
638 statesetComputation->addUniform(new osg::Uniform("numCols", (int)NUM_ELEMENTS_X));
639 statesetComputation->addUniform(new osg::Uniform("numRows", (int)NUM_ELEMENTS_Y));
640 statesetComputation->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
641
642 //blocksize
643 int numParticles = NUM_ELEMENTS_X * NUM_ELEMENTS_Y;
644 const unsigned blockSize = numParticles * __numChannels * __numDataValuesPerChannel* sizeof(GLfloat);
645
646 //init all the particle data array
647 int idx = 0;
648 _data = new GLfloat[NUM_ELEMENTS_X * NUM_ELEMENTS_Y * __numChannels * __numDataValuesPerChannel];
649 _dataArray = new FloatArray;
650
651 //init the data array somehow, this way all is stored in one BufferObject. maybe better using multiple buffers instead? not sure what is faster and better for threading
652 for (int d = 0; d < __numDataValuesPerChannel; ++d)
653 {
654 for (int i = 0; i < NUM_ELEMENTS_X; ++i)
655 {
656
657 for (int j = 0; j < NUM_ELEMENTS_Y; ++j)
658 {
659
660 for (int k = 0; k < __numChannels; ++k)
661 {
662 switch (k)
663 {
664
665 case (RED_CHANNEL) :
666 {
667 if ((d == POSITION_NOW_OFFSET) || (d == POSITION_OLD_OFFSET) || (d == POSITION_INIT_OFFSET))//position
668 {
669 *_data = random(0.25, 0.75);
670 }
671 if ((d == VELOCITY_NOW_OFFSET) || (d == VELOCITY_OLD_OFFSET) || (d == VELOCITY_INIT_OFFSET))//velocity
672 {
673 *_data = random(-2.4, 2.4);
674 }
675 if (d == ACCELERATION_OFFSET) //acceleration
676 {
677 *_data = random(-3.0, 3.0);
678 }
679
680 if (d == PROPERTIES_OFFSET) //property particle mass (compute shader is computing sphere mass from radius instead)
681 {
682 *_data = random(0.2, 15.0);
683 }
684
685 break;
686 }
687
688 case (GREEN_CHANNEL) :
689 {
690 if ((d == POSITION_NOW_OFFSET) || (d == POSITION_OLD_OFFSET) || (d == POSITION_INIT_OFFSET))//position
691 {
692 *_data = random(0.25, 0.75);
693 }
694 if ((d == VELOCITY_NOW_OFFSET) || (d == VELOCITY_OLD_OFFSET) || (d == VELOCITY_INIT_OFFSET))//velocity
695 {
696 *_data = random(-2.4, 2.4);
697 }
698
699 if (d == ACCELERATION_OFFSET)//acceleration
700 {
701 *_data = random(-3.0, 3.0);
702 }
703 if (d == PROPERTIES_OFFSET) //property particle radius
704 {
705 *_data = random(0.07, 0.219);
706 }
707
708 break;
709 }
710
711 case (BLUE_CHANNEL) :
712 {
713 if ((d == POSITION_NOW_OFFSET) || (d == POSITION_OLD_OFFSET) || (d == POSITION_INIT_OFFSET))//position
714 {
715 *_data = random(0.25, 0.75);
716 }
717 if ((d == VELOCITY_NOW_OFFSET) || (d == VELOCITY_OLD_OFFSET) || (d == VELOCITY_INIT_OFFSET))//velocity
718 {
719 *_data = random(-2.4, 2.4);
720 }
721
722 if (d == ACCELERATION_OFFSET)//acceleration
723 {
724 *_data = random(-3.0, 3.0);
725 }
726
727
728 if (d == PROPERTIES_OFFSET) //place for some other property
729 {
730 *_data = random(0.0, 0.0);
731 }
732
733 break;
734 }
735
736 case (ALPHA_CHANNEL) :
737 {
738 if ((d == POSITION_NOW_OFFSET) || (d == POSITION_OLD_OFFSET) || (d == POSITION_INIT_OFFSET))//position
739 {
740 *_data = random(1.0, 1.0);
741 }
742 if ((d == VELOCITY_NOW_OFFSET) || (d == VELOCITY_OLD_OFFSET) || (d == VELOCITY_INIT_OFFSET))//velocity
743 {
744 *_data = random(-2.4, 2.4);
745 }
746
747 if (d == ACCELERATION_OFFSET) //acceleration
748 {
749 //*_data = random(1.0, 1.0);
750 *_data = random(0.0, 0.0);
751 }
752
753 if (d == PROPERTIES_OFFSET) //place for some other property
754 {
755 *_data = random(0.3, 0.3);
756 }
757
758 break;
759 }
760
761
762
763 }
764 _dataArray->push_back(*_data);
765 _data++;
766 idx++;
767 }
768 }
769 }
770 }
771
772 _ssbo = new osg::ShaderStorageBufferObject;
773 _dataArray->setBufferObject(_ssbo.get());
774
775
776 _ssbb = new osg::ShaderStorageBufferBinding(0, _ssbo.get(), 0, blockSize);
777 statesetComputation->setAttributeAndModes(_ssbb.get(), osg::StateAttribute::ON);
778
779
780 //option, do something useful with data or test the transfer speed
781 //_ssbb->setUpdateCallback(new ShaderStorageBufferCallback);
782
783 //adding a quad , visualizing data in buffer
784 addDataMonitor(osg::Vec3(0, -1, 0), osg::Vec3(SUB_PLACEMENT_OFFSET_HORIZONTAL * 0, -SUB_PLACEMENT_OFFSET_VERTICAL * -2.0, SUB_PLACEMENT_OFFSET_HORIZONTAL * 0), 1.0, RGB_CHANNEL, POSITION_NOW_OFFSET, "X,Y,Z - PositionNow", -1.0, 1.0);
785
786 //the coord from default dataset
787 addHelperGeometry();
788
789
790 addComputationResultsRenderTree();
791
792 }
793
794
795 //taken from osgdistorsion example for getting it nice on screen with antialiasing
createPrerenderSubgraph(osg::Node * subgraph,const osg::Vec4 & clearColour)796 osg::Node* createPrerenderSubgraph(osg::Node* subgraph, const osg::Vec4& clearColour)
797 {
798 osg::Group* prerenderNode = new osg::Group;
799
800 unsigned int tex_width = PRERENDER_WIDTH;
801 unsigned int tex_height = PRERENDER_HEIGHT;
802
803 osg::Texture2D* texture = new osg::Texture2D;
804 texture->setTextureSize(tex_width, tex_height);
805 texture->setInternalFormat(GL_RGBA);
806 texture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);
807 texture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);
808
809 {
810 osg::Camera* prerenderCamera = new osg::Camera;
811 prerenderCamera->setClearColor(clearColour);
812 prerenderCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
813 prerenderCamera->setReferenceFrame(osg::Transform::RELATIVE_RF);
814 prerenderCamera->setProjectionMatrix(osg::Matrixd::identity());
815 prerenderCamera->setViewMatrix(osg::Matrixd::identity());
816 prerenderCamera->setViewport(0, 0, tex_width, tex_height);
817 prerenderCamera->setRenderOrder(osg::Camera::PRE_RENDER);
818 prerenderCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
819 prerenderCamera->attach(osg::Camera::COLOR_BUFFER0, texture, 0, 0, false, PRERENDER_ANTIALIASINGMULTISAMPLES, PRERENDER_ANTIALIASINGMULTISAMPLES);
820 prerenderCamera->addChild(subgraph);
821 prerenderNode->addChild(prerenderCamera);
822
823 }
824
825 {
826 osg::Geometry* polyGeom = new osg::Geometry();
827
828 polyGeom->setSupportsDisplayList(false);
829
830 osg::Vec3 origin(0.0f, 0.0f, 0.0f);
831 osg::Vec3 xAxis(1.0f, 0.0f, 0.0f);
832 osg::Vec3 yAxis(0.0f, 1.0f, 0.0f);
833
834 float height = 1024.0f;
835 float width = 1280.0f;
836 int noSteps = 3;
837
838 osg::Vec3Array* vertices = new osg::Vec3Array;
839 osg::Vec2Array* texcoords = new osg::Vec2Array;
840 osg::Vec4Array* colors = new osg::Vec4Array;
841
842 osg::Vec3 bottom = origin;
843 osg::Vec3 dx = xAxis*(width / ((float)(noSteps - 1)));
844 osg::Vec3 dy = yAxis*(height / ((float)(noSteps - 1)));
845
846 osg::Vec2 bottom_texcoord(0.0f, 0.0f);
847 osg::Vec2 dx_texcoord(1.0f / (float)(noSteps - 1), 0.0f);
848 osg::Vec2 dy_texcoord(0.0f, 1.0f / (float)(noSteps - 1));
849
850 int i, j;
851 for (i = 0; i < noSteps; ++i)
852 {
853 osg::Vec3 cursor = bottom + dy*(float)i;
854 osg::Vec2 texcoord = bottom_texcoord + dy_texcoord*(float)i;
855 for (j = 0; j < noSteps; ++j)
856 {
857 vertices->push_back(cursor);
858 texcoords->push_back(osg::Vec2((sin(texcoord.x()*osg::PI - osg::PI*0.5) + 1.0f)*0.5f, (sin(texcoord.y()*osg::PI - osg::PI*0.5) + 1.0f)*0.5f));
859 colors->push_back(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
860 cursor += dx;
861 texcoord += dx_texcoord;
862 }
863 }
864
865 polyGeom->setVertexArray(vertices);
866 polyGeom->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
867 polyGeom->setTexCoordArray(0, texcoords);
868
869 for (i = 0; i < noSteps - 1; ++i)
870 {
871 osg::DrawElementsUShort* elements = new osg::DrawElementsUShort(osg::PrimitiveSet::QUAD_STRIP);
872 for (j = 0; j < noSteps; ++j)
873 {
874 elements->push_back(j + (i + 1)*noSteps);
875 elements->push_back(j + (i)*noSteps);
876 }
877 polyGeom->addPrimitiveSet(elements);
878 }
879
880 osg::StateSet* stateset = polyGeom->getOrCreateStateSet();
881 stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);
882 stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
883
884 osg::Geode* geode = new osg::Geode();
885 geode->addDrawable(polyGeom);
886
887 osg::Camera* nestedRenderCamera = new osg::Camera;
888 nestedRenderCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
889 nestedRenderCamera->setViewMatrix(osg::Matrix::identity());
890 nestedRenderCamera->setProjectionMatrixAsOrtho2D(0, 1280, 0, 1024);
891 nestedRenderCamera->setRenderOrder(osg::Camera::NESTED_RENDER);
892 nestedRenderCamera->addChild(geode);
893
894 prerenderNode->addChild(nestedRenderCamera);
895 }
896
897 return prerenderNode;
898 }
899
900
901
902
main(int argc,char ** argv)903 int main(int argc, char** argv)
904 {
905 osg::ArgumentParser arguments(&argc, argv);
906
907 osgViewer::Viewer viewer;
908
909 osg::ref_ptr<osg::Group> scene = new osg::Group;
910
911
912 viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));
913 viewer.addEventHandler(new osgViewer::StatsHandler);
914 viewer.addEventHandler(new osgViewer::WindowSizeHandler);
915 viewer.addEventHandler(new osgViewer::ThreadingHandler);
916 viewer.getCamera()->setProjectionMatrixAsPerspective(60.0f, 1.33333, 0.01, 100.0);
917 viewer.setCameraManipulator(new osgGA::TrackballManipulator());
918
919 viewer.setUpViewInWindow(11, 11, 800 + 11, 600 + 11);
920 //viewer.setUpViewOnSingleScreen(0); // !!
921
922 viewer.getCamera()->setClearColor(osg::Vec4(0.3, 0.3, 0.3, 1.0));
923 viewer.setThreadingModel(osgViewer::Viewer::SingleThreaded);// we can play with threading models later
924
925 osg::ref_ptr<ComputeNode> computeNode = new ComputeNode();
926 computeNode->setPosition(osg::Vec3(0, 0, 0));
927 computeNode->setUpdateCallback(new ComputeNodeUpdateCallback(computeNode.get())); // on-the-fly reloading the shaders if shader source on disk is changed
928 computeNode->initComputingSetup();
929
930
931
932 scene->addChild(computeNode.get());
933 scene->addChild(computeNode->_computationResultsRenderGroup.get());
934
935
936 #ifdef PRERENDER_HIGH_QUALITY_ANTIALIASING
937 viewer.setSceneData(createPrerenderSubgraph(scene.get(), osg::Vec4(0.3, 0.4, 0.6, 1)));
938 #else
939 viewer.setSceneData(scene.get());
940 #endif
941
942 viewer.realize();
943
944 viewer.getCamera()->getGraphicsContext()->getState()->setUseModelViewAndProjectionUniforms(true);
945
946 viewer.run();
947
948 return 1;
949 }
950