rtx: add first iteration of material flags support

it's clearly suboptimal, but no worries, we'll iterate
This commit is contained in:
Ivan Avdeev 2021-08-26 09:18:20 -07:00 committed by Ivan Avdeev
parent 0259c09e9a
commit 8d06058aa4
13 changed files with 165 additions and 154 deletions

View File

@ -65,6 +65,9 @@
uint8_t data[];
# Planned
- [ ] improve nonuniformEXT usage: https://github.com/KhronosGroup/Vulkan-Samples/pull/243/files#diff-262568ff21d7a618c0069d6a4ddf78e715fe5326c71dd2f5cdf8fc8da929bc4eR31
- [ ] emissive beams
- [ ] emissive particles/sprites
- [ ] issue: transparent brushes are too transparent (train ride)
- [ ] (test_shaders_basic.bsp) shows that for brushes at least there are the following discrepancies with gl renderer:
- [ ] traditional:
@ -147,6 +150,7 @@
- [ ] sometimes it gets very slow (1fps) when ran under lldb (only on stream?)
- [ ] optimize perf: cmdbuf managements and semaphores, upload to gpu, ...
- [ ] rtx: studio models should not pre-transform vertices with modelView matrix
- [ ] include common headers with struct definitions from both shaders and c code
# Someday
- [ ] rtx: dynamic rtx/non-rtx switching breaks dynamic models

View File

@ -97,7 +97,7 @@ bool shadowed(vec3 pos, vec3 dir, float dist) {
return shadow;
}
vec3 computeLighting(int bounce) {
vec3 computeLighting() {
vec3 C = vec3(0.);
const ivec3 light_cell = ivec3(floor(payload.hit_pos_t.xyz / LIGHT_GRID_CELL_SIZE)) - light_grid.grid_min;
const uint cluster_index = uint(dot(light_cell, ivec3(1, light_grid.grid_size.x, light_grid.grid_size.x * light_grid.grid_size.y)));
@ -153,9 +153,6 @@ vec3 computeLighting(int bounce) {
// TODO do we need to do this when we have textures?
//C += kc * vec3(hash(float(kusok_index)), hash(float(kusok_index)+15.43), hash(float(kusok_index)+34.));//kusok.emissive.rgb;
//C = vec3(1., 0., 1.);
if (bounce == 0)
//C += kc * emissive * baseColor;
C += payload.albedo;
continue;
}
@ -285,38 +282,45 @@ void main() {
break;
}
if (payload.material_flags == 0 || payload.material_flags == 3) {
C += kc * computeLighting(bounce);
} else if (payload.material_flags == 1) {
kc *= payload.albedo;
if (rand01() < .5) {
origin = payload.hit_pos_t.xyz + normal_offset_fudge * direction;
//C += kc * vec3(1., 0., 0.);
//break;
continue;
} else {
payload.roughness = 0.;
}
} else if (payload.material_flags == 2) {
//C += kc * vec3(0., 1., 0.);
C += kc * payload.albedo;
origin = payload.hit_pos_t.xyz + normal_offset_fudge * direction;
continue;
} else {
C += kc * vec3(1., 1., 0.);
break;
if ((payload.material_flags & kXVkMaterialFlagEmissive) != 0) {
C += kc * payload.albedo; // TODO albedo is not correct, should have emissive field
}
if ((payload.material_flags & kXVkMaterialFlagDiffuse) != 0) {
C += kc * computeLighting();
}
// HACK
if ((payload.material_flags & kXVkMaterialFlagReflective) != 0) {
payload.roughness = 0.;
}
float through_probablity = .0;
if ((payload.material_flags & kXVkMaterialFlagRefractive) != 0) {
through_probablity = .5; // .... FIXME
}
if ((payload.material_flags & kXVkMaterialFlagAdditive) != 0) {
through_probablity = 1.;
}
if (through_probablity > 0. && rand01() < through_probablity) {
origin = payload.hit_pos_t.xyz - normal_offset_fudge * payload.normal;
kc *= payload.albedo; // ... FIXME
} else {
origin = payload.hit_pos_t.xyz + normal_offset_fudge * payload.normal;
// TODO this is totally not correct
direction = normalize(mix(
reflect(direction, payload.normal),
//vec3(rand01(), rand01(), rand01())*2.-1.,
rand3_f01(uvec3(gl_LaunchIDEXT.xy, (bounce + 1) * push_constants.random_seed)) * 2. - 1.,
payload.roughness
));
direction *= dot(direction, payload.normal);
kc *= mix(vec3(1.), dot(direction, payload.normal) * payload.albedo, payload.roughness);
}
origin = payload.hit_pos_t.xyz + normal_offset_fudge * payload.normal;
// TODO this is totally not correct
direction = normalize(mix(
reflect(direction, payload.normal),
//vec3(rand01(), rand01(), rand01())*2.-1.,
rand3_f01(uvec3(gl_LaunchIDEXT.xy, (bounce + 1) * push_constants.random_seed)) * 2. - 1.,
payload.roughness
));
//direction *= dot(direction, payload.normal);
kc *= mix(vec3(1.), dot(direction, payload.normal) * payload.albedo, payload.roughness);
} // for all bounces
{
@ -325,4 +329,4 @@ void main() {
C = mix(C, prev_frame, push_constants.prev_frame_blend_factor);
}
imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(sqrt(C), 1.));
}
}

