Conway's Game of Life in pure QML, QML+GLSL ShaderEffect, and more recursive ShaderEffectSource fun
-
Just for fun. Conway's in C++ seems to be an old favourite here, but I was curious about QML practicality and performance.
Here's a primarily javascript "implementation":https://bitbucket.org/timday/qtlab/src/001c3f2763141b0ae5b6da05727645aaeef1c4d3/experiment/life-qml/CGoL.qml?at=default using dynamic creation/destruction of cells ("screenshot":https://bitbucket.org/timday/qtlab/src/001c3f2763141b0ae5b6da05727645aaeef1c4d3/experiment/life-qml/screenshot.png?at=default). (Somewhere I have an older version using a Grid of Repeater stamped-out cells with alive/dead states and state transition animations... but it was hideously slow when I last tried it in Qt4.8/QDeclarative).
But what actually got me messing around with this stuff again was noticing the recursive option to "ShaderEffectSource":http://qt-project.org/doc/qt-5/qml-qtquick-shadereffectsource.html which opens up the possibility of computing updates with GLSL: "implementation":https://bitbucket.org/timday/qtlab/src/001c3f2763141b0ae5b6da05727645aaeef1c4d3/experiment/life-qml-shader/CGoL.qml?at=default ("screenshot":https://bitbucket.org/timday/qtlab/src/001c3f2763141b0ae5b6da05727645aaeef1c4d3/experiment/life-qml-shader/screenshot.png?at=default).
Both run well (nice and fast) in Qt 5.3's qmlscene (on Linux, with an old GeForce 9600 anway). Nothing interactive here; this was just to exercise the Javascript engine and for the GLSL to see if recursive updates worked as I hoped they would. They do, which opens the door to efficiently computing "reaction-diffusion type effects":http://www.karlsims.com/rd.html on QtQuick UIs.
-
Relating to reaction-diffusion systems: QML+GLSL code "here":https://bitbucket.org/timday/qtlab/src/df10ca2571605466dd60adb9c08ad270aca53a7b/experiment/diffuse-qml-shader/diffuse.qml?at=default while not technically a diffusion equation gets me a nice blue glow around things: !https://bytebucket.org/timday/qtlab/raw/df10ca2571605466dd60adb9c08ad270aca53a7b/experiment/diffuse-qml-shader/screenshot.png(diffuse blue glow)! Main point is the use of two ShaderEffectSources by the GLSL, one for the "base" content which feeds into the reaction and the other one for the iteratively updated state. Will have to try something more complicated next but I have a nasty feeling the 8-bit/channel quantization of the iterative state will cause issues; the ShaderEffectSources format property lets me choose between RGBA/RGB/A textures... but not the precision (floating-point precision textures might well be useful here).
-
Very cool. Thanks for the post.
I've been wondering whether it would be possible to implement http://http.developer.nvidia.com/GPUGems/gpugems_ch38.html style simulators via a recursive shader effect, and your post makes me think that it shouldn't be too hard. I might give it a try on the weekend, if I find the time.
Cheers,
Chris. -
Been playing with this a bit more; qmlscene-runnable "code here":https://bitbucket.org/timday/qtlab/src/387d7d6b135b33ff175005d8aba971c733bd6e9f/experiment/diffuse-qml-shader/diffuse.qml?at=default implements a classic reaction diffusion equation.
Results before the "enter" button is hit, and then at some times after, are shown below.
As suspected, the quantization to an 8-bit/channel image between each iteration was initially a problem for a "straight" implementation; note the use of the 'p' parameter to randomly change rounding behaviour (and the logic to ensure every p is balanced by a 1-p; the overall brightness suffers a random brightening/dimming otherwise). Without that the system would just stop evolving where the gradients weren't strong enough to produce a sufficiently large change.
Also, it's not that fast; the frames update quickly enough, but there's a "speed of light" problem with this sort of thing that any spatial effects can propgate at maximum one pixel at a time. Not sure if there is some clamp on QSG rerendering rate or I just need faster GPU HW for really fluid effects. A fluid-dynamic system where you click some control and ripples bounce around the screen, or the UI just flows off the screen like water would be stunning though :^)
-
Bit more QML+GLSL hacking gets me some wave propagation:
Video "here":http://youtu.be/o_pZzb8-TQ0 ; code "here":https://bitbucket.org/timday/qtlab/src/f3277d09dd8156b8b992cd69720a1e9713937f5d/experiment/waves-qml-shader/waves.qml?at=default
It's a bit rough; the quantization of saving state in 8-bit/channel framebuffer causes some trouble (improved a bit by some extra scaling using a spare channel) and it was quite hard to get any sort of stable behaviour. Compare with this much smoother numpy reference implementation of the same physics: ("video":http://youtu.be/aTLXcyI2HvE , "code":https://bitbucket.org/timday/qtlab/src/f3277d09dd8156b8b992cd69720a1e9713937f5d/experiment/waves-qml-shader/waves.py?at=default). Still, having waves interacting with the UI elements, and the UI elements creating waves when moved... quite fun. Bit of environment mapping instead of just matt shading, and it could be very bling indeed. If only there was a way of getting recursive ShaderEffectSource to use a floating point buffer, it'd help a lot (I may file a wishlist feature request on Jira; floating point framebuffers have been around for years on desktop platforms, not sure what the OpenGLES situation is though).
If there was ever a case of "It is not done well; but you are surprised to find it done at all", this is it...
-
OK probably the furthest I'm going to take this:
- worked round the limited 8-bit precision by splitting storage of each of the two physical quantities of interest across 2 channels of RGBA (so state is stored in 65536 bits instead of 256).
- Added a skymap option for shiny bling value.
Video "here":http://youtu.be/FM0CbKvYpYI, code "here":https://bitbucket.org/timday/qtlab/src/087a171c0b2008f14a96514d276206fe76a0a168/experiment/waves-qml-shader/waves.qml?at=default. (Note it uses the skymap image "here":https://bitbucket.org/timday/qtlab/src/087a171c0b2008f14a96514d276206fe76a0a168/experiment/waves-qml-shader/skymap.jpg?at=default ; "attribution":https://bitbucket.org/timday/qtlab/src/087a171c0b2008f14a96514d276206fe76a0a168/experiment/waves-qml-shader/README.md?at=default ).
Works on an NVidia GeForce 9600. Also tried on old Intel laptop but clearly it has some rounding/precision differences and the liquid kind of drains away. I've no idea how consistent graphics HW is about this sort of thing (other than that Intel is notoriously bad) or how sensitive my GLSL code is to such details.
There was already an issue for a higher precision ShaderEffectSource buffer "in Jira":https://bugreports.qt-project.org/browse/QTBUG-38279.
-
That is all really cool, thanks for sharing!