#ifndef LIBGARBAGE_INCLUDED #define LIBGARBAGE_INCLUDED #include "UnityCG.cginc" /* ## defines with no defaults # default is object space USE_WORLD_SPACE # renders missed rays as transparent DISCARD_ON_MISS # use if the shape of the scene is significnatly different from the shape of the material space # will use SCENE_FN only for the final step, and DISTANCE_FN for raymarching # DISTANCE_FN must return a float SEPARATE_MATERIAL_AND_DIST_FUNCTIONS # don't write depth (will have the depth defined by the original mesh) DISABLE_DEPTH # meant for frontface culled meshes, makes depth no further than the mesh edges LIMIT_DEPTH_TO_MESH START_RAYS_IN_BOX // TODO: implement START_RAYS_IN_SPHERE // TODO: implement */ // sky used as background unless DISCARD_ON_MISS is set, and used in reflections // TODO: implement #ifndef SKY_FN #define SKY_FN default_sky #endif // scene sdf that includes material data #ifndef SCENE_FN #define SCENE_FN default_material_sdf #endif #ifdef SEPARATE_MATERIAL_AND_DIST_FUNCTIONS // scene sdf with only distance #ifndef DISTANCE_FN #define DISTANCE_FN default_distance_sdf #endif #else #define DISTANCE_FN material_fn_as_dist #endif // calculates a color from a Ray #ifndef LIGHT_FN #define LIGHT_FN default_lighting #endif // set to less than one if there are artifacts in distorted sdfs #ifndef STEP_MULTIPLIER #define STEP_MULTIPLIER 1 #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 #ifndef REFLECTIONS #define REFLECTIONS 0 #endif #ifndef SCENE_SCALE #define SCENE_SCALE 1 #endif struct AppData { float4 vertex : POSITION; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct V2F { float4 vertex : SV_POSITION; float3 cam_pos : TEXCOORD0; float3 hit_pos : TEXCOORD1; UNITY_VERTEX_OUTPUT_STEREO }; struct FragOut { fixed3 col : SV_Target; #ifndef DISABLE_DEPTH float depth : SV_Depth; #endif }; struct Material { float3 col; float gloss; }; #define DEFAULT_MAT {float3(1, 1, 1), 0} Material mat(float3 col = float3(1, 1,1 ), float gloss = 0) { Material m; m.col = col; m.gloss = gloss; return m; } Material mat(float r, float g, float b) { return mat(float3(r, g, b)); } 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) }; float DISTANCE_FN(float3 p); SurfacePoint SCENE_FN(float3 p); float3 LIGHT_FN(Ray ray); float3 SKY_FN(float3 d); Ray cast_ray(float3 p, float3 d, float startDist = 0); #include "libgarbage_shapes.cginc" #include "libgarbage_operations.cginc" #include "libgarbage_lighting.cginc" V2F vert (AppData v) { V2F o; UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_OUTPUT(V2F, o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(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 ray_origin = i.cam_pos / SCENE_SCALE; Ray ray; float3 col; float color_used = 0;// what amount of the final color has been calculated float prev_gloss = 0; float3 first_hit; bool inside_shape; int ray_num = 0; for (; ray_num < REFLECTIONS + 1;) { ray = cast_ray(ray_origin, ray_dir); if (ray_num == 0) { // before any bounces col = LIGHT_FN(ray); first_hit = ray.hit_pos; inside_shape = ray.steps == 0; } else { float col_amount = color_used + ((1 - prev_gloss) * (1 - color_used)); col = lerp(LIGHT_FN(ray), col, col_amount); color_used = col_amount; } ray_num++; if (ray.missed || ray.mat.gloss < 0.01) { break; } prev_gloss = ray.mat.gloss; ray_dir = reflect(ray_dir, ray.normal); ray_origin = ray.hit_pos + ray_dir * SURF_DIST * 2; } #ifdef DISCARD_ON_MISS if (ray.missed && ray_num == 1) discard; #endif FragOut o; o.col = clamp(col, 0, 1); #ifndef DISABLE_DEPTH float3 depth_point = first_hit * SCENE_SCALE; if (ray.missed && ray_num == 1) depth_point = i.hit_pos; #ifdef LIMIT_DEPTH_TO_MESH if (length(i.cam_pos - first_hit * SCENE_SCALE) > length(i.cam_pos - i.hit_pos)) depth_point = i.hit_pos; #endif if (inside_shape) { depth_point = i.cam_pos + normalize(i.hit_pos - i.cam_pos) * 0.01; } #ifdef USE_WORLD_SPACE float4 clip_pos = mul(UNITY_MATRIX_VP, float4(depth_point, 1)); #else float4 clip_pos = mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, float4(depth_point, 1))); #endif 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 * STEP_MULTIPLIER; if (ray_len > MAX_DIST) break; } ray.steps = i; if (ray.missed) { ray.normal = float3(0, 0, 0); ray.mat = mat(); } else { ray.normal = get_normal(ray.hit_pos, ray.dist); ray.mat = SCENE_FN(ray.hit_pos).mat; } return ray; } float3 default_sky(float3 dir) { return lRenderSky(dir, normalize(float3(2, 1, 0))); } float default_distance_sdf(float3 p) { return sdSphere(p, 0.5); } float material_fn_as_dist(float3 p) { return SCENE_FN(p).dist; } 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.5)); if (ray.missed) return SKY_FN(ray.dir); float3 light = lSun(ray.normal, sun_dir); light *= lShadow(ray.hit_pos + ray.normal * SURF_DIST, sun_dir, 50); light += lSky(ray.normal); return light * ray.mat.col; } #endif // LIBGARBAGE_INCLUDED