Add shader file
This commit is contained in:
@@ -1,5 +1 @@
|
||||
Build:
|
||||
`node Kinc/make -g direct3d12 --raytrace dxr`
|
||||
|
||||
Compile shader:
|
||||
`.\dxc.exe -Zpr -Fo ..\..\Bundled\raytrace\raytrace.cso -T lib_6_3 .\raytrace.hlsl`
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
|
||||
struct Vertex {
|
||||
float3 position;
|
||||
float3 normal;
|
||||
float2 tex;
|
||||
};
|
||||
|
||||
struct RayGenConstantBuffer {
|
||||
float4 eye; // xyz, frame
|
||||
float4x4 inv_vp;
|
||||
};
|
||||
|
||||
struct RayPayload {
|
||||
float4 color;
|
||||
float3 ray_origin;
|
||||
float3 ray_dir;
|
||||
};
|
||||
|
||||
RWTexture2D<float4> render_target : register(u0);
|
||||
RaytracingAccelerationStructure scene : register(t0);
|
||||
ByteAddressBuffer indices : register(t1);
|
||||
StructuredBuffer<Vertex> vertices : register(t2);
|
||||
ConstantBuffer<RayGenConstantBuffer> constant_buffer : register(b0);
|
||||
|
||||
Texture2D<float4> mytexture0 : register(t3);
|
||||
Texture2D<float4> mytexture1 : register(t4);
|
||||
Texture2D<float4> mytexture2 : register(t5);
|
||||
Texture2D<float4> mytexture_env : register(t6);
|
||||
|
||||
static uint seed;
|
||||
static const float PI = 3.1415926535f;
|
||||
|
||||
void create_basis(float3 normal, out float3 tangent, out float3 binormal) {
|
||||
tangent = abs(normal.x) > abs(normal.y) ?
|
||||
normalize(float3(0., normal.z, -normal.y)) :
|
||||
normalize(float3(-normal.z, 0., normal.x));
|
||||
binormal = cross(normal, tangent);
|
||||
}
|
||||
|
||||
uint wang_hash(uint seed) {
|
||||
seed = (seed ^ 61) ^ (seed >> 16);
|
||||
seed *= 9;
|
||||
seed = seed ^ (seed >> 4);
|
||||
seed *= 0x27d4eb2d;
|
||||
seed = seed ^ (seed >> 15);
|
||||
return seed;
|
||||
}
|
||||
|
||||
void compute_seed(uint index, uint iteration, uint depth) {
|
||||
seed = uint(wang_hash((1 << 31) | (depth << 22) | iteration) ^ wang_hash(index));
|
||||
}
|
||||
|
||||
float rand() {
|
||||
static const float png_01_convert = (1.0f / 4294967296.0f);
|
||||
seed ^= uint(seed << 13);
|
||||
seed ^= uint(seed >> 17);
|
||||
seed ^= uint(seed << 5);
|
||||
return float(seed * png_01_convert);
|
||||
}
|
||||
|
||||
float schlick(float cosine, float ri) {
|
||||
float r0 = (1 - ri) / (1 + ri);
|
||||
r0 = r0 * r0;
|
||||
return r0 + (1 - r0) * pow(saturate(1 - cosine), 5);
|
||||
}
|
||||
|
||||
float schlick_weight(float cosTheta) {
|
||||
float m = clamp(1. - cosTheta, 0., 1.);
|
||||
float m2 = m * m;
|
||||
return m2 * m2 * m;
|
||||
}
|
||||
|
||||
float2 calculate_concentric_sample_disk(float u, float v) {
|
||||
// Maps a (u,v) in [0, 1)^2 to a 2D unit disk centered at (0,0). Based on PBRT.
|
||||
float2 u_offset = 2.0f * float2(u, v) - float2(1, 1);
|
||||
if (u_offset.x == 0 && u_offset.y == 0) {
|
||||
return float2(0.0f, 0.0f);
|
||||
}
|
||||
float theta, r;
|
||||
if (abs(u_offset.x) > abs(u_offset.y)) {
|
||||
r = u_offset.x;
|
||||
theta = PI / 4 * (u_offset.y / u_offset.x);
|
||||
}
|
||||
else {
|
||||
r = u_offset.y;
|
||||
theta = (PI / 2) - (PI / 4 * (u_offset.x / u_offset.y));
|
||||
}
|
||||
return r * float2(cos(theta), sin(theta));
|
||||
}
|
||||
|
||||
void generate_camera_ray(float2 screen_pos, out float3 ray_origin, out float3 ray_dir) {
|
||||
screen_pos.y = -screen_pos.y;
|
||||
float4 world = mul(float4(screen_pos, 0, 1), constant_buffer.inv_vp);
|
||||
world.xyz /= world.w;
|
||||
ray_origin = constant_buffer.eye.xyz;
|
||||
ray_dir = normalize(world.xyz - ray_origin);
|
||||
|
||||
// Depth of Field
|
||||
// float lens_rad = 0.005f;
|
||||
// float focal_dist = 0.4f;
|
||||
// float3 plens = float3(lens_rad * calculate_concentric_sample_disk(rand(), rand()), 0.0f);
|
||||
// float ft = focal_dist / abs(ray_dir.z);
|
||||
// float3 pfocus = ray_dir * ft;
|
||||
// ray_origin += plens;
|
||||
// ray_dir = normalize(pfocus - plens);
|
||||
}
|
||||
|
||||
[shader("raygeneration")]
|
||||
void raygeneration() {
|
||||
uint2 sample_point = DispatchRaysIndex().xy;
|
||||
uint2 sample_dim = DispatchRaysDimensions().xy;
|
||||
uint id = sample_point.x + sample_dim.x * sample_point.y;
|
||||
const int depth = 5;
|
||||
compute_seed(id, constant_buffer.eye.w, depth);
|
||||
|
||||
float2 xy = DispatchRaysIndex().xy + 0.5f;
|
||||
xy.x += rand(); // AA
|
||||
xy.y += rand();
|
||||
float2 screen_pos = xy / DispatchRaysDimensions().xy * 2.0 - 1.0;
|
||||
|
||||
RayPayload payload;
|
||||
payload.color = float4(1, 1, 1, -1);
|
||||
generate_camera_ray(screen_pos, payload.ray_origin, payload.ray_dir);
|
||||
|
||||
RayDesc ray;
|
||||
ray.TMin = 0.01;
|
||||
ray.TMax = 10.0;
|
||||
ray.Origin = payload.ray_origin;
|
||||
ray.Direction = payload.ray_dir;
|
||||
|
||||
for (int i = 0; i < depth; ++i) {
|
||||
TraceRay(scene, RAY_FLAG_FORCE_OPAQUE, ~0, 0, 1, 0, ray, payload);
|
||||
compute_seed(id, constant_buffer.eye.w, i);
|
||||
|
||||
if (payload.color.a != 0) break;
|
||||
ray.Origin = payload.ray_origin;
|
||||
ray.Direction = payload.ray_dir;
|
||||
}
|
||||
|
||||
float3 color = float3(render_target[DispatchRaysIndex().xy].xyz);
|
||||
if (constant_buffer.eye.w == 1.0) color = float3(0, 0, 0);
|
||||
float a = 1.0 / constant_buffer.eye.w;
|
||||
float b = 1.0 - a;
|
||||
|
||||
color = color * b + payload.color.xyz * a;
|
||||
render_target[DispatchRaysIndex().xy] = float4(color.xyz, 0.0f);
|
||||
}
|
||||
|
||||
float3 hit_world_position() {
|
||||
return WorldRayOrigin() + RayTCurrent() * WorldRayDirection();
|
||||
}
|
||||
|
||||
float3 hit_attribute(float3 vertexAttribute[3], BuiltInTriangleIntersectionAttributes attr) {
|
||||
return vertexAttribute[0] +
|
||||
attr.barycentrics.x * (vertexAttribute[1] - vertexAttribute[0]) +
|
||||
attr.barycentrics.y * (vertexAttribute[2] - vertexAttribute[0]);
|
||||
}
|
||||
|
||||
float2 hit_attribute2d(float2 vertexAttribute[3], BuiltInTriangleIntersectionAttributes attr) {
|
||||
return vertexAttribute[0] +
|
||||
attr.barycentrics.x * (vertexAttribute[1] - vertexAttribute[0]) +
|
||||
attr.barycentrics.y * (vertexAttribute[2] - vertexAttribute[0]);
|
||||
}
|
||||
|
||||
float3 cos_weighted_random_hemisphere_direction(float3 n) {
|
||||
float2 r = float2(rand(), rand());
|
||||
float3 uu = normalize(cross(n, float3(0.0, 1.0, 1.0)));
|
||||
float3 vv = cross(uu, n);
|
||||
float ra = sqrt(r.y);
|
||||
float rx = ra * cos(6.2831 * r.x);
|
||||
float ry = ra * sin(6.2831 * r.x);
|
||||
float rz = sqrt(1.0 - r.y);
|
||||
float3 rr = float3(rx * uu + ry * vv + rz * n);
|
||||
return normalize(rr);
|
||||
}
|
||||
|
||||
[shader("closesthit")]
|
||||
void closesthit(inout RayPayload payload, in BuiltInTriangleIntersectionAttributes attr) {
|
||||
uint2 sample_point = DispatchRaysIndex().xy;
|
||||
uint2 sample_dim = DispatchRaysDimensions().xy;
|
||||
uint id = sample_point.x + sample_dim.x * sample_point.y;
|
||||
compute_seed(id, constant_buffer.eye.w, 0);
|
||||
|
||||
float3 hit_position = hit_world_position();
|
||||
|
||||
const uint triangleIndexStride = 12; // 3 * 4
|
||||
uint base_index = PrimitiveIndex() * triangleIndexStride;
|
||||
uint3 indices_sample = indices.Load3(base_index);
|
||||
|
||||
float3 vertex_normals[3] = {
|
||||
float3(vertices[indices_sample[0]].normal),
|
||||
float3(vertices[indices_sample[1]].normal),
|
||||
float3(vertices[indices_sample[2]].normal)
|
||||
};
|
||||
float3 n = normalize(hit_attribute(vertex_normals, attr));
|
||||
|
||||
float2 vertex_uvs[3] = {
|
||||
float2(vertices[indices_sample[0]].tex),
|
||||
float2(vertices[indices_sample[1]].tex),
|
||||
float2(vertices[indices_sample[2]].tex)
|
||||
};
|
||||
float2 tex_coord = hit_attribute2d(vertex_uvs, attr);
|
||||
|
||||
float emittance = 0.0;
|
||||
float3 texpaint0 = mytexture0.Load(uint3(tex_coord * 2048, 0)).rgb;
|
||||
float3 texpaint1 = mytexture1.Load(uint3(tex_coord * 2048, 0)).rgb;
|
||||
float3 texpaint2 = mytexture2.Load(uint3(tex_coord * 2048, 0)).rgb;
|
||||
if (hit_position.z > 0.99) {
|
||||
emittance = 1.0;
|
||||
texpaint0 = float3(5, 5, 5); // temp
|
||||
}
|
||||
float3 color = payload.color.rgb * texpaint0.rgb;
|
||||
|
||||
if (emittance == 0.0f) {
|
||||
if (texpaint2.b >= 0.99) {
|
||||
payload.ray_dir = lerp(reflect(WorldRayDirection(), n), cos_weighted_random_hemisphere_direction(n), texpaint2.g);
|
||||
}
|
||||
// else if (texpaint2.matid == glass) {
|
||||
// float indexOfRefraction = 1.0f;
|
||||
// // adjust eta & normal according to direction of ray (inside or outside mat)
|
||||
// bool inside = dot(WorldRayDirection(), n) > 0.f;
|
||||
// float3 tempNormal = n * (inside ? -1.0f : 1.0f);
|
||||
// float eta = inside ? indexOfRefraction : (1.0f / indexOfRefraction);
|
||||
// // normal refraction
|
||||
// float3 newDir = refract(WorldRayDirection(), tempNormal, eta);
|
||||
// // internal total reflection
|
||||
// if (length(newDir) < 0.01f) {
|
||||
// color *= 0;
|
||||
// newDir = reflect(WorldRayDirection(), n);
|
||||
// }
|
||||
// // use schlick's approx
|
||||
// float schlick_0 = pow((inside ? indexOfRefraction - 1.0f : 1.0f - indexOfRefraction) /
|
||||
// (1.0f + indexOfRefraction), 2.0f);
|
||||
// float schlick_coef = schlick_0 +
|
||||
// (1 - schlick_0) * pow(1 - max(0.0f, dot(WorldRayDirection(), n)), 5);
|
||||
// // based on coef, pick either a refraction or reflection
|
||||
// newDir = schlick_coef < rand() ? reflect(WorldRayDirection(), n) : newDir;
|
||||
// payload.ray_dir = newDir;
|
||||
// }
|
||||
else {
|
||||
// TEMP
|
||||
float fresnel = schlick(dot(n, payload.ray_dir), 1.35) * (1.0 - texpaint2.g);
|
||||
if (rand() > fresnel) {
|
||||
payload.ray_dir = cos_weighted_random_hemisphere_direction(n);
|
||||
|
||||
// float3 wo = -payload.ray_dir;
|
||||
// payload.ray_dir = cos_weighted_random_hemisphere_direction(n);
|
||||
// float3 tangent = float3(0, 0, 0);
|
||||
// float3 binormal = float3(0, 0, 0);
|
||||
// create_basis(n, tangent, binormal);
|
||||
// float3 wi = payload.ray_dir.x * tangent + payload.ray_dir.y * binormal + payload.ray_dir.z * n;
|
||||
// float dotNL = dot(n, wo);
|
||||
// float dotNV = dot(n, wi);
|
||||
// float3 H = normalize(wo + wi);
|
||||
// float dotLH = dot(wo, H);
|
||||
// float FL = schlick_weight(dotNL);
|
||||
// float FV = schlick_weight(dotNV);
|
||||
// float Fss90 = dotLH * dotLH * texpaint2.g;
|
||||
// float Fss = lerp(1.0, Fss90, FL) * lerp(1.0, Fss90, FV);
|
||||
// float ss = 1.25 * (Fss * (1.0 / (dotNL + dotNV) - 0.5) + 0.5);
|
||||
// color = (1 / PI) * ss * color;
|
||||
// if (dotNL < 0.0 || dotNV < 0.0) color = float3(0, 0, 0);
|
||||
}
|
||||
else {
|
||||
payload.ray_dir = reflect(WorldRayDirection(), n);
|
||||
}
|
||||
}
|
||||
payload.ray_origin = hit_position + payload.ray_dir * 0.0001f;
|
||||
}
|
||||
|
||||
payload.color = float4(color.xyz, emittance);
|
||||
}
|
||||
|
||||
float2 equirect(float3 normal) {
|
||||
const float PI = 3.1415926535;
|
||||
const float PI2 = PI * 2.0;
|
||||
float phi = acos(normal.z);
|
||||
float theta = atan2(-normal.y, normal.x) + PI;
|
||||
return float2(theta / PI2, phi / PI);
|
||||
}
|
||||
|
||||
[shader("miss")]
|
||||
void miss(inout RayPayload payload) {
|
||||
float2 tex_coord = equirect(normalize(payload.ray_dir));
|
||||
float3 texenv = mytexture_env.Load(uint3(tex_coord.x * 1024, tex_coord.y * 512, 0)).rgb * 3;
|
||||
payload.color = float4(payload.color.rgb * texenv.rgb, -1);
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user