c/[util/shader]: Implement cylinder layer for compute renderer

This commit is contained in:
Simon Zeni 2023-10-25 15:49:07 -04:00 committed by Jakob Bornecrantz
parent 3259fb583a
commit 9b288a6a1a
3 changed files with 212 additions and 2 deletions

View file

@ -1149,11 +1149,25 @@ struct render_compute_layer_ubo_data
uint32_t padding[2];
} images_samplers[RENDER_MAX_LAYERS];
//! Shared between cylinder and equirect2.
struct xrt_matrix_4x4 mv_inverse[RENDER_MAX_LAYERS];
/*!
* For cylinder layer
*/
struct
{
float radius;
float central_angle;
float aspect_ratio;
float padding;
} cylinder_data[RENDER_MAX_LAYERS];
/*!
* For equirect2 layers
*/
struct xrt_matrix_4x4 mv_inverse[RENDER_MAX_LAYERS];
struct
{
float radius;

View file

@ -44,9 +44,15 @@ layout(set = 0, binding = 3, std140) uniform restrict Config
// which image/sampler(s) correspond to each layer
ivec2 images_samplers[RENDER_MAX_LAYERS];
// shared between cylinder and equirect2
mat4 mv_inverse[RENDER_MAX_LAYERS];
// for cylinder layer
vec4 cylinder_data[RENDER_MAX_LAYERS];
// for equirect2 layer
mat4 mv_inverse[RENDER_MAX_LAYERS];
vec4 eq2_data[RENDER_MAX_LAYERS];
@ -127,6 +133,111 @@ vec2 transform_uv(vec2 uv, uint layer)
}
}
vec4 do_cylinder(vec2 view_uv, uint layer)
{
// Get ray position in model space.
const vec3 ray_origin = (ubo.mv_inverse[layer] * vec4(0, 0, 0, 1)).xyz;
// [0 .. 1] to tangent lengths (at unit Z).
const vec2 uv = fma(view_uv, ubo.pre_transform.zw, ubo.pre_transform.xy);
// With Z at the unit plane and flip y for OpenXR coordinate system,
// transform the ray into model space.
const vec3 ray_dir = normalize((ubo.mv_inverse[layer] * vec4(uv.x, -uv.y, -1, 0)).xyz);
const float radius = ubo.cylinder_data[layer].x;
const float central_angle = ubo.cylinder_data[layer].y;
const float aspect_ratio = ubo.cylinder_data[layer].z;
vec3 dir_from_cyl;
// CPU code will set +INFINITY to zero.
if (radius == 0) {
dir_from_cyl = ray_dir;
} else {
// Find if the cylinder intersects with the ray direction
// Inspired by Inigo Quilez
// https://iquilezles.org/articles/intersectors/
const vec3 axis = vec3(0.f, 1.f, 0.f);
float card = dot(axis, ray_dir);
float caoc = dot(axis, ray_origin);
float a = 1.f - card * card;
float b = dot(ray_origin, ray_dir) - caoc * card;
float c = dot(ray_origin, ray_origin) - caoc * caoc - radius * radius;
float h = b * b - a * c;
if(h < 0.f) {
// no intersection
return vec4(0.f);
}
h = sqrt(h);
vec2 distances = vec2(-b - h, -b + h) / a;
if (distances.y < 0) {
return vec4(0.f);
}
dir_from_cyl = normalize(ray_origin + (ray_dir * distances.y));
}
const float lon = atan(dir_from_cyl.x, -dir_from_cyl.z) / (2 * PI) + 0.5; // => [0, 1]
// float lat = -asin(dir_from_cyl.y); // => [-π/2, π/2]
// float y = tan(lat); // => [-inf, inf]
// simplified: -y/sqrt(1 - y^2)
const float y = -dir_from_cyl.y / sqrt(1 - (dir_from_cyl.y * dir_from_cyl.y)); // => [-inf, inf]
vec4 out_color = vec4(0.f);
#ifdef DEBUG
const int lon_int = int(lon * 1000.f);
const int y_int = int(y * 1000.f);
if (lon < 0.001 && lon > -0.001) {
out_color = vec4(1, 0, 0, 1);
} else if (lon_int % 50 == 0) {
out_color = vec4(1, 1, 1, 1);
} else if (y_int % 50 == 0) {
out_color = vec4(1, 1, 1, 1);
} else {
out_color = vec4(lon, y, 0, 1);
}
#endif
const float chan = central_angle / (PI * 2.f);
// height in radii, radius only matters for determining intersection
const float height = central_angle * aspect_ratio;
// Normalize [0, 2π] to [0, 1]
const float uhan = 0.5 + chan / 2.f;
const float lhan = 0.5 - chan / 2.f;
const float ymin = -height / 2;
const float ymax = height / 2;
if (y < ymax && y > ymin && lon < uhan && lon > lhan) {
// map configured display region to whole texture
vec2 offset = vec2(lhan, ymin);
vec2 extent = vec2(uhan - lhan, ymax - ymin);
vec2 sample_point = (vec2(lon, y) - offset) / extent;
vec2 uv_sub = fma(sample_point, ubo.post_transform[layer].zw, ubo.post_transform[layer].xy);
uint index = ubo.images_samplers[layer].x;
#ifdef DEBUG
out_color += texture(source[index], uv_sub) / 2.f;
#else
out_color = texture(source[index], uv_sub);
#endif
} else {
out_color += vec4(0.f);
}
return out_color;
}
vec4 do_equirect2(vec2 view_uv, uint layer)
{
// Get ray position in model space.
@ -347,6 +458,9 @@ vec4 do_layers(vec2 view_uv)
vec4 rgba = vec4(0, 0, 0, 0);
switch (ubo.layer_type_and_unpremultiplied[layer].x) {
case XRT_LAYER_CYLINDER:
rgba = do_cylinder(view_uv, layer);
break;
case XRT_LAYER_EQUIRECT2:
rgba = do_equirect2(view_uv, layer);
break;

View file

@ -242,6 +242,71 @@ do_cs_quad_layer(const struct xrt_layer_data *data,
}
static inline void
do_cs_cylinder_layer(const struct xrt_layer_data *data,
const struct comp_layer *layer,
const struct xrt_matrix_4x4 *eye_view_mat,
const struct xrt_matrix_4x4 *world_view_mat,
uint32_t view_index,
uint32_t cur_layer,
uint32_t cur_image,
VkSampler clamp_to_edge,
VkSampler clamp_to_border_black,
VkSampler src_samplers[RENDER_MAX_IMAGES],
VkImageView src_image_views[RENDER_MAX_IMAGES],
struct render_compute_layer_ubo_data *ubo_data,
uint32_t *out_cur_image)
{
const struct xrt_layer_cylinder_data *c = &data->cylinder;
const struct comp_swapchain_image *image = &layer->sc_array[0]->images[c->sub.image_index];
uint32_t array_index = c->sub.array_index;
// Image to use.
src_samplers[cur_image] = clamp_to_edge;
src_image_views[cur_image] = get_image_view(image, data->flags, array_index);
// Used for Subimage and OpenGL flip.
set_post_transform_rect( //
data, // data
&c->sub.norm_rect, // src_norm_rect
false, // invert_flip
&ubo_data->post_transforms[cur_layer]); // out_norm_rect
ubo_data->cylinder_data[cur_layer].central_angle = c->central_angle;
ubo_data->cylinder_data[cur_layer].aspect_ratio = c->aspect_ratio;
struct xrt_vec3 scale = {1.f, 1.f, 1.f};
struct xrt_matrix_4x4 model;
math_matrix_4x4_model(&c->pose, &scale, &model);
struct xrt_matrix_4x4 model_inv;
math_matrix_4x4_inverse(&model, &model_inv);
const struct xrt_matrix_4x4 *v = is_layer_view_space(data) ? eye_view_mat : world_view_mat;
struct xrt_matrix_4x4 v_inv;
math_matrix_4x4_inverse(v, &v_inv);
math_matrix_4x4_multiply(&model_inv, &v_inv, &ubo_data->mv_inverse[cur_layer]);
// Simplifies the shader.
if (c->radius >= INFINITY) {
ubo_data->cylinder_data[cur_layer].radius = 0.f;
} else {
ubo_data->cylinder_data[cur_layer].radius = c->radius;
}
ubo_data->cylinder_data[cur_layer].central_angle = c->central_angle;
ubo_data->cylinder_data[cur_layer].aspect_ratio = c->aspect_ratio;
ubo_data->images_samplers[cur_layer].images[0] = cur_image;
cur_image++;
*out_cur_image = cur_image;
}
/*
*
* Compute distortion helpers.
@ -408,6 +473,7 @@ comp_render_cs_layer(struct render_compute *crc,
*/
uint32_t required_image_samplers;
switch (data->type) {
case XRT_LAYER_CYLINDER: required_image_samplers = 1; break;
case XRT_LAYER_EQUIRECT2: required_image_samplers = 1; break;
case XRT_LAYER_STEREO_PROJECTION: required_image_samplers = 1; break;
case XRT_LAYER_STEREO_PROJECTION_DEPTH: required_image_samplers = 2; break;
@ -423,6 +489,22 @@ comp_render_cs_layer(struct render_compute *crc,
}
switch (data->type) {
case XRT_LAYER_CYLINDER:
do_cs_cylinder_layer( //
data, // data
layer, // layer
&eye_view_mat, // eye_view_mat
&world_view_mat, // world_view_mat
view_index, // view_index
cur_layer, // cur_layer
cur_image, // cur_image
clamp_to_edge, // clamp_to_edge
clamp_to_border_black, // clamp_to_border_black
src_samplers, // src_samplers
src_image_views, // src_image_views
ubo_data, // ubo_data
&cur_image); // out_cur_image
break;
case XRT_LAYER_EQUIRECT2:
do_cs_equirect2_layer( //
data, // data