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