{"id":35,"date":"2025-06-01T20:42:12","date_gmt":"2025-06-01T20:42:12","guid":{"rendered":"https:\/\/blog.metu.edu.tr\/e264325\/?p=35"},"modified":"2026-02-10T15:11:09","modified_gmt":"2026-02-10T15:11:09","slug":"particle-magic-ceng469-hw3","status":"publish","type":"post","link":"https:\/\/blog.metu.edu.tr\/e264325\/2025\/06\/01\/particle-magic-ceng469-hw3\/","title":{"rendered":"Particle Magic &#8211; CENG469 HW3"},"content":{"rendered":"\n<p>Hello again, dear graphics enjoyers!<\/p>\n\n\n\n<p>This post details my approach to the &#8220;Particle Magic&#8221; assignment for CENG469 Computer Graphics II. The objective was to develop an OpenGL application capable of simulating a large number of points\u2014up to one million\u2014whose movements are influenced by user-configurable gravitational attractors. A key requirement was the use of compute shaders for updating particle states efficiently.<\/p>\n\n\n<figure class=\"wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-4-3 wp-has-aspect-ratio\"><div class=\"lyte-wrapper fourthree\" style=\"width:420px;max-width:100%;margin:5px;\"><div class=\"lyMe\" id=\"WYL_GmpyP3x0DEU\"><div id=\"lyte_GmpyP3x0DEU\" data-src=\"\/\/i.ytimg.com\/vi\/GmpyP3x0DEU\/hqdefault.jpg\" class=\"pL\"><div class=\"tC\"><div class=\"tT\"><\/div><\/div><div class=\"play\"><\/div><div class=\"ctrl\"><div class=\"Lctrl\"><\/div><div class=\"Rctrl\"><\/div><\/div><\/div><noscript><a href=\"https:\/\/youtu.be\/GmpyP3x0DEU\" rel=\"nofollow\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/i.ytimg.com\/vi\/GmpyP3x0DEU\/0.jpg\" alt=\"YouTube video thumbnail\" width=\"420\" height=\"295\" \/><br \/>Watch this video on YouTube<\/a><\/noscript><\/div><\/div><div class=\"lL\" style=\"max-width:100%;width:420px;margin:5px;\"><\/div><figcaption><\/figcaption><\/figure>\n\n\n<h2 class=\"wp-block-heading\">Architectural Overview<\/h2>\n\n\n\n<p>Key classes and their responsibilities:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>InputHandler<\/code><\/strong>: Processes keyboard and mouse inputs, translating them into actions within the <code>Application<\/code> and <code>SceneManager<\/code>.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>Application<\/code><\/strong>: Serves as the central orchestrator, managing the GLFW window, the main application loop, and the interaction between other components.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>ShaderManager<\/code><\/strong>: Handles the loading, compilation, and linking of all GLSL shader programs (vertex, fragment, and compute).<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>ParticleSystem<\/code><\/strong>: Manages the core particle data, including positions, velocities, and ages. It interfaces directly with the compute shader for state updates and handles the rendering of particles.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>SceneManager<\/code><\/strong>: Oversees the simulation environment, including the collection of gravitational attractors, the particle spawn origin, simulation speed (delta time scaling), and user interaction modes.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>TextRenderer<\/code><\/strong>: Responsible for rendering on-screen text for the user interface, utilizing the FreeType library.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Core Simulation: Particle Behavior and Compute Shader Mechanics<\/h2>\n\n\n\n<p>Each particle maintains a state comprising its <strong>position<\/strong> (<code>glm::vec3<\/code>), <strong>velocity<\/strong> (<code>glm::vec3<\/code>), and current <strong>age<\/strong> (<code>float<\/code>). The age attribute decrements over time, and upon reaching zero, the particle is &#8220;respawned.&#8221;<\/p>\n\n\n\n<p>Gravitational <strong>attractors<\/strong>, defined by their <strong>position<\/strong> and <strong>mass<\/strong> (<code>float<\/code>), exert forces on the particles. The magnitude of this force is proportional to the attractor&#8217;s mass. The simulation is initialized with a default set of attractors, and users can add or remove attractors dynamically.<\/p>\n\n\n\n<p>The computational core of the simulation resides in the <strong>compute shader<\/strong>. For each frame, the compute shader performs the following operations for every active particle:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>State Retrieval<\/strong>: The particle&#8217;s current position, velocity, age, and its initial maximum age (used for normalization) are read from a Shader Storage Buffer Object (SSBO).<\/li>\n\n\n\n<li><strong>Gravitational Force Accumulation<\/strong>:\n<ul class=\"wp-block-list\">\n<li>A net force vector for the particle is initialized to zero.<\/li>\n\n\n\n<li>The shader iterates through all active attractors. For each attractor:\n<ul class=\"wp-block-list\">\n<li>The vector from the particle to the attractor (<code>Direction = AttractorPosition - ParticlePosition<\/code>) is calculated.<\/li>\n\n\n\n<li>The squared distance (<code>DistanceSquared<\/code>) is computed.<\/li>\n\n\n\n<li>The magnitude of the gravitational force is determined using a formula: <code>ForceMagnitude = (GravitationalConstant * AttractorMass) \/ DistanceSquared<\/code>. The  particle mass is implicitly assumed to be 1.<\/li>\n\n\n\n<li>The force vector from the current attractor is <code>normalize(Direction) * ForceMagnitude<\/code>.<\/li>\n\n\n\n<li>This vector is added to the particle&#8217;s net force accumulator.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Velocity Update<\/strong>:\n<ul class=\"wp-block-list\">\n<li>The accumulated net force is treated as acceleration (assuming unit mass for particles: <code>Acceleration = NetForce<\/code>).<\/li>\n\n\n\n<li>The particle&#8217;s velocity is updated: <code>newVelocity = oldVelocity + Acceleration * deltaTime<\/code>.<\/li>\n\n\n\n<li>A damping factor (e.g., <code>newVelocity *= 0.995f<\/code>) can be applied to gradually reduce velocity, contributing to simulation stability and visual style. Commented out in the version I submitted.<\/li>\n\n\n\n<li>The velocity is capped at a <code>MAX_SPEED<\/code> to prevent particles from becoming uncontrollably fast.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Position Update<\/strong>:\n<ul class=\"wp-block-list\">\n<li>The particle&#8217;s position is updated based on its new velocity: <code>newPosition = oldPosition + newVelocity * deltaTime<\/code>.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Age Progression and Respawn Logic<\/strong>:\n<ul class=\"wp-block-list\">\n<li>The particle&#8217;s age is decremented: <code>currentAge -= deltaTime<\/code>.<\/li>\n\n\n\n<li>If <code>currentAge &lt;= 0.0f<\/code>:\n<ul class=\"wp-block-list\">\n<li>The particle&#8217;s position is reset to the current <code>particleOrigin<\/code> (a user-controllable point).<\/li>\n\n\n\n<li>Its <code>currentAge<\/code> is reset, often to its <code>initial_max_age<\/code> (which can be randomized slightly upon each respawn for variation).<\/li>\n\n\n\n<li>A new initial velocity is assigned. This is a key area for artistic control, allowing for various emission patterns.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>State Storage<\/strong>: The updated position, velocity, and age are written back to the particle&#8217;s entry in the SSBO.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>It was a very fun experience and a great opportunity to enhance my portfolio with such a nice project. Many thanks to O\u011fuz Hocam and Kadir Hocam. I&#8217;m thrilled to apply this knowledge to create various visual effects in my future projects. Thanks for reading!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hello again, dear graphics enjoyers! This post details my approach to the &#8220;Particle Magic&#8221; assignment for CENG469 Computer Graphics II. The objective was to develop an OpenGL application capable of simulating a large number of points\u2014up to one million\u2014whose movements are influenced by user-configurable gravitational attractors. A key requirement was the use of compute shaders [&hellip;]<\/p>\n","protected":false},"author":9370,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"[]","_links_to":"","_links_to_target":""},"categories":[1],"tags":[],"class_list":["post-35","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/blog.metu.edu.tr\/e264325\/wp-json\/wp\/v2\/posts\/35","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.metu.edu.tr\/e264325\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.metu.edu.tr\/e264325\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.metu.edu.tr\/e264325\/wp-json\/wp\/v2\/users\/9370"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.metu.edu.tr\/e264325\/wp-json\/wp\/v2\/comments?post=35"}],"version-history":[{"count":0,"href":"https:\/\/blog.metu.edu.tr\/e264325\/wp-json\/wp\/v2\/posts\/35\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.metu.edu.tr\/e264325\/wp-json\/wp\/v2\/media?parent=35"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.metu.edu.tr\/e264325\/wp-json\/wp\/v2\/categories?post=35"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.metu.edu.tr\/e264325\/wp-json\/wp\/v2\/tags?post=35"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}