rt: add alpha test to primary rays

105us, 57(64)v, 60(128)s, 2048l, 16/16
This commit is contained in:
Ivan Avdeev 2022-01-08 17:21:04 -08:00
parent 3453a2f563
commit 090fb3bfbb
5 changed files with 160 additions and 118 deletions

View File

@ -6,119 +6,19 @@
#include "ray_kusochki.glsl"
layout (constant_id = 6) const uint MAX_TEXTURES = 4096;
layout(constant_id = 6) const uint MAX_TEXTURES = 4096;
layout(set = 0, binding = 6) uniform sampler2D textures[MAX_TEXTURES];
layout(set = 0, binding = 2) uniform UBO { UniformBuffer ubo; };
layout(location = PAYLOAD_LOCATION_PRIMARY) rayPayloadInEXT RayPayloadPrimary payload;
hitAttributeEXT vec2 bary;
vec3 baryMix(vec3 v1, vec3 v2, vec3 v3, vec2 bary) {
return v1 * (1. - bary.x - bary.y) + v2 * bary.x + v3 * bary.y;
}
vec2 baryMix(vec2 v1, vec2 v2, vec2 v3, vec2 bary) {
return v1 * (1. - bary.x - bary.y) + v2 * bary.x + v3 * bary.y;
}
#include "rt_geometry.glsl"
vec4 sampleTexture(uint tex_index, vec2 uv, vec4 uv_lods) {
return textureGrad(textures[nonuniformEXT(tex_index)], uv, uv_lods.xy, uv_lods.zw);
}
// Taken from Journal of Computer Graphics Techniques, Vol. 10, No. 1, 2021.
// Improved Shader and Texture Level of Detail Using Ray Cones,
// by T. Akenine-Moller, C. Crassin, J. Boksansky, L. Belcour, A. Panteleev, and O. Wright
// https://jcgt.org/published/0010/01/01/
// P is the intersection point
// f is the triangle normal
// d is the ray cone direction
vec4 computeAnisotropicEllipseAxes(in vec3 P, in vec3 f,
in vec3 d, in float rayConeRadiusAtIntersection,
in vec3 positions[3], in vec2 txcoords[3],
in vec2 interpolatedTexCoordsAtIntersection)
{
vec4 texGradient;
// Compute ellipse axes.
vec3 a1 = d - dot(f, d) * f;
vec3 p1 = a1 - dot(d, a1) * d;
a1 *= rayConeRadiusAtIntersection / max(0.0001, length(p1));
vec3 a2 = cross(f, a1);
vec3 p2 = a2 - dot(d, a2) * d;
a2 *= rayConeRadiusAtIntersection / max(0.0001, length(p2));
// Compute texture coordinate gradients.
vec3 eP, delta = P - positions[0];
vec3 e1 = positions[1] - positions[0];
vec3 e2 = positions[2] - positions[0];
float oneOverAreaTriangle = 1.0 / dot(f, cross(e1, e2));
eP = delta + a1;
float u1 = dot(f, cross(eP, e2)) * oneOverAreaTriangle;
float v1 = dot(f, cross(e1, eP)) * oneOverAreaTriangle;
texGradient.xy = (1.0-u1-v1) * txcoords[0] + u1 * txcoords[1] +
v1 * txcoords[2] - interpolatedTexCoordsAtIntersection;
eP = delta + a2;
float u2 = dot(f, cross(eP, e2)) * oneOverAreaTriangle;
float v2 = dot(f, cross(e1, eP)) * oneOverAreaTriangle;
texGradient.zw = (1.0-u2-v2) * txcoords[0] + u2 * txcoords[1] +
v2 * txcoords[2] - interpolatedTexCoordsAtIntersection;
return texGradient;
}
struct Geometry {
vec3 pos;
vec2 uv;
vec4 uv_lods;
vec3 normal_geometry;
vec3 normal_shading;
int kusok_index;
};
Geometry readHitGeometry() {
Geometry geom;
const int instance_kusochki_offset = gl_InstanceCustomIndexEXT;
geom.kusok_index = instance_kusochki_offset + gl_GeometryIndexEXT;
const Kusok kusok = kusochki[geom.kusok_index];
const uint first_index_offset = kusok.index_offset + gl_PrimitiveID * 3;
const uint vi1 = uint(indices[first_index_offset+0]) + kusok.vertex_offset;
const uint vi2 = uint(indices[first_index_offset+1]) + kusok.vertex_offset;
const uint vi3 = uint(indices[first_index_offset+2]) + kusok.vertex_offset;
const vec3 pos[3] = {
gl_ObjectToWorldEXT * vec4(vertices[vi1].pos, 1.f),
gl_ObjectToWorldEXT * vec4(vertices[vi2].pos, 1.f),
gl_ObjectToWorldEXT * vec4(vertices[vi3].pos, 1.f),
};
const vec2 uvs[3] = {
vertices[vi1].gl_tc,
vertices[vi2].gl_tc,
vertices[vi3].gl_tc,
};
geom.pos = baryMix(pos[0], pos[1], pos[2], bary);
geom.uv = baryMix(uvs[0], uvs[1], uvs[2], bary);
//TODO or not TODO? const vec2 texture_uv = texture_uv_stationary + push_constants.time * kusok.uv_speed;
// NOTE: need to flip if back-facing
geom.normal_geometry = normalize(cross(pos[2]-pos[0], pos[1]-pos[0]));
// NOTE: only support rotations, for arbitrary transform would need to do transpose(inverse(mat3(gl_ObjectToWorldEXT)))
const mat3 normalTransform = mat3(gl_ObjectToWorldEXT);
geom.normal_shading = normalTransform * baryMix(
vertices[vi1].normal,
vertices[vi2].normal,
vertices[vi3].normal,
bary);
geom.uv_lods = computeAnisotropicEllipseAxes(geom.pos, geom.normal_geometry, gl_WorldRayDirectionEXT, ubo.ray_cone_width * gl_HitTEXT, pos, uvs, geom.uv);
return geom;
}
void main() {
const Geometry geom = readHitGeometry();

View File

@ -3,8 +3,9 @@
#include "ray_primary_common.glsl"
layout(set = 0, binding = 0, rgba8) uniform image2D out_image_base_color_r;
layout(set = 0, binding = 0, rgba8) uniform image2D out_image_base_color_a;
layout(set = 0, binding = 7, rgba32f) uniform image2D out_image_position_t;
//layout(set = 0, binding = 8, rgba8) uniform image2D out_image_rough_metal;
layout(set = 0, binding = 1) uniform accelerationStructureEXT tlas;
layout(set = 0, binding = 2) uniform UBO { UniformBuffer ubo; };
@ -29,5 +30,5 @@ void main() {
PAYLOAD_LOCATION_PRIMARY);
imageStore(out_image_position_t, ivec2(gl_LaunchIDEXT.xy), payload.hit_t);
imageStore(out_image_base_color_r, ivec2(gl_LaunchIDEXT.xy), payload.base_color_a);
imageStore(out_image_base_color_a, ivec2(gl_LaunchIDEXT.xy), payload.base_color_a);
}

