3
$\begingroup$

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:

steppy ray cube

Ryan Brucks has some nice write up here where he describes some workarounds. I'm specifically trying to understand the plane alignment technique:

Ryan Brucks plane alignment

his relevant shader snippet:

//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!

$\endgroup$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.