Hey devs,
I’m currently experimenting with ray marching in a normal 3D mesh environment. Instead of make the ray-march operate on the whole screen, I added a box to the scene with a ray-marching shader attached, which acts as a window to the ray-march scene. So far so good, but I have trouble making shadows behave correctly.
In the first image the shadows look right, in the second one the shadow is cut off weirdly. This cutout happens when I move the camera around.
Here is my code for the shadowPass:
{
Tags {"LightMode" = "ShadowCaster"}
HLSLPROGRAM
#pragma vertex vertex
#pragma fragment fragment
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
// Idk why, but with shadows included, I need to include this as well or it won't compile.
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
struct vertex_in {
float4 vertex : POSITION;
};
struct vertex_to_fragment {
float4 vertex : SV_POSITION;
float4 vertex_world: TEXCOORD1;
};
float linear_depth_to_raw_depth(const float linear_depth) {
return (1.0f - (linear_depth * _ZBufferParams.y)) / (linear_depth * _ZBufferParams.x);
}
float sdf_circle(const float3 position) {
const float3 circle_position = float3(0, 1, 0);
const float radius = 1;
return distance(position, circle_position) - radius;
}
float sdf_cube(const float3 position) {
const float3 cube_position = float3(0,0,2);
const float3 cube_half_extent = float3(1, .25, 1);
const float3 relative_position = position - cube_position;
float3 q = abs(position - relative_position) - cube_half_extent;
return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0);
}
float sdf_cylinder(const float3 position) {
const float3 cylinder_position = float3(0, 1, 0);
const float3 relative_position = position - cylinder_position;
const float h = 1;
const float r = 1;
float2 d = abs(float2(length(relative_position.xz),relative_position.y)) - float2(r,h);
return min(max(d.x,d.y),0.0) + length(max(d,0.0));
}
float sdf_torus(const float3 position) {
const float3 torus_position = float3(0, 0, 1);
const float3 relative_position = position - torus_position;
const float2 t = float2(1, .6);
const float2 q = float2(length(relative_position.xz)-t.x,relative_position.y);
return length(q)-t.y;
}
float smooth_max(const float a, const float b, const float k){
return log(exp(k * a) + exp(k * b)) / k;
}
float smooth_min(const float a, const float b, const float k){
return -smooth_max(-a, -b, k);
}
float sdf_world(const float3 position) {
// return sdf_cylinder(position); //smooth_min(sdf_circle(position), sdf_cube(position), 4);
// return sdf_circle(position);
return smooth_min(sdf_torus(position), sdf_circle(position), 4);
}
float3 calculate_normal(const float3 position) {
const float2 offset = float2(0.001, 0.0);
float gradient_x = sdf_world(position + offset.xyy) - sdf_world(position - offset.xyy);
float gradient_y = sdf_world(position + offset.yxy) - sdf_world(position - offset.yxy);
float gradient_z = sdf_world(position + offset.yyx) - sdf_world(position - offset.yyx);
const float3 normal = float3(gradient_x, gradient_y, gradient_z);
return normalize(normal);
}
float sample_ray_march(const float3 origin, const float3 direction){
float distance = 0;
for (int i=0; i<100; i++) {
const float3 position = origin + direction * distance;
const float next_distance = sdf_world(position);
distance += next_distance;
if (next_distance<0.01) {
const float3 hit_position = origin + direction * distance;
const float3 normal = calculate_normal(hit_position);
const float3 shadow = ComputeNormalizedDeviceCoordinatesWithZ(ApplyShadowBias(hit_position, normal, direction), UNITY_MATRIX_VP);
return shadow.z;
}
// todo: use light camera far
if (distance >= 1000){
break;
}
}
return 0;
}
vertex_to_fragment vertex (const vertex_in v_in) {
vertex_to_fragment v_2_f;
v_2_f.vertex = TransformObjectToHClip(v_in.vertex);
v_2_f.vertex_world = mul(unity_ObjectToWorld, v_in.vertex);
return v_2_f;
}
float fragment (const vertex_to_fragment v_2_f) : SV_DEPTH {
const float3 direction = normalize(_MainLightPosition.xyz);
// magic number, shadow is rendering depending on this value or not. Idk why..
return sample_ray_march(v_2_f.vertex_world.xyz - direction * 20, direction);
}
ENDHLSL
In the fragment part I used the interpolated vertex position of the box in world space and offsetting it by the opposite light direction times 20. This number however works, or works not, based on the position of the final rendering camera.
My Idea for a more robust ray origin would be a point on the light’s near plane but I don’t know how to do this (Main Light is directional) nor if it would fix the problem.
Does anyone have an idea to this?
Any help is greatly appreciated
qulol