View File

@ -0,0 +1,26 @@
#version 460 core
#extension GL_EXT_nonuniform_qualifier : enable
#extension GL_GOOGLE_include_directive : require
#include "ray_primary_common.glsl"
#include "ray_kusochki.glsl"
layout(constant_id = 6) const uint MAX_TEXTURES = 4096;
layout(set = 0, binding = 6) uniform sampler2D textures[MAX_TEXTURES];
layout(set = 0, binding = 2) uniform UBO { UniformBuffer ubo; };
hitAttributeEXT vec2 bary;
#include "rt_geometry.glsl"
const float alpha_mask_threshold = .1f;
void main() {
const Geometry geom = readHitGeometry();
const uint tex_index = kusochki[geom.kusok_index].tex_base_color;
const vec4 texture_color = texture(textures[nonuniformEXT(tex_index)], geom.uv);
if (texture_color.a < alpha_mask_threshold) {
ignoreIntersectionEXT;
}
}

View File

@ -0,0 +1,101 @@
vec3 baryMix(vec3 v1, vec3 v2, vec3 v3, vec2 bary) {
return v1 * (1. - bary.x - bary.y) + v2 * bary.x + v3 * bary.y;
}
vec2 baryMix(vec2 v1, vec2 v2, vec2 v3, vec2 bary) {
return v1 * (1. - bary.x - bary.y) + v2 * bary.x + v3 * bary.y;
}
// Taken from Journal of Computer Graphics Techniques, Vol. 10, No. 1, 2021.
// Improved Shader and Texture Level of Detail Using Ray Cones,
// by T. Akenine-Moller, C. Crassin, J. Boksansky, L. Belcour, A. Panteleev, and O. Wright
// https://jcgt.org/published/0010/01/01/
// P is the intersection point
// f is the triangle normal
// d is the ray cone direction
vec4 computeAnisotropicEllipseAxes(in vec3 P, in vec3 f,
in vec3 d, in float rayConeRadiusAtIntersection,
in vec3 positions[3], in vec2 txcoords[3],
in vec2 interpolatedTexCoordsAtIntersection)
{
vec4 texGradient;
// Compute ellipse axes.
vec3 a1 = d - dot(f, d) * f;
vec3 p1 = a1 - dot(d, a1) * d;
a1 *= rayConeRadiusAtIntersection / max(0.0001, length(p1));
vec3 a2 = cross(f, a1);
vec3 p2 = a2 - dot(d, a2) * d;
a2 *= rayConeRadiusAtIntersection / max(0.0001, length(p2));
// Compute texture coordinate gradients.
vec3 eP, delta = P - positions[0];
vec3 e1 = positions[1] - positions[0];
vec3 e2 = positions[2] - positions[0];
float oneOverAreaTriangle = 1.0 / dot(f, cross(e1, e2));
eP = delta + a1;
float u1 = dot(f, cross(eP, e2)) * oneOverAreaTriangle;
float v1 = dot(f, cross(e1, eP)) * oneOverAreaTriangle;
texGradient.xy = (1.0-u1-v1) * txcoords[0] + u1 * txcoords[1] +
v1 * txcoords[2] - interpolatedTexCoordsAtIntersection;
eP = delta + a2;
float u2 = dot(f, cross(eP, e2)) * oneOverAreaTriangle;
float v2 = dot(f, cross(e1, eP)) * oneOverAreaTriangle;
texGradient.zw = (1.0-u2-v2) * txcoords[0] + u2 * txcoords[1] +
v2 * txcoords[2] - interpolatedTexCoordsAtIntersection;
return texGradient;
}
struct Geometry {
vec3 pos;
vec2 uv;
vec4 uv_lods;
vec3 normal_geometry;
vec3 normal_shading;
int kusok_index;
};
Geometry readHitGeometry() {
Geometry geom;
const int instance_kusochki_offset = gl_InstanceCustomIndexEXT;
geom.kusok_index = instance_kusochki_offset + gl_GeometryIndexEXT;
const Kusok kusok = kusochki[geom.kusok_index];
const uint first_index_offset = kusok.index_offset + gl_PrimitiveID * 3;
const uint vi1 = uint(indices[first_index_offset+0]) + kusok.vertex_offset;
const uint vi2 = uint(indices[first_index_offset+1]) + kusok.vertex_offset;
const uint vi3 = uint(indices[first_index_offset+2]) + kusok.vertex_offset;
const vec3 pos[3] = {
gl_ObjectToWorldEXT * vec4(vertices[vi1].pos, 1.f),
gl_ObjectToWorldEXT * vec4(vertices[vi2].pos, 1.f),
gl_ObjectToWorldEXT * vec4(vertices[vi3].pos, 1.f),
};
const vec2 uvs[3] = {
vertices[vi1].gl_tc,
vertices[vi2].gl_tc,
vertices[vi3].gl_tc,
};
geom.pos = baryMix(pos[0], pos[1], pos[2], bary);
geom.uv = baryMix(uvs[0], uvs[1], uvs[2], bary);
//TODO or not TODO? const vec2 texture_uv = texture_uv_stationary + push_constants.time * kusok.uv_speed;
// NOTE: need to flip if back-facing
geom.normal_geometry = normalize(cross(pos[2]-pos[0], pos[1]-pos[0]));
// NOTE: only support rotations, for arbitrary transform would need to do transpose(inverse(mat3(gl_ObjectToWorldEXT)))
const mat3 normalTransform = mat3(gl_ObjectToWorldEXT);
geom.normal_shading = normalTransform * baryMix(
vertices[vi1].normal,
vertices[vi2].normal,
vertices[vi3].normal,
bary);
geom.uv_lods = computeAnisotropicEllipseAxes(geom.pos, geom.normal_geometry, gl_WorldRayDirectionEXT, ubo.ray_cone_width * gl_HitTEXT, pos, uvs, geom.uv);
return geom;
}

