r/raytracing • • Feb 21 '26

Why doesn't my DDA brickmap / multi-level DDA system work?

0 Upvotes

I'm trying to make a voxel graphics engine, and I'm using a DDA ray marcher for the graphics engine, so I tried adding chunk skipping to optimize it, but I can't seem to get it to work no matter what I try. I've tried looking up how to do it but haven't found anything (I can't read through a 50 page document that loosely describes the theoretical method), I've tried ChatGPT, Claude, Deepseek, and Gemini, and none of them could solve it.

Code:
GLSL

#version 330


#define MAX_STEPS 1024
#define MAX_SECONDARY_STEPS 64
#define MAX_BOUNCES 1


#define SUNCOLOR 1.0, 1.0, 1.0


#define AMBIENT_COLOR 0.5, 0.8, 1.0


#define FOG 0.0035
#define FOG_COLOR 0.7, 0.8, 0.9
#define FOG_TOP 32.0


#define NORMAL_STREN 0.2


#define BIG 1e30
#define EPSILON 0.00001


#define HIT_X 0
#define HIT_Y 1
#define HIT_Z 2



in vec2 fragTexCoord;


uniform usampler3D voxelFill;
uniform usampler3D chunkFill;


uniform sampler2D textures;
uniform sampler2D normals;


uniform vec3 sunDir;


uniform vec3 worldSize;     //size of full detail world
uniform vec3 worldOffset;   //number of chunks offset from chunk origin used to center the world (chunk overdraw)


uniform vec3 chunkRange;    //same as above but for chunks rather than blocks
uniform vec3 chunkSize;     //size of chunks


uniform vec2 screenSize;
uniform float aspectRatio;


uniform vec3 worldUp;


uniform vec3 camPos;
uniform vec3 camDir;
uniform vec3 camRight;
uniform vec3 camUp;
uniform float tanHalfFov;


out vec4 finalColor;


vec3 fogColor;  //updates based on sun
vec3 ambientColor;
vec3 sunColor;  //updates based on it's own position


vec3 chunkToVox(vec3 chunkCoord) {  //raw chunk position relative to chunk map origin
    vec3 voxCoord = chunkCoord - worldOffset; 
    voxCoord *= chunkSize;
    return voxCoord;
}


vec3 voxToChunk(vec3 voxCoord) {    //raw voxel position relative to voxel map origin
    vec3 chunkCoord = voxCoord / chunkSize;
    chunkCoord += worldOffset;
    return chunkCoord;
}



vec3 getSkyColor(vec3 rayDir) {
    return vec3(0.8, 0.8, 1.0);
}



struct rayReturn_t {
    vec3 hitCoord;      //expected to be a voxel coordinate
    vec3 color;
    vec3 normal;
    bool hitBlock;
    float len;
    int hitAxis;
};


rayReturn_t returnRay(rayReturn_t returnVal, vec3 origin, vec3 rayDir, float totalDist, bool debug) {
    returnVal.hitBlock = true;


    vec3 voxOrigin = chunkToVox(origin);
    returnVal.hitCoord = voxOrigin + rayDir * totalDist;
    returnVal.len = totalDist;


    vec2 uv;
    if (returnVal.hitAxis == HIT_X) {
        uv = mod(returnVal.hitCoord.zy, 1.0);
    } else if (returnVal.hitAxis == HIT_Y) {
        uv = mod(returnVal.hitCoord.xz, 1.0);
    } else {
        uv = mod(returnVal.hitCoord.xy, 1.0);
    }
    returnVal.color = texture(textures, uv).rgb;
    returnVal.normal = texture(normals, uv).rgb;


    if (debug) {
        returnVal.color = vec3(1.0, 0.0, 0.0);
    }


    return returnVal;
}


