{"id":49258653,"url":"https://github.com/debittccard/bezier-based-cpu-renderer","last_synced_at":"2026-04-25T06:04:21.194Z","repository":{"id":347361607,"uuid":"1193796623","full_name":"debittccard/Bezier-Based-CPU-Renderer","owner":"debittccard","description":"3D software rendering engine written in C. This project implements a full custom graphics pipeline from scratch, optimised for CPU‑bound environments. It features volumetric extrusion, multithreaded rasterisation, shadow mapping, distance‑based LOD, and a configurable projection system with supersampling anti‑aliasing.","archived":false,"fork":false,"pushed_at":"2026-04-24T17:51:30.000Z","size":12722,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-24T19:32:35.054Z","etag":null,"topics":["3d","aces-tonemapping","bezier-curves","c","command-line-tool","cpu-rendering","distance-lod","from-scratch","frustum-culling","gourand-shading","graphics-engine","high-performance","png-output","ppm","pthreads","rasterization","rendering","shadow-mapping","software-renderer","ssaa"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/debittccard.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-27T15:34:55.000Z","updated_at":"2026-04-24T17:51:35.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/debittccard/Bezier-Based-CPU-Renderer","commit_stats":null,"previous_names":["devmilanchakraborty/bezier-based-cpu-renderer","debittccard/bezier-based-cpu-renderer"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/debittccard/Bezier-Based-CPU-Renderer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/debittccard%2FBezier-Based-CPU-Renderer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/debittccard%2FBezier-Based-CPU-Renderer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/debittccard%2FBezier-Based-CPU-Renderer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/debittccard%2FBezier-Based-CPU-Renderer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/debittccard","download_url":"https://codeload.github.com/debittccard/Bezier-Based-CPU-Renderer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/debittccard%2FBezier-Based-CPU-Renderer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32251812,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-25T04:23:17.126Z","status":"ssl_error","status_checked_at":"2026-04-25T04:21:53.360Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["3d","aces-tonemapping","bezier-curves","c","command-line-tool","cpu-rendering","distance-lod","from-scratch","frustum-culling","gourand-shading","graphics-engine","high-performance","png-output","ppm","pthreads","rasterization","rendering","shadow-mapping","software-renderer","ssaa"],"created_at":"2026-04-25T06:04:19.041Z","updated_at":"2026-04-25T06:04:21.180Z","avatar_url":"https://github.com/debittccard.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Bezier-Based CPU Tube Renderer\n\nA professional-grade, high-performance 3D software rendering engine written in C. This project implements a full custom graphics pipeline from scratch, optimised for CPU-bound environments. It features volumetric extrusion, multithreaded rasterisation, shadow mapping, distance-based LOD, and a configurable projection system with supersampling anti-aliasing and a full cinematic post-processing stack.\n\n-----\n\n## Renders Produced\n\n\u003cimg src=\"./Images/art.png\" width=\"1000\"\u003e\n\u003cimg src=\"./Images/cinematic_final.png\" width=\"1000\"\u003e\n\n#### Command:\n\n```bash\n./renderer \\\n  -t 800000 \\\n  -seg 80 -sid 60 \\\n  -r 0.04 \\\n  -scale 1.8 \\\n  -ry 1.3 -rz 0.7 -rx 0.15 \\\n  -as 0.0025 \\\n  -cz -100 -cy 1.5 \\\n  -fov 95 \\\n  -focus 0,-2,-10 \\\n  -lx 12 -ly 8 -lz -22 \\\n  -bg 240,240,240 \\\n  -fog -fogcolor 60,80,140 -fogdensity 0.15 \\\n  -vignette 0.25 \\\n  -aces \\\n  -sw 1920 -sh 1080 \\\n  -iw 3840 -ih 2160 \\\n  -ow 1920 -oh 1080 \\\n  -png \\\n  -o cinematic_final.png\n```\n\u003cimg src=\"./Images/ultimate_test.png\" width=\"1000\"\u003e\n\n#### Command:\n\n```bash\n./renderer \\\n  -t 2000000 \\\n  -seg 150 -sid 250 \\\n  -r 0.04 \\\n  -scale 1.8 \\\n  -ry 1.3 -rz 0.7 -rx 0.15 \\\n  -as 0.0025 \\\n  -cz -75 -cy 1 \\\n  -fov 150.0f \\\n  -focus 0,-2,-10 \\\n  -lx 12 -ly 8 -lz -22 \\\n  -bg 250,250,180 -color 255,10,10 \\\n  -fog -fogcolor 70,90,150 -fogdensity 0.12 \\\n  -vignette 0.45 \\\n  -aces \\\n  -dof -focal 0.4 -aperture 3.5 \\\n  -bloom -bloomthreshold 0.65 -bloomintensity 0.35 \\\n  -sw 4096 -sh 4096 \\\n  -threads 8 \\\n  -iw 3840 -ih 2160 \\\n  -ow 1920 -oh 1080 \\\n  -png \\\n  -o ultimate_test.png\n```\n\u003cimg src=\"./Images/helix5mil.png\" width=\"1000\"\u003e\n\u003cimg src=\"./Images/blue.png\" width=\"1000\"\u003e\n\n-----\n\n## Architecture Overview\n\nThe system is designed as a modular pipeline where data flows from abstract mathematical definitions to a discrete pixel grid.\n\n  * **[`types.h`](./files/include/types.h)**: Defines the core data structures (`Vec3`, `Mat4`, `AABB`) and engine constants, with runtime-configurable resolutions and thread count.\n  * **[`main.c`](./files/src/main.c)**: Command-line interface, system initialisation, and the final output supersampling/downscaling plus all post-processing effects (background, fog, ACES, vignette, DOF, bloom).\n  * **[`geometry.c`](./files/src/geometry.c)**: Implementation of Cubic Bezier evaluation (de Casteljau) and stable reference frame generation.\n  * **[`scene.c`](./files/src/scene.c)**: Orchestrates thread management (`pthreads`) and high-level pass logic (Shadow Pass vs. Render Pass) with dynamic thread count.\n  * **[`renderer.c`](./files/src/renderer.c)**: The rasterisation engine, managing scan-line filling, Gouraud shading, shadow depth-testing, and LOD distance culling.\n  * **[`math.c`](./files/src/math.c)**: Linear algebra suite including matrix multiplication, vertex transformation, and frustum culling logic.\n\n-----\n\n## Technical Deep-Dive \u0026 Code Walkthrough\n\n### 1. Geometry Generation \u0026 The Bezier Logic (`geometry.c`)\n\nThe engine does not store static meshes. Instead, it extrudes geometry along a **Cubic Bezier Curve**.\n\nA Bezier curve is defined by four control points: $$( P_0, P_1, P_2, P_3 )$$. The function `bezier_eval` calculates the 3D position at any time $$t \\in [0,1]$$ using **de Casteljau's algorithm**: successive linear interpolations that are numerically stable and equivalent to evaluating the Bernstein polynomials.\n\n```c\nVec3 bezier_eval(BezierCubic b, float t){\n    float u = 1.0f - t;\n\n    // First level of interpolation\n    Vec3 a = { u*b.p0.x + t*b.p1.x, u*b.p0.y + t*b.p1.y, u*b.p0.z + t*b.p1.z };\n    Vec3 c = { u*b.p1.x + t*b.p2.x, u*b.p1.y + t*b.p2.y, u*b.p1.z + t*b.p2.z };\n    Vec3 d = { u*b.p2.x + t*b.p3.x, u*b.p2.y + t*b.p3.y, u*b.p2.z + t*b.p3.z };\n\n    // Second level\n    Vec3 e = { u*a.x + t*c.x, u*a.y + t*c.y, u*a.z + t*c.z };\n    Vec3 f = { u*c.x + t*d.x, u*c.y + t*d.y, u*c.z + t*d.z };\n\n    // Final point\n    return (Vec3){ u*e.x + t*f.x, u*e.y + t*f.y, u*e.z + t*f.z };\n}\n```\n\nTo create a tube we also need an orientation. The `bezier_tangent` function computes the first derivative:\n\n$$\nB'(t) = 3(1-t)^2 (P_1-P_0) + 6(1-t)t (P_2-P_1) + 3t^2 (P_3-P_2)\n$$\n\nThat tangent serves as the \"forward\" direction. By taking the cross product of the tangent and a chosen up-vector (with a fallback when they are parallel) we generate a **stable local coordinate frame** at every segment. A 2D circle of vertices is then rotated to be always perpendicular to the path, preventing the tube from becoming flat or twisted.\n\n### 2. Multi-Core Threading \u0026 Job Dispatch (`scene.c`)\n\nBecause software rasterisation is computationally heavy, `scene.c` employs a **data-parallel** architecture.\n\nThe engine divides the total `tube_count` into $$N$$ equal slices, where $$N$$ is the runtime-configurable thread count (`-threads`). Each thread processes its own chunk of tubes independently.\n\n```c\nint per = scene.tube_count / num_threads;\nfor(int t = 0; t \u003c num_threads; t++){\n    jobs[t].start = t * per;\n    jobs[t].end   = (t == num_threads-1) ? scene.tube_count : (t+1)*per;\n    pthread_create(\u0026threads[t], NULL, render_thread, \u0026jobs[t]);\n}\n```\n\n  * **Shadow Pass** - All threads render their tubes from the light's point of view into private depth buffers that are then merged into the global `shadow_map`.\n  * **Render Pass** - All threads render from the camera's perspective, performing depth-tests against the shared `zbuffer` and occlusion tests against the `shadow_map`.\n\n### 3. Frustum Culling (`math.c`)\n\nTo maintain interactive performance with millions of tubes, geometry outside the view frustum is discarded early.\n\nEvery tube is enclosed in an **Axis-Aligned Bounding Box (AABB)**. The function `aabb_in_frustum` transforms the eight corners of this box into **clip space** using the View-Projection matrix. If at least one corner falls inside the canonical clip volume $$[-1,1] \\times [-1,1] \\times [0,1]$$, the tube is considered potentially visible. Otherwise the thread skips it entirely, saving enormous amounts of vertex and pixel processing.\n\n```c\nint aabb_in_frustum(AABB box, Mat4 mvp){\n    Vec3 corners[8] = {\n        {box.min.x, box.min.y, box.min.z}, …\n    };\n    for(int i = 0; i \u003c 8; i++){\n        Vec4 c = mat4_mul_vec4(mvp, (Vec4){corners[i].x, corners[i].y, corners[i].z, 1.0f});\n        if(c.w \u003e 0){\n            float nx = c.x/c.w, ny = c.y/c.w, nz = c.z/c.w;\n            if(nx \u003e= -1 \u0026\u0026 nx \u003c= 1 \u0026\u0026 ny \u003e= -1 \u0026\u0026 ny \u003c= 1 \u0026\u0026 nz \u003e= 0 \u0026\u0026 nz \u003c= 1.1f)\n                return 1;  // visible\n        }\n    }\n    return 0;  // culled\n}\n```\n\n### 4. Rasterisation \u0026 Shading (`renderer.c`)\n\nOnce geometry is projected to screen space, `renderer.c` fills the resulting triangles with a **scanline rasteriser**.\n\nThe engine uses **Gouraud shading**: lighting intensity is computed per vertex and then linearly interpolated across the triangle. For every pixel the pipeline performs:\n\n1. **Z-Buffer Test** - Compare the pixel's depth against `zbuffer[y][x]`. If farther, skip.\n2. **Shadow Test** - The light-space coordinates of the pixel are likewise interpolated. The interpolated depth is compared to the value in the pre-computed `shadow_map`; if the pixel lies deeper than the stored depth (with a small bias), it is in shadow and its intensity is reduced.\n\n```c\n// Inside the scanline loop for each pixel\nif(depth \u003e= zrow[x]) continue;\n\nfloat intensity = …, shadow_factor = 1.0f;\nif(intensity \u003e 0.35f){\n    // Interpolate light-space coordinates\n    LightCoord lc = { la.x + t*(lb.x - la.x), … };\n    if(lc.x \u003e= 0.0f \u0026\u0026 lc.x \u003c= 1.0f \u0026\u0026 lc.y \u003e= 0.0f \u0026\u0026 lc.y \u003c= 1.0f){\n        int sxi = (int)(lc.x * (shadow_w - 1));\n        int syi = (int)(lc.y * (shadow_h - 1));\n        if(lc.z \u003e shadow_map[syi * shadow_w + sxi] + 0.001f)\n            intensity *= 0.25f;   // in shadow\n    }\n}\nzrow[x] = depth;\nrow[x].r = (unsigned char)(intensity * cr);\n// …\n```\n\nIn addition, a **distance-based LOD** system inside `render_tube` dynamically reduces the segment and side counts for tubes far from the camera, trading detail for speed while preserving visual quality.\n\n### 5. Supersampling Anti-Aliasing (SSAA) \u0026 Output (`main.c`)\n\nThe renderer draws to a high-resolution internal framebuffer (configurable via `-iw`/`-ih`). For each final output pixel, a small region of the high-res buffer is averaged using a box filter. This is equivalent to a 2x2 ordered-grid SSAA and removes jagged edges without blurring detail.\n\n```c\n// Downsampling loop\nfor(int y = 0; y \u003c out_h; y++){\n    for(int x = 0; x \u003c out_w; x++){\n        float u  = (x + 0.5f) / out_w, v  = (y + 0.5f) / out_h;\n        float du = 0.5f / out_w,      dv = 0.5f / out_h;\n\n        int sx0 = (int)((u-du)*render_width);  if(sx0 \u003c 0) sx0 = 0;\n        int sx1 = (int)((u+du)*render_width);  if(sx1 \u003e= render_width)  sx1 = render_width-1;\n        int sy0 = (int)((v-dv)*render_height); if(sy0 \u003c 0) sy0 = 0;\n        int sy1 = (int)((v+dv)*render_height); if(sy1 \u003e= render_height) sy1 = render_height-1;\n\n        int r=0, g=0, b=0, count=0;\n        for(int sy = sy0; sy \u003c= sy1; sy++)\n            for(int sx = sx0; sx \u003c= sx1; sx++){\n                Pixel *p = \u0026fb[sy * render_width + sx];\n                r += p-\u003er; g += p-\u003eg; b += p-\u003eb; count++;\n            }\n        unsigned char px[3] = { r/count, g/count, b/count };\n        fwrite(px, 1, 3, outfile);\n    }\n}\n```\n\nThe internal and output resolutions are independent, giving users full control over quality vs. performance. Output is supported in PPM (default) or PNG via a flag.\n\n### 6. ACES Filmic Tone Mapping (`main.c`)\n\nRaw linear RGB values can clip harshly in bright areas, losing detail in highlights and making the image look synthetic. The engine optionally applies an **ACES filmic tone map**, a curve that smoothly rolls off the highlights while preserving shadow detail, giving every render a cinematic, film-like quality.\n\nThe implementation uses the Narkowicz approximation to the ACES Reference Render Transform, a rational function of the form:\n\n$$\nf(x) = \\frac{x \\cdot (2.51\\,x + 0.03)}{x \\cdot (2.43\\,x + 0.59) + 0.14}\n$$\n\n```c\n// ACES applied per-channel as a fast post-process\nfloat r = pixel.r / 255.0f;\nr = (r * (2.51f * r + 0.03f)) / (r * (2.43f * r + 0.59f) + 0.14f);\n// clamp and convert back to 8-bit\npixel.r = (unsigned char)(fmaxf(0.0f, fminf(1.0f, r)) * 255.0f);\n```\n\n#### ACES enabled\n\u003cimg src=\"./Images/ACES.png\" width=\"1000\"\u003e\n\n#### ACES disabled\n\u003cimg src=\"./Images/noACES.png\" width=\"1000\"\u003e\n\n### 7. Atmospheric Post-Processing (`main.c`)\n\nThe engine offers optional post-processing effects that are applied as fast per-pixel passes over the finished framebuffer.\n\n* **Background Fill**: Replaces untouched pixels (sky) with a user-defined colour.\n* **Exponential Depth Fog**: Simulates atmospheric scattering by blending tube colours toward a fog colour as depth increases, using `fog_factor = 1 - e^(-depth * density)`. Fog colour and density are configurable.\n* **Vignette**: Darkens the image corners with a soft radial gradient, drawing the viewer's eye to the centre. Strength is adjustable.\n\nAll three are toggleable independently via the CLI.\n\n### 8. Depth of Field (`main.c`)\n\nA configurable depth-of-field effect blurs objects that are far from the focal plane, simulating camera lens focus. The implementation uses a pre-blurred image pyramid (full, half, quarter resolution) and bilinear sampling to produce a smooth, physically plausible blur with minimal performance impact.\n\n### 9. Bloom (`main.c`)\n\nBloom adds a soft glow around bright areas. The effect downsamples the framebuffer to half resolution, thresholds to isolate bright pixels, applies a separable Gaussian blur, and then up-samples and adds the result back to the original image. Threshold, intensity, and blur width are fully controllable.\n\n-----\n\n## CLI Reference\n\n### Geometry Parameters\n\n  * `-t \u003cint\u003e`: Total number of tubes (default 100000).\n  * `-seg \u003cint\u003e`: Longitudinal segments per tube. Higher -\u003e smoother curves.\n  * `-sid \u003cint\u003e`: Radial sides per tube. `3` = triangular, `12+` = smooth cylinder.\n  * `-r \u003cfloat\u003e`: Tube radius.\n  * `-scale \u003cfloat\u003e`: Global scale of the scene.\n\n### Bezier Control Points\n\n  * `-p0 \u003cx,y,z\u003e`: Origin point of the spline.\n  * `-p1 \u003cx,y,z\u003e`: First control point (influences curve exit).\n  * `-p2 \u003cx,y,z\u003e`: Second control point (influences curve entry).\n  * `-p3 \u003cx,y,z\u003e`: Destination point of the spline.\n\n### Transformations \u0026 Animation\n\n  * `-rx, -ry, -rz \u003cfloat\u003e`: Rotation multipliers (per-tube angle step x index).\n  * `-rcx, -rcy, -rcz \u003cfloat\u003e`: Constant rotation offsets.\n  * `-as \u003cfloat\u003e`: Angle step - increment applied to rotation per successive tube.\n  * `-tx, -ty, -tz \u003cfloat\u003e`: Global translation (world position offset).\n  * `-mtx, -mty, -mtz \u003cfloat\u003e`: Translation multipliers that scale the per-index step.\n  * `-ts \u003cfloat\u003e`: Translate step - linear offset per tube index, used with the multipliers.\n\n### Camera \u0026 Lighting\n\n  * `-cx, -cy, -cz \u003cfloat\u003e`: Camera position.\n  * `-fov \u003cfloat\u003e`: Field of View in degrees.\n  * `-focus \u003cx,y,z\u003e`: LookAt target (default 0,0,0).\n  * `-lx, -ly, -lz \u003cfloat\u003e`: Point light position (affects shading and shadows).\n\n### Shading \u0026 Output\n\n  * `-rgb`: Enable rainbow-cycling colours based on tube index.\n  * `-color \u003cr,g,b\u003e`: Set a static colour (e.g. `255,128,0`).\n  * `-cycles \u003cfloat\u003e`: Number of full hue cycles when using `-rgb` (default 1.0).\n  * `-aces`: Applies ACES filmic tone mapping.\n  * `-bg \u003cr,g,b\u003e`: Background colour for empty pixels.\n  * `-fog`: Enables exponential depth fog.\n  * `-fogcolor \u003cr,g,b\u003e`: Fog colour (default 180,200,255).\n  * `-fogdensity \u003cfloat\u003e`: Fog density (default 0.15, higher = thicker).\n  * `-vignette [strength]`: Enables vignette darkening with optional strength (default 0.4).\n  * `-dof`: Enables depth of field.\n  * `-focal \u003cfloat\u003e`: Focal depth in NDC (0..1, default 0.5).\n  * `-aperture \u003cfloat\u003e`: Blur amount for DOF (default 8.0).\n  * `-bloom`: Enables bloom (glow).\n  * `-bloomthreshold \u003cfloat\u003e`: Brightness threshold for bloom (0..1, default 0.7).\n  * `-bloomintensity \u003cfloat\u003e`: Bloom strength (default 0.4).\n  * `-threads \u003cint\u003e`: Number of rendering threads (default 8).\n  * `-iw \u003cint\u003e`: Internal render width (default 3840).\n  * `-ih \u003cint\u003e`: Internal render height (default 2160).\n  * `-ow \u003cint\u003e`: Output image width (default 1920).\n  * `-oh \u003cint\u003e`: Output image height (default 1080).\n  * `-sw \u003cint\u003e`: Shadow map width (default 1024).\n  * `-sh \u003cint\u003e`: Shadow map height (default 1024).\n  * `-png`: Output as PNG instead of PPM.\n  * `-o \u003cstring\u003e`: Output filename (default `output.ppm`, or `output.png` if `-png` is used).\n\n-----\n\n## Build Instructions\n\nThe engine is written in standard C99 and requires a compiler with `pthread` support (POSIX threads). A Makefile is provided for convenience.\n\n```bash\n# Build the project\nmake\n\n# Clean build artifacts\nmake clean\n\n# Optional: Generate LSP database for IDE support (requires 'bear')\nbear -- make\n```\n\n**Compile Flags Explained:**\n\n  * `-O3`: Maximum optimisation.\n  * `-march=native`: Utilises all instruction-set extensions of your CPU (AVX, SSE, etc.).\n  * `-ffast-math`: Relaxes IEEE floating-point compliance for extra speed.\n  * `-flto`: Link-time optimisation for cross-file inlining.\n  * `-pthread`: Links the POSIX threads library.\n\n-----\n\n## License\n\nThis source code is provided as an open-source reference for high-performance software rendering techniques.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdebittccard%2Fbezier-based-cpu-renderer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdebittccard%2Fbezier-based-cpu-renderer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdebittccard%2Fbezier-based-cpu-renderer/lists"}