From 3432ab4c1d64ef40a92153f7d8aceb46acfba90c Mon Sep 17 00:00:00 2001 From: Michael Fabian 'Xaymar' Dirks Date: Wed, 1 Jan 2020 16:02:45 +0100 Subject: [PATCH] examples: 3D Raytracing Shader Example Very very basic 3D raytracing, nowhere close to what people manage to do in combo productions. But it does work to some degree. --- data/examples/shaders/source/3d-sphere.effect | 372 ++++++++++++++++++ 1 file changed, 372 insertions(+) create mode 100644 data/examples/shaders/source/3d-sphere.effect diff --git a/data/examples/shaders/source/3d-sphere.effect b/data/examples/shaders/source/3d-sphere.effect new file mode 100644 index 00000000..1aa1e471 --- /dev/null +++ b/data/examples/shaders/source/3d-sphere.effect @@ -0,0 +1,372 @@ +uniform float4x4 ViewProj< + bool automatic = true; +>; +uniform float4 ViewSize< + bool automatic = true; +>; +uniform float4 Time< + bool automatic = true; +>; + +// Camera Parameters +uniform float3 CameraPosition< + string name = "Camera Position"; + string field_type = "slider"; + float3 minimum = {-10.0, -10.0, -10.0}; + float3 maximum = {10.0, 10.0, 10.0}; +> = {0., 0., 0.}; + +uniform float3 CameraRotation< + string name = "Camera Rotation"; + string suffix = " °Deg"; + string field_type = "slider"; + float3 minimum = {-180.0, -90.0, -180.0}; + float3 maximum = {180.0, 90.0, 180.0}; + float3 scale = {0.01745329251994329576923690768489, 0.01745329251994329576923690768489, 0.01745329251994329576923690768489}; +> = {0., 0., 0.}; + +uniform float CameraFieldOfView< + string name = "Camera Field Of View"; + string suffix = " °Deg"; + string field_type = "slider"; + float minimum = 1.0; + float maximum = 180.0; + float scale = 0.00872664625997164788461845384244; +> = 90.0; + +uniform float2 CameraRange< + string name = "Camera Range"; + string field_type = "slider"; + float2 minimum = {0.0, 1.00}; + float2 maximum = {10000.0, 10000.0}; + float2 scale = {0.01, 0.01}; +> = {0.1, 256.0}; + +uniform int RayMarchAccuracy< + string name = "Ray March Steps"; + string field_type = "slider"; + int minimum = 32; + int maximum = 1024; +> = 256; + +//----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- +// Configuration +#define MAX_ACCURACY 1024 + +// Camera Type +//#define CAMERA_ORTHOGRAPHIC // Orthographic projection + +//----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- +#define INFINITY 1.#INF + +//----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- +float4x4 make_rotation_matrix(float3 axis, float angle) +{ + axis = normalize(axis); + float s = sin(angle); + float c = cos(angle); + float oc = 1.0 - c; + + float x_x = axis.x * axis.x; + float x_y = axis.x * axis.y; + float x_z = axis.x * axis.z; + float y_x = x_y; + float y_y = axis.y * axis.y; + float y_z = axis.y * axis.z; + float z_x = x_z; + float z_y = y_z; + float z_z = axis.z * axis.z; + + return float4x4(oc * x_x + c, oc * x_y - axis.z * s, oc * z_x + axis.y * s, 0.0, + oc * x_y + axis.z * s, oc * y_y + c, oc * z_y - axis.x * s, 0.0, + oc * x_z - axis.y * s, oc * y_z + axis.x * s, oc * z_z + c, 0.0, + 0.0, 0.0, 0.0, 1.0); +}; + +float3 rotate_float3(float3 v, float3 rotation) +{ + float4x4 rz = make_rotation_matrix(float3(0., 0., 1.), rotation.z); + float4x4 ry = make_rotation_matrix(float3(0., 1., 0.), rotation.y); + float4x4 rx = make_rotation_matrix(float3(1., 0., 0.), rotation.x); + + float4 p = float4(v, 1.); + float4 rtd = mul(mul(mul(p, rz), rx), ry); + + return rtd.xyz; +}; + +bool solve_quadratic(float a, float b, float c, out float x0, out float x1) +{ + float discr = b * b - 4. * a * c; + if (discr < 0.) { + return false; + } else if (discr == 0.) { + x0 = x1 = - 0.5 * b / a; + } else { + float q = (b > 0.) ? + -0.5 * (b + sqrt(discr)) : + -0.5 * (b - sqrt(discr)); + x0 = q / a; + x1 = c / q; + } + + if (x0 > x1) { + float tmp = x1; + x1 = x0; + x0 = tmp; + } + + return true; +} + +bool collide_point_aabb(float3 pos, float3 aabb, float3 size) { + float3 aabb_min = aabb - size; + float3 aabb_max = aabb + size; + return (pos.x >= aabb_min.x) + && (pos.y >= aabb_min.y) + && (pos.z >= aabb_min.z) + && (pos.x <= aabb_max.x) + && (pos.y <= aabb_max.y) + && (pos.z <= aabb_max.z); +} + +bool collide_aabb_aabb(float3 pos1, float3 size1, float3 pos2, float3 size2) { + float3 min1 = pos1 - size1; + float3 max1 = pos1 + size1; + float3 min2 = pos2 - size2; + float3 max2 = pos2 + size2; + + return (min1.x <= max2.x && max1.x >= min2.x) && + (min1.y <= max2.y && max1.y >= min2.y) && + (min1.z <= max2.z && max1.z >= min2.z); +} + +bool intersect_box(float3 pos, float3 size, float3 ray_pos, float3 ray_dir, out float t) { + float3 aabb_size = float3(max(size.x, max(size.y, size.z)), 0., 0.); + if (!collide_aabb_aabb(ray_pos, ray_dir, pos, aabb_size.xxx)) + return false; + + t = 0.; + return true; +} + +bool intersect_sphere(float3 center, float radius, float3 orig, float3 dir, out float t) +{ + if (!collide_aabb_aabb(orig, dir, center, float3(radius, radius, radius))) + return false; + + float t0, t1; // solutions for t if the ray intersects + float radius2 = radius * radius; + + // analytic solution + float3 L = orig - center; + float a = dot(dir, dir); + float b = 2. * dot(dir, L); + float c = dot(L, L) - radius2; + + if (!solve_quadratic(a, b, c, t0, t1)) { + return false; + } + + if (t0 > t1) { + float tmp = t0; + t0 = t1; + t1 = tmp; + } + + if (t0 < 0.) { + t0 = t1; // if t0 is negative, let's use t1 instead + if (t0 < 0.) { + return false; // both t0 and t1 are negative + } + } + + t = t0; + return true; +} + +//----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- +struct default_data { + float4 pos : POSITION; + float2 uv : TEXCOORD0; +}; + +default_data default_vs(default_data data) { + data.pos = mul(float4(data.pos.xyz, 1.0), ViewProj); + return data; +} + +//----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- +struct material_data { + float4 color; + float metallic; + float specular; + float roughness; + float4 emissive_color; + float3 normal; + float index_of_refraction; +}; + +void material_data_constructor(out material_data material) +{ + material.color = float4(0., 0., 0., 0.); + material.metallic = 0.; + material.specular = .5; + material.roughness = 1.; + material.emissive_color = float4(0., 0., 0., 0.); + material.normal = float3(0., 0., 0.); + material.index_of_refraction = 1.; +} + +struct ray_data { + // Status + float3 position; // Current position. + float3 direction; // Direction of the ray. + int depth; // Ray Calculation Depth, limited by ACCURACY define. + + // Hit + bool hit; // Did we hit anything? + float3 hit_position; // If so, where? + float4 hit_color; // What color does it have? + float3 hit_normal; // And what is the normal for that hit? + float hit_depth; +}; + +void ray_data_constructor(out ray_data ray) +{ + ray.position = float3(0., 0., 0.); + ray.direction = float3(0., 0., 0.); + ray.depth = 0; + + ray.hit = false; + ray.hit_position = float3(0., 0., 0.); + ray.hit_color = float4(0., 0., 0., 0.); + ray.hit_normal = float3(0., 0., 0.); + ray.hit_depth = 0.; +} + +float get_view_aspect_ratio() { + return ViewSize.x / ViewSize.y; +} + +float get_raymarch_step_length() { + return CameraRange.y / float(RayMarchAccuracy); +} + +ray_data initialize_camera_ray(float2 uv) { + ray_data ray; + ray_data_constructor(ray); + + uv -= .5; + //uv *= 2.; + + float aspect = get_view_aspect_ratio(); + +#ifdef CAMERA_ORTHOGRAPHIC + ray.direction = rotate_float3(float3(0., 0., 1.), CameraRotation); + ray.position = CameraPosition + float3(uv.x * ViewSize.x, uv.y * ViewSize.y, CameraRange.x); +#else + ray.direction = rotate_float3(rotate_float3(float3(0., 0., 1.), CameraRotation), float3( + uv.y * CameraFieldOfView / aspect, + uv.x * CameraFieldOfView, + 0.)); + ray.position = CameraPosition + float3(-uv.x, uv.y / aspect, CameraRange.x); +#endif + ray.direction *= get_raymarch_step_length(); + + return ray; +} + +bool raymarch_box(inout ray_data ray, float3 pos, float3 rotation, float3 size, material_data material) { + float step = 0.; + if (!intersect_box(pos, size, ray.position, ray.direction, step)) + return false; + +// if (step > 1.) +// return false; + + float depth = (step + float(ray.depth)); +// if (ray.hit && (ray.hit_depth <= depth)) +// return false; + + ray.hit = true; + ray.hit_position = ray.position + ray.direction * step; + ray.hit_depth = depth; + ray.hit_color = material.color; + + return true; +} + +bool raymarch_sphere(inout ray_data ray, float3 pos, float3 rotation, float radius, material_data material) { + float step = 0.; + if (!intersect_sphere(pos, radius, ray.position, ray.direction, step)) + return false; + + if (step > 1.) // Ray start to end actually did not hit. + return false; + + float depth = (step + float(ray.depth)); + if (ray.hit && (ray.hit_depth <= depth)) + return false; + + ray.hit = true; + ray.hit_position = ray.position + ray.direction * step; + ray.hit_depth = depth; + ray.hit_color = material.color; + + return true; +} + +bool scene(inout ray_data ray) { + material_data box1; + material_data_constructor(box1); + box1.color = float4(0., 0., 0., 1.); + raymarch_box(ray, float3(0., -1., 2.), float3(0., 0., 0.), float3(1., 1., 1.), box1); + + material_data sphere1; + material_data_constructor(sphere1); + sphere1.color = float4(1., 0., 0., 1.); + raymarch_sphere(ray, float3(-1., 0., 1.), float3(0., 0., 0.), 0.5, sphere1); + + material_data sphere2; + material_data_constructor(sphere2); + sphere2.color = float4(0., 0., 1., 1.); + raymarch_sphere(ray, float3(1., 0., 1.), float3(0., 0., 0.), 0.5, sphere2); + + return ray.hit; +} + +bool raymarch(inout ray_data ray) { + // Simulate hitting a sphere. + if (ray.depth >= RayMarchAccuracy) { + return false; + } + + for (; (ray.depth < MAX_ACCURACY) && (ray.depth < RayMarchAccuracy); ray.depth++) { + if (scene(ray)) + break; + + ray.position = ray.position + ray.direction; + } + + return ray.hit; +} + +float4 pass1_ps(default_data data) : TARGET { + // Set up camera. + ray_data ray = initialize_camera_ray(data.uv); + // Raymarch + raymarch(ray); + // Finally just return the color + //return float4(ray.hit_depth / float(CameraRange.y), float(ray.depth) / float(RayMarchAccuracy), 0., 1.); + return ray.hit_color; +} + +//----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- +technique Draw { + pass + { + vertex_shader = default_vs(data); + pixel_shader = pass1_ps(data); + } +} \ No newline at end of file