Why do my HLSL arrays not accept changes in this specific context?
05:25 17 May 2026

I'm working on a shader for drawing rain. I have this statement in a loop that fires whenever the ray hits a raindrop. It's meant to stop overlapping drops from refracting off each other. In the end what I get is a black circle where the drops overlap and, as far as I can tell, a very strange issue.

if (inWater && dot(currentRay.direction, surfaceNormal) < 0)
This line fires and does everything perfectly fine. I can test it and see that yes, the raindrop depth increases and allows for entry into the second loop, when applicable.

if (inWater && dot(currentRay.direction, surfaceNormal) > 0 && currentRay.raindropDepth > 1)
This one, however, based on all my tests and changes and everything, is cursed. As far as I can tell, this if statement CANNOT save data to the arrays. The statement runs. I can change a completely different variable after setting these values. I have a debug color that I can set, I do so, and it works. But if I change the debug color, then when I get to the top of the loop via the continue, I check to see if the debug color is changed, and it is, I can then look through the ENTIRE array for a ray that has rayDebug set to true, and I wont find a single one. It's killing me. I assume I left something here and that I'm so stupid that I can't figure this out, but I genuinely can't see a place where the issue could be. The two statements are quite similar and yet one fails and I can't see why.

What SHOULD be happening is that the ray enters a drop, then hits the overlapping drop, and that triggers the first if, which it does. That first if then increases the drop depth to 2. Now when we hit the exit side, the second if statement should fire, which it does, and it should set the variables accordingly, pretty much the same as the first if, except we're now decreasing depth instead of increasing. This isnt happening and the ray instead completely dies here and I get a black circle where the overlap is, becuase the ray is not reset and pulled from the arrays again.

Any help would be greatly appreciated, I'm quite new to shaders so I apologize if I missed something fundamental. Attached is the full code. The second if statement is the problem one.

float3 waterDropPos = WaterDrops[closestbjectIndex - startingIndex].xyz;
float waterDropRadius = WaterDrops[closestObjectIndex - startingIndex].a;

float fresnelAmount = 0.2;
//Rain splits the ray in 2. One reflected and one refracted.

int dropIndex = closestObjectIndex - startingIndex;

bool inWater = currentRay.raindropDepth != 0;

float3 hitLocation = currentRay.origin + currentRay.direction * closestT; // The point where the ray hits the sphere, which we'll use to calculate the surface normal and the reflection and refraction directions.

float3 surfaceNormal = normalize(hitLocation - waterDropPos); //We'll need to flip this depending on if we're in the water

bool inserted = false;

//If we're in water, and we're entering water, we can actually just ignore this intersection. This would be the ray going through 2 drops overlapping, which wouldn't actually change the look.
if (inWater && dot(currentRay.direction, surfaceNormal) < 0)
{
    Ray continuedRay = CreateRay(hitLocation + currentRay.direction * 0.0001, currentRay.direction, currentRay.strength, currentRay.currentMedium, currentRay.depth, 0, currentRay.raindropDepth + 1);
    //This is the only situation I can think of where we won't increase the ray depth, because in all honesty both drops should merge into one anyway.
    for (int k = 0; k < MaxRays; k++)
    {
        if (rayOverwrites[k])
        {
            //rays[j] = continuedRay;
            rayOrigins[k] = continuedRay.origin;
            rayDirections[k] = continuedRay.direction;
            rayStrengths[k] = continuedRay.strength;
            rayCurrentMediums[k] = continuedRay.currentMedium;
            rayDepths[k] = continuedRay.depth;
            rayOverwrites[k] = continuedRay.overwrite;
            rayRaindropDepths[k] = continuedRay.raindropDepth;
            rayDebugs[k] = false;
            
            
            
            inserted = true;
            break;
        }
    }
    if (inserted == false)
    {
        Result[id.xy] = float4(1.0, 0.0, 1.0, 1.0); // Magenta for this specific case, just to make sure it's working correctly.
        return;
    }
    continue;
}



//If our droplet depth is greater than 1, nd we're exiting a water drop, we can also ignore the intersection, because this would be the ray exiting one drop and 
//immediately entering another overlapping drop, which again wouldn't actually change the look because the drops would merge.
if (inWater && dot(currentRay.direction, surfaceNormal) > 0 && currentRay.raindropDepth > 1)
{
    //Ray continuedRay = CreateRay(hitLocation + currentRay.direction * 0.0001, currentRay.direction, currentRay.strength, currentRay.currentMedium, currentRay.depth, 0, currentRay.raindropDepth - 1);
    for (int h = 0; h < MaxRays; h++)
    {
        if (rayOverwrites[h])
        {
            //rays[j] = continuedRay;
            rayOrigins[h] = hitLocation + currentRay.direction * 0.0001;
            rayDirections[h] = currentRay.direction;
            rayStrengths[h] = currentRay.strength;
            rayCurrentMediums[h] = currentRay.currentMedium;
            rayDepths[h] = currentRay.depth;
            rayOverwrites[h] = false;
            rayRaindropDepths[h] = currentRay.raindropDepth - 1;
            rayDebugs[h] = true;
            
            inserted = true;
            break;
        }
    }
    if (inserted == false)
    {
        Result[id.xy] = float4(1.0, 0.0, 1.0, 1.0); // Magenta for this specific case, just to make sure it's working correctly.
        return;
    }
    continue;
}

if (currentRay.depth >= MaxBounces)
{
    //Result[id.xy] = float4(1.0, 0.0, 0.0, 1.0); // Red for testing to make sure we're actually hitting the raindrop and not just skipping it because of the max bounces.
    //return;
    endColor += AmbientIntensity * LightColor * currentRay.strength; // If we've reached the max bounces, we can just add the light color modulated by the strength of the ray, and then end the ray.
    continue;
    
}
unity-game-engine hlsl