{"id":1015,"date":"2012-05-01T22:47:48","date_gmt":"2012-05-01T14:47:48","guid":{"rendered":"http:\/\/www.magicandlove.com\/blog\/?p=1015"},"modified":"2012-05-01T23:01:54","modified_gmt":"2012-05-01T15:01:54","slug":"opencl-particles-system-example-in-processing","status":"publish","type":"post","link":"http:\/\/www.magicandlove.com\/blog\/2012\/05\/01\/opencl-particles-system-example-in-processing\/","title":{"rendered":"OpenCL Particles System Example in Processing"},"content":{"rendered":"<p>The second example is adopted from the Million Particles example from the <a href=\"http:\/\/www.pixelnerve.com\/v\/2010\/07\/29\/msaopencl-for-java-and-processing\/\">MSAOpenCL library<\/a>. I developed it with Processing 2.0 alpha version without using the library and the Pointer class. All the memory buffers are standard <a href=\"http:\/\/docs.oracle.com\/javase\/1.4.2\/docs\/api\/java\/nio\/Buffer.html\">Java.nio.Buffer<\/a>. The performance is very acceptable with one million particles in the ATI Radeon HD 4670 graphics card.<br \/>\n&nbsp;<br \/>\n<iframe loading=\"lazy\" width=\"480\" height=\"360\" src=\"http:\/\/www.youtube.com\/embed\/hOTDfXoYxoc?rel=0\" frameborder=\"0\" allowfullscreen><\/iframe><br \/>\n&nbsp;<br \/>\nThe Processing source<\/p>\n<pre lang=\"java\">\r\nimport processing.opengl.*;\r\nimport javax.media.opengl.*;\r\nimport javax.media.opengl.glu.GLU;\r\nimport java.nio.FloatBuffer;\r\nimport java.nio.ByteBuffer;\r\n\r\nimport com.nativelibs4java.opencl.*;\r\nimport com.nativelibs4java.opencl.CLMem.Usage;\r\n\r\nfinal int PARTICLES_COUNT = 1000000;\r\nfloat halfWidth, halfHeight;\r\n\r\nGL2 gl;\r\nPGL pgl;\r\n\r\nint [] vbo = new int[1];\r\n\r\nCLContext context;\r\nCLQueue queue;\r\nCLKernel kernel;\r\n\r\nCLBuffer<Float> partMem;\r\nFloatBuffer partBuf;\r\n\r\nCLBuffer<Byte> posMem;\r\nByteBuffer partPos;\r\n\r\nPVector mousePos;\r\n\r\nvoid setup() {\r\n  size(screenWidth, screenHeight, OPENGL);\r\n  background(0);\r\n  randomSeed(millis());\r\n  halfWidth = width\/2;\r\n  halfHeight = height\/2;\r\n  mousePos = new PVector(float(mouseX) - halfWidth, halfHeight - float(mouseY));\r\n\r\n  PGraphicsOpenGL pg = (PGraphicsOpenGL) g;\r\n  pgl = pg.beginPGL();\r\n  gl = pgl.gl.getGL().getGL2();\r\n  gl.glClearColor(0, 0, 0, 1);\r\n  gl.glClear(GL.GL_COLOR_BUFFER_BIT);\r\n  gl.glEnable(GL2.GL_POINT_SMOOTH);\r\n  gl.glPointSize(2f);\r\n\r\n  initOpenCL();\r\n  pg.endPGL();\r\n}\r\n\r\nvoid initOpenCL() {\r\n  context = JavaCL.createContextFromCurrentGL();\r\n  queue = context.createDefaultQueue();\r\n\r\n  partBuf = FloatBuffer.allocate(PARTICLES_COUNT * 4);\r\n  partPos = ByteBuffer.allocateDirect(PARTICLES_COUNT * 2 * Float.SIZE\/8).order(context.getByteOrder());\r\n  FloatBuffer tmpPos = partPos.asFloatBuffer();\r\n\r\n  for (int i = 0; i < PARTICLES_COUNT; i++) {\r\n\r\n    partBuf.put(0.0f);\r\n    partBuf.put(0.0f);\r\n    partBuf.put(random(0.2, 2.0));\r\n    partBuf.put(0.0f);\r\n\r\n    tmpPos.put(random(width));\r\n    tmpPos.put(random(height));\r\n  }\r\n\r\n  partBuf.rewind();\r\n  partPos.rewind();\r\n\r\n  gl.glGenBuffers(1, vbo, 0);\r\n  gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vbo[0]);\r\n\r\n  gl.glBufferData(GL.GL_ARRAY_BUFFER, (int) partPos.capacity(), partPos, GL2.GL_DYNAMIC_COPY);\r\n  gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);\r\n\r\n  posMem = context.createBufferFromGLBuffer(Usage.InputOutput, vbo[0]);\r\n  partMem = context.createFloatBuffer(Usage.InputOutput, partBuf, true);\r\n\r\n  String pgmSrc = join(loadStrings(dataPath(\"Particle.cl\")), \"\\n\");\r\n  CLProgram program = context.createProgram(pgmSrc);\r\n  kernel = program.build().createKernel(\"updateParticle\");\r\n  kernel.setArg(0, partMem);\r\n  kernel.setArg(1, posMem);\r\n  kernel.setArg(2, new float [] {\r\n    mousePos.x, mousePos.y\r\n  }\r\n  );\r\n}\r\n\r\nvoid draw() {\r\n  background(0);\r\n  gl.glMatrixMode(GL2.GL_PROJECTION);\r\n  gl.glLoadIdentity();\r\n  pgl.glu.gluOrtho2D(-halfWidth - 1, halfWidth + 1, -halfHeight - 1, halfHeight + 1);\r\n  gl.glMatrixMode(GL2.GL_MODELVIEW);\r\n  gl.glColor3f(1.0f, 0.8f, 0.0f);\r\n  gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, vbo[0]);\r\n  queue.finish();\r\n  gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);\r\n  gl.glVertexPointer(2, GL.GL_FLOAT, 0, 0);\r\n  gl.glDrawArrays(GL2.GL_POINTS, 0, PARTICLES_COUNT);\r\n\r\n  gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);\r\n  gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);\r\n  callKernel();\r\n}\r\n\r\nvoid callKernel() {\r\n  mousePos.set(float(mouseX) - halfWidth, halfHeight - float(mouseY), 0.0f);\r\n  CLEvent kernelCompletion;\r\n  synchronized(kernel) {\r\n    posMem.acquireGLObject(queue);\r\n    kernel.setArg(2, new float [] {\r\n      mousePos.x, mousePos.y\r\n    }\r\n    );\r\n    int [] globalSizes = new int[] {\r\n      PARTICLES_COUNT\r\n    };\r\n    kernelCompletion = kernel.enqueueNDRange(queue, globalSizes);\r\n    posMem.releaseGLObject(queue);\r\n  }\r\n}\r\n<\/pre>\n<p>&nbsp;<br \/>\nThe kernel source<\/p>\n<pre lang=\"cpp\">\r\n#define DAMP\t\t\t0.95f\r\n#define CENTER_FORCE\t\t0.005f\r\n#define MOUSE_FORCE\t\t200.0f\r\n#define MIN_SPEED\t\t0.2f\r\n\r\ntypedef struct{\r\n\tfloat2 vel;\r\n\tfloat mass;\r\n\tfloat dummy;\t\t\r\n} Particle;\r\n\r\n__kernel void updateParticle(__global Particle* particles, \r\n\t__global float2* posBuffer, \r\n\tconst float2 mousePos)\r\n{\r\n\tint id = get_global_id(0);\r\n\t__global Particle *p = &particles[id];\r\n\t\r\n\tfloat2 diff = mousePos - posBuffer[id];\r\n\tfloat invDistSQ = 1.0f \/ dot(diff, diff);\r\n\tdiff *= (MOUSE_FORCE * invDistSQ);\r\n\r\n\tp->vel += -posBuffer[id] * CENTER_FORCE - diff * p->mass;\r\n\t\r\n\tfloat speed2 = dot(p->vel, p->vel);\r\n\tif (speed2 < MIN_SPEED) \r\n\t\tposBuffer[id] = mousePos + diff * (1 + p->mass);\r\n\r\n\tposBuffer[id] += p->vel;\r\n\tp->vel *= DAMP;\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>The second example is adopted from the Million Particles example from the MSAOpenCL library. I developed it with Processing 2.0 alpha version without using the library and the Pointer class. All the memory buffers are standard Java.nio.Buffer. The performance is very acceptable with one million particles in the ATI Radeon HD 4670 graphics card. &nbsp; [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[79,66],"tags":[106,62],"class_list":["post-1015","post","type-post","status-publish","format-standard","hentry","category-software-2","category-testing","tag-opencl","tag-processing-org"],"_links":{"self":[{"href":"http:\/\/www.magicandlove.com\/blog\/wp-json\/wp\/v2\/posts\/1015","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.magicandlove.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.magicandlove.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.magicandlove.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.magicandlove.com\/blog\/wp-json\/wp\/v2\/comments?post=1015"}],"version-history":[{"count":8,"href":"http:\/\/www.magicandlove.com\/blog\/wp-json\/wp\/v2\/posts\/1015\/revisions"}],"predecessor-version":[{"id":1023,"href":"http:\/\/www.magicandlove.com\/blog\/wp-json\/wp\/v2\/posts\/1015\/revisions\/1023"}],"wp:attachment":[{"href":"http:\/\/www.magicandlove.com\/blog\/wp-json\/wp\/v2\/media?parent=1015"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.magicandlove.com\/blog\/wp-json\/wp\/v2\/categories?post=1015"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.magicandlove.com\/blog\/wp-json\/wp\/v2\/tags?post=1015"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}