Posts mit dem Label pixel buffer object werden angezeigt. Alle Posts anzeigen
Posts mit dem Label pixel buffer object werden angezeigt. Alle Posts anzeigen

Samstag, 18. Mai 2013

Planet Gravitation Map Simulator

Screenshot CUDA / OpenGL Demo of Gravitation Map Simulator
This is just another fun demo for CUDA and OpenGL. It is a slightly modified version of the so called "random oscillating magnetic pendulum" (ROMP). A pixel represents a start position of one particle. This object becomes attracted by all the planets around due to their gravity. After a while the object is going to hit a planet. Now that corresponding pixel gets the color of that planet. You see an evolving map of "gravity" structures in realtime (more or less *cough*).


Download: (tested on Linux and Windows (VS2010), needs CUDA, OpenGL, GLUT, GLEW)

Download Source Code v1.1
Download Source Code v1.0



Positions and masses of the planets can be changed by the user as well as the scale of the map.

The simulation uses Euler or Runge-Kutta integration method for solving the differential equation. It is also possible to use doubles instead of floats for better precision.

The idea for this demo comes from a friend, he also implemented the algorithm in processing. I ported this to CUDA, so the stuff is entirely computed and rendered on the GPU. You need a Nvidia CUDA capable GPU for that.
Screenshot 2.1, different coloring
Screenshot 2.2

Computation

Given a particle at position $ \vec{y}(t)=\left(x(t), y(t)\right)$ at a time t. This particle has a (positive) mass m. In our space there are n planets with their fixed positions $ \vec p_1,\ldots,\vec p_n$ and (positive) masses $ m_1,\ldots,m_n$.
The following equation represents the gravitational force of planet i acting on our particle. This force comes from Newton's law of universal gravitation:

$\displaystyle \vec F_i = G\frac{m_i\cdot m}{r_i^2}\vec e_{r_i}$
G is just a gravitation constant and can be neglected for our purposes. r is the distance between the particle and the planet, $ \vec e_r$ is the force direction vector:
$\displaystyle \vec e_r = \frac{\vec p_i - \vec y}{\Vert \vec p_i - \vec y\Vert}$
Hence, there is the following force at a time t:
$\displaystyle \sum_{i=1}^n\vec F_i(t) = \sum_{i=1}^n m\cdot \frac{m_i\cdot\left(\vec p_i - \vec y(t)\right)}{\Vert\vec p_i-\vec y(t)\Vert^3}$
Newton's second law gives the equality:

$\displaystyle \vec F=m\cdot\vec a\Rightarrow \sum_{i=1}^n\vec F_i(t) = m\cdot \frac{\mathrm d^2 \vec y(t)}{\mathrm dt^2}$
The mass m of the particle can be eliminated in the equation. This ordinary differential equation system of order 2 need to be integrated two times to obtain the position function:

\begin{displaymath}\begin{split}\vec v(t) =& \frac{\mathrm dy(t)}{\mathrm dt}=\i...
...c y(t) =& \int\limits_0^t \vec v(\tau)\mathrm d\tau \end{split}\end{displaymath}    

This integration can be solved numerically, e.g. by Euler's method:

\begin{displaymath}\begin{split}\vec v_{k+1} =& \vec v_k + h\cdot\sum_{i=1}^n\fr...
...y_k\Vert^3}\\ \vec y_{k+1} =& \vec y_k + h\cdot v_k \end{split}\end{displaymath}    

with $ y_k{=}y(t_k),\, v_k{=}(t_k),$ and $ y(t_0){=}y_0,\, v(t_0){=}v_0$ as the initial values. h is the size of every step and v represents the velocity of the particle.


Here you see some videos, which are showing the simulation more or less in realtime.


