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); } }