298 lines
6.8 KiB
HLSL
298 lines
6.8 KiB
HLSL
#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));
|
|
}
|
|
Material mat(float r, float g, float b, float gloss) {
|
|
return mat(float3(r, g, b), gloss);
|
|
}
|
|
|
|
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)
|
|
};
|
|
|
|
#define UP float3(0, 1, 0)
|
|
|
|
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_acc = 1;
|
|
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
|
|
first_hit = ray.hit_pos;
|
|
inside_shape = ray.steps == 0;
|
|
}
|
|
float3 light = clamp(LIGHT_FN(ray), 0, 1);
|
|
col_acc *= ray.mat.col * lerp(light, 1, ray.mat.gloss);
|
|
ray_num++;
|
|
if (ray.missed || ray.mat.gloss < 0.01) {
|
|
break;
|
|
}
|
|
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;
|
|
// something something sRGB, colour space is annoying
|
|
col_acc = pow(col_acc, 2.2);
|
|
o.col = clamp(col_acc, 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;
|
|
}
|
|
|
|
#endif // LIBGARBAGE_INCLUDED
|