#ifndef LIBGARBAGE_INCLUDED #define LIBGARBAGE_INCLUDED #include "UnityCG.cginc" /* # defines with no defaults USE_WORLD_SPACE DISCARD_ON_MISS USE_MATERIALS_IN_RAYMARCH // TODO: implement, this is easier to work with but more expensive DISABLE_DEPTH START_RAYS_IN_BOX // TODO: implement START_RAYS_IN_SPHERE // TODO: implement */ // scene sdf with only distance #ifndef DISTANCE_FN #define DISTANCE_FN default_distance_sdf #endif // scene sdf that includes material data #ifndef MATERIAL_FN #define MATERIAL_FN default_material_sdf #endif // calculates a color from a Ray #ifndef LIGHT_FN #define LIGHT_FN default_lighting #endif // max ray steps, from last bounce (reset on each reflection) #ifndef MAX_STEPS #define MAX_STEPS 128 #endif // max ray length, from last bounce (length is reset on each reflection) #ifndef MAX_DIST #define MAX_DIST 128 #endif // the largest surface distance that is counted as a hit #ifndef SURF_DIST #define SURF_DIST 0.001 #endif // TODO: implement #ifndef REFLECTIONS #define REFLECTIONS 0 #endif // TODO: implement #ifndef SCENE_SCALE #define SCENE_SCALE 1 #endif // TODO: implement #ifndef AMBIENT_OCCLUSION_STEPS #define AMBIENT_OCCLUSION_STEPS 4 #endif struct AppData { float4 vertex : POSITION; }; struct V2F { float4 vertex : SV_POSITION; float3 cam_pos : TEXCOORD0; float3 hit_pos : TEXCOORD1; }; struct FragOut { fixed3 col : SV_Target; #ifndef DISABLE_DEPTH float depth : SV_Depth; #endif }; struct Material { float3 col; float gloss; }; #define DEFAULT_MAT {float3(0.2, 0.2, 0.2), 0} struct SurfacePoint { float dist; Material mat; }; //used for lighting a point struct Ray { float dist; int steps; Material mat; float3 start; float3 dir; float3 hit_pos; float3 normal; bool missed; float min_dist; // smallest distance encountered along the path (only useful for misses; shadow calculations) }; #include "libgarbage_shapes.cginc" float DISTANCE_FN(float3 p); SurfacePoint MATERIAL_FN(float3 p); float3 LIGHT_FN(Ray ray); Ray cast_ray(float3 p, float3 d, float startDist = 0); V2F vert (AppData v) { V2F o; o.vertex = UnityObjectToClipPos(v.vertex); #ifdef USE_WORLD_SPACE o.cam_pos = _WorldSpaceCameraPos; o.hit_pos = mul(unity_ObjectToWorld, v.vertex); #else o.cam_pos = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1)); o.hit_pos = v.vertex; #endif return o; } FragOut frag (V2F i) { float ray_len = 0; float3 ray_dir = normalize(i.hit_pos - i.cam_pos); float3 last_bounce = i.cam_pos; SurfacePoint point_data; Ray ray; float3 col; float color_used = 0;// what amount of the final color has been calculated float prev_gloss = 0; float3 first_hit; for (int ray_num = 0; ray_num < REFLECTIONS + 1; ray_num++) { ray = cast_ray(last_bounce, ray_dir); if (ray_num == 0) { // before any bounces col = LIGHT_FN(ray); first_hit = ray.hit_pos; } else { float col_amount = color_used + ((1 - prev_gloss) * (1 - color_used)); col = lerp(LIGHT_FN(ray), col, col_amount); color_used = col_amount; } if (ray.missed || ray.mat.gloss < 0.01) { break; } prev_gloss = ray.mat.gloss; ray_dir = reflect(ray_dir, ray.normal); last_bounce = ray.hit_pos + ray_dir * 0.01; } #ifdef DISCARD_ON_MISS if (ray.missed && ray_num == 0) discard; #endif FragOut o; o.col = col; #ifndef DISABLE_DEPTH float4 clip_pos = mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, float4(first_hit, 1))); o.depth = clip_pos.z / clip_pos.w; #ifndef UNITY_REVERSED_Z // basically only OpenGL (unity editor on linux) o.depth = o.depth * 0.5 + 0.5; // remap -1 to 1 range to 0.0 to 1.0 #endif #endif return o; } inline float3 get_normal(float3 pos, float surface_distance, float epsilon = 0.001) { // if epsilon is smaller than 0.001, there are often artifacts const float2 e = float2(epsilon, 0); float3 n = surface_distance - float3( DISTANCE_FN(pos - e.xyy), DISTANCE_FN(pos - e.yxy), DISTANCE_FN(pos - e.yyx)); return normalize(n); } Ray cast_ray(float3 start, float3 dir, float start_len) { float ray_len = start_len; Ray ray; ray.dir = dir; ray.start = start; ray.min_dist = 100100100; // budget "infinity" ray.missed = true; for (int i = 0; i < MAX_STEPS; i++) { ray.hit_pos = start + ray_len * dir; ray.dist = DISTANCE_FN(ray.hit_pos); if (ray.dist < SURF_DIST) { ray.missed = false; break; } ray.min_dist = min(ray.min_dist, ray.dist); ray_len += ray.dist; if (ray_len > MAX_DIST) break; } ray.steps = i; if (ray.missed) { ray.normal = float3(0, 0, 0); } else { ray.normal = get_normal(ray.hit_pos, ray.dist); ray.mat = MATERIAL_FN(ray.hit_pos).mat; } return ray; } float default_distance_sdf(float3 p) { return sdSphere(p, 0.5); } SurfacePoint default_material_sdf(float3 p) { return mSphere(p, 0.5); } float3 default_lighting(Ray ray) { float3 sun_dir = normalize(float3(1, 0.5, 0)); if (ray.missed) return 0.1; float3 col = 0; col = ray.mat.col * dot(ray.normal, sun_dir); return col; } #endif // LIBGARBAGE_INCLUDED