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.
This commit is contained in:
Michael Fabian 'Xaymar' Dirks 2020-01-01 16:02:45 +01:00
parent 3062d3b331
commit 3432ab4c1d

View file

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