<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Game Development on Tiberiu Petre - Software Engineer</title><link>https://petretiberiu.dev/categories/game-development/</link><description>Recent content in Game Development on Tiberiu Petre - Software Engineer. Currently exploring backend/systems/infrastructure roles — get in touch: https://petretiberiu.dev/contact/</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Mon, 24 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://petretiberiu.dev/categories/game-development/index.xml" rel="self" type="application/rss+xml"/><item><title>Four Years at Ubisoft: Anvil Pipeline, Pressure Plates, and What Came After</title><link>https://petretiberiu.dev/posts/four-years-at-ubisoft/</link><pubDate>Mon, 24 Aug 2026 00:00:00 +0000</pubDate><guid>https://petretiberiu.dev/posts/four-years-at-ubisoft/</guid><description>&lt;p&gt;Four years of my career were at Ubisoft, on Assassin&amp;rsquo;s Creed, on the Anvil Pipeline team — the part of the studio that builds the tools and infrastructure the rest of the development teams use, not the gameplay players see directly.&lt;/p&gt;
&lt;p&gt;My biggest project there was the RankInfo migration: a legacy system rewritten into a modular, data-driven architecture, with its own descriptor tags and an adapter pattern for backward compatibility. I also built a migration driver that mapped the old code to the new one, plus internal ImGui tooling for the teams using the system daily. The kind of work that never shows up in a trailer, but without which the rest of the game doesn&amp;rsquo;t get built.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Four years of my career were at Ubisoft, on Assassin&rsquo;s Creed, on the Anvil Pipeline team — the part of the studio that builds the tools and infrastructure the rest of the development teams use, not the gameplay players see directly.</p>
<p>My biggest project there was the RankInfo migration: a legacy system rewritten into a modular, data-driven architecture, with its own descriptor tags and an adapter pattern for backward compatibility. I also built a migration driver that mapped the old code to the new one, plus internal ImGui tooling for the teams using the system daily. The kind of work that never shows up in a trailer, but without which the rest of the game doesn&rsquo;t get built.</p>
<p>At some point the team reorganized — Anvil Pipeline split, with part of the work moving to Vantage Studios. The kind of restructuring the games industry keeps going through, not something specific to me. In the middle of that transition I worked briefly on Assassin&rsquo;s Creed Hexe, where I shipped Pressure Plates — a complete mechanic, from zero to working, in about a week.</p>
<p>That week stuck with me more than I expected. Not because it was easy, but because I noticed something about how I work best: under a clear external deadline, with a concrete result at the end, I produce. It&rsquo;s not the first time I&rsquo;ve noticed this pattern. Cindy&rsquo;s Adventures — my solo entry for the Game Off 2021 jam — was built entirely in a month, with the jam&rsquo;s rules as the only deadline, and it shipped finished. Same with EvoAgents, my bachelor&rsquo;s thesis on behavior trees for NPC AI: a fixed academic deadline, a clear deliverable, seen through to the end.</p>
<p>My projects without an external deadline? Most of them stayed good ideas on paper. That&rsquo;s not a coincidence, it&rsquo;s a pattern.</p>
<p>I can&rsquo;t say the pivot to backend and infrastructure was a clean, planned choice from the start — part of that path was decided by context, not by me. But the reasons infrastructure fits me stay real regardless of how I got here: clear ownership, a system that either works or doesn&rsquo;t, a feedback loop I control, not a creative committee.</p>
<p>That doesn&rsquo;t mean gaming stopped mattering to me. Cindy&rsquo;s Adventures, EvoAgents, four years of AAA production on Assassin&rsquo;s Creed — those aren&rsquo;t just old lines on a résumé, they&rsquo;re proof that I&rsquo;m already a game developer, not just someone curious about gaming. I don&rsquo;t have a clean conclusion about where that goes, and I don&rsquo;t think I need one right now. What I do have is a clear pattern for how I work best — under an external deadline, with a concrete deliverable — and things that genuinely interest me, regardless of which professional label sits on top of them.</p>
<p>Sovereign AI Nexus is the next portfolio project built on that logic. It&rsquo;s probably not the last, and it&rsquo;s probably not the only kind of project you&rsquo;ll see here.</p>
<p>I&rsquo;m not ruling out, at some point, a separate series about game development directly — an engine built from scratch, an Assassin&rsquo;s Creed feature rebuilt in an evening, whatever else keeps me in that space. It doesn&rsquo;t have to be either/or. There&rsquo;s room here for both.</p>
]]></content:encoded></item><item><title>Developing a Ray Tracer Using Compute Shaders</title><link>https://petretiberiu.dev/posts/ray-tracer-compute-shaders/</link><pubDate>Fri, 21 Jan 2022 00:00:00 +0000</pubDate><guid>https://petretiberiu.dev/posts/ray-tracer-compute-shaders/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Originally published on &lt;a href="https://medium.com/@tiberiu.petre/developing-a-ray-tracer-using-compute-shaders-4b1db8fdb402"&gt;Medium&lt;/a&gt;, January 2022. Republished here with light edits — the original project&amp;rsquo;s source code no longer exists, only this write-up survived, so the code snippets below are described rather than shown.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A ray-tracer is a rendering method that simulates light reflections, refractions and shadows. It follows a light path from a specific source and computes each pixel in the image to simulate the effect of the light.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Unity ray tracing image rendered with compute shaders." loading="lazy" src="https://cdn-images-1.medium.com/max/1024/1*kQl_ZtKoJaHyKCknYUidwA.png"&gt;&lt;/p&gt;</description><content:encoded><![CDATA[<blockquote>
<p>Originally published on <a href="https://medium.com/@tiberiu.petre/developing-a-ray-tracer-using-compute-shaders-4b1db8fdb402">Medium</a>, January 2022. Republished here with light edits — the original project&rsquo;s source code no longer exists, only this write-up survived, so the code snippets below are described rather than shown.</p>
</blockquote>
<p>A ray-tracer is a rendering method that simulates light reflections, refractions and shadows. It follows a light path from a specific source and computes each pixel in the image to simulate the effect of the light.</p>
<p><img alt="Unity ray tracing image rendered with compute shaders." loading="lazy" src="https://cdn-images-1.medium.com/max/1024/1*kQl_ZtKoJaHyKCknYUidwA.png"></p>
<p>Following the <a href="https://raytracing.github.io/books/RayTracingInOneWeekend.html">Ray Tracing in One Weekend</a> book by Peter Shirley, I wrote an implementation of a ray-tracing algorithm using Unity compute shaders and the Universal Render Pipeline to render spheres in real time.</p>
<p>The ray-tracing algorithm works by casting rays from a camera to each pixel on the screen using UV space, where each pixel has coordinates between zero and one. A ray represents a light path between a source and an observer camera.</p>
<p>When a ray hits an object, it gets reflected in the world by using the laws of optics. Each object surface has an orientation determined by its normal vector.</p>
<p>Unity already has an implementation of a camera that uses linear algebra and matrix multiplication to convert a vector between world space and screen space. To find the ray origin in world space, you multiply this matrix with the camera position. To cast a ray toward a specific pixel, a series of matrix multiplications maps the pixel&rsquo;s UV coordinates to a normalized direction — and finally, a ray carries a specific amount of energy that &ldquo;illuminates&rdquo; the world.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-hlsl" data-lang="hlsl"><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> Ray {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">float3</span> origin;
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">float3</span> direction;
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">float3</span> energy;
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Ray CastRay(<span style="color:#66d9ef">float2</span> uv) {
</span></span><span style="display:flex;"><span>	Ray o;
</span></span><span style="display:flex;"><span>	o.origin    <span style="color:#f92672">=</span> mul(_CameraToWorld, <span style="color:#66d9ef">float4</span>(_CameraPosition, <span style="color:#ae81ff">1.0f</span>)).xyz;
</span></span><span style="display:flex;"><span>	o.direction <span style="color:#f92672">=</span> mul(_CameraInverseProjection, <span style="color:#66d9ef">float4</span>(uv, <span style="color:#ae81ff">0.0f</span>, <span style="color:#ae81ff">1.0f</span>)).xyz;
</span></span><span style="display:flex;"><span>	o.direction <span style="color:#f92672">=</span> mul(_CameraToWorld, <span style="color:#66d9ef">float4</span>(o.direction, <span style="color:#ae81ff">0.0f</span>)).xyz;
</span></span><span style="display:flex;"><span>	o.direction <span style="color:#f92672">=</span> normalize(o.direction);
</span></span><span style="display:flex;"><span>	o.energy    <span style="color:#f92672">=</span> <span style="color:#66d9ef">float3</span>(<span style="color:#ae81ff">1.0f</span>, <span style="color:#ae81ff">1.0f</span>, <span style="color:#ae81ff">1.0f</span>);
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> o;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>A sphere has a central location in 3D space and a radius. Its surface, as seen by the ray-tracer, is found using an approach related to ray-marching: by taking smaller steps you can approximate the exact position where a ray meets the sphere&rsquo;s surface, which reduces to finding the coefficients of a quadratic equation.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-hlsl" data-lang="hlsl"><span style="display:flex;"><span><span style="color:#66d9ef">bool</span> Hit(<span style="color:#66d9ef">in</span> Ray ray, <span style="color:#66d9ef">inout</span> HitInfo hit) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">float3</span> location <span style="color:#f92672">=</span> ray.origin <span style="color:#f92672">-</span> center;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">float</span> a <span style="color:#f92672">=</span> dot(ray.direction, ray.direction);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">float</span> half_b <span style="color:#f92672">=</span> dot(location, ray.direction);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">float</span> c <span style="color:#f92672">=</span> dot(location, location) <span style="color:#f92672">-</span> radius <span style="color:#f92672">*</span> radius;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">float</span> delta <span style="color:#f92672">=</span> half_b <span style="color:#f92672">*</span> half_b <span style="color:#f92672">-</span> a <span style="color:#f92672">*</span> c;
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (delta <span style="color:#f92672">&gt;=</span> <span style="color:#ae81ff">0</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">float</span> root <span style="color:#f92672">=</span> <span style="color:#f92672">-</span><span style="color:#ae81ff">1.0f</span> <span style="color:#f92672">*</span> (half_b <span style="color:#f92672">+</span> sqrt(delta)) <span style="color:#f92672">/</span> a;
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> (root <span style="color:#f92672">&lt;</span> <span style="color:#ae81ff">0</span> <span style="color:#f92672">||</span> hit.distance <span style="color:#f92672">&lt;</span> root) {
</span></span><span style="display:flex;"><span>            root <span style="color:#f92672">=</span> <span style="color:#f92672">-</span><span style="color:#ae81ff">1.0f</span> <span style="color:#f92672">*</span> (half_b <span style="color:#f92672">-</span> sqrt(delta)) <span style="color:#f92672">/</span> a;
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> (root <span style="color:#f92672">&lt;</span> <span style="color:#ae81ff">0</span> <span style="color:#f92672">||</span> hit.distance <span style="color:#f92672">&lt;</span> root)
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        hit.distance <span style="color:#f92672">=</span> root;
</span></span><span style="display:flex;"><span>        hit.position <span style="color:#f92672">=</span> ray.Evaluate(hit.distance);
</span></span><span style="display:flex;"><span>        hit.normal   <span style="color:#f92672">=</span> (hit.position <span style="color:#f92672">-</span> center) <span style="color:#f92672">/</span> radius;
</span></span><span style="display:flex;"><span>        hit.material <span style="color:#f92672">=</span> material;
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">false</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Changing a ray&rsquo;s origin, direction and energy when it hits a surface is called ray scattering. A material&rsquo;s attributes determine how an object looks by changing the way light scatters off it. The new ray direction is calculated by reflecting the incoming ray against the surface normal. A rough surface deflects light more randomly around that normal, an emissive object behaves like an &ldquo;artificial light&rdquo; source, and a metallic object scatters much more light than a diffuse one.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-hlsl" data-lang="hlsl"><span style="display:flex;"><span><span style="color:#66d9ef">float3</span> Scatter(<span style="color:#66d9ef">inout</span> Ray o, <span style="color:#66d9ef">float3</span> p, Material mat) {
</span></span><span style="display:flex;"><span>    o.origin    <span style="color:#f92672">=</span> p <span style="color:#f92672">+</span> mat.normal <span style="color:#f92672">*</span> <span style="color:#ae81ff">0.001f</span>;
</span></span><span style="display:flex;"><span>    o.direction <span style="color:#f92672">=</span> reflect(o.direction, mat.normal) <span style="color:#f92672">+</span> mat.roughness <span style="color:#f92672">*</span> SampleHemisphere(mat.normal);
</span></span><span style="display:flex;"><span>    o.energy   <span style="color:#f92672">*=</span> mat.specular <span style="color:#f92672">*</span> sdot(o.direction, mat.normal, mat.metallic);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    Ray sRay <span style="color:#f92672">=</span> CreateRay(o.origin <span style="color:#f92672">+</span> mat.normal <span style="color:#f92672">*</span> <span style="color:#ae81ff">0.001f</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">1.0f</span> <span style="color:#f92672">*</span> _DirectionalLight.xyz);
</span></span><span style="display:flex;"><span>    HitInfo sHit <span style="color:#f92672">=</span> Trace(sRay);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (sHit.distance <span style="color:#f92672">!=</span> <span style="color:#ae81ff">1.</span><span style="color:#960050;background-color:#1e0010">#</span>INF) <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">float3</span>(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">else</span> <span style="color:#66d9ef">return</span> mat.albedo;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>A fragment shader computes the material attributes that determine the final pixel color during the scattering stage — the emissive color and intensity from albedo values, lighting and shadows on the object, and the reflection direction from normals and roughness.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-hlsl" data-lang="hlsl"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> Frag(<span style="color:#66d9ef">inout</span> Material o, <span style="color:#66d9ef">float3</span> normal) {
</span></span><span style="display:flex;"><span>    o.roughness <span style="color:#f92672">=</span> o.roughness <span style="color:#f92672">/</span> <span style="color:#ae81ff">10.0f</span>;
</span></span><span style="display:flex;"><span>    o.normal    <span style="color:#f92672">=</span> normal <span style="color:#f92672">+</span> o.roughness <span style="color:#f92672">*</span> SampleHemisphere(o.normal);
</span></span><span style="display:flex;"><span>    o.lighting  <span style="color:#f92672">=</span> sdot(o.normal, _DirectionalLight.xyz) <span style="color:#f92672">*</span> _DirectionalLight.w;
</span></span><span style="display:flex;"><span>    o.albedo    <span style="color:#f92672">=</span> o.lighting <span style="color:#f92672">*</span> (<span style="color:#ae81ff">10.0f</span> <span style="color:#f92672">*</span> o.emission <span style="color:#f92672">*</span> o.intensity <span style="color:#f92672">+</span> o.albedo);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">float4</span> Shade(<span style="color:#66d9ef">inout</span> Ray ray, <span style="color:#66d9ef">in</span> HitInfo hit) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (hit.distance <span style="color:#f92672">&lt;</span> <span style="color:#ae81ff">1.</span><span style="color:#960050;background-color:#1e0010">#</span>INF) {
</span></span><span style="display:flex;"><span>        Frag(hit.material, hit.normal);
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">float4</span>(Scatter(ray, hit.position, hit.material), <span style="color:#ae81ff">0.0f</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">else</span> <span style="color:#66d9ef">return</span> ray.CalculateSkybox();
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="conclusion">Conclusion</h2>
<p>Ray tracing is a technology that can produce beautiful graphics and realistic lighting by casting rays from the camera and computing reflections, refractions and shadows to approximate real-world light behavior. It&rsquo;s a technology that keeps evolving, and keeps promising more convincing worlds and light.</p>
<p>To learn more about compute shaders and ray tracing, I&rsquo;d recommend <a href="https://raytracing.github.io/books/RayTracingInOneWeekend.html">Ray Tracing in One Weekend</a> by Peter Shirley and <a href="http://three-eyed-games.com/2018/05/03/gpu-ray-tracing-in-unity-part-1/">GPU Ray Tracing in Unity</a> by David Kuri — the two sources I originally learned from.</p>
]]></content:encoded></item></channel></rss>