The open source OpenXR runtime
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

c/render: Add cylinder and equirect2 shaders

Co-authored-by: Simon Zeni <simon.zeni@collabora.com>
Co-authored-by: Charlton Rodda <charlton.rodda@collabora.com>

+525
+4
src/xrt/compositor/CMakeLists.txt
··· 95 95 shaders/mesh.vert 96 96 shaders/layer.frag 97 97 shaders/layer.vert 98 + shaders/layer_cylinder.frag 99 + shaders/layer_cylinder.vert 100 + shaders/layer_equirect2.frag 101 + shaders/layer_equirect2.vert 98 102 shaders/layer_projection.vert 99 103 shaders/layer_quad.vert 100 104 shaders/layer_shared.frag
+136
src/xrt/compositor/render/render_gfx.c
··· 755 755 const VkBlendFactor blend_factor_premultiplied_alpha = VK_BLEND_FACTOR_ONE; 756 756 const VkBlendFactor blend_factor_unpremultiplied_alpha = VK_BLEND_FACTOR_SRC_ALPHA; 757 757 758 + // Cylinder 759 + ret = create_layer_pipeline( // 760 + vk, // vk 761 + rgrp->render_pass, // render_pass 762 + r->gfx.layer.shared.pipeline_layout, // pipeline_layout 763 + r->pipeline_cache, // pipeline_cache 764 + blend_factor_premultiplied_alpha, // src_blend_factor 765 + r->shaders->layer_cylinder_vert, // module_vert 766 + r->shaders->layer_cylinder_frag, // module_frag 767 + &rgrp->layer.cylinder_premultiplied_alpha); // out_pipeline 768 + VK_CHK_WITH_RET(ret, "create_layer_pipeline", false); 769 + 770 + ret = create_layer_pipeline( // 771 + vk, // vk 772 + rgrp->render_pass, // render_pass 773 + r->gfx.layer.shared.pipeline_layout, // pipeline_layout 774 + r->pipeline_cache, // pipeline_cache 775 + blend_factor_unpremultiplied_alpha, // src_blend_factor 776 + r->shaders->layer_cylinder_vert, // module_vert 777 + r->shaders->layer_cylinder_frag, // module_frag 778 + &rgrp->layer.cylinder_unpremultiplied_alpha); // out_pipeline 779 + VK_CHK_WITH_RET(ret, "create_layer_pipeline", false); 780 + 781 + // Equirect2 782 + ret = create_layer_pipeline( // 783 + vk, // vk 784 + rgrp->render_pass, // render_pass 785 + r->gfx.layer.shared.pipeline_layout, // pipeline_layout 786 + r->pipeline_cache, // pipeline_cache 787 + blend_factor_premultiplied_alpha, // src_blend_factor 788 + r->shaders->layer_equirect2_vert, // module_vert 789 + r->shaders->layer_equirect2_frag, // module_frag 790 + &rgrp->layer.equirect2_premultiplied_alpha); // out_pipeline 791 + VK_CHK_WITH_RET(ret, "create_layer_pipeline", false); 792 + 793 + ret = create_layer_pipeline( // 794 + vk, // vk 795 + rgrp->render_pass, // render_pass 796 + r->gfx.layer.shared.pipeline_layout, // pipeline_layout 797 + r->pipeline_cache, // pipeline_cache 798 + blend_factor_unpremultiplied_alpha, // src_blend_factor 799 + r->shaders->layer_equirect2_vert, // module_vert 800 + r->shaders->layer_equirect2_frag, // module_frag 801 + &rgrp->layer.equirect2_unpremultiplied_alpha); // out_pipeline 802 + VK_CHK_WITH_RET(ret, "create_layer_pipeline", false); 803 + 804 + // Projection. 758 805 ret = create_layer_pipeline( // 759 806 vk, // vk 760 807 rgrp->render_pass, // render_pass ··· 777 824 &rgrp->layer.proj_unpremultiplied_alpha); // out_pipeline 778 825 VK_CHK_WITH_RET(ret, "create_layer_pipeline", false); 779 826 827 + // Quad 780 828 ret = create_layer_pipeline( // 781 829 vk, // vk 782 830 rgrp->render_pass, // render_pass ··· 818 866 D(Pipeline, rgrp->mesh.pipeline); 819 867 D(Pipeline, rgrp->mesh.pipeline_timewarp); 820 868 869 + D(Pipeline, rgrp->layer.cylinder_premultiplied_alpha); 870 + D(Pipeline, rgrp->layer.cylinder_unpremultiplied_alpha); 871 + D(Pipeline, rgrp->layer.equirect2_premultiplied_alpha); 872 + D(Pipeline, rgrp->layer.equirect2_unpremultiplied_alpha); 821 873 D(Pipeline, rgrp->layer.proj_premultiplied_alpha); 822 874 D(Pipeline, rgrp->layer.proj_unpremultiplied_alpha); 823 875 D(Pipeline, rgrp->layer.quad_premultiplied_alpha); ··· 1164 1216 */ 1165 1217 1166 1218 XRT_CHECK_RESULT VkResult 1219 + render_gfx_layer_cylinder_alloc_and_write(struct render_gfx *rr, 1220 + const struct render_gfx_layer_cylinder_data *data, 1221 + VkSampler src_sampler, 1222 + VkImageView src_image_view, 1223 + VkDescriptorSet *out_descriptor_set) 1224 + { 1225 + struct render_resources *r = rr->r; 1226 + 1227 + return do_ubo_and_src_alloc_and_write( // 1228 + rr, // rr 1229 + RENDER_BINDING_LAYER_SHARED_UBO, // ubo_binding 1230 + data, // ubo_ptr 1231 + sizeof(*data), // ubo_size 1232 + RENDER_BINDING_LAYER_SHARED_SRC, // src_binding 1233 + src_sampler, // src_sampler 1234 + src_image_view, // src_image_view 1235 + r->gfx.ubo_and_src_descriptor_pool, // descriptor_pool 1236 + r->gfx.layer.shared.descriptor_set_layout, // descriptor_set_layout 1237 + out_descriptor_set); // out_descriptor_set 1238 + } 1239 + 1240 + XRT_CHECK_RESULT VkResult 1241 + render_gfx_layer_equirect2_alloc_and_write(struct render_gfx *rr, 1242 + const struct render_gfx_layer_equirect2_data *data, 1243 + VkSampler src_sampler, 1244 + VkImageView src_image_view, 1245 + VkDescriptorSet *out_descriptor_set) 1246 + { 1247 + struct render_resources *r = rr->r; 1248 + 1249 + return do_ubo_and_src_alloc_and_write( // 1250 + rr, // rr 1251 + RENDER_BINDING_LAYER_SHARED_UBO, // ubo_binding 1252 + data, // ubo_ptr 1253 + sizeof(*data), // ubo_size 1254 + RENDER_BINDING_LAYER_SHARED_SRC, // src_binding 1255 + src_sampler, // src_sampler 1256 + src_image_view, // src_image_view 1257 + r->gfx.ubo_and_src_descriptor_pool, // descriptor_pool 1258 + r->gfx.layer.shared.descriptor_set_layout, // descriptor_set_layout 1259 + out_descriptor_set); // out_descriptor_set 1260 + } 1261 + 1262 + XRT_CHECK_RESULT VkResult 1167 1263 render_gfx_layer_projection_alloc_and_write(struct render_gfx *rr, 1168 1264 const struct render_gfx_layer_projection_data *data, 1169 1265 VkSampler src_sampler, ··· 1205 1301 r->gfx.ubo_and_src_descriptor_pool, // descriptor_pool 1206 1302 r->gfx.layer.shared.descriptor_set_layout, // descriptor_set_layout 1207 1303 out_descriptor_set); // out_descriptor_set 1304 + } 1305 + 1306 + void 1307 + render_gfx_layer_cylinder(struct render_gfx *rr, bool premultiplied_alpha, VkDescriptorSet descriptor_set) 1308 + { 1309 + VkPipeline pipeline = // 1310 + premultiplied_alpha // 1311 + ? rr->rtr->rgrp->layer.cylinder_premultiplied_alpha // 1312 + : rr->rtr->rgrp->layer.cylinder_unpremultiplied_alpha; // 1313 + 1314 + // One per degree. 1315 + uint32_t subdivisions = 360; 1316 + 1317 + // One edge on either endstop and one between each subdivision. 1318 + uint32_t edges = subdivisions + 1; 1319 + 1320 + // With triangle strip we get 2 vertices per edge. 1321 + uint32_t vertex_count = edges * 2; 1322 + 1323 + dispatch_no_vbo( // 1324 + rr, // rr 1325 + vertex_count, // vertex_count 1326 + pipeline, // pipeline 1327 + descriptor_set); // descriptor_set 1328 + } 1329 + 1330 + void 1331 + render_gfx_layer_equirect2(struct render_gfx *rr, bool premultiplied_alpha, VkDescriptorSet descriptor_set) 1332 + { 1333 + VkPipeline pipeline = // 1334 + premultiplied_alpha // 1335 + ? rr->rtr->rgrp->layer.equirect2_premultiplied_alpha // 1336 + : rr->rtr->rgrp->layer.equirect2_unpremultiplied_alpha; // 1337 + 1338 + // Hardcoded to 4 vertices. 1339 + dispatch_no_vbo( // 1340 + rr, // rr 1341 + 4, // vertex_count 1342 + pipeline, // pipeline 1343 + descriptor_set); // descriptor_set 1208 1344 } 1209 1345 1210 1346 void
+89
src/xrt/compositor/render/render_interface.h
··· 155 155 * New layer renderer. 156 156 */ 157 157 158 + VkShaderModule layer_cylinder_vert; 159 + VkShaderModule layer_cylinder_frag; 160 + 161 + VkShaderModule layer_equirect2_vert; 162 + VkShaderModule layer_equirect2_frag; 163 + 158 164 VkShaderModule layer_projection_vert; 159 165 VkShaderModule layer_quad_vert; 160 166 VkShaderModule layer_shared_frag; ··· 721 727 722 728 struct 723 729 { 730 + VkPipeline cylinder_premultiplied_alpha; 731 + VkPipeline cylinder_unpremultiplied_alpha; 732 + 733 + VkPipeline equirect2_premultiplied_alpha; 734 + VkPipeline equirect2_unpremultiplied_alpha; 735 + 724 736 VkPipeline proj_premultiplied_alpha; 725 737 VkPipeline proj_unpremultiplied_alpha; 738 + 726 739 VkPipeline quad_premultiplied_alpha; 727 740 VkPipeline quad_unpremultiplied_alpha; 728 741 } layer; ··· 873 886 }; 874 887 875 888 /*! 889 + * UBO data that is sent to the layer cylinder shader. 890 + */ 891 + struct render_gfx_layer_cylinder_data 892 + { 893 + struct xrt_normalized_rect post_transform; 894 + struct xrt_matrix_4x4 mvp; 895 + float radius; 896 + float central_angle; 897 + float aspect_ratio; 898 + float _pad; 899 + }; 900 + 901 + /*! 902 + * UBO data that is sent to the layer equirect2 shader. 903 + */ 904 + struct render_gfx_layer_equirect2_data 905 + { 906 + struct xrt_normalized_rect post_transform; 907 + struct xrt_matrix_4x4 mv_inverse; 908 + 909 + //! See @ref render_calc_uv_to_tangent_lengths_rect. 910 + struct xrt_normalized_rect to_tangent; 911 + 912 + float radius; 913 + float central_horizontal_angle; 914 + float upper_vertical_angle; 915 + float lower_vertical_angle; 916 + }; 917 + 918 + /*! 876 919 * UBO data that is sent to the layer projection shader. 877 920 */ 878 921 struct render_gfx_layer_projection_data ··· 952 995 render_gfx_mesh_draw(struct render_gfx *rr, uint32_t mesh_index, VkDescriptorSet descriptor_set, bool do_timewarp); 953 996 954 997 /*! 998 + * Allocate and write a UBO and descriptor_set to be used for cylinder layer 999 + * rendering, the content of @p data need to be valid at the time of the call. 1000 + * 1001 + * @public @memberof render_gfx 1002 + */ 1003 + XRT_CHECK_RESULT VkResult 1004 + render_gfx_layer_cylinder_alloc_and_write(struct render_gfx *rr, 1005 + const struct render_gfx_layer_cylinder_data *data, 1006 + VkSampler src_sampler, 1007 + VkImageView src_image_view, 1008 + VkDescriptorSet *out_descriptor_set); 1009 + 1010 + /*! 1011 + * Allocate and write a UBO and descriptor_set to be used for equirect2 layer 1012 + * rendering, the content of @p data need to be valid at the time of the call. 1013 + * 1014 + * @public @memberof render_gfx 1015 + */ 1016 + XRT_CHECK_RESULT VkResult 1017 + render_gfx_layer_equirect2_alloc_and_write(struct render_gfx *rr, 1018 + const struct render_gfx_layer_equirect2_data *data, 1019 + VkSampler src_sampler, 1020 + VkImageView src_image_view, 1021 + VkDescriptorSet *out_descriptor_set); 1022 + 1023 + /*! 955 1024 * Allocate and write a UBO and descriptor_set to be used for projection layer 956 1025 * rendering, the content of @p data need to be valid at the time of the call. 957 1026 * ··· 976 1045 VkSampler src_sampler, 977 1046 VkImageView src_image_view, 978 1047 VkDescriptorSet *out_descriptor_set); 1048 + 1049 + /*! 1050 + * Dispatch a cylinder layer shader into the current target and view, 1051 + * allocate @p descriptor_set and ubo with 1052 + * @ref render_gfx_layer_cylinder_alloc_and_write. 1053 + * 1054 + * @public @memberof render_gfx 1055 + */ 1056 + void 1057 + render_gfx_layer_cylinder(struct render_gfx *rr, bool premultiplied_alpha, VkDescriptorSet descriptor_set); 1058 + 1059 + /*! 1060 + * Dispatch a equirect2 layer shader into the current target and view, 1061 + * allocate @p descriptor_set and ubo with 1062 + * @ref render_gfx_layer_equirect2_alloc_and_write. 1063 + * 1064 + * @public @memberof render_gfx 1065 + */ 1066 + void 1067 + render_gfx_layer_equirect2(struct render_gfx *rr, bool premultiplied_alpha, VkDescriptorSet descriptor_set); 979 1068 980 1069 /*! 981 1070 * Dispatch a projection layer shader into the current target and view,
+13
src/xrt/compositor/render/render_shaders.c
··· 36 36 #include "shaders/equirect1.vert.h" 37 37 #include "shaders/equirect2.frag.h" 38 38 #include "shaders/equirect2.vert.h" 39 + #include "shaders/layer_cylinder.frag.h" 40 + #include "shaders/layer_cylinder.vert.h" 41 + #include "shaders/layer_equirect2.frag.h" 42 + #include "shaders/layer_equirect2.vert.h" 43 + #include "shaders/layer_projection.vert.h" 39 44 #include "shaders/layer_projection.vert.h" 40 45 #include "shaders/layer_quad.vert.h" 41 46 #include "shaders/layer_shared.frag.h" ··· 132 137 LOAD(layer_vert); 133 138 LOAD(layer_frag); 134 139 140 + LOAD(layer_cylinder_frag); 141 + LOAD(layer_cylinder_vert); 142 + LOAD(layer_equirect2_frag); 143 + LOAD(layer_equirect2_vert); 135 144 LOAD(layer_projection_vert); 136 145 LOAD(layer_quad_vert); 137 146 LOAD(layer_shared_frag); ··· 161 170 D(ShaderModule, s->layer_vert); 162 171 D(ShaderModule, s->layer_frag); 163 172 173 + D(ShaderModule, s->layer_cylinder_frag); 174 + D(ShaderModule, s->layer_cylinder_vert); 175 + D(ShaderModule, s->layer_equirect2_frag); 176 + D(ShaderModule, s->layer_equirect2_vert); 164 177 D(ShaderModule, s->layer_projection_vert); 165 178 D(ShaderModule, s->layer_quad_vert); 166 179 D(ShaderModule, s->layer_shared_frag);
+17
src/xrt/compositor/shaders/layer_cylinder.frag
··· 1 + // Copyright 2023, Collabora Ltd. 2 + // Author: Jakob Bornecrantz <jakob@collabora.com> 3 + // SPDX-License-Identifier: BSL-1.0 4 + 5 + #version 460 6 + 7 + 8 + layout (binding = 1) uniform sampler2D image; 9 + 10 + layout (location = 0) in vec2 uv; 11 + layout (location = 0) out vec4 out_color; 12 + 13 + 14 + void main () 15 + { 16 + out_color = texture(image, uv); 17 + }
+88
src/xrt/compositor/shaders/layer_cylinder.vert
··· 1 + // Copyright 2023, Collabora Ltd. 2 + // Author: Jakob Bornecrantz <jakob@collabora.com> 3 + // SPDX-License-Identifier: BSL-1.0 4 + 5 + #version 460 6 + 7 + 8 + // Number of subdivisions. 9 + layout(constant_id = 0) const uint subdivision_count = 360; 10 + 11 + layout (binding = 0, std140) uniform Config 12 + { 13 + vec4 post_transform; 14 + mat4 mvp; 15 + float radius; 16 + float central_angle; 17 + float aspect_ratio; 18 + float _pad; 19 + } ubo; 20 + 21 + layout (location = 0) out vec2 out_uv; 22 + 23 + out gl_PerVertex 24 + { 25 + vec4 gl_Position; 26 + }; 27 + 28 + 29 + vec2 get_uv_for_vertex() 30 + { 31 + // One edge on either endstop and one between each subdivision. 32 + uint edges = subdivision_count + 1; 33 + 34 + // Goes from [0 .. 2^31], two vertices per edge. 35 + uint x_u32 = bitfieldExtract(uint(gl_VertexIndex), 1, 31); 36 + 37 + // Goes from [0 .. 1] every other vertex. 38 + uint y_u32 = bitfieldExtract(uint(gl_VertexIndex), 0, 1); 39 + 40 + // Starts at zero to get to [0 .. 1], there is two vertices per edge. 41 + float x = float(x_u32) / float(edges); 42 + 43 + // Already in [0 .. 1] just transform to float. 44 + float y = float(y_u32); 45 + 46 + return vec2(x, y); 47 + } 48 + 49 + vec3 get_position_for_uv(vec2 uv) 50 + { 51 + float radius = ubo.radius; 52 + float angle = ubo.central_angle; 53 + float ratio = ubo.aspect_ratio; 54 + 55 + // [0 .. 1] to [-0.5 .. 0.5] 56 + float mixed_u = uv.x - 0.5; 57 + 58 + // [-0.5 .. 0.5] to [-angle / 2 .. angle / 2]. 59 + float a = mixed_u * angle; 60 + 61 + // [0 .. 1] to [0.5 .. -0.5] notice fliped sign to be in OpenXR space. 62 + float mixed_v = 0.5 - uv.y; 63 + 64 + // This the total height acording to the spec. 65 + float total_height = (angle * radius) / ratio; 66 + 67 + // Calculate the position. 68 + float x = sin(a) * radius; // At angle zero at x = 0. 69 + float y = total_height * mixed_v; 70 + float z = -cos(a) * radius; // At angle zero at z = -1. 71 + 72 + return vec3(x, y, z); 73 + } 74 + 75 + void main() 76 + { 77 + // We now get a unmodified UV position. 78 + vec2 raw_uv = get_uv_for_vertex(); 79 + 80 + // Get the position for the raw UV. 81 + vec3 position = get_position_for_uv(raw_uv); 82 + 83 + // To deal with OpenGL flip and sub image view. 84 + vec2 uv = fma(raw_uv, ubo.post_transform.zw, ubo.post_transform.xy); 85 + 86 + gl_Position = ubo.mvp * vec4(position, 1.0); 87 + out_uv = uv; 88 + }
+113
src/xrt/compositor/shaders/layer_equirect2.frag
··· 1 + // Copyright 2023, Collabora Ltd. 2 + // Author: Jakob Bornecrantz <jakob@collabora.com> 3 + // Author: Charlton Rodda <charlton.rodda@collabora.com> 4 + // Author: Simon Zeni <simon.zeni@collabora.com> 5 + // SPDX-License-Identifier: BSL-1.0 6 + 7 + #version 460 8 + 9 + layout (binding = 0, std140) uniform Config 10 + { 11 + vec4 post_transform; 12 + mat4 mv_inverse; 13 + vec4 to_tangent; 14 + float radius; 15 + float central_horizontal_angle; 16 + float upper_vertical_angle; 17 + float lower_vertical_angle; 18 + } ubo; 19 + 20 + layout (binding = 1) uniform sampler2D image; 21 + 22 + layout (location = 0) flat in vec3 in_camera_position; 23 + layout (location = 1) in vec3 in_camera_ray_unnormalized; 24 + layout (location = 0) out vec4 out_color; 25 + 26 + const float PI = acos(-1); 27 + 28 + 29 + vec2 sphere_intersect(vec3 ray_origin, vec3 ray_direction, vec3 sphere_center, float radius) 30 + { 31 + vec3 ray_sphere_diff = ray_origin - sphere_center; 32 + 33 + float B = dot(ray_sphere_diff, ray_direction); 34 + 35 + vec3 QC = ray_sphere_diff - B * ray_direction; 36 + 37 + float H = radius * radius - dot(QC, QC); 38 + 39 + if (H < 0.0) { 40 + return vec2(-1.0); // no intersection 41 + } 42 + 43 + H = sqrt(H); 44 + 45 + return vec2(-B - H, -B + H); 46 + } 47 + 48 + void main () 49 + { 50 + vec3 ray_origin = in_camera_position; 51 + vec3 ray_dir = normalize(in_camera_ray_unnormalized); 52 + 53 + vec3 dir_from_sph; 54 + // CPU code will set +INFINITY to zero. 55 + if (ubo.radius == 0) { 56 + dir_from_sph = ray_dir; 57 + } else { 58 + vec2 distances = sphere_intersect(ray_origin, ray_dir, vec3(0, 0, 0), ubo.radius); 59 + 60 + if (distances.y < 0) { 61 + out_color = vec4(0.0); 62 + return; 63 + } 64 + 65 + vec3 pos = ray_origin + (ray_dir * distances.y); 66 + dir_from_sph = normalize(pos); 67 + } 68 + 69 + float lon = atan(dir_from_sph.x, -dir_from_sph.z) / (2 * PI) + 0.5; 70 + float lat = acos(dir_from_sph.y) / PI; 71 + 72 + #ifdef DEBUG 73 + int lon_int = int(lon * 1000.0); 74 + int lat_int = int(lat * 1000.0); 75 + 76 + if (lon < 0.001 && lon > -0.001) { 77 + out_color = vec4(1, 0, 0, 1); 78 + } else if (lon_int % 50 == 0) { 79 + out_color = vec4(1, 1, 1, 1); 80 + } else if (lat_int % 50 == 0) { 81 + out_color = vec4(1, 1, 1, 1); 82 + } else { 83 + out_color = vec4(lon, lat, 0, 1); 84 + } 85 + #endif 86 + 87 + float chan = ubo.central_horizontal_angle / (PI * 2.0f); 88 + 89 + // Normalize [0, 2π] to [0, 1] 90 + float uhan = 0.5 + chan / 2.0f; 91 + float lhan = 0.5 - chan / 2.0f; 92 + 93 + // Normalize [-π/2, π/2] to [0, 1] 94 + float uvan = ubo.upper_vertical_angle / PI + 0.5f; 95 + float lvan = ubo.lower_vertical_angle / PI + 0.5f; 96 + 97 + if (lat < uvan && lat > lvan && lon < uhan && lon > lhan) { 98 + // map configured display region to whole texture 99 + vec2 ll_offset = vec2(lhan, lvan); 100 + vec2 ll_extent = vec2(uhan - lhan, uvan - lvan); 101 + vec2 sample_point = (vec2(lon, lat) - ll_offset) / ll_extent; 102 + 103 + vec2 uv_sub = fma(sample_point, ubo.post_transform.zw, ubo.post_transform.xy); 104 + 105 + #ifdef DEBUG 106 + out_color += texture(image, uv_sub) / 2.0; 107 + #else 108 + out_color = texture(image, uv_sub); 109 + } else { 110 + out_color = vec4(0, 0, 0, 0); 111 + #endif 112 + } 113 + }
+65
src/xrt/compositor/shaders/layer_equirect2.vert
··· 1 + // Copyright 2023, Collabora Ltd. 2 + // Author: Jakob Bornecrantz <jakob@collabora.com> 3 + // Author: Charlton Rodda <charlton.rodda@collabora.com> 4 + // Author: Simon Zeni <simon.zeni@collabora.com> 5 + // SPDX-License-Identifier: BSL-1.0 6 + 7 + #version 460 8 + 9 + layout (binding = 0, std140) uniform Config 10 + { 11 + vec4 post_transform; 12 + mat4 mv_inverse; 13 + vec4 to_tangent; 14 + float radius; 15 + float central_horizontal_angle; 16 + float upper_vertical_angle; 17 + float lower_vertical_angle; 18 + } ubo; 19 + 20 + layout (location = 0) flat out vec3 out_camera_position; 21 + layout (location = 1) out vec3 out_camera_ray_unnormalized; 22 + 23 + out gl_PerVertex 24 + { 25 + vec4 gl_Position; 26 + }; 27 + 28 + vec2 pos[4] = { 29 + vec2(0, 0), 30 + vec2(0, 1), 31 + vec2(1, 0), 32 + vec2(1, 1), 33 + }; 34 + 35 + vec3 intersection_with_unit_plane(vec2 uv_0_to_1) 36 + { 37 + // [0 .. 1] to tangent lengths (at unit Z). 38 + vec2 tangent_factors = fma(uv_0_to_1, ubo.to_tangent.zw, ubo.to_tangent.xy); 39 + 40 + // With Z at the unit plane and flip y for OpenXR coordinate system. 41 + vec3 point_on_unit_plane = vec3(tangent_factors.x, -tangent_factors.y, -1); 42 + 43 + return point_on_unit_plane; 44 + } 45 + 46 + void main() 47 + { 48 + vec2 uv = pos[gl_VertexIndex % 4]; 49 + 50 + // Get camera position in model space. 51 + out_camera_position = (ubo.mv_inverse * vec4(0, 0, 0, 1)).xyz; 52 + 53 + // Get the point on the Z=-1 plane in view space that this pixel's ray intersects. 54 + vec3 out_camera_ray_in_view_space = intersection_with_unit_plane(uv); 55 + 56 + // Transform into model space. Normalising here doesn't work because 57 + // the values are interpeted linearly in space, where a normal 58 + // doesn't move linearly in space, so do that in the fragment shader. 59 + out_camera_ray_unnormalized = (ubo.mv_inverse * vec4(out_camera_ray_in_view_space, 0)).xyz; 60 + 61 + // Go from [0 .. 1] to [-1 .. 1] to fill whole view in NDC. 62 + vec2 pos = fma(uv, vec2(2.0), vec2(-1.0)); 63 + 64 + gl_Position = vec4(pos, 0.f, 1.f); 65 + }