# volume raymarching - aligning sample coords with the 3d texture

I'm implementing a volume raymarcher and the unlit basic version seems to be working fine. This article helped a ton grokking the view-to-volume-space transform: http://www.lebarba.com

However, it quickly gets ugly when reducing the ray step count to something that's feasible for doing a lighting nested loop.

exaggerated sampling artifacts, 32 steps: Ryan Brucks has some nice write up here where he describes some workarounds. I'm specifically trying to understand the plane alignment technique: //bring vectors into local space to support object transforms
float3 localcampos = mul(float4( ResolvedView.WorldCameraOrigin,1.00000000), (Primitive.WorldToLocal)).xyz;
float3 localcamvec = -normalize( mul(Parameters.CameraVector, Primitive.WorldToLocal) );

//make camera position 0-1
localcampos = (localcampos / (Primitive.LocalObjectBoundsMax.x * 2)) + 0.5;

float3 invraydir = 1 / localcamvec;

float3 firstintersections = (0 - localcampos) * invraydir;
float3 secondintersections = (1 - localcampos) * invraydir;
float3 closest = min(firstintersections, secondintersections);
float3 furthest = max(firstintersections, secondintersections);

float t0 = max(closest.x, max(closest.y, closest.z));
float t1 = min(furthest.x, min(furthest.y, furthest.z));

float planeoffset = 1-frac( ( t0 - length(localcampos-0.5) ) * MaxSteps );

t0 += (planeoffset / MaxSteps) * PlaneAlignment;
t0 = max(0, t0);

float boxthickness = max(0, t1 - t0);
float3 entrypos = localcampos + (max(0,t0) * localcamvec);

return float4( entrypos, boxthickness );


For the life of me, I don't get how this works. I'm not familiar with Unreal Engine at all and doing my version in Metal.

here's my version without plane offset adjustment:

constant bool cam_inside        [[ function_constant(FogVolumeCamInsideIndex) ]];

vertex VolumeVertexInOut vert_volume_raymarch(VolumeCubeVertex                   in             [[ stage_in   ]],
constant float4x4                  &mvp           [[ buffer(10) ]],
constant float4x4                  &model_inv     [[ buffer(11) ]],
constant float4                    &cam_pos       [[ buffer(12) ]]) {
VolumeVertexInOut out;
out.local_cam_pos         = model_inv * cam_pos;
float4 world_pos          = cam_inside ? out.local_cam_pos : float4(in.position, 1.0);
out.position              = mvp * float4(in.position, 1);
out.volume_space_coords   = float4(world_pos.xyz + 0.5, 1).xyz;
out.projected_coords      = mvp * (cam_inside ? float4(in.position, 1) : world_pos);
return out;
}

constant float MAX_STEPS = 512;
constexpr sampler depth_sampler(filter::nearest);

fragment float4 frag_volume_raymarch_compute_rays(VolumeVertexInOut                  in                           [[ stage_in     ]],
texture2d<float, access::sample>   tex_back_pos                 [[ texture(1)   ]],
texture2d<float, access::sample>   tex_front_pos                [[ texture(2)   ]],
texture2d<float, access::sample>   scene_depth_buffer           [[ texture(3)   ]],
texture2d<float, access::sample>   volume_depth_buffer_back     [[ texture(4)   ]],
texture2d<float, access::sample>   volume_depth_buffer_front    [[ texture(5)   ]],
constant float                     &zNear_scene                 [[ buffer(1)    ]],
constant float                     &zNear_local                 [[ buffer(2)    ]],
constant float                     &zFar                        [[ buffer(3)    ]],
constant uint                      &plane_alignment             [[ buffer(4)    ]]) {

float2 texcoord         = (in.projected_coords.xy / in.projected_coords.w + 1) / 2;
texcoord.y              = 1-texcoord.y;

float3 furthest         = tex_back_pos.sample(linearSampler, texcoord).xyz;
float3 entry_pos        = in.volume_space_coords;

float scene_depth       = scene_depth_buffer.sample(depth_sampler, texcoord).r;
float closest_depth     = cam_inside ? 0 : volume_depth_buffer_front.sample(depth_sampler, texcoord).r;
float furthest_depth    = volume_depth_buffer_back.sample(depth_sampler, texcoord).r;

scene_depth             = linearize_depth(scene_depth, zNear_scene, zFar);
closest_depth           = linearize_depth(closest_depth, zNear_local, zFar);
furthest_depth          = linearize_depth(furthest_depth, zNear_local, zFar);
closest_depth           = min(closest_depth, scene_depth);

float thickness         = length(furthest - entry_pos);
scene_depth             = map(scene_depth, closest_depth, furthest_depth, 0.0, thickness);
scene_depth             = clamp(scene_depth, 0.0, thickness);
thickness               = min(scene_depth, thickness);

return float4(entry_pos, thickness);
}


I think the method of computing the ray start/end points differs a bit. The scene depth bits are for interacting with classic vertex geometry.

So the question is, how do I change my shader code to include that plane alignment thing? Or if there are other ways to do this right... I'm all ears. thanks!