View File

@ -1,4 +1,5 @@
#include "vk_ray_primary.h"
#include "vk_ray_internal.h"
#include "vk_descriptor.h"
#include "vk_pipeline.h"
@ -8,8 +9,13 @@
enum {
RtPrim_SBT_RayGen,
RtPrim_SBT_RayMiss,
RtPrim_SBT_RayHit,
RtPrim_SBT_RayHit_WithAlphaTest,
RtPrim_SBT_RayHit_END = RtPrim_SBT_RayHit_WithAlphaTest,
RtPrim_SBT_COUNT,
};
@ -98,6 +104,7 @@ static VkPipeline createPipeline( void ) {
ShaderStageIndex_RayGen,
ShaderStageIndex_RayMiss,
ShaderStageIndex_RayClosestHit,
ShaderStageIndex_RayAnyHit_AlphaTest,
ShaderStageIndex_COUNT,
};
@ -126,22 +133,20 @@ static VkPipeline createPipeline( void ) {
DEFINE_SHADER("ray_primary.rgen", RAYGEN, ShaderStageIndex_RayGen);
DEFINE_SHADER("ray_primary.rchit", CLOSEST_HIT, ShaderStageIndex_RayClosestHit);
DEFINE_SHADER("ray_primary_alphatest.rahit", ANY_HIT, ShaderStageIndex_RayAnyHit_AlphaTest);
DEFINE_SHADER("ray_primary.rmiss", MISS, ShaderStageIndex_RayMiss);
// TODO static assert
/* #define ASSERT_SHADER_OFFSET(sbt_kind, sbt_index, offset) \ */
/* ASSERT((offset) == (sbt_index - sbt_kind)) */
/* */
/* ASSERT_SHADER_OFFSET(ShaderBindingTable_RayGen, ShaderBindingTable_RayGen, 0); */
/* ASSERT_SHADER_OFFSET(ShaderBindingTable_Miss, ShaderBindingTable_Miss, SHADER_OFFSET_MISS_REGULAR); */
/* ASSERT_SHADER_OFFSET(ShaderBindingTable_Miss, ShaderBindingTable_Miss_Shadow, SHADER_OFFSET_MISS_SHADOW); */
/* */
/* ASSERT_SHADER_OFFSET(ShaderBindingTable_Hit_Base, ShaderBindingTable_Hit_Base, SHADER_OFFSET_HIT_REGULAR_BASE + SHADER_OFFSET_HIT_REGULAR); */
/* ASSERT_SHADER_OFFSET(ShaderBindingTable_Hit_Base, ShaderBindingTable_Hit_WithAlphaTest, SHADER_OFFSET_HIT_REGULAR_BASE + SHADER_OFFSET_HIT_ALPHA_TEST); */
/* ASSERT_SHADER_OFFSET(ShaderBindingTable_Hit_Base, ShaderBindingTable_Hit_Additive, SHADER_OFFSET_HIT_REGULAR_BASE + SHADER_OFFSET_HIT_ADDITIVE); */
/* */
/* ASSERT_SHADER_OFFSET(ShaderBindingTable_Hit_Base, ShaderBindingTable_Hit_Shadow_Base, SHADER_OFFSET_HIT_SHADOW_BASE + 0); */
/* ASSERT_SHADER_OFFSET(ShaderBindingTable_Hit_Base, ShaderBindingTable_Hit_Shadow_AlphaTest, SHADER_OFFSET_HIT_SHADOW_BASE + SHADER_OFFSET_HIT_ALPHA_TEST); */
#define ASSERT_SHADER_OFFSET(sbt_kind, sbt_index, offset) \
ASSERT((offset) == (sbt_index - sbt_kind))
ASSERT_SHADER_OFFSET(RtPrim_SBT_RayGen, RtPrim_SBT_RayGen, 0);
ASSERT_SHADER_OFFSET(RtPrim_SBT_RayMiss, RtPrim_SBT_RayMiss, SHADER_OFFSET_MISS_REGULAR);
ASSERT_SHADER_OFFSET(RtPrim_SBT_RayHit, RtPrim_SBT_RayHit, SHADER_OFFSET_HIT_REGULAR_BASE + SHADER_OFFSET_HIT_REGULAR);
ASSERT_SHADER_OFFSET(RtPrim_SBT_RayHit, RtPrim_SBT_RayHit_WithAlphaTest, SHADER_OFFSET_HIT_REGULAR_BASE + SHADER_OFFSET_HIT_ALPHA_TEST);
//ASSERT_SHADER_OFFSET(ShaderBindingTable_Hit_Base, ShaderBindingTable_Hit_Additive, SHADER_OFFSET_HIT_REGULAR_BASE + SHADER_OFFSET_HIT_ADDITIVE);
shader_groups[RtPrim_SBT_RayGen] = (VkRayTracingShaderGroupCreateInfoKHR) {
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
@ -161,6 +166,15 @@ static VkPipeline createPipeline( void ) {
.intersectionShader = VK_SHADER_UNUSED_KHR,
};
shader_groups[RtPrim_SBT_RayHit_WithAlphaTest] = (VkRayTracingShaderGroupCreateInfoKHR) {
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR,
.anyHitShader = ShaderStageIndex_RayAnyHit_AlphaTest,
.closestHitShader = ShaderStageIndex_RayClosestHit,
.generalShader = VK_SHADER_UNUSED_KHR,
.intersectionShader = VK_SHADER_UNUSED_KHR,
};
shader_groups[RtPrim_SBT_RayMiss] = (VkRayTracingShaderGroupCreateInfoKHR) {
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR,
@ -292,7 +306,7 @@ void XVK_RayTracePrimary( VkCommandBuffer cmdbuf, const xvk_ray_trace_primary_t
}
const VkStridedDeviceAddressRegionKHR sbt_raygen = SBT_INDEX(RtPrim_SBT_RayGen, 1);
const VkStridedDeviceAddressRegionKHR sbt_miss = SBT_INDEX(RtPrim_SBT_RayMiss, 1); //ShaderBindingTable_Miss_Empty - ShaderBindingTable_Miss);
const VkStridedDeviceAddressRegionKHR sbt_hit = SBT_INDEX(RtPrim_SBT_RayHit, 1); //ShaderBindingTable_Hit__END - ShaderBindingTable_Hit_Base);
const VkStridedDeviceAddressRegionKHR sbt_hit = SBT_INDEX(RtPrim_SBT_RayHit, RtPrim_SBT_RayHit_END - RtPrim_SBT_RayHit);
const VkStridedDeviceAddressRegionKHR sbt_callable = { 0 };
vkCmdTraceRaysKHR(cmdbuf, &sbt_raygen, &sbt_miss, &sbt_hit, &sbt_callable, args->width, args->height, 1 );