View File

@ -1,6 +1,18 @@
#extension GL_EXT_shader_16bit_storage : require
//#extension GL_EXT_shader_8bit_storage : require
// Shared with native
#define kXVkMaterialFlagEmissive (1<<0)
#define kXVkMaterialFlagDiffuse (1<<1) // means: compute lighting
#define kXVkMaterialFlagAdditive (1<<2)
#define kXVkMaterialFlagReflective (1<<3)
#define kXVkMaterialFlagRefractive (1<<4)
#define kXVkMaterialFlagAlphaTest (1<<5)
// TODO?
// DoLighting
// PassThrough ??? reflective vs refractive + additive
struct Kusok {
uint index_offset;
uint vertex_offset;
@ -25,4 +37,4 @@ struct Vertex {
layout(std430, binding = 3, set = 0) readonly buffer Kusochki { Kusok kusochki[]; };
layout(std430, binding = 4, set = 0) readonly buffer Indices { uint16_t indices[]; };
layout(std430, binding = 5, set = 0) readonly buffer Vertices { Vertex vertices[]; };
layout(std430, binding = 5, set = 0) readonly buffer Vertices { Vertex vertices[]; };

View File

@ -383,7 +383,7 @@ static void R_DrawSegs( vec3_t source, vec3_t delta, float width, float scale, f
{
const vk_render_geometry_t geometry = {
.texture = texture,
.material = kXVkMaterialDiffuse,
.material = kXVkMaterialEmissive,
.max_vertex = total_vertices,
.vertex_offset = vertex_buffer.buffer.unit.offset,

View File

@ -484,7 +484,7 @@ static qboolean loadBrushSurfaces( model_sizes_t sizes, const model_t *mod ) {
if( FBitSet( surf->flags, SURF_DRAWSKY )) {
model_geometry->material = kXVkMaterialSky;
} else {
model_geometry->material = kXVkMaterialDiffuse;
model_geometry->material = kXVkMaterialRegular;
VK_CreateSurfaceLightmap( surf, mod );
}
@ -573,7 +573,7 @@ qboolean VK_BrushModelLoad( model_t *mod )
bmodel->render_model.debug_name = mod->name;
bmodel->render_model.render_mode = kRenderNormal;
bmodel->num_water_surfaces = sizes.water_surfaces != 0;
bmodel->num_water_surfaces = sizes.water_surfaces;
if (sizes.num_surfaces != 0) {
bmodel->render_model.geometries = (vk_render_geometry_t*)((char*)(bmodel + 1));

View File

@ -418,79 +418,77 @@ static void addSurfaceLightToCell( const int light_cell[3], int emissive_surface
++cluster->num_emissive_surfaces;
}
void VK_LightsAddEmissiveSurfacesFromModel( const struct vk_render_model_s *model, const matrix3x4 *transform_row )
{
for (int i = 0; i < model->num_geometries; ++i) {
const vk_render_geometry_t *geom = model->geometries + i;
const int texture_num = geom->texture; // Animated texture
if (!geom->surf)
continue; // TODO break? no surface means that model is not brush
qboolean VK_LightsAddEmissiveSurface( const struct vk_render_geometry_s *geom, const matrix3x4 *transform_row ) {
const int texture_num = geom->texture; // Animated texture
if (!geom->surf)
return false; // TODO break? no surface means that model is not brush
if (geom->material != kXVkMaterialSky && !g_lights.map.emissive_textures[texture_num].set)
continue;
if (geom->material != kXVkMaterialSky && geom->material != kXVkMaterialEmissive && !g_lights.map.emissive_textures[texture_num].set)
return false;
if (g_lights.num_emissive_surfaces < 256) {
// Insert into emissive surfaces
vk_emissive_surface_t *esurf = g_lights.emissive_surfaces + g_lights.num_emissive_surfaces;
esurf->kusok_index = geom->kusok_index;
if (geom->material != kXVkMaterialSky) {
VectorCopy(g_lights.map.emissive_textures[texture_num].emissive, esurf->emissive);
} else {
// TODO per-map sky emissive
VectorSet(esurf->emissive, 1000.f, 1000.f, 1000.f);
}
Matrix3x4_Copy(esurf->transform, *transform_row);
if (g_lights.num_emissive_surfaces < 256) {
// Insert into emissive surfaces
vk_emissive_surface_t *esurf = g_lights.emissive_surfaces + g_lights.num_emissive_surfaces;
esurf->kusok_index = geom->kusok_index;
if (geom->material != kXVkMaterialSky && geom->material != kXVkMaterialEmissive) {
VectorCopy(g_lights.map.emissive_textures[texture_num].emissive, esurf->emissive);
} else {
// TODO per-map sky emissive
VectorSet(esurf->emissive, 1000.f, 1000.f, 1000.f);
}
Matrix3x4_Copy(esurf->transform, *transform_row);
// Insert into light grid cell
// Insert into light grid cell
{
int cluster_index;
vec3_t light_cell;
float effective_radius;
const float intensity_threshold = 1.f / 255.f; // TODO better estimate
const float intensity = Q_max(Q_max(esurf->emissive[0], esurf->emissive[1]), esurf->emissive[2]);
ASSERT(geom->surf->info);
// FIXME using just origin is incorrect
{
int cluster_index;
vec3_t light_cell;
float effective_radius;
const float intensity_threshold = 1.f / 255.f; // TODO better estimate
const float intensity = Q_max(Q_max(esurf->emissive[0], esurf->emissive[1]), esurf->emissive[2]);
ASSERT(geom->surf->info);
// FIXME using just origin is incorrect
{
vec3_t light_cell_f;
vec3_t origin;
Matrix3x4_VectorTransform(*transform_row, geom->surf->info->origin, origin);
VectorDivide(origin, LIGHT_GRID_CELL_SIZE, light_cell_f);
light_cell[0] = floorf(light_cell_f[0]);
light_cell[1] = floorf(light_cell_f[1]);
light_cell[2] = floorf(light_cell_f[2]);
}
VectorSubtract(light_cell, g_lights.map.grid_min_cell, light_cell);
vec3_t light_cell_f;
vec3_t origin;
Matrix3x4_VectorTransform(*transform_row, geom->surf->info->origin, origin);
VectorDivide(origin, LIGHT_GRID_CELL_SIZE, light_cell_f);
light_cell[0] = floorf(light_cell_f[0]);
light_cell[1] = floorf(light_cell_f[1]);
light_cell[2] = floorf(light_cell_f[2]);
}
VectorSubtract(light_cell, g_lights.map.grid_min_cell, light_cell);
ASSERT(light_cell[0] >= 0);
ASSERT(light_cell[1] >= 0);
ASSERT(light_cell[2] >= 0);
ASSERT(light_cell[0] < g_lights.map.grid_size[0]);
ASSERT(light_cell[1] < g_lights.map.grid_size[1]);
ASSERT(light_cell[2] < g_lights.map.grid_size[2]);
ASSERT(light_cell[0] >= 0);
ASSERT(light_cell[1] >= 0);
ASSERT(light_cell[2] >= 0);
ASSERT(light_cell[0] < g_lights.map.grid_size[0]);
ASSERT(light_cell[1] < g_lights.map.grid_size[1]);
ASSERT(light_cell[2] < g_lights.map.grid_size[2]);
// 3.3 Add it to those cells
effective_radius = sqrtf(intensity / intensity_threshold);
{
const int irad = ceilf(effective_radius / LIGHT_GRID_CELL_SIZE);
//gEngine.Con_Reportf("Emissive surface %d: max intensity: %f; eff rad: %f; cell rad: %d\n", i, intensity, effective_radius, irad);
for (int x = -irad; x <= irad; ++x)
for (int y = -irad; y <= irad; ++y)
for (int z = -irad; z <= irad; ++z) {
const int cell[3] = { light_cell[0] + x, light_cell[1] + y, light_cell[2] + z};
// TODO culling, ...
// 3.1 Compute light size and intensity (?)
// 3.2 Compute which cells it might affect
// - light orientation
// - light intensity
// - PVS
addSurfaceLightToCell(cell, g_lights.num_emissive_surfaces);
}
}
// 3.3 Add it to those cells
effective_radius = sqrtf(intensity / intensity_threshold);
{
const int irad = ceilf(effective_radius / LIGHT_GRID_CELL_SIZE);
//gEngine.Con_Reportf("Emissive surface %d: max intensity: %f; eff rad: %f; cell rad: %d\n", i, intensity, effective_radius, irad);
for (int x = -irad; x <= irad; ++x)
for (int y = -irad; y <= irad; ++y)
for (int z = -irad; z <= irad; ++z) {
const int cell[3] = { light_cell[0] + x, light_cell[1] + y, light_cell[2] + z};
// TODO culling, ...
// 3.1 Compute light size and intensity (?)
// 3.2 Compute which cells it might affect
// - light orientation
// - light intensity
// - PVS
addSurfaceLightToCell(cell, g_lights.num_emissive_surfaces);
}
}
}
++g_lights.num_emissive_surfaces;
}
++g_lights.num_emissive_surfaces;
return true;
}
void VK_LightsFrameFinalize( void )

View File

@ -48,7 +48,7 @@ void VK_LightsNewMap( void );
void VK_LightsFrameInit( void );
struct vk_render_model_s;
void VK_LightsAddEmissiveSurfacesFromModel( const struct vk_render_model_s *model, const matrix3x4 *transform_row);
struct vk_render_geometry_s;
qboolean VK_LightsAddEmissiveSurface( const struct vk_render_geometry_s *geom, const matrix3x4 *transform_row );
void VK_LightsFrameFinalize( void );

View File

@ -8,6 +8,14 @@
#define MAX_EMISSIVE_KUSOCHKI 256
#define MODEL_CACHE_SIZE 1024
// Shared with shaders
#define kXVkMaterialFlagEmissive (1<<0)
#define kXVkMaterialFlagDiffuse (1<<1)
#define kXVkMaterialFlagAdditive (1<<2)
#define kXVkMaterialFlagReflective (1<<3)
#define kXVkMaterialFlagRefractive (1<<4)
#define kXVkMaterialFlagAlphaTest (1<<5)
typedef struct vk_ray_model_s {
VkAccelerationStructureKHR as;
VkAccelerationStructureGeometryKHR *geoms;
@ -31,7 +39,7 @@ typedef struct {
// Material parameters
uint32_t texture;
float roughness;
uint32_t flags; // 0 -- opaque, 1 -- alpha mix, 2 -- additive, 3 -- alpha test
uint32_t material_flags;
} vk_kusok_data_t;
typedef struct {
@ -103,4 +111,4 @@ extern xvk_ray_model_state_t g_ray_model_state;
void XVK_RayModel_ClearForNextFrame( void );
void XVK_RayModel_Validate(void);
VkDeviceAddress getBufferDeviceAddress(VkBuffer buffer);
VkDeviceAddress getBufferDeviceAddress(VkBuffer buffer);

View File

@ -185,7 +185,7 @@ vk_ray_model_t* VK_RayModelCreate( vk_ray_model_init_t args ) {
geoms[i] = (VkAccelerationStructureGeometryKHR)
{
.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR,
.flags = VK_GEOMETRY_OPAQUE_BIT_KHR,
.flags = VK_GEOMETRY_OPAQUE_BIT_KHR, // FIXME this is not true. incoming mode might have transparency eventually (and also dynamically)
.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR,
.geometry.triangles =
(VkAccelerationStructureGeometryTrianglesDataKHR){
@ -216,35 +216,12 @@ vk_ray_model_t* VK_RayModelCreate( vk_ray_model_init_t args ) {
kusochki[i].triangles = prim_count;
kusochki[i].texture = mg->texture;
kusochki[i].roughness = mg->material == kXVkMaterialWater ? 0. : 1.;
// FIXME this should not be done here, as we generally don't really have
// render_mode information yet. (does this affect BLAS building?)
switch (args.model->render_mode) {
case kRenderNormal:
kusochki[i].flags = 0;
break;
kusochki[i].roughness = mg->material == kXVkMaterialWater ? 0. : 1.; // FIXME
// C = (1 - alpha) * DST + alpha * SRC (TODO is this right?)
case kRenderTransColor:
case kRenderTransTexture:
kusochki[i].flags = 1;
break;
// Additive blending: C = SRC * alpha + DST
case kRenderGlow:
case kRenderTransAdd:
kusochki[i].flags = 2;
break;
// Alpha test (TODO additive? mixing?)
case kRenderTransAlpha:
kusochki[i].flags = 3;
break;
default:
gEngine.Host_Error("Unexpected render mode %d\n", args.model->render_mode);
}
// We don't know render_mode yet
// TODO: assume diffuse-only surface by default?
// TODO: does this affect BLAS building?
kusochki[i].material_flags = 0; // kXVkMaterialFlagDiffuse;
mg->kusok_index = i + kusochki_count_offset;
}
@ -326,29 +303,26 @@ void VK_RayFrameAddModel( vk_ray_model_t *model, const vk_render_model_t *render
g_ray_model_state.frame.num_models++;
}
if (render_model)
VK_LightsAddEmissiveSurfacesFromModel( render_model, transform_row );
switch (render_model->render_mode) {
case kRenderNormal:
material_flags = 0;
material_flags = kXVkMaterialFlagDiffuse;
break;
// C = (1 - alpha) * DST + alpha * SRC (TODO is this right?)
case kRenderTransColor:
case kRenderTransTexture:
material_flags = 1;
material_flags = kXVkMaterialFlagDiffuse | kXVkMaterialFlagReflective | kXVkMaterialFlagRefractive;
break;
// Additive blending: C = SRC * alpha + DST
case kRenderGlow:
case kRenderTransAdd:
material_flags = 2;
material_flags = kXVkMaterialFlagAdditive | kXVkMaterialFlagEmissive;
break;
// Alpha test (TODO additive? mixing?)
case kRenderTransAlpha:
material_flags = 3;
material_flags = kXVkMaterialFlagDiffuse | kXVkMaterialFlagAlphaTest;
break;
default:
@ -357,9 +331,10 @@ void VK_RayFrameAddModel( vk_ray_model_t *model, const vk_render_model_t *render
for (int i = 0; i < render_model->num_geometries; ++i) {
const vk_render_geometry_t *geom = render_model->geometries + i;
const qboolean emissive = VK_LightsAddEmissiveSurface( geom, transform_row );
vk_kusok_data_t *kusok = (vk_kusok_data_t*)(g_ray_model_state.kusochki_buffer.mapped) + geom->kusok_index;
kusok->texture = geom->texture;
kusok->flags = material_flags;
kusok->material_flags = material_flags | (emissive ? kXVkMaterialFlagEmissive : 0);
}
}

View File

@ -10,7 +10,7 @@ void VK_RenderShutdown( void );
// 1. alloc (allocates buffer mem, stores allocation data)
// 2. (returns void* buf and handle) write to buf
// 3. upload and lock (ensures that all this data is in gpu mem, e.g. uploads from staging)
// 4. ... use it
// 4. ... use it
// 5. free (frame/map end)
typedef struct {
@ -61,18 +61,29 @@ typedef struct vk_vertex_s {
float _padding[3];
} vk_vertex_t;
// TODO not sure how to do materials yet. Figure this out
// Quirk for passing surface type to the renderer
// xash3d does not really have a notion of materials. Instead there are custom code paths
// for different things. There's also render_mode for entities which determine blending mode
// and stuff.
// For ray tracing we do need to assing a material to each rendered surface, so we need to
// figure out what it is given heuristics like render_mode, texture name, etc.
// For some things we don't even have that. E.g. water and sky surfaces are weird.
// Lets just assigne water and sky materials to those geometries (and probably completely
// disregard render_mode, as it should be irrelevant).
typedef enum {
kXVkMaterialDiffuse,
kXVkMaterialRegular = 0,
kXVkMaterialWater,
kXVkMaterialSky,
kXVkMaterialEmissive,
} XVkMaterialType;
typedef struct {
typedef struct vk_render_geometry_s {
int index_offset, vertex_offset;
// TODO can be dynamic
// Animated textures will be dynamic and change between frames
int texture;
// If this geometry is special, it will have a material type override
XVkMaterialType material;
uint32_t element_count;
@ -98,11 +109,10 @@ typedef struct vk_render_model_s {
int num_geometries;
vk_render_geometry_t *geometries;
// TODO potentially dynamic data: textures
// This model will be one-frame only, its buffers are not preserved between frames
qboolean dynamic;
// Non-NULL only for ray tracing
struct vk_ray_model_s *ray_model;
} vk_render_model_t;

View File

@ -469,7 +469,7 @@ static void prepareTlas( VkCommandBuffer cmdbuf ) {
.instanceCustomIndex = model->model->kusochki_offset,
.mask = 0xff,
.instanceShaderBindingTableRecordOffset = 0,
.flags = model->render_mode == kRenderNormal ? VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR : VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_KHR,
.flags = model->render_mode == kRenderNormal ? VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR : VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_KHR, // TODO is render_mode a good indicator of transparency in general case?
.accelerationStructureReference = getASAddress(model->model->as), // TODO cache this addr
};
memcpy(&inst[i].transform, model->transform_row, sizeof(VkTransformMatrixKHR));

View File

@ -705,7 +705,7 @@ static void R_DrawSpriteQuad( const char *debug_name, mspriteframe_t *frame, vec
{
const vk_render_geometry_t geometry = {
.texture = texture,
.material = kXVkMaterialDiffuse,
.material = kXVkMaterialEmissive,
.max_vertex = 4,
.vertex_offset = vertex_buffer.buffer.unit.offset,

View File

@ -2002,7 +2002,7 @@ static void R_StudioDrawNormalMesh( short *ptricmds, vec3_t *pstudionorms, float
const vk_render_geometry_t geometry = {
//.lightmap = tglob.whiteTexture,
.texture = texture,
.material = kXVkMaterialDiffuse,
.material = kXVkMaterialRegular,
.vertex_offset = vertex_buffer.buffer.unit.offset,
.max_vertex = num_vertices,