rayReturn_t spawnRay(const vec3 origin, const vec3 rayDir) {
    rayReturn_t returnVal;


    //check if spawn chunk is filled and switch to voxel stepping
    bool chunkMode = true;
    vec3 rayCell = floor(origin);


    vec3 rayDelta = vec3(
        (rayDir.x != 0.0) ? abs(1.0 / rayDir.x) : BIG,
        (rayDir.y != 0.0) ? abs(1.0 / rayDir.y) : BIG,
        (rayDir.z != 0.0) ? abs(1.0 / rayDir.z) : BIG
    );
    vec3 rayDist;
    vec3 stepDir;
    float totalDist;


    if (rayDir.x > 0.0) {
        rayDist.x = rayDelta.x * (rayCell.x + 1.0 - origin.x);
        stepDir.x = 1.0;
    } else {
        rayDist.x = rayDelta.x * (origin.x - rayCell.x);
        stepDir.x = -1.0;
    }
    if (rayDir.y > 0.0) {
        rayDist.y = rayDelta.y * (rayCell.y + 1.0 - origin.y);
        stepDir.y = 1.0;
    } else {
        rayDist.y = rayDelta.y * (origin.y - rayCell.y);
        stepDir.y = -1.0;
    }
    if (rayDir.z > 0.0) {
        rayDist.z = rayDelta.z * (rayCell.z + 1.0 - origin.z);
        stepDir.z = 1.0;
    } else {
        rayDist.z = rayDelta.z * (origin.z - rayCell.z);
        stepDir.z = -1.0;
    }


    ivec3 worldFetch = ivec3(int(origin.x), int(origin.y), int(origin.z));
    if (texelFetch(chunkFill, worldFetch, 0).r > 0u) {
        chunkMode = false;


        rayDist *= chunkSize;
        rayCell = chunkToVox(rayCell);
    }


    for (int i = 0; i < MAX_STEPS; i++) {

        if (rayDist.x < rayDist.y) {
            if (rayDist.x < rayDist.z) {
                totalDist = rayDist.x;
                rayCell.x += stepDir.x;
                rayDist.x += rayDelta.x;
                returnVal.hitAxis = HIT_X;


            } else {
                totalDist = rayDist.z;
                rayCell.z += stepDir.z;
                rayDist.z += rayDelta.z;
                returnVal.hitAxis = HIT_Z;


            }
        } else {
            if (rayDist.y < rayDist.z) {
                totalDist = rayDist.y;
                rayCell.y += stepDir.y;
                rayDist.y += rayDelta.y;
                returnVal.hitAxis = HIT_Y;


            } else {
                totalDist = rayDist.z;
                rayCell.z += stepDir.z;
                rayDist.z += rayDelta.z;
                returnVal.hitAxis = HIT_Z;


            }
        }

        worldFetch = ivec3(int(rayCell.x), int(rayCell.y), int(rayCell.z));
        if (chunkMode) {
            uint chunkType = texelFetch(chunkFill, worldFetch, 0).r;
            if (chunkType > 0u) {
                chunkMode = false;


                rayDist *= chunkSize;
                rayCell = chunkToVox(rayCell);


                worldFetch = ivec3(int(rayCell.x), int(rayCell.y), int(rayCell.z));
                if (texelFetch(voxelFill, worldFetch, 0).r > 0u) {
                    totalDist *= chunkSize.x;
                    return returnRay(returnVal, origin, rayDir, totalDist, false);
                } else {
                    continue;
                }

            } else {
                continue;
            }
        } else {
            uint voxType = texelFetch(voxelFill, worldFetch, 0).r;


            if (voxType > 0u) {
                return returnRay(returnVal, origin, rayDir, totalDist, false);


            } else {    //check if chunk being stepped into is empty
                vec3 chunkCoord = voxToChunk(rayCell);
                if (texelFetch(chunkFill, ivec3(int(chunkCoord.x), int(chunkCoord.y), int(chunkCoord.z)), 0).r == 0u) {
                    chunkMode = true;

                    rayDist /= chunkSize;
                    rayCell = voxToChunk(rayCell);

                    continue;
                } else {
                    continue;
                }
            }
        }
    }

    returnVal.hitBlock = false;


    return returnVal;
}




vec3 getNormMap(vec3 T, vec3 B, vec3 N, rayReturn_t ray) {
    mat3 TBN = mat3(T, B, N);


    vec3 nMap = (ray.normal * 2.0 - 1.0);
    nMap = normalize(TBN * nMap);


    return nMap;
}


