Blue noise sampling

This commit is contained in:
luboslenco
2019-09-27 15:38:24 +02:00
parent eee807137e
commit 76099c24b6
8 changed files with 471 additions and 265 deletions
Binary file not shown.
Binary file not shown.
+1
View File
@@ -1,2 +1,3 @@
.\dxc.exe -Zpr -Fo ..\raytrace.cso -T lib_6_3 .\raytrace.hlsl
.\dxc.exe -Zpr -Fo ..\raytrace_brute.cso -T lib_6_3 .\raytrace_brute.hlsl
.\dxc.exe -Zpr -Fo ..\raytrace_bake.cso -T lib_6_3 .\raytrace_bake.hlsl
+28
View File
@@ -0,0 +1,28 @@
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) {
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);
}
+101 -171
View File
@@ -11,7 +11,7 @@ struct RayGenConstantBuffer {
};
struct RayPayload {
float4 color;
float4 color; // rgb, frame
float3 ray_origin;
float3 ray_dir;
};
@@ -26,9 +26,42 @@ Texture2D<float4> mytexture0 : register(t3);
Texture2D<float4> mytexture1 : register(t4);
Texture2D<float4> mytexture2 : register(t5);
Texture2D<float4> mytexture_env : register(t6);
Texture2D<float4> mytexture_sobol : register(t7);
Texture2D<float4> mytexture_scramble : register(t8);
Texture2D<float4> mytexture_rank : register(t9);
static uint seed;
static const float PI = 3.1415926535f;
static const int DIFFUSE_RAYS = 64;
// A Low-Discrepancy Sampler that Distributes Monte Carlo Errors as a Blue Noise in Screen Space
// Eric Heitz, Laurent Belcour, Victor Ostromoukhov, David Coeurjolly and Jean-Claude Iehl
// https://eheitzresearch.wordpress.com/762-2/
float rand(int pixel_i, int pixel_j, int sampleIndex, int sampleDimension) {
// wrap arguments
pixel_i += constant_buffer.eye.w * 9;
pixel_j += constant_buffer.eye.w * 11;
pixel_i = pixel_i & 127;
pixel_j = pixel_j & 127;
sampleIndex = sampleIndex & 255;
sampleDimension = sampleDimension & 255;
// xor index based on optimized ranking
int i = sampleDimension + (pixel_i + pixel_j*128)*8;
int rankedSampleIndex = sampleIndex ^ int(mytexture_rank.Load(uint3(i % 128, uint(i / 128), 0)).r * 255);
// fetch value in sequence
i = sampleDimension + rankedSampleIndex*256;
int value = int(mytexture_sobol.Load(uint3(i % 256, uint(i / 256), 0)).r * 255);
// If the dimension is optimized, xor sequence value based on optimized scrambling
i = (sampleDimension%8) + (pixel_i + pixel_j*128)*8;
value = value ^ int(mytexture_scramble.Load(uint3(i % 128, uint(i / 128), 0)).r * 255);
// convert to float and return
float v = (0.5f+value)/256.0f;
return v;
}
void create_basis(float3 normal, out float3 tangent, out float3 binormal) {
tangent = abs(normal.x) > abs(normal.y) ?
@@ -37,113 +70,96 @@ void create_basis(float3 normal, out float3 tangent, out float3 binormal) {
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 m = saturate(1. - cosTheta);
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);
float3 cos_weighted_hemisphere_direction(float3 n, uint sample) {
float2 r = float2(
rand(DispatchRaysIndex().x, DispatchRaysIndex().y, sample, seed),
rand(DispatchRaysIndex().x, DispatchRaysIndex().y, sample, seed + 1)
);
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("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);
float3 accum = float3(0, 0, 0);
// AA
float2 xy = DispatchRaysIndex().xy + 0.5f;
xy.x += rand(); // AA
xy.y += rand();
float2 screen_pos = xy / DispatchRaysDimensions().xy * 2.0 - 1.0;
xy.x += rand(DispatchRaysIndex().x, DispatchRaysIndex().y, constant_buffer.eye.w, seed);
seed += 1;
xy.y += rand(DispatchRaysIndex().x, DispatchRaysIndex().y, constant_buffer.eye.w, seed);
RayPayload payload;
payload.color = float4(1, 1, 1, -1);
generate_camera_ray(screen_pos, payload.ray_origin, payload.ray_dir);
float2 screen_pos = xy / DispatchRaysDimensions().xy * 2.0 - 1.0;
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);
generate_camera_ray(screen_pos, ray.Origin, ray.Direction);
if (payload.color.a != 0) break;
ray.Origin = payload.ray_origin;
ray.Direction = payload.ray_dir;
RayPayload payload;
payload.color = float4(1, 1, 1, 0);
TraceRay(scene, RAY_FLAG_FORCE_OPAQUE, ~0, 0, 1, 0, ray, payload);
// if (payload.color.a < 0) {
// if (i == 0) {
// return;
// payload.color.rgb = float3(0.05, 0.05, 0.05);
// }
// }
ray.Origin = payload.ray_origin;
// ray.Direction = payload.ray_dir;
accum += payload.color.rgb;
if (payload.color.a >= 0) {
// DIFFUSE_RAYS
int rays = 64;//(int)(payload.color.a * DIFFUSE_RAYS);
float3 accum2 = float3(0, 0, 0);
float3 norr = payload.ray_dir;
for (int k = 0; k < rays; ++k) {
ray.Direction = cos_weighted_hemisphere_direction(norr, k);
payload.color = float4(1, 1, 1, (k + 1));
TraceRay(scene, RAY_FLAG_FORCE_OPAQUE, ~0, 0, 1, 0, ray, payload);
accum2 += payload.color.rgb;
}
accum += accum2 / rays;
}
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;
// float a = 1.0 / constant_buffer.eye.w;
// float b = 1.0 - a;
// color = color * b + (accum.xyz) * a;
// render_target[DispatchRaysIndex().xy] = float4(color.xyz, 0.0f);
color = color * b + payload.color.xyz * a;
render_target[DispatchRaysIndex().xy] = float4(color.xyz, 0.0f);
if (constant_buffer.eye.w == 0) {
color = accum.xyz;
}
render_target[DispatchRaysIndex().xy] = float4(lerp(color.xyz, accum.xyz, 1.0 / 4.0), 0.0f);
}
float3 hit_world_position() {
@@ -162,27 +178,8 @@ float2 hit_attribute2d(float2 vertexAttribute[3], BuiltInTriangleIntersectionAtt
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);
@@ -201,84 +198,17 @@ void closesthit(inout RayPayload payload, in BuiltInTriangleIntersectionAttribut
};
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);
if (dotNL < 0.0 || dotNV < 0.0) color = float3(0, 0, 0);
payload.ray_origin = hit_world_position() + n * 0.0001f;
payload.color.xyz = color.xyz;
// 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);
// DIFFUSE_RAYS
payload.ray_dir = n;
payload.color.a = texpaint2.g; // roughness
}
float2 equirect(float3 normal) {
@@ -291,7 +221,7 @@ float2 equirect(float3 normal) {
[shader("miss")]
void miss(inout RayPayload payload) {
float2 tex_coord = equirect(normalize(payload.ray_dir));
float2 tex_coord = equirect(WorldRayDirection());
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);
}
+249
View File
@@ -0,0 +1,249 @@
struct Vertex {
float3 position;
float3 normal;
float2 tex;
};
struct RayGenConstantBuffer {
float4 eye; // xyz, frame
float4x4 inv_vp;
};
struct RayPayload {
float4 color; // rgb, frame
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);
Texture2D<float4> mytexture_sobol : register(t7);
Texture2D<float4> mytexture_scramble : register(t8);
Texture2D<float4> mytexture_rank : register(t9);
static uint seed = 0;
static const float PI = 3.1415926535f;
static const int SAMPLES = 64;
static const int DEPTH = 3; // 3 - 5
static const int rrStart = 2;
static const float rrProbability = 0.5; // map to albedo
// A Low-Discrepancy Sampler that Distributes Monte Carlo Errors as a Blue Noise in Screen Space
// Eric Heitz, Laurent Belcour, Victor Ostromoukhov, David Coeurjolly and Jean-Claude Iehl
// https://eheitzresearch.wordpress.com/762-2/
float rand(int pixel_i, int pixel_j, int sampleIndex, int sampleDimension) {
// wrap arguments
pixel_i += constant_buffer.eye.w * 9;
pixel_j += constant_buffer.eye.w * 11;
pixel_i = pixel_i & 127;
pixel_j = pixel_j & 127;
sampleIndex = sampleIndex & 255;
sampleDimension = sampleDimension & 255;
// xor index based on optimized ranking
int i = sampleDimension + (pixel_i + pixel_j*128)*8;
int rankedSampleIndex = sampleIndex ^ int(mytexture_rank.Load(uint3(i % 128, uint(i / 128), 0)).r * 255);
// fetch value in sequence
i = sampleDimension + rankedSampleIndex*256;
int value = int(mytexture_sobol.Load(uint3(i % 256, uint(i / 256), 0)).r * 255);
// If the dimension is optimized, xor sequence value based on optimized scrambling
i = (sampleDimension%8) + (pixel_i + pixel_j*128)*8;
value = value ^ int(mytexture_scramble.Load(uint3(i % 128, uint(i / 128), 0)).r * 255);
// convert to float and return
float v = (0.5f+value)/256.0f;
return v;
}
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);
}
float schlick_weight(float cosTheta) {
float m = saturate(1. - cosTheta);
float m2 = m * m;
return m2 * m2 * m;
}
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);
}
[shader("raygeneration")]
void raygeneration() {
float3 accum = float3(0, 0, 0);
for (int j = 0; j < SAMPLES; ++j) {
// AA
float2 xy = DispatchRaysIndex().xy + 0.5f;
xy.x += rand(DispatchRaysIndex().x, DispatchRaysIndex().y, j, seed);
seed += 1;
xy.y += rand(DispatchRaysIndex().x, DispatchRaysIndex().y, j, seed);
float2 screen_pos = xy / DispatchRaysDimensions().xy * 2.0 - 1.0;
RayDesc ray;
ray.TMin = 0.01;
ray.TMax = 10.0;
generate_camera_ray(screen_pos, ray.Origin, ray.Direction);
RayPayload payload;
payload.color = float4(1, 1, 1, j);
for (int i = 0; i < DEPTH; ++i) {
float rrFactor = 1.0;
// if (i >= rrStart) {
// float f = rand(DispatchRaysIndex().x, DispatchRaysIndex().y, j, seed);
// if (f <= rrProbability) {
// break;
// }
// rrFactor = 1.0 / (1.0 - rrProbability);
// }
TraceRay(scene, RAY_FLAG_FORCE_OPAQUE, ~0, 0, 1, 0, ray, payload);
if (payload.color.a < 0) {
if (i == 0) {
// return;
payload.color.rgb = float3(0.05, 0.05, 0.05);
}
break;
}
payload.color.rgb *= rrFactor;
ray.Origin = payload.ray_origin;
ray.Direction = payload.ray_dir;
}
accum += payload.color.rgb;
}
float3 color = float3(render_target[DispatchRaysIndex().xy].xyz);
// float a = 1.0 / constant_buffer.eye.w;
// float b = 1.0 - a;
// color = color * b + (accum.xyz / SAMPLES) * a;
// render_target[DispatchRaysIndex().xy] = float4(color.xyz, 0.0f);
if (constant_buffer.eye.w == 0) {
color = accum.xyz / SAMPLES;
}
render_target[DispatchRaysIndex().xy] = float4(lerp(color.xyz, accum.xyz / SAMPLES, 1.0 / 4.0), 0.0f);
// render_target[DispatchRaysIndex().xy] = float4(accum.xyz / SAMPLES, 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_hemisphere_direction(float3 n, uint sample) {
float2 r = float2(
rand(DispatchRaysIndex().x, DispatchRaysIndex().y, sample, seed),
rand(DispatchRaysIndex().x, DispatchRaysIndex().y, sample, seed + 1)
);
seed += 1;
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) {
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);
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;
float3 color = payload.color.rgb * texpaint0.rgb;
if (texpaint2.b >= 0.99) {
payload.ray_dir = lerp(reflect(WorldRayDirection(), n), cos_weighted_hemisphere_direction(n, payload.color.a), texpaint2.g);
}
else {
float fresnel = schlick_weight(dot(n, WorldRayDirection())) * (1.0 - texpaint2.g);
float f = rand(DispatchRaysIndex().x, DispatchRaysIndex().y, payload.color.a, seed);
if (f > fresnel) {
float3 wo = -WorldRayDirection();
payload.ray_dir = cos_weighted_hemisphere_direction(n, payload.color.a);
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);
if (dotNL < 0.0 || dotNV < 0.0) color = float3(0, 0, 0);
}
else {
payload.ray_dir = reflect(WorldRayDirection(), n);
}
}
payload.ray_origin = hit_world_position() + payload.ray_dir * 0.0001f;
payload.color.xyz = color.xyz;
}
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(WorldRayDirection());
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);
}