More:
Article by Ingo Berg, 2006, on magnetic pendulum with implementation (using Beeman's algorithm):
http://www.codeproject.com/Articles/16166/The-magnetic-pendulum-fractal

Mathematica implementation:
http://nylander.wordpress.com/2007/10/27/magnetic-pendulum-strange-attractor/

Some more stuff, videos:
http://magnetmfa.wikispaces.com/pendula?responseToken=05ae0f7708a5c4c989455051dc970f570
http://www.youtube.com/watch?v=duy8s8C7-Uc
http://www.youtube.com/watch?v=QXf95_EKS6E

Sonntag, 16. Oktober 2011

Mandelbrot - Qt and CUDA with OpenGL Visual Studio 2010


Do you like Mandelbrot Set? Do you like Animations? Wanna have all together right now? Here it comes. (It has been supposed to be an exciting announcement, nevermind...)

A small demonstration example for CUDA with OpenGL Pixelbuffer put together in a Qt4 Application.



Source Project Solution VS 2010 "qt4_mandelbrot"
16.04.12: This example is "deprecated". I have reworked and bugfixed it for the Linux tutorial, see here. I will release the VS2010 edition soon, but you can copy the new source from the Linux tutorial.
19.10.11: Bugfix: cudaGLSetGLDevice(0) must be called only in initGL()

(Requirements see previous tutorial on qt and cuda in vs2010)


In my previous tutorial on Qt, CUDA, VS2010 I have shown you how to integrate all the libs together. You have been able to run an empty CUDA kernel in a Qt Application. It already has an OpenGL Widget (yet black empty screen). Now you want to create a 2D Image with Pixel-Fun-Stuff processed right on the GPU. OpenGL is just used as Presenter.

Before I have started I had following questions:
  1. How to connect .cu source with .cpp source ?
    (i.e. running kernel functions by extern classes)
  2. How to calculate and paint the result on gpu side ?
    (without involving cpu cycles/host such as memcopy)
1. Our cuda source is compiled by nvcc as you know. On the other hand we have classes in C++ such as our OpenGL Widget or our custom class SimplePBO which is our "Pixelbuffer-CUDA-Manager". Since SimplePBO accesses the kernel function in kernelPBO.cu, we have to declare the accessing function. This is done in globals.h.
extern "C" void launch_kernel(uchar4*, unsigned int, unsigned int, int);
(You cannot include your .cu files, since they are not simply C files. The implementation on cuda side will be linked after compilation, so launch_kernel() will find its definition here.)

2. I assume you know how to create and bind textures in OpenGL. You may heard of pixel buffer objects too. It's well explained on this site. We will allocate our image space on gpu side creating a pixelbuffer object. CUDA will use this object for pixel manipulation (of course on gpu side as well). Our image then will be bound to an OpenGL Quad as texture. I want to give you an encouraging quote from one of my references ([1]):
As we will see, CUDA and OpenGL interoperability is very fast!
The reason (aside from the speed of CUDA) is that CUDA maps OpenGL buffer(s) into the CUDA memory space with a call to cudaGLMapBufferObject(). On a single GPU system, no data movement is required! Once provided with a pointer, CUDA programmers are then free to exploit their knowledge of CUDA to write fast and efficient kernels that operate on the mapped OpenGL buffers. ([1])
But there is a little restriction you should know: The Pixelbuffer Access is exclusive. Only one can access the pixel buffer at the same time, either CUDA or OpenGL.

I also recommend presentation [3] about CUDA and OpenGL, especially the part starting on page 22 (Steps To Draw An Image From Cuda). You will see how to work with the pixel buffer in OpenGL and Cuda.

In our Demonstration Project we use Qt (QGLBuffer) for dealing with the Pixelbuffer, so we dont have to care for OpenGL extensions (maybe glew for proc adresses and so on). We create the pixel buffer object as follows (simplePBO.cpp::createPBO()):

pixelBuffer = new QGLBuffer(QGLBuffer::PixelUnpackBuffer);
pixelBuffer->setUsagePattern(QGLBuffer::DynamicCopy);
pixelBuffer->create();

pixelBuffer->bind();
pixelBuffer->allocate(size_tex_data);

HANDLE_ERROR( cudaGLRegisterBufferObject( pixelBuffer->bufferId() ) );

In simplePBO.cpp::initCuda() the first cuda device is choosen ( cudaGLSetGLDevice(0) ). You will have to change on your own, if it doesnt fit. You can check your cuda devices with this little exe I wrote from [2]: CUDA Device Checker (output on console). A more advanced GUI based CUDA Checker you can obtain here named as CUDA-Z.

Ok, I do not want to explain every method here, just catch the code and explore the comments and consider the references [1; 3].

Last thing I want to mention is the image size. Due to the thread dimensions (16 per block) image size has to be a multiple of 16. So dont get confused about it. You also could set a fixed image size of 512 or 528 or something like that (see simplePBO.cpp::initCuda()).


References:
[1] - CUDA, Supercomputing for the Masses, from http://drdobbs.com/cpp/222600097
[2] - CUDA By Example, An Introduction To General-Purpose GPU Programming. Book source codes you can download here
[3] - What Every CUDA Programmer Should Know About OpenGL, PDF Version