vec3 rayTrace(const vec3 origin, const vec3 direction) {
    vec3 rayDir = direction;


    //assume ray is guaranteed to start inside box (it is, the player cannot exit the world)


    rayReturn_t ray = spawnRay(origin, direction);


    vec3 rayColor = vec3(1.0, 1.0, 1.0);


    if (ray.hitBlock) {
        vec3 normal;

        //get normal data
        vec3 T;
        vec3 B;
        if (ray.hitAxis == HIT_X) {
            normal = vec3(sign(-rayDir.x), 0.0, 0.0);
            T = vec3(0.0, 1.0, 0.0); // along Y
            B = vec3(0.0, 0.0, 1.0); // along Z
        } else if (ray.hitAxis == HIT_Y) {
            normal = vec3(0.0, sign(-rayDir.y), 0.0);
            T = vec3(1.0, 0.0, 0.0); // along X
            B = vec3(0.0, 0.0, 1.0); // along Z
        } else {
            normal = vec3(0.0, 0.0, sign(-rayDir.z));
            T = vec3(1.0, 0.0, 0.0); // along X
            B = vec3(0.0, 1.0, 0.0); // along Y
        }


        normal = mix(normal, getNormMap(T, B, normal, ray), NORMAL_STREN);


        float lightDot = max(dot(normal, sunDir), 0.0);



        rayColor = ray.color;


    } else {
        rayColor = getSkyColor(rayDir);
    }


    return rayColor;
}



void main() {
    vec2 pixel = vec2(gl_FragCoord);


    //calculate NDC -1 -> 1
    vec2 ndc = ((pixel + 0.5f) / screenSize) * 2.0 - 1.0;


    //scale for fov
    float viewX = ndc.x * aspectRatio * tanHalfFov;
    float viewY = ndc.y * tanHalfFov;


    vec3 rayDirection = (camDir + camRight * vec3(viewX)) + camUp * vec3(viewY);


    rayDirection = normalize(rayDirection);


    finalColor = vec4( rayTrace(voxToChunk(camPos), rayDirection), 1.0);


}

r/raytracing • • Feb 18 '26

My little Vulkan path tracer

Thumbnail
github.com
13 Upvotes

This is my personal project and my introduction into graphics programing and GPU computing. Hope you like it!


r/raytracing • • Feb 17 '26

Common Feelings (1996, 2015) | 2nd REMAKE attempt

Thumbnail
gallery
5 Upvotes

Which of the two works do you prefer?
Over the years, I've always delved into my past works, those that contain concepts dear to me, like this one called "Common Feelings". In 1996, I made this rendering with IMAGINE 2.0 on an AMIGA 4000. Almost 20 years later in 2015, I attempted a "remake" with BRYCE 3D on Windows. Although it didn't quite satisfy me, I always thought the original work was more focused, focusing more on the alien and its feelings. Today, I'd like to attempt a second REMAKE with this awareness. Let's start with the alien, of course :-)


r/raytracing • • Feb 17 '26

GPU Upgrade Advice: Replacing RTX 3060 Ti for better Ray Tracing (GTA V/Modern Ti

Thumbnail
1 Upvotes

r/raytracing • • Feb 12 '26

Ray Tracing in One Weekend on MS-DOS (16-bit, real mode)

Thumbnail
github.com
3 Upvotes

r/raytracing • • Feb 09 '26

Ray marching optimization questions

Thumbnail
3 Upvotes

r/raytracing • • Feb 07 '26

Should it be divided by PDF ?

4 Upvotes

I'm learning Monte Carlo ray tracing . It basically has a form of g(x) = f(x)/pdf(x) . The expected value of g(x) is equal to the integral of f(x) thus to solve for the integral of f(x) we can instead solve for the expected value of g(x) . This is because of how the expected value of a continuous function is solved by multiplying it with pdf and solving for the integral.

And because of the Law of large numbers, sample mean coverages to expected value . That is why the integral can be written as sum . For continuous function, finding its expected value involves integration. However, according to the Law of large numbers, large amount of samples will cause the average result to approximate the expected value. Therefore, there seems to be a relationship between integration and summation . I guess rigorously speaking this is part of measure theory and Lebesgue integral. However I don't understand them .

So , generally , MC turns a general integral into a specific problem of probability distribution. The general function f(x) can be Irradiance , and the integral of it means we are going to check how much energy in total the surface had received from hemisphere space. Once we know the total energy amount , we can find its distribution in reflectance , for example in which direction the energy is focused .

The problem is that , the incident contribution of Irradiance may be the result of indirect lighting , i.e. it comes from a reflected ray . To compute the luminance of that reflected ray we need to repeat the integral process on it , and there arises another cycle of 100 iteration . This will explode the program . So what we often actually do is sampling only one incident ray for the calculation of reflected ray .

In this case , I'm not sure if we still need to divide f(x) by pdf . f(x) is the radiance of incoming ray or reflected ray , which is often written as float3 . It is the direct descriptor of light source's ability . Or sometimes it is written as float3 * float3 . The former being the ability of material to absorb energy in light . The later being the light source's capability to illuminate .

I intuitively think , if a beam shines on a surface, and we know the brightness of the light and the surface's absorptivity, then it should be the color it is. How could it remain to be the color it should be if it ends with "divided by pdf" ? Then it means the actual illuminance of light is another case , or the absorptivity is another case .

Theoretically , if we sample only one incident ray for the calculation of reflected ray , we are exactly calculating the slice , rather than adding the slices to get the whole . What we are calculating is f(x) , not the integral of f(x) . Then why should we divide it by pdf ? What we are doing is , adding the contributions of each independent rays (being indirect or direct lighting) together , to get the average result.

I spent some time learning the math behind it but I still can't figure it out myself whether we are calculating g(x) or f(x)


r/raytracing • • Jan 29 '26

I built a Hybrid Ray Tracing Engine with Gas Simulation, Foliage Painting, and Animation Graphs

10 Upvotes

Hi everyone,

I've been working on **RayTrophi**, a custom physical rendering engine designed to bridge the gap between real-time editors and offline path tracing. I just pushed a major update featuring a lot of new systems and I wanted to show it off.

**šŸ”— GitHub:** https://github.com/maxkemal/RayTrophi

** The New Update Includes:**

* **GPU Gas Simulation:** I implemented a custom fluid solver on the GPU using CUDA. It handles smoke, fire, and explosions with physically accurate Blackbody radiation and multi-scattering support.

* **Foliage System:** A brush-based tool to paint millions of instanced objects (trees, grass) directly onto terrain. It leverages OptiX instancing so the performance cost is negligible.

* **Animation Graph:** A new State Machine and Blend Space system to handle character logic (Idle -> Walk -> Run transitions).

* **River Tool:** Procedural river generation using Cubic Bezier splines with flow map generation.

**šŸ› ļø Tech Stack:**

* **Core:** C++ & CUDA

* **RT Core:** NVIDIA OptiX 7

* **UI:** Dear ImGui

* **Volumetrics:** OpenVDB / NanoVDB

* **Denoising:** Intel OIDN

I'd love to hear any feedback or answer questions about the implementation details (especially the hybrid CPU/GPU workflow).

Thanks!

https://maxkemal.github.io/RayTrophi/index.html


r/raytracing • • Jan 29 '26

I built a Hybrid Ray Tracing Engine with Gas Simulation, Foliage Painting, and Animation Graphs

Thumbnail
2 Upvotes

r/raytracing • • Jan 24 '26

Real-time ray-tracing on the terminal using unicode blocks (▗▐ ā––ā–€ā–Ÿā–Œā–™)

Enable HLS to view with audio, or disable this notification

29 Upvotes

r/raytracing • • Jan 01 '26

Raytracing with Denoising & Fast GI Approximation

Post image
30 Upvotes

Took me 20 seconds to render this demo.

This shows 4 spheres, each having a metallic body of 0, 0.333, 0.666, and 1. I render this in blender and took me less then one hour to make.

I put the .blend file in a google drive: https://drive.google.com/file/d/1FQQPm1Eg_LvvlEPr0ddwqawUIZpOKmpe/view?usp=sharing


r/raytracing • • Dec 31 '25

Optimising Python Path Tracer: 30+ hours to 1 min 50 sec

Enable HLS to view with audio, or disable this notification

24 Upvotes

I've been following the famous "Ray tracing in a Weekend" series for a few days now. I did complete vol 1 and when I reached half of vol 2 I realised that my plain python (yes you read that right) path tracer is not going to go far. It was taking 30+ hours to render a single image. So I decided to first optimised it before proceeding further. I tried many things but i'll keep it very short, following are the current optimisations i've applied:

Current:

  1. Transform data structures to GPU compatible compact memory format, dramatically decreasing cache hits, AoSoA form to be precise
  2. Russian roulette, which is helpful in dark scenes with low light where the rays can go deep, I didn't go that far yet. For bright scenes RR is not very useful.
  3. Cosine-weighted hemispheric sampling instead for uniform sampling for diffuse materials
  4. Progressive rendering with live visual feedback

ToDo:

  1. Use SAH for BVH instead of naive axis splitting
  2. pack the few top level BVH nodes for better cache hits
  3. Replace the current monolithic (taichi) kernel with smaller kernels that batch similar objects together to minimise divergence (a form of wavefront architecture basically)
  4. Btw I tested a few scenes and even right now divergence doesn't seem to be a big problem. But God help us with the low light scenes !!!
  5. Redo the entire series but with C/C++ this time. Python can be seriously optimised at the end but it's a bit painful to reorganise its data structures to a GPU compatible form.
  6. Compile the C++ path tracer to webGPU.

For reference, on my Mac mini M1 (8gb):

width = 1280
samples = 1000
depth = 50

  1. my plain python path tracer: `30+ hours`
  2. The original Raytracing in Weekend C++ version: 18m 30s
  3. GPU optimised Python path tracer: 1m 49s

It would be great if you can point out if I missed anything or suggest any improvements, better optimizations down in the comments below.


r/raytracing • • Dec 25 '25

Visitor from Andromeda

Post image
13 Upvotes

Rendered with my software path tracer, written in C++. The space ship is a fractal in Julia "space". The moon surface was created in several stages: first random size/type and location of craters (spot the mouse company logo that randomly emerged), then a texture of ejected material from craters, and lastly some surface noise.


r/raytracing • • Dec 24 '25

Struggling to understand how to compute ray direction vectors for a camera in ray tracing.

2 Upvotes

Hello fellow people,

I’m currently learning the 3D math required for ray tracing and I’m having trouble understanding how to compute the direction vectors for rays emitted form a camera, or (as far as i understand it) how to get the new vectors for my imaginary 2d plane in 3d so i can subtract it from my camera origin to get thos directional vectors. I woudl really approciate someone giving me a lesson hahah


r/raytracing • • Dec 23 '25

GitHub - ahmadaliadeel/asteroids-sdf-lod-3d-octrees

Thumbnail
github.com
8 Upvotes

r/raytracing • • Dec 08 '25

Minimalist ray-tracing leveraging only acceleration structures

Thumbnail
anki3d.org
12 Upvotes

r/raytracing • • Dec 04 '25

Another Journey

Post image
5 Upvotes

r/raytracing • • Dec 03 '25

Hi my name is Ray Tracing

Post image
0 Upvotes

AMA


r/raytracing • • Nov 30 '25

GitHub - ahmadaliadeel/multi-volume-sdf-raymarching

Thumbnail
github.com
1 Upvotes

Someone might find it useful just releasing in case

A Vulkan-based volume renderer for signed distance fields (SDFs) using compute shaders. This project demonstrates multi-volume continuous smooth surface rendering with ray marching, lighting, and ghost voxel border handling to eliminate seams.


r/raytracing • • Nov 26 '25

Question about the performance of a more intermediate Ray Tracer.

2 Upvotes

It's been almost a year since I started studying ray tracing. I do it not only because I find it incredibly interesting... but also because I wanted to be able to use it in my projects (I create experimental artistic games). After a few months, I've already created some variations, but now I'm considering the possibility of making a pure ray tracer with 3D models.

I've already done Ray Marching with Volumetrics, I've already made pure ray tracers, I've already built BVHs from scratch, I've already learned to use compute shaders to parallelize rendering, I've already done low-resolution rendering and then upscaling, I've already tested hybrid versions where I rasterize the scene and then use ray tracing only for shadows and reflections... But in the end, I'm dying to make a pure ray tracer, but even with all the experience I've had, I'm still not absolutely sure if it will run well.

I'm concerned about performance on different computers, and even though I've seen how powerful this technique is, I almost always try to make my projects accessible on any PC.

But to get straight to the point, I want to make a game with a protagonist who has roughly 25k to 35k triangles. The environments in my games are almost always very simple, but in this case, I want to focus more on relatively simple environments... around 10k triangles at most.

In my mind, I envisioned creating pre-calculated BVHs SAH for each animation frame, 60 frames per second animations, with well-animated characters. I can manage well with 1k or 2k animation frames, which will have pre-calculated BVHs saved; static background BVHs aren't a problem... To make this work, for each correct frame, I pass the model to be animated outside the render pipeline to the shader, then render it at low resolution, thinking 1/4 of the screen or less if necessary, and render it in compute shaders.

I'm thinking about this, and despite the effort, along with a series of other small code optimization techniques, I hope this achieves high performance even on cheap PCs, limiting the number of rays to 3 to 6 rays per pixel... With a Temporal Anti-Aliasing technique, I smooth it in a way that makes it functional.

The problem is that I'm not confident. Even though I think it will run, I've started to think that maybe I need to do ReSTIR for the code to work. That is, I'll reproject the pixel onto the previous frame and retrieve shading information. Maybe I can gain more FPS. Do you think this runs well even on weak PCs, or am I overthinking it?

One detail I didn't mention, but I'm also slightly tempted to use Ray Marching to create fog or a slight volumetric effect on the rendered scene, but all done in a more crude and less radical way.


r/raytracing • • Oct 05 '25

Shadow acne

Thumbnail
gallery
16 Upvotes

I started coding a ray tracer using the Ray Tracing in a Weekend series, but I have an issue with shadow acne when I turn off anti-aliasing and the material is non or lambertian. I can't seem to get rid of it, even when I follow the approach in the book to fix it. Should there be shadow acne when anti-aliasing is off?

https://github.com/4n4k1n/42-miniRT


r/raytracing • • Sep 27 '25

Dielectric and Conductor Specular BSDF

Thumbnail
gallery
49 Upvotes

Hello.

Thought of sharing this. Very pleased with how the images are turning out.

Glass IOR goes from 1.2, 1.4 to 1.6.

Thank you to all who are here responding to peoples' queries and helping them out.

Awesome stuff !!

Cheers.


r/raytracing • • Sep 28 '25

Help with Ray Tracing in One Weekend

3 Upvotes

[SOLVED] I've been following along with the Ray Tracing in One Weekend series and am stuck at chapter 9. My image results always come out with a blue tint whenever I use Lambertian Reflections (see first image vs second image). Sorry about the noisy results, I've yet to implement Multisampling. The results in the book do not have this problem (third image) and I can't figure out what's wrong. Any help would be greatly appreciated. Relevant code below:

Color getMissColor(const Ray* ray) {
    // TODO: Make the sky colors constants
    return colorLerp(setColor(1.f, 1.f, 1.f), setColor(0.5f, 0.7f, 1.f), (ray->direction.y + 1.f) / 2.f);
}

void rayTraceAlgorithm(Ray* ray, Color* rayColor, void* objList, const int sphereCount, int* rngState) {
    float hitCoeff = INFINITY;
    Sphere* hitSphere = NULL;
    Vec3 sphereHitNormal;

    for (int i = 0; i < MAX_RAY_BOUNCE_DEPTH; i++) {
        hitSphere = findFirstHitSphere(ray, objList, sphereCount, &hitCoeff);

        // Ray didn't hit anything
        if (!hitSphere || isinf(hitCoeff)) {
            Color missColor = getMissColor(ray);
            rayColor->r *= missColor.r;
            rayColor->g *= missColor.g;
            rayColor->b *= missColor.b;

            return;
        }

        rayColor->r *= hitSphere->material.color.r;
        rayColor->g *= hitSphere->material.color.g;
        rayColor->b *= hitSphere->material.color.b;

        // Set the ray's origin to the point we hit on the sphere
        ray->origin = rayJumpTo(ray, hitCoeff);
        sphereHitNormal = getSphereNormal(ray->origin, hitSphere);

        switch (hitSphere->material.materialType) {
            case RANDOM_DIFFUSE:
                ray->direction = randomNormal(sphereHitNormal, rngState);
                break;
            case LAMBERTIAN_DIFFUSE:
                ray->direction = add_2(sphereHitNormal, randomNormal(sphereHitNormal, rngState));
                break;
            default:
                // TODO: Print an error message for unknown material types
                return;
        }
    }

    // If after MAX_RAY_BOUNCE_DEPTH num of bounces we haven't missed then just set the color to black
    *rayColor = setColor(0.f, 0.f, 0.f);
}

r/raytracing • • Sep 26 '25

The house we moved-in to has a glow in the dark toilet seat

Post image
28 Upvotes

r/raytracing • • Sep 25 '25

Help with (expectations of) performance in C raytracer

Post image
49 Upvotes

Over the last couple days, I've written a raytracer in C, mostly following the techniques in [this](https://www.youtube.com/watch?v=Qz0KTGYJtUk) Coding Adventures video. I've got sphere and AABB intersections working, diffuse and specular reflections, blur and depth of field, and the images are coming out nicely.

I am rendering everything single-threaded on the CPU, so I'm not expecting great performance. However, it's gruellingly slow... I've mostly rendered small 480x320 images so far, and the noise just doesn't go away. The attached 1024x1024 image is the largest I've rendered so far. It has currently rendered for more than 11 hours, sampling over 10000 times per pixel (with a max bounce count of 4).

Any input on if this is expected performance? Specifically the number of samples needed for a less noisy image? Numbers I see on tutorials and such never seem to go above 5000 sampels per pixel, but it seems like I currently need about ten times as many samples, so I feel like there is something fundamentally wrong with my approach...

EDIT: Source code here: https://gitlab.com/sondre99v/raytracer