Add stereoscopic rendering through multiview

This commit is contained in:
Bastiaan Olij 2021-05-07 23:19:04 +10:00
parent 600b4c9c7b
commit 15c1a76361
53 changed files with 1062 additions and 467 deletions

View File

@ -1612,6 +1612,9 @@
</member> </member>
<member name="rendering/vulkan/staging_buffer/texture_upload_region_size_px" type="int" setter="" getter="" default="64"> <member name="rendering/vulkan/staging_buffer/texture_upload_region_size_px" type="int" setter="" getter="" default="64">
</member> </member>
<member name="rendering/xr/enabled" type="bool" setter="" getter="" default="false">
If [code]true[/code], XR support is enabled in Godot, this ensures required shaders are compiled.
</member>
<member name="world/2d/cell_size" type="int" setter="" getter="" default="100"> <member name="world/2d/cell_size" type="int" setter="" getter="" default="100">
Cell size used for the 2D hash grid that [VisibilityNotifier2D] uses (in pixels). Cell size used for the 2D hash grid that [VisibilityNotifier2D] uses (in pixels).
</member> </member>

View File

@ -46,6 +46,13 @@
If supported, returns the status of our tracking. This will allow you to provide feedback to the user whether there are issues with positional tracking. If supported, returns the status of our tracking. This will allow you to provide feedback to the user whether there are issues with positional tracking.
</description> </description>
</method> </method>
<method name="get_view_count">
<return type="int">
</return>
<description>
Returns the number of views that need to be rendered for this device. 1 for Monoscopic, 2 for Stereoscopic.
</description>
</method>
<method name="initialize"> <method name="initialize">
<return type="bool"> <return type="bool">
</return> </return>
@ -57,13 +64,6 @@
While currently not used, you can activate additional interfaces. You may wish to do this if you want to track controllers from other platforms. However, at this point in time only one interface can render to an HMD. While currently not used, you can activate additional interfaces. You may wish to do this if you want to track controllers from other platforms. However, at this point in time only one interface can render to an HMD.
</description> </description>
</method> </method>
<method name="is_stereo">
<return type="bool">
</return>
<description>
Returns [code]true[/code] if the current output of this interface is in stereo.
</description>
</method>
<method name="uninitialize"> <method name="uninitialize">
<return type="void"> <return type="void">
</return> </return>

View File

@ -3244,7 +3244,7 @@ bool RenderingDeviceVulkan::texture_is_format_supported_for_usage(DataFormat p_f
/**** ATTACHMENT ****/ /**** ATTACHMENT ****/
/********************/ /********************/
VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentFormat> &p_format, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, int *r_color_attachment_count) { VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentFormat> &p_format, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, int *r_color_attachment_count, uint32_t p_view_count) {
Vector<VkAttachmentDescription> attachments; Vector<VkAttachmentDescription> attachments;
Vector<VkAttachmentReference> color_references; Vector<VkAttachmentReference> color_references;
Vector<VkAttachmentReference> depth_stencil_references; Vector<VkAttachmentReference> depth_stencil_references;
@ -3469,6 +3469,31 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF
render_pass_create_info.dependencyCount = 0; render_pass_create_info.dependencyCount = 0;
render_pass_create_info.pDependencies = nullptr; render_pass_create_info.pDependencies = nullptr;
const uint32_t view_mask = (1 << p_view_count) - 1;
const uint32_t correlation_mask = (1 << p_view_count) - 1;
VkRenderPassMultiviewCreateInfo render_pass_multiview_create_info;
if (p_view_count > 1) {
const VulkanContext::MultiviewCapabilities capabilities = context->get_multiview_capabilities();
// For now this only works with multiview!
ERR_FAIL_COND_V_MSG(!capabilities.is_supported, VK_NULL_HANDLE, "Multiview not supported");
// Make sure we limit this to the number of views we support.
ERR_FAIL_COND_V_MSG(p_view_count > capabilities.max_view_count, VK_NULL_HANDLE, "Hardware does not support requested number of views for Multiview render pass");
render_pass_multiview_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO;
render_pass_multiview_create_info.pNext = nullptr;
render_pass_multiview_create_info.subpassCount = 1;
render_pass_multiview_create_info.pViewMasks = &view_mask;
render_pass_multiview_create_info.dependencyCount = 0;
render_pass_multiview_create_info.pViewOffsets = nullptr;
render_pass_multiview_create_info.correlationMaskCount = 1;
render_pass_multiview_create_info.pCorrelationMasks = &correlation_mask;
render_pass_create_info.pNext = &render_pass_multiview_create_info;
}
VkRenderPass render_pass; VkRenderPass render_pass;
VkResult res = vkCreateRenderPass(device, &render_pass_create_info, nullptr, &render_pass); VkResult res = vkCreateRenderPass(device, &render_pass_create_info, nullptr, &render_pass);
ERR_FAIL_COND_V_MSG(res, VK_NULL_HANDLE, "vkCreateRenderPass failed with error " + itos(res) + "."); ERR_FAIL_COND_V_MSG(res, VK_NULL_HANDLE, "vkCreateRenderPass failed with error " + itos(res) + ".");
@ -3479,11 +3504,12 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF
return render_pass; return render_pass;
} }
RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create(const Vector<AttachmentFormat> &p_format) { RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count) {
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_
FramebufferFormatKey key; FramebufferFormatKey key;
key.attachments = p_format; key.attachments = p_format;
key.view_count = p_view_count;
const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key); const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key);
if (E) { if (E) {
@ -3492,7 +3518,7 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c
} }
int color_references; int color_references;
VkRenderPass render_pass = _render_pass_create(p_format, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, &color_references); //actions don't matter for this use case VkRenderPass render_pass = _render_pass_create(p_format, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, &color_references, p_view_count); //actions don't matter for this use case
if (render_pass == VK_NULL_HANDLE) { //was likely invalid if (render_pass == VK_NULL_HANDLE) { //was likely invalid
return INVALID_ID; return INVALID_ID;
@ -3505,6 +3531,7 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c
fb_format.color_attachments = color_references; fb_format.color_attachments = color_references;
fb_format.render_pass = render_pass; fb_format.render_pass = render_pass;
fb_format.samples = p_format[0].samples; fb_format.samples = p_format[0].samples;
fb_format.view_count = p_view_count;
framebuffer_formats[id] = fb_format; framebuffer_formats[id] = fb_format;
return id; return id;
} }
@ -3580,11 +3607,12 @@ RID RenderingDeviceVulkan::framebuffer_create_empty(const Size2i &p_size, Textur
framebuffer.format_id = framebuffer_format_create_empty(p_samples); framebuffer.format_id = framebuffer_format_create_empty(p_samples);
ERR_FAIL_COND_V(p_format_check != INVALID_FORMAT_ID && framebuffer.format_id != p_format_check, RID()); ERR_FAIL_COND_V(p_format_check != INVALID_FORMAT_ID && framebuffer.format_id != p_format_check, RID());
framebuffer.size = p_size; framebuffer.size = p_size;
framebuffer.view_count = 1;
return framebuffer_owner.make_rid(framebuffer); return framebuffer_owner.make_rid(framebuffer);
} }
RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check) { RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check, uint32_t p_view_count) {
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_
Vector<AttachmentFormat> attachments; Vector<AttachmentFormat> attachments;
@ -3594,6 +3622,8 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac
Texture *texture = texture_owner.getornull(p_texture_attachments[i]); Texture *texture = texture_owner.getornull(p_texture_attachments[i]);
ERR_FAIL_COND_V_MSG(!texture, RID(), "Texture index supplied for framebuffer (" + itos(i) + ") is not a valid texture."); ERR_FAIL_COND_V_MSG(!texture, RID(), "Texture index supplied for framebuffer (" + itos(i) + ") is not a valid texture.");
ERR_FAIL_COND_V_MSG(texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer");
if (i == 0) { if (i == 0) {
size.width = texture->width; size.width = texture->width;
size.height = texture->height; size.height = texture->height;
@ -3609,7 +3639,7 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac
attachments.push_back(af); attachments.push_back(af);
} }
FramebufferFormatID format_id = framebuffer_format_create(attachments); FramebufferFormatID format_id = framebuffer_format_create(attachments, p_view_count);
if (format_id == INVALID_ID) { if (format_id == INVALID_ID) {
return RID(); return RID();
} }
@ -3621,6 +3651,7 @@ RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attac
framebuffer.format_id = format_id; framebuffer.format_id = format_id;
framebuffer.texture_ids = p_texture_attachments; framebuffer.texture_ids = p_texture_attachments;
framebuffer.size = size; framebuffer.size = size;
framebuffer.view_count = p_view_count;
RID id = framebuffer_owner.make_rid(framebuffer); RID id = framebuffer_owner.make_rid(framebuffer);
@ -5904,12 +5935,13 @@ Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebu
vk.final_color_action = p_final_color_action; vk.final_color_action = p_final_color_action;
vk.initial_depth_action = p_initial_depth_action; vk.initial_depth_action = p_initial_depth_action;
vk.final_depth_action = p_final_depth_action; vk.final_depth_action = p_final_depth_action;
vk.view_count = p_framebuffer->view_count;
if (!p_framebuffer->framebuffers.has(vk)) { if (!p_framebuffer->framebuffers.has(vk)) {
//need to create this version //need to create this version
Framebuffer::Version version; Framebuffer::Version version;
version.render_pass = _render_pass_create(framebuffer_formats[p_framebuffer->format_id].E->key().attachments, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action); version.render_pass = _render_pass_create(framebuffer_formats[p_framebuffer->format_id].E->key().attachments, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, nullptr, p_framebuffer->view_count);
VkFramebufferCreateInfo framebuffer_create_info; VkFramebufferCreateInfo framebuffer_create_info;
framebuffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebuffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;

View File

@ -234,7 +234,12 @@ class RenderingDeviceVulkan : public RenderingDevice {
struct FramebufferFormatKey { struct FramebufferFormatKey {
Vector<AttachmentFormat> attachments; Vector<AttachmentFormat> attachments;
uint32_t view_count = 1;
bool operator<(const FramebufferFormatKey &p_key) const { bool operator<(const FramebufferFormatKey &p_key) const {
if (view_count != p_key.view_count) {
return view_count < p_key.view_count;
}
int as = attachments.size(); int as = attachments.size();
int bs = p_key.attachments.size(); int bs = p_key.attachments.size();
if (as != bs) { if (as != bs) {
@ -261,7 +266,7 @@ class RenderingDeviceVulkan : public RenderingDevice {
} }
}; };
VkRenderPass _render_pass_create(const Vector<AttachmentFormat> &p_format, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depthcolor_action, int *r_color_attachment_count = nullptr); VkRenderPass _render_pass_create(const Vector<AttachmentFormat> &p_format, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depthcolor_action, int *r_color_attachment_count = nullptr, uint32_t p_view_count = 1);
// This is a cache and it's never freed, it ensures // This is a cache and it's never freed, it ensures
// IDs for a given format are always unique. // IDs for a given format are always unique.
@ -271,6 +276,7 @@ class RenderingDeviceVulkan : public RenderingDevice {
VkRenderPass render_pass = VK_NULL_HANDLE; //here for constructing shaders, never used, see section (7.2. Render Pass Compatibility from Vulkan spec) VkRenderPass render_pass = VK_NULL_HANDLE; //here for constructing shaders, never used, see section (7.2. Render Pass Compatibility from Vulkan spec)
int color_attachments = 0; //used for pipeline validation int color_attachments = 0; //used for pipeline validation
TextureSamples samples; TextureSamples samples;
uint32_t view_count = 1; // number of views
}; };
Map<FramebufferFormatID, FramebufferFormat> framebuffer_formats; Map<FramebufferFormatID, FramebufferFormat> framebuffer_formats;
@ -282,11 +288,16 @@ class RenderingDeviceVulkan : public RenderingDevice {
FinalAction final_color_action; FinalAction final_color_action;
InitialAction initial_depth_action; InitialAction initial_depth_action;
FinalAction final_depth_action; FinalAction final_depth_action;
uint32_t view_count;
bool operator<(const VersionKey &p_key) const { bool operator<(const VersionKey &p_key) const {
if (initial_color_action == p_key.initial_color_action) { if (initial_color_action == p_key.initial_color_action) {
if (final_color_action == p_key.final_color_action) { if (final_color_action == p_key.final_color_action) {
if (initial_depth_action == p_key.initial_depth_action) { if (initial_depth_action == p_key.initial_depth_action) {
return final_depth_action < p_key.final_depth_action; if (final_depth_action == p_key.final_depth_action) {
return view_count < p_key.view_count;
} else {
return final_depth_action < p_key.final_depth_action;
}
} else { } else {
return initial_depth_action < p_key.initial_depth_action; return initial_depth_action < p_key.initial_depth_action;
} }
@ -309,6 +320,7 @@ class RenderingDeviceVulkan : public RenderingDevice {
Map<VersionKey, Version> framebuffers; Map<VersionKey, Version> framebuffers;
Size2 size; Size2 size;
uint32_t view_count;
}; };
RID_Owner<Framebuffer, true> framebuffer_owner; RID_Owner<Framebuffer, true> framebuffer_owner;
@ -938,11 +950,11 @@ public:
/**** FRAMEBUFFER ****/ /**** FRAMEBUFFER ****/
/*********************/ /*********************/
virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format); virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count = 1);
virtual FramebufferFormatID framebuffer_format_create_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1); virtual FramebufferFormatID framebuffer_format_create_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1);
virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format); virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format);
virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID); virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1);
virtual RID framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples = TEXTURE_SAMPLES_1, FramebufferFormatID p_format_check = INVALID_ID); virtual RID framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples = TEXTURE_SAMPLES_1, FramebufferFormatID p_format_check = INVALID_ID);
virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer); virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer);

View File

@ -337,6 +337,9 @@ Error VulkanContext::_initialize_extensions() {
extension_names[enabled_extension_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; extension_names[enabled_extension_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
enabled_debug_utils = true; enabled_debug_utils = true;
} }
if (!strcmp(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, instance_extensions[i].extensionName)) {
extension_names[enabled_extension_count++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME;
}
if (enabled_extension_count >= MAX_EXTENSIONS) { if (enabled_extension_count >= MAX_EXTENSIONS) {
free(instance_extensions); free(instance_extensions);
ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG"); ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG");
@ -504,6 +507,8 @@ Error VulkanContext::_check_capabilities() {
// assume not supported until proven otherwise // assume not supported until proven otherwise
multiview_capabilities.is_supported = false; multiview_capabilities.is_supported = false;
multiview_capabilities.geometry_shader_is_supported = false;
multiview_capabilities.tessellation_shader_is_supported = false;
multiview_capabilities.max_view_count = 0; multiview_capabilities.max_view_count = 0;
multiview_capabilities.max_instance_count = 0; multiview_capabilities.max_instance_count = 0;
subgroup_capabilities.size = 0; subgroup_capabilities.size = 0;
@ -529,7 +534,8 @@ Error VulkanContext::_check_capabilities() {
device_features_func(gpu, &device_features); device_features_func(gpu, &device_features);
multiview_capabilities.is_supported = multiview_features.multiview; multiview_capabilities.is_supported = multiview_features.multiview;
// For now we ignore if multiview is available in geometry and tessellation as we do not currently support those multiview_capabilities.geometry_shader_is_supported = multiview_features.multiviewGeometryShader;
multiview_capabilities.tessellation_shader_is_supported = multiview_features.multiviewTessellationShader;
} }
// check extended properties // check extended properties
@ -573,7 +579,7 @@ Error VulkanContext::_check_capabilities() {
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
print_line("- Vulkan multiview supported:"); print_line("- Vulkan multiview supported:");
print_line(" max views: " + itos(multiview_capabilities.max_view_count)); print_line(" max view count: " + itos(multiview_capabilities.max_view_count));
print_line(" max instances: " + itos(multiview_capabilities.max_instance_count)); print_line(" max instances: " + itos(multiview_capabilities.max_instance_count));
} else { } else {
print_line("- Vulkan multiview not supported"); print_line("- Vulkan multiview not supported");
@ -772,6 +778,10 @@ Error VulkanContext::_create_physical_device() {
swapchainExtFound = 1; swapchainExtFound = 1;
extension_names[enabled_extension_count++] = VK_KHR_SWAPCHAIN_EXTENSION_NAME; extension_names[enabled_extension_count++] = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
} }
if (!strcmp(VK_KHR_MULTIVIEW_EXTENSION_NAME, device_extensions[i].extensionName)) {
// if multiview is supported, enable it
extension_names[enabled_extension_count++] = VK_KHR_MULTIVIEW_EXTENSION_NAME;
}
if (enabled_extension_count >= MAX_EXTENSIONS) { if (enabled_extension_count >= MAX_EXTENSIONS) {
free(device_extensions); free(device_extensions);
ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG"); ERR_FAIL_V_MSG(ERR_BUG, "Enabled extension count reaches MAX_EXTENSIONS, BUG");
@ -964,6 +974,39 @@ Error VulkanContext::_create_device() {
queues[1].flags = 0; queues[1].flags = 0;
sdevice.queueCreateInfoCount = 2; sdevice.queueCreateInfoCount = 2;
} }
#ifdef VK_VERSION_1_2
VkPhysicalDeviceVulkan11Features vulkan11features;
vulkan11features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
vulkan11features.pNext = nullptr;
// !BAS! Need to figure out which ones of these we want enabled...
vulkan11features.storageBuffer16BitAccess = 0;
vulkan11features.uniformAndStorageBuffer16BitAccess = 0;
vulkan11features.storagePushConstant16 = 0;
vulkan11features.storageInputOutput16 = 0;
vulkan11features.multiview = multiview_capabilities.is_supported;
vulkan11features.multiviewGeometryShader = multiview_capabilities.geometry_shader_is_supported;
vulkan11features.multiviewTessellationShader = multiview_capabilities.tessellation_shader_is_supported;
vulkan11features.variablePointersStorageBuffer = 0;
vulkan11features.variablePointers = 0;
vulkan11features.protectedMemory = 0;
vulkan11features.samplerYcbcrConversion = 0;
vulkan11features.shaderDrawParameters = 0;
sdevice.pNext = &vulkan11features;
#elif VK_VERSION_1_1
VkPhysicalDeviceMultiviewFeatures multiview_features;
multiview_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES;
multiview_features.pNext = nullptr;
multiview_features.multiview = multiview_capabilities.is_supported;
multiview_features.multiviewGeometryShader = multiview_capabilities.geometry_shader_is_supported;
multiview_features.multiviewTessellationShader = multiview_capabilities.tessellation_shader_is_supported;
sdevice.pNext = &multiview_features;
#endif
err = vkCreateDevice(gpu, &sdevice, nullptr, &device); err = vkCreateDevice(gpu, &sdevice, nullptr, &device);
ERR_FAIL_COND_V(err, ERR_CANT_CREATE); ERR_FAIL_COND_V(err, ERR_CANT_CREATE);

View File

@ -56,8 +56,10 @@ public:
struct MultiviewCapabilities { struct MultiviewCapabilities {
bool is_supported; bool is_supported;
int32_t max_view_count; bool geometry_shader_is_supported;
int32_t max_instance_count; bool tessellation_shader_is_supported;
uint32_t max_view_count;
uint32_t max_instance_count;
}; };
private: private:

View File

@ -5068,7 +5068,7 @@
}, },
{ {
"name": "godot_xr_get_worldscale", "name": "godot_xr_get_worldscale",
"return_type": "godot_float", "return_type": "godot_real_t",
"arguments": [] "arguments": []
}, },
{ {
@ -5189,7 +5189,7 @@
"p_exis" "p_exis"
], ],
[ [
"godot_float", "godot_real_t",
"p_value" "p_value"
], ],
[ [
@ -5200,7 +5200,7 @@
}, },
{ {
"name": "godot_xr_get_controller_rumble", "name": "godot_xr_get_controller_rumble",
"return_type": "godot_float", "return_type": "godot_real_t",
"arguments": [ "arguments": [
[ [
"godot_int", "godot_int",

View File

@ -41,8 +41,8 @@ extern "C" {
// version info to detect whether a call is available // version info to detect whether a call is available
// Use these to populate version in your plugin // Use these to populate version in your plugin
#define GODOTVR_API_MAJOR 1 #define GODOTVR_API_MAJOR 4
#define GODOTVR_API_MINOR 1 #define GODOTVR_API_MINOR 0
typedef struct { typedef struct {
godot_gdnative_api_version version; /* version of our API */ godot_gdnative_api_version version; /* version of our API */
@ -52,24 +52,31 @@ typedef struct {
godot_int (*get_capabilities)(const void *); godot_int (*get_capabilities)(const void *);
godot_bool (*get_anchor_detection_is_enabled)(const void *); godot_bool (*get_anchor_detection_is_enabled)(const void *);
void (*set_anchor_detection_is_enabled)(void *, godot_bool); void (*set_anchor_detection_is_enabled)(void *, godot_bool);
godot_bool (*is_stereo)(const void *); godot_int (*get_view_count)(const void *);
godot_bool (*is_initialized)(const void *); godot_bool (*is_initialized)(const void *);
godot_bool (*initialize)(void *); godot_bool (*initialize)(void *);
void (*uninitialize)(void *); void (*uninitialize)(void *);
godot_vector2 (*get_render_targetsize)(const void *); godot_vector2 (*get_render_targetsize)(const void *);
godot_transform3d (*get_transform_for_eye)(void *, godot_int, godot_transform3d *);
void (*fill_projection_for_eye)(void *, godot_float *, godot_int, godot_float, godot_float, godot_float); godot_transform3d (*get_camera_transform)(void *);
void (*commit_for_eye)(void *, godot_int, godot_rid *, godot_rect2 *); godot_transform3d (*get_transform_for_view)(void *, godot_int, godot_transform3d *);
void (*fill_projection_for_view)(void *, godot_real_t *, godot_int, godot_real_t, godot_real_t, godot_real_t);
void (*commit_views)(void *, godot_rid *, godot_rect2 *);
void (*process)(void *); void (*process)(void *);
godot_int (*get_external_texture_for_eye)(void *, godot_int);
void (*notification)(void *, godot_int); void (*notification)(void *, godot_int);
godot_int (*get_camera_feed_id)(void *); godot_int (*get_camera_feed_id)(void *);
// possibly deprecate but adding/keeping as a reminder these are in Godot 3
void (*commit_for_eye)(void *, godot_int, godot_rid *, godot_rect2 *);
godot_int (*get_external_texture_for_eye)(void *, godot_int);
godot_int (*get_external_depth_for_eye)(void *, godot_int);
} godot_xr_interface_gdnative; } godot_xr_interface_gdnative;
void GDAPI godot_xr_register_interface(const godot_xr_interface_gdnative *p_interface); void GDAPI godot_xr_register_interface(const godot_xr_interface_gdnative *p_interface);
// helper functions to access XRServer data // helper functions to access XRServer data
godot_float GDAPI godot_xr_get_worldscale(); godot_real_t GDAPI godot_xr_get_worldscale();
godot_transform3d GDAPI godot_xr_get_reference_frame(); godot_transform3d GDAPI godot_xr_get_reference_frame();
// helper functions for rendering // helper functions for rendering
@ -81,8 +88,8 @@ godot_int GDAPI godot_xr_add_controller(char *p_device_name, godot_int p_hand, g
void GDAPI godot_xr_remove_controller(godot_int p_controller_id); void GDAPI godot_xr_remove_controller(godot_int p_controller_id);
void GDAPI godot_xr_set_controller_transform(godot_int p_controller_id, godot_transform3d *p_transform, godot_bool p_tracks_orientation, godot_bool p_tracks_position); void GDAPI godot_xr_set_controller_transform(godot_int p_controller_id, godot_transform3d *p_transform, godot_bool p_tracks_orientation, godot_bool p_tracks_position);
void GDAPI godot_xr_set_controller_button(godot_int p_controller_id, godot_int p_button, godot_bool p_is_pressed); void GDAPI godot_xr_set_controller_button(godot_int p_controller_id, godot_int p_button, godot_bool p_is_pressed);
void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_float p_value, godot_bool p_can_be_negative); void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_real_t p_value, godot_bool p_can_be_negative);
godot_float GDAPI godot_xr_get_controller_rumble(godot_int p_controller_id); godot_real_t GDAPI godot_xr_get_controller_rumble(godot_int p_controller_id);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -70,8 +70,13 @@ void XRInterfaceGDNative::set_interface(const godot_xr_interface_gdnative *p_int
// this should only be called once, just being paranoid.. // this should only be called once, just being paranoid..
if (interface) { if (interface) {
cleanup(); cleanup();
interface = NULL;
} }
// validate
ERR_FAIL_NULL(p_interface);
ERR_FAIL_COND_MSG(p_interface->version.major < 4, "This is an incompatible GDNative XR plugin.");
// bind to our interface // bind to our interface
interface = p_interface; interface = p_interface;
@ -119,14 +124,14 @@ int XRInterfaceGDNative::get_camera_feed_id() {
return (unsigned int)interface->get_camera_feed_id(data); return (unsigned int)interface->get_camera_feed_id(data);
} }
bool XRInterfaceGDNative::is_stereo() { uint32_t XRInterfaceGDNative::get_view_count() {
bool stereo; uint32_t view_count;
ERR_FAIL_COND_V(interface == nullptr, false); ERR_FAIL_COND_V(interface == nullptr, 1);
stereo = interface->is_stereo(data); view_count = interface->get_view_count(data);
return stereo; return view_count;
} }
bool XRInterfaceGDNative::is_initialized() const { bool XRInterfaceGDNative::is_initialized() const {
@ -173,28 +178,52 @@ Size2 XRInterfaceGDNative::get_render_targetsize() {
return *vec; return *vec;
} }
Transform3D XRInterfaceGDNative::get_transform_for_eye(XRInterface::Eyes p_eye, const Transform3D &p_cam_transform) { Transform3D XRInterfaceGDNative::get_camera_transform() {
Transform3D *ret; Transform3D *ret;
ERR_FAIL_COND_V(interface == nullptr, Transform3D()); ERR_FAIL_COND_V(interface == nullptr, Transform3D());
godot_transform3d t = interface->get_transform_for_eye(data, (int)p_eye, (godot_transform3d *)&p_cam_transform); godot_transform3d t = interface->get_camera_transform(data);
ret = (Transform3D *)&t; ret = (Transform3D *)&t;
return *ret; return *ret;
} }
CameraMatrix XRInterfaceGDNative::get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) { Transform3D XRInterfaceGDNative::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) {
Transform3D *ret;
ERR_FAIL_COND_V(interface == nullptr, Transform3D());
godot_transform3d t = interface->get_transform_for_view(data, (int)p_view, (godot_transform3d *)&p_cam_transform);
ret = (Transform3D *)&t;
return *ret;
}
CameraMatrix XRInterfaceGDNative::get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) {
CameraMatrix cm; CameraMatrix cm;
ERR_FAIL_COND_V(interface == nullptr, CameraMatrix()); ERR_FAIL_COND_V(interface == nullptr, CameraMatrix());
interface->fill_projection_for_eye(data, (godot_float *)cm.matrix, (godot_int)p_eye, p_aspect, p_z_near, p_z_far); interface->fill_projection_for_view(data, (godot_real_t *)cm.matrix, (godot_int)p_view, p_aspect, p_z_near, p_z_far);
return cm; return cm;
} }
Vector<BlitToScreen> XRInterfaceGDNative::commit_views(RID p_render_target, const Rect2 &p_screen_rect) {
// possibly move this as a member variable and add a callback to populate?
Vector<BlitToScreen> blit_to_screen;
ERR_FAIL_COND_V(interface == nullptr, blit_to_screen);
// must implement
interface->commit_views(data, (godot_rid *)&p_render_target, (godot_rect2 *)&p_screen_rect);
return blit_to_screen;
}
unsigned int XRInterfaceGDNative::get_external_texture_for_eye(XRInterface::Eyes p_eye) { unsigned int XRInterfaceGDNative::get_external_texture_for_eye(XRInterface::Eyes p_eye) {
ERR_FAIL_COND_V(interface == nullptr, 0); ERR_FAIL_COND_V(interface == nullptr, 0);
@ -234,7 +263,7 @@ void GDAPI godot_xr_register_interface(const godot_xr_interface_gdnative *p_inte
XRServer::get_singleton()->add_interface(new_interface); XRServer::get_singleton()->add_interface(new_interface);
} }
godot_float GDAPI godot_xr_get_worldscale() { godot_real_t GDAPI godot_xr_get_worldscale() {
XRServer *xr_server = XRServer::get_singleton(); XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL_V(xr_server, 1.0); ERR_FAIL_NULL_V(xr_server, 1.0);
@ -388,7 +417,7 @@ void GDAPI godot_xr_set_controller_button(godot_int p_controller_id, godot_int p
} }
} }
void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_float p_value, godot_bool p_can_be_negative) { void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_real_t p_value, godot_bool p_can_be_negative) {
XRServer *xr_server = XRServer::get_singleton(); XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL(xr_server); ERR_FAIL_NULL(xr_server);
@ -407,7 +436,7 @@ void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_a
} }
} }
godot_float GDAPI godot_xr_get_controller_rumble(godot_int p_controller_id) { godot_real_t GDAPI godot_xr_get_controller_rumble(godot_int p_controller_id) {
XRServer *xr_server = XRServer::get_singleton(); XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL_V(xr_server, 0.0); ERR_FAIL_NULL_V(xr_server, 0.0);

View File

@ -72,20 +72,24 @@ public:
/** rendering and internal **/ /** rendering and internal **/
virtual Size2 get_render_targetsize() override; virtual Size2 get_render_targetsize() override;
virtual bool is_stereo() override; virtual uint32_t get_view_count() override;
virtual Transform3D get_transform_for_eye(XRInterface::Eyes p_eye, const Transform3D &p_cam_transform) override; virtual Transform3D get_camera_transform() override;
virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
// we expose a Vector<float> version of this function to GDNative // we expose a Vector<float> version of this function to GDNative
Vector<float> _get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); Vector<float> _get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far);
// and a CameraMatrix version to XRServer // and a CameraMatrix version to XRServer
virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) override; virtual CameraMatrix get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) override;
virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye) override; virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) override;
virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override;
virtual void process() override; virtual void process() override;
virtual void notification(int p_what) override; virtual void notification(int p_what) override;
// deprecated
virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override;
virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye) override;
}; };
#endif // XR_INTERFACE_GDNATIVE_H #endif // XR_INTERFACE_GDNATIVE_H

View File

@ -116,6 +116,10 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
} }
} }
if (p_capabilities->supports_multiview) {
preamble += "#define has_VK_KHR_multiview 1\n";
}
if (preamble != "") { if (preamble != "") {
shader.setPreamble(preamble.c_str()); shader.setPreamble(preamble.c_str());
} }

View File

@ -300,9 +300,9 @@ real_t MobileVRInterface::get_k2() const {
return k2; return k2;
}; };
bool MobileVRInterface::is_stereo() { uint32_t MobileVRInterface::get_view_count() {
// needs stereo... // needs stereo...
return true; return 2;
}; };
bool MobileVRInterface::is_initialized() const { bool MobileVRInterface::is_initialized() const {
@ -361,7 +361,29 @@ Size2 MobileVRInterface::get_render_targetsize() {
return target_size; return target_size;
}; };
Transform3D MobileVRInterface::get_transform_for_eye(XRInterface::Eyes p_eye, const Transform3D &p_cam_transform) { Transform3D MobileVRInterface::get_camera_transform() {
_THREAD_SAFE_METHOD_
Transform3D transform_for_eye;
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL_V(xr_server, transform_for_eye);
if (initialized) {
float world_scale = xr_server->get_world_scale();
// just scale our origin point of our transform
Transform3D hmd_transform;
hmd_transform.basis = orientation;
hmd_transform.origin = Vector3(0.0, eye_height * world_scale, 0.0);
transform_for_eye = (xr_server->get_reference_frame()) * hmd_transform;
}
return transform_for_eye;
};
Transform3D MobileVRInterface::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) {
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_
Transform3D transform_for_eye; Transform3D transform_for_eye;
@ -374,12 +396,12 @@ Transform3D MobileVRInterface::get_transform_for_eye(XRInterface::Eyes p_eye, co
// we don't need to check for the existence of our HMD, doesn't affect our values... // we don't need to check for the existence of our HMD, doesn't affect our values...
// note * 0.01 to convert cm to m and * 0.5 as we're moving half in each direction... // note * 0.01 to convert cm to m and * 0.5 as we're moving half in each direction...
if (p_eye == XRInterface::EYE_LEFT) { if (p_view == 0) {
transform_for_eye.origin.x = -(intraocular_dist * 0.01 * 0.5 * world_scale); transform_for_eye.origin.x = -(intraocular_dist * 0.01 * 0.5 * world_scale);
} else if (p_eye == XRInterface::EYE_RIGHT) { } else if (p_view == 1) {
transform_for_eye.origin.x = intraocular_dist * 0.01 * 0.5 * world_scale; transform_for_eye.origin.x = intraocular_dist * 0.01 * 0.5 * world_scale;
} else { } else {
// for mono we don't reposition, we want our center position. // should not have any other values..
}; };
// just scale our origin point of our transform // just scale our origin point of our transform
@ -396,21 +418,13 @@ Transform3D MobileVRInterface::get_transform_for_eye(XRInterface::Eyes p_eye, co
return transform_for_eye; return transform_for_eye;
}; };
CameraMatrix MobileVRInterface::get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) { CameraMatrix MobileVRInterface::get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) {
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_
CameraMatrix eye; CameraMatrix eye;
if (p_eye == XRInterface::EYE_MONO) { aspect = p_aspect;
///@TODO for now hardcode some of this, what is really needed here is that this needs to be in sync with the real camera's properties eye.set_for_hmd(p_view + 1, p_aspect, intraocular_dist, display_width, display_to_lens, oversample, p_z_near, p_z_far);
// which probably means implementing a specific class for iOS and Android. For now this is purely here as an example.
// Note also that if you use a normal viewport with AR/VR turned off you can still use the tracker output of this interface
// to position a stock standard Godot camera and have control over this.
// This will make more sense when we implement ARkit on iOS (probably a separate interface).
eye.set_perspective(60.0, p_aspect, p_z_near, p_z_far, false);
} else {
eye.set_for_hmd(p_eye == XRInterface::EYE_LEFT ? 1 : 2, p_aspect, intraocular_dist, display_width, display_to_lens, oversample, p_z_near, p_z_far);
};
return eye; return eye;
}; };
@ -440,6 +454,45 @@ void MobileVRInterface::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_tar
eye_center.y = 0.0; eye_center.y = 0.0;
} }
Vector<BlitToScreen> MobileVRInterface::commit_views(RID p_render_target, const Rect2 &p_screen_rect) {
_THREAD_SAFE_METHOD_
Vector<BlitToScreen> blit_to_screen;
// We must have a valid render target
ERR_FAIL_COND_V(!p_render_target.is_valid(), blit_to_screen);
// Because we are rendering to our device we must use our main viewport!
ERR_FAIL_COND_V(p_screen_rect == Rect2(), blit_to_screen);
// and add our blits
BlitToScreen blit;
blit.render_target = p_render_target;
blit.multi_view.use_layer = true;
blit.lens_distortion.apply = true;
blit.lens_distortion.k1 = k1;
blit.lens_distortion.k2 = k2;
blit.lens_distortion.upscale = oversample;
blit.lens_distortion.aspect_ratio = aspect;
// left eye
blit.rect = p_screen_rect;
blit.rect.size.width *= 0.5;
blit.multi_view.layer = 0;
blit.lens_distortion.eye_center.x = ((-intraocular_dist / 2.0) + (display_width / 4.0)) / (display_width / 2.0);
blit_to_screen.push_back(blit);
// right eye
blit.rect = p_screen_rect;
blit.rect.size.width *= 0.5;
blit.rect.position.x = blit.rect.size.width;
blit.multi_view.layer = 1;
blit.lens_distortion.eye_center.x = ((intraocular_dist / 2.0) - (display_width / 4.0)) / (display_width / 2.0);
blit_to_screen.push_back(blit);
return blit_to_screen;
}
void MobileVRInterface::process() { void MobileVRInterface::process() {
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_

View File

@ -63,9 +63,9 @@ private:
real_t display_to_lens = 4.0; real_t display_to_lens = 4.0;
real_t oversample = 1.5; real_t oversample = 1.5;
//@TODO not yet used, these are needed in our distortion shader...
real_t k1 = 0.215; real_t k1 = 0.215;
real_t k2 = 0.215; real_t k2 = 0.215;
real_t aspect = 1.0;
/* /*
logic for processing our sensor data, this was originally in our positional tracker logic but I think logic for processing our sensor data, this was originally in our positional tracker logic but I think
@ -138,16 +138,20 @@ public:
virtual void uninitialize() override; virtual void uninitialize() override;
virtual Size2 get_render_targetsize() override; virtual Size2 get_render_targetsize() override;
virtual bool is_stereo() override; virtual uint32_t get_view_count() override;
virtual Transform3D get_transform_for_eye(XRInterface::Eyes p_eye, const Transform3D &p_cam_transform) override; virtual Transform3D get_camera_transform() override;
virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) override; virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override; virtual CameraMatrix get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) override;
virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) override;
virtual void process() override; virtual void process() override;
virtual void notification(int p_what) override {} virtual void notification(int p_what) override {}
MobileVRInterface(); MobileVRInterface();
~MobileVRInterface(); ~MobileVRInterface();
// deprecated
virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override;
}; };
#endif // !MOBILE_VR_INTERFACE_H #endif // !MOBILE_VR_INTERFACE_H

View File

@ -265,7 +265,7 @@ void WebXRInterfaceJS::uninitialize() {
}; };
}; };
Transform WebXRInterfaceJS::_js_matrix_to_transform(float *p_js_matrix) { Transform3D WebXRInterfaceJS::_js_matrix_to_transform(float *p_js_matrix) {
Transform3D transform; Transform3D transform;
transform.basis.elements[0].x = p_js_matrix[0]; transform.basis.elements[0].x = p_js_matrix[0];
@ -305,13 +305,30 @@ Size2 WebXRInterfaceJS::get_render_targetsize() {
return render_targetsize; return render_targetsize;
}; };
Transform WebXRInterfaceJS::get_transform_for_eye(XRInterface::Eyes p_eye, const Transform3D &p_cam_transform) { Transform3D WebXRInterfaceJS::get_camera_transform() {
Transform3D transform_for_eye; Transform3D transform_for_eye;
XRServer *xr_server = XRServer::get_singleton(); XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL_V(xr_server, transform_for_eye); ERR_FAIL_NULL_V(xr_server, transform_for_eye);
float *js_matrix = godot_webxr_get_transform_for_eye(p_eye); float *js_matrix = godot_webxr_get_transform_for_eye(0);
if (!initialized || js_matrix == nullptr) {
return transform_for_eye;
}
transform_for_eye = _js_matrix_to_transform(js_matrix);
free(js_matrix);
return xr_server->get_reference_frame() * transform_for_eye;
};
Transform3D WebXRInterfaceJS::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) {
Transform3D transform_for_eye;
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL_V(xr_server, transform_for_eye);
float *js_matrix = godot_webxr_get_transform_for_eye(p_view + 1);
if (!initialized || js_matrix == nullptr) { if (!initialized || js_matrix == nullptr) {
transform_for_eye = p_cam_transform; transform_for_eye = p_cam_transform;
return transform_for_eye; return transform_for_eye;
@ -323,10 +340,10 @@ Transform WebXRInterfaceJS::get_transform_for_eye(XRInterface::Eyes p_eye, const
return p_cam_transform * xr_server->get_reference_frame() * transform_for_eye; return p_cam_transform * xr_server->get_reference_frame() * transform_for_eye;
}; };
CameraMatrix WebXRInterfaceJS::get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) { CameraMatrix WebXRInterfaceJS::get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) {
CameraMatrix eye; CameraMatrix eye;
float *js_matrix = godot_webxr_get_projection_for_eye(p_eye); float *js_matrix = godot_webxr_get_projection_for_eye(p_view + 1);
if (!initialized || js_matrix == nullptr) { if (!initialized || js_matrix == nullptr) {
return eye; return eye;
} }

View File

@ -84,8 +84,9 @@ public:
virtual Size2 get_render_targetsize() override; virtual Size2 get_render_targetsize() override;
virtual bool is_stereo() override; virtual bool is_stereo() override;
virtual Transform get_transform_for_eye(XRInterface::Eyes p_eye, const Transform3D &p_cam_transform) override; virtual Transform3D get_camera_transform() override;
virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) override; virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
virtual CameraMatrix get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) override;
virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye) override; virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye) override;
virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override; virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override;

View File

@ -86,7 +86,8 @@ Vector3 XRCamera3D::project_local_ray_normal(const Point2 &p_pos) const {
Vector2 cpos = get_viewport()->get_camera_coords(p_pos); Vector2 cpos = get_viewport()->get_camera_coords(p_pos);
Vector3 ray; Vector3 ray;
CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_near(), get_far()); // Just use the first view, if multiple views are supported this function has no good result
CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
Vector2 screen_he = cm.get_viewport_half_extents(); Vector2 screen_he = cm.get_viewport_half_extents();
ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -get_near()).normalized(); ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -get_near()).normalized();
@ -108,7 +109,8 @@ Point2 XRCamera3D::unproject_position(const Vector3 &p_pos) const {
Size2 viewport_size = get_viewport()->get_visible_rect().size; Size2 viewport_size = get_viewport()->get_visible_rect().size;
CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_near(), get_far()); // Just use the first view, if multiple views are supported this function has no good result
CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
Plane p(get_camera_transform().xform_inv(p_pos), 1.0); Plane p(get_camera_transform().xform_inv(p_pos), 1.0);
@ -137,7 +139,8 @@ Vector3 XRCamera3D::project_position(const Point2 &p_point, float p_z_depth) con
Size2 viewport_size = get_viewport()->get_visible_rect().size; Size2 viewport_size = get_viewport()->get_visible_rect().size;
CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_near(), get_far()); // Just use the first view, if multiple views are supported this function has no good result
CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
Vector2 vp_he = cm.get_viewport_half_extents(); Vector2 vp_he = cm.get_viewport_half_extents();
@ -165,7 +168,8 @@ Vector<Plane> XRCamera3D::get_frustum() const {
ERR_FAIL_COND_V(!is_inside_world(), Vector<Plane>()); ERR_FAIL_COND_V(!is_inside_world(), Vector<Plane>());
Size2 viewport_size = get_viewport()->get_visible_rect().size; Size2 viewport_size = get_viewport()->get_visible_rect().size;
CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_near(), get_far()); // TODO Just use the first view for now, this is mostly for debugging so we may look into using our combined projection here.
CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far());
return cm.get_projection_planes(get_camera_transform()); return cm.get_projection_planes(get_camera_transform());
}; };
@ -516,6 +520,11 @@ TypedArray<String> XROrigin3D::get_configuration_warnings() const {
} }
} }
bool vr_enabled = GLOBAL_GET("rendering/vr/enabled");
if (!vr_enabled) {
warnings.push_back(TTR("VR is not enabled in rendering project settings. Stereoscopic output is not supported unless this is enabled."));
}
return warnings; return warnings;
}; };
@ -571,7 +580,7 @@ void XROrigin3D::_notification(int p_what) {
Ref<XRInterface> xr_interface = xr_server->get_primary_interface(); Ref<XRInterface> xr_interface = xr_server->get_primary_interface();
if (xr_interface.is_valid() && tracked_camera != nullptr) { if (xr_interface.is_valid() && tracked_camera != nullptr) {
// get our positioning transform for our headset // get our positioning transform for our headset
Transform3D t = xr_interface->get_transform_for_eye(XRInterface::EYE_MONO, Transform3D()); Transform3D t = xr_interface->get_camera_transform();
// now apply this to our camera // now apply this to our camera
tracked_camera->set_transform(t); tracked_camera->set_transform(t);

View File

@ -175,7 +175,7 @@ public:
void voxel_gi_set_quality(RS::VoxelGIQuality) override {} void voxel_gi_set_quality(RS::VoxelGIQuality) override {}
void render_scene(RID p_render_buffers, const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr) override {} void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr) override {}
void render_material(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override {} void render_material(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override {}
void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<GeometryInstance *> &p_instances) override {} void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<GeometryInstance *> &p_instances) override {}
@ -184,7 +184,7 @@ public:
void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) override {} void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) override {}
RID render_buffers_create() override { return RID(); } RID render_buffers_create() override { return RID(); }
void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding) override {} void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding, uint32_t p_view_count) override {}
void gi_set_use_half_resolution(bool p_enable) override {} void gi_set_use_half_resolution(bool p_enable) override {}
void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_curve) override {} void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_curve) override {}
@ -649,7 +649,7 @@ public:
RID render_target_create() override { return RID(); } RID render_target_create() override { return RID(); }
void render_target_set_position(RID p_render_target, int p_x, int p_y) override {} void render_target_set_position(RID p_render_target, int p_x, int p_y) override {}
void render_target_set_size(RID p_render_target, int p_width, int p_height) override {} void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) override {}
RID render_target_get_texture(RID p_render_target) override { return RID(); } RID render_target_get_texture(RID p_render_target) override { return RID(); }
void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) override {} void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) override {}
void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) override {} void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) override {}

View File

@ -30,6 +30,7 @@
#include "renderer_compositor.h" #include "renderer_compositor.h"
#include "core/config/project_settings.h"
#include "core/os/os.h" #include "core/os/os.h"
#include "core/string/print_string.h" #include "core/string/print_string.h"
@ -39,4 +40,12 @@ RendererCompositor *RendererCompositor::create() {
return _create_func(); return _create_func();
} }
bool RendererCompositor::is_xr_enabled() const {
return xr_enabled;
}
RendererCompositor::RendererCompositor() {
xr_enabled = GLOBAL_GET("rendering/xr/enabled");
}
RendererCanvasRender *RendererCanvasRender::singleton = nullptr; RendererCanvasRender *RendererCanvasRender::singleton = nullptr;

View File

@ -40,7 +40,31 @@
#include "servers/rendering/renderer_storage.h" #include "servers/rendering/renderer_storage.h"
#include "servers/rendering_server.h" #include "servers/rendering_server.h"
struct BlitToScreen {
RID render_target;
Rect2i rect;
struct {
bool use_layer = false;
uint32_t layer = 0;
} multi_view;
struct {
//lens distorted parameters for VR
bool apply = false;
Vector2 eye_center;
float k1 = 0.0;
float k2 = 0.0;
float upscale = 1.0;
float aspect_ratio = 1.0;
} lens_distortion;
};
class RendererCompositor { class RendererCompositor {
private:
bool xr_enabled = false;
protected: protected:
static RendererCompositor *(*_create_func)(); static RendererCompositor *(*_create_func)();
@ -56,27 +80,6 @@ public:
virtual void initialize() = 0; virtual void initialize() = 0;
virtual void begin_frame(double frame_step) = 0; virtual void begin_frame(double frame_step) = 0;
struct BlitToScreen {
RID render_target;
Rect2i rect;
struct {
bool use_layer = false;
uint32_t layer = 0;
} multi_view;
struct {
//lens distorted parameters for VR
bool apply = false;
Vector2 eye_center;
float k1 = 0.0;
float k2 = 0.0;
float upscale = 1.0;
float aspect_ratio = 1.0;
} lens_distortion;
};
virtual void prepare_for_blitting_render_targets() = 0; virtual void prepare_for_blitting_render_targets() = 0;
virtual void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount) = 0; virtual void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount) = 0;
@ -86,7 +89,9 @@ public:
virtual float get_frame_delta_time() const = 0; virtual float get_frame_delta_time() const = 0;
virtual bool is_low_end() const = 0; virtual bool is_low_end() const = 0;
virtual bool is_xr_enabled() const;
RendererCompositor();
virtual ~RendererCompositor() {} virtual ~RendererCompositor() {}
}; };

View File

@ -34,6 +34,7 @@
#include "core/math/math_defs.h" #include "core/math/math_defs.h"
#include "core/os/os.h" #include "core/os/os.h"
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
#include "thirdparty/misc/cubemap_coeffs.h" #include "thirdparty/misc/cubemap_coeffs.h"
static _FORCE_INLINE_ void store_transform_3x3(const Basis &p_basis, float *p_array) { static _FORCE_INLINE_ void store_transform_3x3(const Basis &p_basis, float *p_array) {
@ -732,6 +733,11 @@ void EffectsRD::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Tone
tonemap.push_constant.pixel_size[0] = 1.0 / p_settings.texture_size.x; tonemap.push_constant.pixel_size[0] = 1.0 / p_settings.texture_size.x;
tonemap.push_constant.pixel_size[1] = 1.0 / p_settings.texture_size.y; tonemap.push_constant.pixel_size[1] = 1.0 / p_settings.texture_size.y;
if (p_settings.view_count > 1) {
// Use MULTIVIEW versions
mode += 4;
}
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, tonemap.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer))); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, tonemap.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_color), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_color), 0);
@ -1365,15 +1371,18 @@ void EffectsRD::cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap,
RD::get_singleton()->compute_list_end(); RD::get_singleton()->compute_list_end();
} }
void EffectsRD::render_sky(RD::DrawListID p_list, float p_time, RID p_fb, RID p_samplers, RID p_fog, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, const CameraMatrix &p_camera, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position) { void EffectsRD::render_sky(RD::DrawListID p_list, float p_time, RID p_fb, RID p_samplers, RID p_fog, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const CameraMatrix *p_projections, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position) {
SkyPushConstant sky_push_constant; SkyPushConstant sky_push_constant;
memset(&sky_push_constant, 0, sizeof(SkyPushConstant)); memset(&sky_push_constant, 0, sizeof(SkyPushConstant));
sky_push_constant.proj[0] = p_camera.matrix[2][0]; for (uint32_t v = 0; v < p_view_count; v++) {
sky_push_constant.proj[1] = p_camera.matrix[0][0]; // We only need key components of our projection matrix
sky_push_constant.proj[2] = p_camera.matrix[2][1]; sky_push_constant.projections[v][0] = p_projections[v].matrix[2][0];
sky_push_constant.proj[3] = p_camera.matrix[1][1]; sky_push_constant.projections[v][1] = p_projections[v].matrix[0][0];
sky_push_constant.projections[v][2] = p_projections[v].matrix[2][1];
sky_push_constant.projections[v][3] = p_projections[v].matrix[1][1];
}
sky_push_constant.position[0] = p_position.x; sky_push_constant.position[0] = p_position.x;
sky_push_constant.position[1] = p_position.y; sky_push_constant.position[1] = p_position.y;
sky_push_constant.position[2] = p_position.z; sky_push_constant.position[2] = p_position.z;
@ -1553,8 +1562,21 @@ EffectsRD::EffectsRD() {
tonemap_modes.push_back("\n#define USE_1D_LUT\n"); tonemap_modes.push_back("\n#define USE_1D_LUT\n");
tonemap_modes.push_back("\n#define USE_GLOW_FILTER_BICUBIC\n#define USE_1D_LUT\n"); tonemap_modes.push_back("\n#define USE_GLOW_FILTER_BICUBIC\n#define USE_1D_LUT\n");
// multiview versions of our shaders
tonemap_modes.push_back("\n#define MULTIVIEW\n");
tonemap_modes.push_back("\n#define MULTIVIEW\n#define USE_GLOW_FILTER_BICUBIC\n");
tonemap_modes.push_back("\n#define MULTIVIEW\n#define USE_1D_LUT\n");
tonemap_modes.push_back("\n#define MULTIVIEW\n#define USE_GLOW_FILTER_BICUBIC\n#define USE_1D_LUT\n");
tonemap.shader.initialize(tonemap_modes); tonemap.shader.initialize(tonemap_modes);
if (!RendererCompositorRD::singleton->is_xr_enabled()) {
tonemap.shader.set_variant_enabled(TONEMAP_MODE_NORMAL_MULTIVIEW, false);
tonemap.shader.set_variant_enabled(TONEMAP_MODE_BICUBIC_GLOW_FILTER_MULTIVIEW, false);
tonemap.shader.set_variant_enabled(TONEMAP_MODE_1D_LUT_MULTIVIEW, false);
tonemap.shader.set_variant_enabled(TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT_MULTIVIEW, false);
}
tonemap.shader_version = tonemap.shader.version_create(); tonemap.shader_version = tonemap.shader.version_create();
for (int i = 0; i < TONEMAP_MODE_MAX; i++) { for (int i = 0; i < TONEMAP_MODE_MAX; i++) {

View File

@ -55,6 +55,7 @@
#include "servers/rendering/renderer_rd/shaders/ssao_interleave.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/ssao_interleave.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/subsurface_scattering.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/subsurface_scattering.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/tonemap.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/tonemap.glsl.gen.h"
#include "servers/rendering/renderer_scene_render.h"
#include "servers/rendering_server.h" #include "servers/rendering_server.h"
@ -170,6 +171,12 @@ class EffectsRD {
TONEMAP_MODE_BICUBIC_GLOW_FILTER, TONEMAP_MODE_BICUBIC_GLOW_FILTER,
TONEMAP_MODE_1D_LUT, TONEMAP_MODE_1D_LUT,
TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT, TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT,
TONEMAP_MODE_NORMAL_MULTIVIEW,
TONEMAP_MODE_BICUBIC_GLOW_FILTER_MULTIVIEW,
TONEMAP_MODE_1D_LUT_MULTIVIEW,
TONEMAP_MODE_BICUBIC_GLOW_FILTER_1D_LUT_MULTIVIEW,
TONEMAP_MODE_MAX TONEMAP_MODE_MAX
}; };
@ -453,12 +460,12 @@ class EffectsRD {
} filter; } filter;
struct SkyPushConstant { struct SkyPushConstant {
float orientation[12]; float orientation[12]; // 48 - 48
float proj[4]; float projections[RendererSceneRender::MAX_RENDER_VIEWS][4]; // 2 x 16 - 64, if we ever need more then 3 we should consider adding this to a set.
float position[3]; float position[3]; // 12 - 92
float multiplier; float multiplier; // 4 - 96
float time; float time; // 4 - 100
float pad[3]; float pad[3]; // 12 - 112
}; };
enum SpecularMergeMode { enum SpecularMergeMode {
@ -714,6 +721,7 @@ public:
bool use_fxaa = false; bool use_fxaa = false;
bool use_debanding = false; bool use_debanding = false;
Vector2i texture_size; Vector2i texture_size;
uint32_t view_count = 1;
}; };
struct SSAOSettings { struct SSAOSettings {
@ -744,7 +752,7 @@ public:
void roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve); void roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve);
void cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size); void cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size);
void cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, bool p_use_array); void cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, bool p_use_array);
void render_sky(RD::DrawListID p_list, float p_time, RID p_fb, RID p_samplers, RID p_fog, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, const CameraMatrix &p_camera, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position); void render_sky(RD::DrawListID p_list, float p_time, RID p_fb, RID p_samplers, RID p_fog, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const CameraMatrix *p_projections, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position);
void screen_space_reflection(RID p_diffuse, RID p_normal_roughness, RS::EnvironmentSSRRoughnessQuality p_roughness_quality, RID p_blur_radius, RID p_blur_radius2, RID p_metallic, const Color &p_metallic_mask, RID p_depth, RID p_scale_depth, RID p_scale_normal, RID p_output, RID p_output_blur, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const CameraMatrix &p_camera); void screen_space_reflection(RID p_diffuse, RID p_normal_roughness, RS::EnvironmentSSRRoughnessQuality p_roughness_quality, RID p_blur_radius, RID p_blur_radius2, RID p_metallic, const Color &p_metallic_mask, RID p_depth, RID p_scale_depth, RID p_scale_normal, RID p_output, RID p_output_blur, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const CameraMatrix &p_camera);
void merge_specular(RID p_dest_framebuffer, RID p_specular, RID p_base, RID p_reflection); void merge_specular(RID p_dest_framebuffer, RID p_specular, RID p_base, RID p_reflection);

View File

@ -183,9 +183,11 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::clear() {
} }
} }
void RenderForwardClustered::RenderBufferDataForwardClustered::configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa) { void RenderForwardClustered::RenderBufferDataForwardClustered::configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count) {
clear(); clear();
ERR_FAIL_COND_MSG(p_view_count != 1, "Multiple views is currently not supported in this renderer, please use the mobile renderer for VR support");
msaa = p_msaa; msaa = p_msaa;
width = p_width; width = p_width;
@ -1091,6 +1093,8 @@ void RenderForwardClustered::_setup_lightmaps(const PagedArray<RID> &p_lightmaps
} }
void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Color &p_default_bg_color) { void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Color &p_default_bg_color) {
ERR_FAIL_COND_MSG(p_render_data->view_count != 1, "Multiview is currently not supported in the clustered renderer. Please use the mobile renderer for VR.");
RenderBufferDataForwardClustered *render_buffer = nullptr; RenderBufferDataForwardClustered *render_buffer = nullptr;
if (p_render_data->render_buffers.is_valid()) { if (p_render_data->render_buffers.is_valid()) {
render_buffer = (RenderBufferDataForwardClustered *)render_buffers_get_data(p_render_data->render_buffers); render_buffer = (RenderBufferDataForwardClustered *)render_buffers_get_data(p_render_data->render_buffers);
@ -1431,7 +1435,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
projection = correction * p_render_data->cam_projection; projection = correction * p_render_data->cam_projection;
} }
RD::get_singleton()->draw_command_begin_label("Draw Sky"); RD::get_singleton()->draw_command_begin_label("Draw Sky");
sky.draw(env, can_continue_color, can_continue_depth, opaque_framebuffer, projection, p_render_data->cam_transform, time); sky.draw(env, can_continue_color, can_continue_depth, opaque_framebuffer, 1, &projection, p_render_data->cam_transform, time);
RD::get_singleton()->draw_command_end_label(); RD::get_singleton()->draw_command_end_label();
} }

View File

@ -103,7 +103,7 @@ class RenderForwardClustered : public RendererSceneRenderRD {
void ensure_specular(); void ensure_specular();
void ensure_voxelgi(); void ensure_voxelgi();
void clear(); void clear();
virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa); virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count);
~RenderBufferDataForwardClustered(); ~RenderBufferDataForwardClustered();
}; };
@ -204,7 +204,6 @@ class RenderForwardClustered : public RendererSceneRenderRD {
struct UBO { struct UBO {
float projection_matrix[16]; float projection_matrix[16];
float inv_projection_matrix[16]; float inv_projection_matrix[16];
float camera_matrix[16]; float camera_matrix[16];
float inv_camera_matrix[16]; float inv_camera_matrix[16];

View File

@ -560,17 +560,18 @@ void SceneShaderForwardClustered::init(RendererStorageRD *p_storage, const Strin
{ {
Vector<String> shader_versions; Vector<String> shader_versions;
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n"); shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_DEPTH_PASS
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n"); shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n"); // SHADER_VERSION_DEPTH_PASS_DP
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n"); shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n"); shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_GIPROBE
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n"); shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n"); // SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_SDF\n"); shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_SDF\n"); // SHADER_VERSION_DEPTH_PASS_WITH_SDF
shader_versions.push_back(""); shader_versions.push_back(""); // SHADER_VERSION_COLOR_PASS
shader_versions.push_back("\n#define USE_FORWARD_GI\n"); shader_versions.push_back("\n#define USE_FORWARD_GI\n"); // SHADER_VERSION_COLOR_PASS_WITH_FORWARD_GI
shader_versions.push_back("\n#define MODE_MULTIPLE_RENDER_TARGETS\n"); shader_versions.push_back("\n#define MODE_MULTIPLE_RENDER_TARGETS\n"); // SHADER_VERSION_COLOR_PASS_WITH_SEPARATE_SPECULAR
shader_versions.push_back("\n#define USE_LIGHTMAP\n"); shader_versions.push_back("\n#define USE_LIGHTMAP\n"); // SHADER_VERSION_LIGHTMAP_COLOR_PASS
shader_versions.push_back("\n#define MODE_MULTIPLE_RENDER_TARGETS\n#define USE_LIGHTMAP\n"); shader_versions.push_back("\n#define MODE_MULTIPLE_RENDER_TARGETS\n#define USE_LIGHTMAP\n"); // SHADER_VERSION_LIGHTMAP_COLOR_PASS_WITH_SEPARATE_SPECULAR
shader.initialize(shader_versions, p_defines); shader.initialize(shader_versions, p_defines);
} }
@ -656,6 +657,11 @@ void SceneShaderForwardClustered::init(RendererStorageRD *p_storage, const Strin
actions.renames["CUSTOM2"] = "custom2_attrib"; actions.renames["CUSTOM2"] = "custom2_attrib";
actions.renames["CUSTOM3"] = "custom3_attrib"; actions.renames["CUSTOM3"] = "custom3_attrib";
// not implemented but need these just in case code is in the shaders
actions.renames["VIEW_INDEX"] = "0";
actions.renames["VIEW_MONO_LEFT"] = "0";
actions.renames["VIEW_RIGHT"] = "1";
//for light //for light
actions.renames["VIEW"] = "view"; actions.renames["VIEW"] = "view";
actions.renames["LIGHT_COLOR"] = "light_color"; actions.renames["LIGHT_COLOR"] = "light_color";

View File

@ -56,6 +56,7 @@ public:
SHADER_VERSION_COLOR_PASS_WITH_SEPARATE_SPECULAR, SHADER_VERSION_COLOR_PASS_WITH_SEPARATE_SPECULAR,
SHADER_VERSION_LIGHTMAP_COLOR_PASS, SHADER_VERSION_LIGHTMAP_COLOR_PASS,
SHADER_VERSION_LIGHTMAP_COLOR_PASS_WITH_SEPARATE_SPECULAR, SHADER_VERSION_LIGHTMAP_COLOR_PASS_WITH_SEPARATE_SPECULAR,
SHADER_VERSION_MAX SHADER_VERSION_MAX
}; };

View File

@ -53,13 +53,14 @@ void RenderForwardMobile::RenderBufferDataForwardMobile::clear() {
color_fb = RID(); color_fb = RID();
} }
void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa) { void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count) {
clear(); clear();
msaa = p_msaa; msaa = p_msaa;
width = p_width; width = p_width;
height = p_height; height = p_height;
view_count = p_view_count;
color = p_color_buffer; color = p_color_buffer;
depth = p_depth_buffer; depth = p_depth_buffer;
@ -71,13 +72,18 @@ void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_b
fb.push_back(p_color_buffer); fb.push_back(p_color_buffer);
fb.push_back(depth); fb.push_back(depth);
color_fb = RD::get_singleton()->framebuffer_create(fb); color_fb = RD::get_singleton()->framebuffer_create(fb, RenderingDevice::INVALID_ID, view_count);
} else { } else {
RD::TextureFormat tf; RD::TextureFormat tf;
if (view_count > 1) {
tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
} else {
tf.texture_type = RD::TEXTURE_TYPE_2D;
}
tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
tf.width = p_width; tf.width = p_width;
tf.height = p_height; tf.height = p_height;
tf.texture_type = RD::TEXTURE_TYPE_2D; tf.array_layers = view_count; // create a layer for every view
tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
RD::TextureSamples ts[RS::VIEWPORT_MSAA_MAX] = { RD::TextureSamples ts[RS::VIEWPORT_MSAA_MAX] = {
@ -103,7 +109,7 @@ void RenderForwardMobile::RenderBufferDataForwardMobile::configure(RID p_color_b
fb.push_back(color_msaa); fb.push_back(color_msaa);
fb.push_back(depth_msaa); fb.push_back(depth_msaa);
color_fb = RD::get_singleton()->framebuffer_create(fb); color_fb = RD::get_singleton()->framebuffer_create(fb, RenderingDevice::INVALID_ID, view_count);
} }
} }
} }
@ -479,7 +485,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
Vector<Color> c; Vector<Color> c;
c.push_back(clear_color.to_linear()); c.push_back(clear_color.to_linear());
RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold); RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count);
_render_list_with_threads(&render_list_params, opaque_framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0); _render_list_with_threads(&render_list_params, opaque_framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0);
} }
@ -488,14 +494,16 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
if (draw_sky || draw_sky_fog_only) { if (draw_sky || draw_sky_fog_only) {
RENDER_TIMESTAMP("Render Sky"); RENDER_TIMESTAMP("Render Sky");
CameraMatrix projection = p_render_data->cam_projection; RD::get_singleton()->draw_command_begin_label("Draw Sky");
if (p_render_data->reflection_probe.is_valid()) { if (p_render_data->reflection_probe.is_valid()) {
CameraMatrix correction; CameraMatrix correction;
correction.set_depth_correction(true); correction.set_depth_correction(true);
projection = correction * p_render_data->cam_projection; CameraMatrix projection = correction * p_render_data->cam_projection;
sky.draw(env, can_continue_color, can_continue_depth, opaque_framebuffer, 1, &projection, p_render_data->cam_transform, time);
} else {
sky.draw(env, can_continue_color, can_continue_depth, opaque_framebuffer, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time);
} }
RD::get_singleton()->draw_command_begin_label("Draw Sky");
sky.draw(env, can_continue_color, can_continue_depth, opaque_framebuffer, projection, p_render_data->cam_transform, time);
RD::get_singleton()->draw_command_end_label(); RD::get_singleton()->draw_command_end_label();
} }
@ -522,7 +530,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
_setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, false); _setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, false);
{ {
RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold); RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->lod_camera_plane, p_render_data->lod_distance_multiplier, p_render_data->screen_lod_threshold, p_render_data->view_count);
_render_list_with_threads(&render_list_params, alpha_framebuffer, can_continue_color ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ); _render_list_with_threads(&render_list_params, alpha_framebuffer, can_continue_color ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ);
} }
@ -555,6 +563,7 @@ void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedAr
RenderDataRD render_data; RenderDataRD render_data;
render_data.cam_projection = p_projection; render_data.cam_projection = p_projection;
render_data.cam_transform = p_transform; render_data.cam_transform = p_transform;
render_data.view_projection[0] = p_projection;
render_data.z_near = 0.0; render_data.z_near = 0.0;
render_data.z_far = p_zfar; render_data.z_far = p_zfar;
render_data.instances = &p_instances; render_data.instances = &p_instances;
@ -622,7 +631,7 @@ void RenderForwardMobile::_render_shadow_end(uint32_t p_barrier) {
for (uint32_t i = 0; i < scene_state.shadow_passes.size(); i++) { for (uint32_t i = 0; i < scene_state.shadow_passes.size(); i++) {
SceneState::ShadowPass &shadow_pass = scene_state.shadow_passes[i]; SceneState::ShadowPass &shadow_pass = scene_state.shadow_passes[i];
RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, shadow_pass.rp_uniform_set, false, Vector2(), shadow_pass.camera_plane, shadow_pass.lod_distance_multiplier, shadow_pass.screen_lod_threshold, shadow_pass.element_from, RD::BARRIER_MASK_NO_BARRIER); RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, shadow_pass.rp_uniform_set, false, Vector2(), shadow_pass.camera_plane, shadow_pass.lod_distance_multiplier, shadow_pass.screen_lod_threshold, 1, shadow_pass.element_from, RD::BARRIER_MASK_NO_BARRIER);
_render_list_with_threads(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, shadow_pass.final_depth_action, Vector<Color>(), 1.0, 0, shadow_pass.rect); _render_list_with_threads(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, shadow_pass.final_depth_action, Vector<Color>(), 1.0, 0, shadow_pass.rect);
} }
@ -647,6 +656,7 @@ void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, c
RenderDataRD render_data; RenderDataRD render_data;
render_data.cam_projection = p_cam_projection; render_data.cam_projection = p_cam_projection;
render_data.cam_transform = p_cam_transform; render_data.cam_transform = p_cam_transform;
render_data.view_projection[0] = p_cam_projection;
render_data.instances = &p_instances; render_data.instances = &p_instances;
_setup_environment(&render_data, true, Vector2(1, 1), false, Color()); _setup_environment(&render_data, true, Vector2(1, 1), false, Color());
@ -757,6 +767,7 @@ void RenderForwardMobile::_render_particle_collider_heightfield(RID p_fb, const
RenderDataRD render_data; RenderDataRD render_data;
render_data.cam_projection = p_cam_projection; render_data.cam_projection = p_cam_projection;
render_data.cam_transform = p_cam_transform; render_data.cam_transform = p_cam_transform;
render_data.view_projection[0] = p_cam_projection;
render_data.z_near = 0.0; render_data.z_near = 0.0;
render_data.z_far = p_cam_projection.get_z_far(); render_data.z_far = p_cam_projection.get_z_far();
render_data.instances = &p_instances; render_data.instances = &p_instances;
@ -1089,6 +1100,12 @@ void RenderForwardMobile::_setup_environment(const RenderDataRD *p_render_data,
RendererStorageRD::store_transform(p_render_data->cam_transform, scene_state.ubo.camera_matrix); RendererStorageRD::store_transform(p_render_data->cam_transform, scene_state.ubo.camera_matrix);
RendererStorageRD::store_transform(p_render_data->cam_transform.affine_inverse(), scene_state.ubo.inv_camera_matrix); RendererStorageRD::store_transform(p_render_data->cam_transform.affine_inverse(), scene_state.ubo.inv_camera_matrix);
for (uint32_t v = 0; v < p_render_data->view_count; v++) {
projection = correction * p_render_data->view_projection[v];
RendererStorageRD::store_camera(projection, scene_state.ubo.projection_matrix_view[v]);
RendererStorageRD::store_camera(projection.inverse(), scene_state.ubo.inv_projection_matrix_view[v]);
}
scene_state.ubo.z_far = p_render_data->z_far; scene_state.ubo.z_far = p_render_data->z_far;
scene_state.ubo.z_near = p_render_data->z_near; scene_state.ubo.z_near = p_render_data->z_near;
@ -1426,18 +1443,20 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
case PASS_MODE_COLOR: case PASS_MODE_COLOR:
case PASS_MODE_COLOR_TRANSPARENT: { case PASS_MODE_COLOR_TRANSPARENT: {
if (element_info.uses_lightmap) { if (element_info.uses_lightmap) {
shader_version = SceneShaderForwardMobile::SHADER_VERSION_LIGHTMAP_COLOR_PASS; shader_version = p_params->view_count > 1 ? SceneShaderForwardMobile::SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW : SceneShaderForwardMobile::SHADER_VERSION_LIGHTMAP_COLOR_PASS;
} else { } else {
shader_version = SceneShaderForwardMobile::SHADER_VERSION_COLOR_PASS; shader_version = p_params->view_count > 1 ? SceneShaderForwardMobile::SHADER_VERSION_COLOR_PASS_MULTIVIEW : SceneShaderForwardMobile::SHADER_VERSION_COLOR_PASS;
} }
} break; } break;
case PASS_MODE_SHADOW: { case PASS_MODE_SHADOW: {
shader_version = SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS; shader_version = p_params->view_count > 1 ? SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS_MULTIVIEW : SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS;
} break; } break;
case PASS_MODE_SHADOW_DP: { case PASS_MODE_SHADOW_DP: {
shader_version = SceneShaderForwardMobile::SHADER_VERSION_DEPTH_PASS_DP; ERR_FAIL_COND_MSG(p_params->view_count > 1, "Multiview not supported for shadow DP pass");
shader_version = SceneShaderForwardMobile::SHADER_VERSION_SHADOW_PASS_DP;
} break; } break;
case PASS_MODE_DEPTH_MATERIAL: { case PASS_MODE_DEPTH_MATERIAL: {
ERR_FAIL_COND_MSG(p_params->view_count > 1, "Multiview not supported for material pass");
shader_version = SceneShaderForwardMobile::SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL; shader_version = SceneShaderForwardMobile::SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL;
} break; } break;
} }

View File

@ -85,9 +85,10 @@ protected:
RID color_fb; RID color_fb;
int width, height; int width, height;
uint32_t view_count;
void clear(); void clear();
virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa); virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count);
~RenderBufferDataForwardMobile(); ~RenderBufferDataForwardMobile();
}; };
@ -120,6 +121,7 @@ protected:
bool reverse_cull = false; bool reverse_cull = false;
PassMode pass_mode = PASS_MODE_COLOR; PassMode pass_mode = PASS_MODE_COLOR;
// bool no_gi = false; // bool no_gi = false;
uint32_t view_count = 1;
RID render_pass_uniform_set; RID render_pass_uniform_set;
bool force_wireframe = false; bool force_wireframe = false;
Vector2 uv_offset; Vector2 uv_offset;
@ -130,13 +132,14 @@ protected:
uint32_t element_offset = 0; uint32_t element_offset = 0;
uint32_t barrier = RD::BARRIER_MASK_ALL; uint32_t barrier = RD::BARRIER_MASK_ALL;
RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, RID p_render_pass_uniform_set, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), const Plane &p_lod_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_lod_threshold = 0.0, uint32_t p_element_offset = 0, uint32_t p_barrier = RD::BARRIER_MASK_ALL) { RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, RID p_render_pass_uniform_set, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), const Plane &p_lod_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0, uint32_t p_barrier = RD::BARRIER_MASK_ALL) {
elements = p_elements; elements = p_elements;
element_info = p_element_info; element_info = p_element_info;
element_count = p_element_count; element_count = p_element_count;
reverse_cull = p_reverse_cull; reverse_cull = p_reverse_cull;
pass_mode = p_pass_mode; pass_mode = p_pass_mode;
// no_gi = p_no_gi; // no_gi = p_no_gi;
view_count = p_view_count;
render_pass_uniform_set = p_render_pass_uniform_set; render_pass_uniform_set = p_render_pass_uniform_set;
force_wireframe = p_force_wireframe; force_wireframe = p_force_wireframe;
uv_offset = p_uv_offset; uv_offset = p_uv_offset;
@ -196,10 +199,12 @@ protected:
struct UBO { struct UBO {
float projection_matrix[16]; float projection_matrix[16];
float inv_projection_matrix[16]; float inv_projection_matrix[16];
float camera_matrix[16]; float camera_matrix[16];
float inv_camera_matrix[16]; float inv_camera_matrix[16];
float projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16];
float inv_projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16];
float viewport_size[2]; float viewport_size[2];
float screen_pixel_size[2]; float screen_pixel_size[2];

View File

@ -32,6 +32,7 @@
#include "core/config/project_settings.h" #include "core/config/project_settings.h"
#include "core/math/math_defs.h" #include "core/math/math_defs.h"
#include "render_forward_mobile.h" #include "render_forward_mobile.h"
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
using namespace RendererSceneRenderImplementation; using namespace RendererSceneRenderImplementation;
@ -291,12 +292,12 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
multisample_state.enable_alpha_to_one = true; multisample_state.enable_alpha_to_one = true;
} }
if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS) { if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_COLOR_PASS_MULTIVIEW || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW) {
blend_state = blend_state_blend; blend_state = blend_state_blend;
if (depth_draw == DEPTH_DRAW_OPAQUE) { if (depth_draw == DEPTH_DRAW_OPAQUE) {
depth_stencil.enable_depth_write = false; //alpha does not draw depth depth_stencil.enable_depth_write = false; //alpha does not draw depth
} }
} else if (k == SHADER_VERSION_SHADOW_PASS || k == SHADER_VERSION_DEPTH_PASS_DP) { } else if (k == SHADER_VERSION_SHADOW_PASS || k == SHADER_VERSION_SHADOW_PASS_MULTIVIEW || k == SHADER_VERSION_SHADOW_PASS_DP) {
//none, blend state contains nothing //none, blend state contains nothing
} else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) { } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) {
blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way
@ -304,57 +305,16 @@ void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) {
pipelines[i][j][k].clear(); pipelines[i][j][k].clear();
continue; // do not use this version (will error if using it is attempted) continue; // do not use this version (will error if using it is attempted)
} }
/*
if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_COLOR_PASS_WITH_FORWARD_GI || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS) {
blend_state = blend_state_blend;
if (depth_draw == DEPTH_DRAW_OPAQUE) {
depth_stencil.enable_depth_write = false; //alpha does not draw depth
}
} else if (uses_depth_pre_pass && (k == SHADER_VERSION_DEPTH_PASS || k == SHADER_VERSION_DEPTH_PASS_DP || k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS || k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL)) {
if (k == SHADER_VERSION_DEPTH_PASS || k == SHADER_VERSION_DEPTH_PASS_DP) {
//none, blend state contains nothing
} else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) {
blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way
} else {
blend_state = blend_state_opaque; //writes to normal and roughness in opaque way
}
} else {
pipelines[i][j][k].clear();
continue; // do not use this version (will error if using it is attempted)
}
*/
} else { } else {
if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS) { if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_COLOR_PASS_MULTIVIEW || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW) {
blend_state = blend_state_opaque; blend_state = blend_state_opaque;
} else if (k == SHADER_VERSION_SHADOW_PASS || k == SHADER_VERSION_DEPTH_PASS_DP) { } else if (k == SHADER_VERSION_SHADOW_PASS || k == SHADER_VERSION_SHADOW_PASS_MULTIVIEW || k == SHADER_VERSION_SHADOW_PASS_DP) {
//none, leave empty //none, leave empty
} else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) { } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) {
blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way
} else { } else {
// ??? // ???
} }
/*
if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_COLOR_PASS_WITH_FORWARD_GI || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS) {
blend_state = blend_state_opaque;
} else if (k == SHADER_VERSION_DEPTH_PASS || k == SHADER_VERSION_DEPTH_PASS_DP) {
//none, leave empty
} else if (k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS) {
blend_state = blend_state_depth_normal_roughness;
} else if (k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI) {
blend_state = blend_state_depth_normal_roughness_giprobe;
} else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) {
blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way
} else if (k == SHADER_VERSION_DEPTH_PASS_WITH_SDF) {
blend_state = RD::PipelineColorBlendState(); //no color targets for SDF
} else {
//specular write
blend_state = blend_state_opaque_specular;
depth_stencil.enable_depth_test = false;
depth_stencil.enable_depth_write = false;
}
*/
} }
RID shader_variant = shader_singleton->shader.version_get_shader(version, k); RID shader_variant = shader_singleton->shader.version_get_shader(version, k);
@ -585,10 +545,22 @@ void SceneShaderForwardMobile::init(RendererStorageRD *p_storage, const String p
Vector<String> shader_versions; Vector<String> shader_versions;
shader_versions.push_back(""); // SHADER_VERSION_COLOR_PASS shader_versions.push_back(""); // SHADER_VERSION_COLOR_PASS
shader_versions.push_back("\n#define USE_LIGHTMAP\n"); // SHADER_VERSION_LIGHTMAP_COLOR_PASS shader_versions.push_back("\n#define USE_LIGHTMAP\n"); // SHADER_VERSION_LIGHTMAP_COLOR_PASS
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n"); // !BAS! SHADER_VERSION_SHADOW_PASS, should probably change this to MODE_RENDER_SHADOW because we don't have a depth pass here... shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_SHADOW_PASS, should probably change this to MODE_RENDER_SHADOW because we don't have a depth pass here...
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n"); // SHADER_VERSION_DEPTH_PASS_DP (maybe rename to SHADER_VERSION_SHADOW_PASS_DP?) shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n"); // SHADER_VERSION_SHADOW_PASS_DP
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n"); // SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n"); // SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL
// multiview versions of our shaders
shader_versions.push_back("\n#define USE_MULTIVIEW\n"); // SHADER_VERSION_COLOR_PASS_MULTIVIEW
shader_versions.push_back("\n#define USE_MULTIVIEW\n#define USE_LIGHTMAP\n"); // SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW
shader_versions.push_back("\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_SHADOW_PASS_MULTIVIEW
shader.initialize(shader_versions, p_defines); shader.initialize(shader_versions, p_defines);
if (!RendererCompositorRD::singleton->is_xr_enabled()) {
shader.set_variant_enabled(SHADER_VERSION_COLOR_PASS_MULTIVIEW, false);
shader.set_variant_enabled(SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW, false);
shader.set_variant_enabled(SHADER_VERSION_SHADOW_PASS_MULTIVIEW, false);
}
} }
storage->shader_set_data_request_function(RendererStorageRD::SHADER_TYPE_3D, _create_shader_funcs); storage->shader_set_data_request_function(RendererStorageRD::SHADER_TYPE_3D, _create_shader_funcs);
@ -603,7 +575,7 @@ void SceneShaderForwardMobile::init(RendererStorageRD *p_storage, const String p
actions.renames["INV_CAMERA_MATRIX"] = "scene_data.inv_camera_matrix"; actions.renames["INV_CAMERA_MATRIX"] = "scene_data.inv_camera_matrix";
actions.renames["CAMERA_MATRIX"] = "scene_data.camera_matrix"; actions.renames["CAMERA_MATRIX"] = "scene_data.camera_matrix";
actions.renames["PROJECTION_MATRIX"] = "projection_matrix"; actions.renames["PROJECTION_MATRIX"] = "projection_matrix";
actions.renames["INV_PROJECTION_MATRIX"] = "scene_data.inv_projection_matrix"; actions.renames["INV_PROJECTION_MATRIX"] = "inv_projection_matrix";
actions.renames["MODELVIEW_MATRIX"] = "modelview"; actions.renames["MODELVIEW_MATRIX"] = "modelview";
actions.renames["MODELVIEW_NORMAL_MATRIX"] = "modelview_normal"; actions.renames["MODELVIEW_NORMAL_MATRIX"] = "modelview_normal";
@ -673,6 +645,10 @@ void SceneShaderForwardMobile::init(RendererStorageRD *p_storage, const String p
actions.renames["CUSTOM2"] = "custom2_attrib"; actions.renames["CUSTOM2"] = "custom2_attrib";
actions.renames["CUSTOM3"] = "custom3_attrib"; actions.renames["CUSTOM3"] = "custom3_attrib";
actions.renames["VIEW_INDEX"] = "ViewIndex";
actions.renames["VIEW_MONO_LEFT"] = "0";
actions.renames["VIEW_RIGHT"] = "1";
//for light //for light
actions.renames["VIEW"] = "view"; actions.renames["VIEW"] = "view";
actions.renames["LIGHT_COLOR"] = "light_color"; actions.renames["LIGHT_COLOR"] = "light_color";

View File

@ -47,8 +47,13 @@ public:
SHADER_VERSION_COLOR_PASS, SHADER_VERSION_COLOR_PASS,
SHADER_VERSION_LIGHTMAP_COLOR_PASS, SHADER_VERSION_LIGHTMAP_COLOR_PASS,
SHADER_VERSION_SHADOW_PASS, SHADER_VERSION_SHADOW_PASS,
SHADER_VERSION_DEPTH_PASS_DP, SHADER_VERSION_SHADOW_PASS_DP,
SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL, SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL,
SHADER_VERSION_COLOR_PASS_MULTIVIEW,
SHADER_VERSION_LIGHTMAP_COLOR_PASS_MULTIVIEW,
SHADER_VERSION_SHADOW_PASS_MULTIVIEW,
SHADER_VERSION_MAX SHADER_VERSION_MAX
}; };

View File

@ -1857,6 +1857,8 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende
} }
} }
tonemap.view_count = p_render_data->view_count;
storage->get_effects()->tonemapper(rb->texture, storage->render_target_get_rd_framebuffer(rb->render_target), tonemap); storage->get_effects()->tonemapper(rb->texture, storage->render_target_get_rd_framebuffer(rb->render_target), tonemap);
} }
@ -2112,7 +2114,9 @@ float RendererSceneRenderRD::render_buffers_get_volumetric_fog_detail_spread(RID
return rb->volumetric_fog->spread; return rb->volumetric_fog->spread;
} }
void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding) { void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding, uint32_t p_view_count) {
ERR_FAIL_COND_MSG(p_view_count == 0, "Must have atleast 1 view");
RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
rb->width = p_width; rb->width = p_width;
rb->height = p_height; rb->height = p_height;
@ -2120,6 +2124,7 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p
rb->msaa = p_msaa; rb->msaa = p_msaa;
rb->screen_space_aa = p_screen_space_aa; rb->screen_space_aa = p_screen_space_aa;
rb->use_debanding = p_use_debanding; rb->use_debanding = p_use_debanding;
rb->view_count = p_view_count;
if (is_clustered_enabled()) { if (is_clustered_enabled()) {
if (rb->cluster_builder == nullptr) { if (rb->cluster_builder == nullptr) {
@ -2132,9 +2137,13 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p
{ {
RD::TextureFormat tf; RD::TextureFormat tf;
if (rb->view_count > 1) {
tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
}
tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
tf.width = rb->width; tf.width = rb->width;
tf.height = rb->height; tf.height = rb->height;
tf.array_layers = rb->view_count; // create a layer for every view
tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
if (rb->msaa != RS::VIEWPORT_MSAA_DISABLED) { if (rb->msaa != RS::VIEWPORT_MSAA_DISABLED) {
tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
@ -2147,6 +2156,9 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p
{ {
RD::TextureFormat tf; RD::TextureFormat tf;
if (rb->view_count > 1) {
tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
}
if (rb->msaa == RS::VIEWPORT_MSAA_DISABLED) { if (rb->msaa == RS::VIEWPORT_MSAA_DISABLED) {
tf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT; tf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT;
} else { } else {
@ -2156,6 +2168,7 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p
tf.width = p_width; tf.width = p_width;
tf.height = p_height; tf.height = p_height;
tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT; tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT;
tf.array_layers = rb->view_count; // create a layer for every view
if (rb->msaa != RS::VIEWPORT_MSAA_DISABLED) { if (rb->msaa != RS::VIEWPORT_MSAA_DISABLED) {
tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
@ -2166,7 +2179,7 @@ void RendererSceneRenderRD::render_buffers_configure(RID p_render_buffers, RID p
rb->depth_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); rb->depth_texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
} }
rb->data->configure(rb->texture, rb->depth_texture, p_width, p_height, p_msaa); rb->data->configure(rb->texture, rb->depth_texture, p_width, p_height, p_msaa, p_view_count);
if (is_clustered_enabled()) { if (is_clustered_enabled()) {
rb->cluster_builder->setup(Size2i(p_width, p_height), max_cluster_elements, rb->depth_texture, storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED), rb->texture); rb->cluster_builder->setup(Size2i(p_width, p_height), max_cluster_elements, rb->depth_texture, storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED), rb->texture);
@ -3628,7 +3641,7 @@ void RendererSceneRenderRD::_pre_opaque_render(RenderDataRD *p_render_data, bool
} }
} }
void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data) { void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData *p_camera_data, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data) {
// getting this here now so we can direct call a bunch of things more easily // getting this here now so we can direct call a bunch of things more easily
RenderBuffers *rb = nullptr; RenderBuffers *rb = nullptr;
if (p_render_buffers.is_valid()) { if (p_render_buffers.is_valid()) {
@ -3641,11 +3654,19 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform3D
{ {
render_data.render_buffers = p_render_buffers; render_data.render_buffers = p_render_buffers;
render_data.cam_transform = p_cam_transform; // Our first camera is used by default
render_data.cam_projection = p_cam_projection; render_data.cam_transform = p_camera_data->main_transform;
render_data.cam_ortogonal = p_cam_projection.is_orthogonal(); // !BAS! Shouldn't this be p_cam_ortogonal ? render_data.cam_projection = p_camera_data->main_projection;
render_data.z_near = p_cam_projection.get_z_near(); render_data.view_projection[0] = p_camera_data->main_projection;
render_data.z_far = p_cam_projection.get_z_far(); render_data.cam_ortogonal = p_camera_data->is_ortogonal;
render_data.view_count = p_camera_data->view_count;
for (uint32_t v = 0; v < p_camera_data->view_count; v++) {
render_data.view_projection[v] = p_camera_data->view_projection[v];
}
render_data.z_near = p_camera_data->main_projection.get_z_near();
render_data.z_far = p_camera_data->main_projection.get_z_far();
render_data.instances = &p_instances; render_data.instances = &p_instances;
render_data.lights = &p_lights; render_data.lights = &p_lights;
@ -3660,8 +3681,9 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform3D
render_data.reflection_probe = p_reflection_probe; render_data.reflection_probe = p_reflection_probe;
render_data.reflection_probe_pass = p_reflection_probe_pass; render_data.reflection_probe_pass = p_reflection_probe_pass;
render_data.lod_distance_multiplier = p_cam_projection.get_lod_multiplier(); // this should be the same for all cameras..
render_data.lod_camera_plane = Plane(p_cam_transform.get_origin(), -p_cam_transform.basis.get_axis(Vector3::AXIS_Z)); render_data.lod_distance_multiplier = p_camera_data->main_projection.get_lod_multiplier();
render_data.lod_camera_plane = Plane(p_camera_data->main_transform.get_origin(), -p_camera_data->main_transform.basis.get_axis(Vector3::AXIS_Z));
if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_DISABLE_LOD) { if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_DISABLE_LOD) {
render_data.screen_lod_threshold = 0.0; render_data.screen_lod_threshold = 0.0;
@ -3733,7 +3755,7 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform3D
if (rb != nullptr) { if (rb != nullptr) {
if (rb->sdfgi) { if (rb->sdfgi) {
rb->sdfgi->update_cascades(); rb->sdfgi->update_cascades();
rb->sdfgi->pre_process_gi(p_cam_transform, &render_data, this); rb->sdfgi->pre_process_gi(render_data.cam_transform, &render_data, this);
rb->sdfgi->update_light(); rb->sdfgi->update_light();
} }
@ -3781,7 +3803,7 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const Transform3D
_render_buffers_post_process_and_tonemap(&render_data); _render_buffers_post_process_and_tonemap(&render_data);
_render_buffers_debug_draw(p_render_buffers, p_shadow_atlas, p_occluder_debug_tex); _render_buffers_debug_draw(p_render_buffers, p_shadow_atlas, p_occluder_debug_tex);
if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SDFGI && rb != nullptr && rb->sdfgi != nullptr) { if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SDFGI && rb != nullptr && rb->sdfgi != nullptr) {
rb->sdfgi->debug_draw(p_cam_projection, p_cam_transform, rb->width, rb->height, rb->render_target, rb->texture); rb->sdfgi->debug_draw(render_data.cam_projection, render_data.cam_transform, rb->width, rb->height, rb->render_target, rb->texture);
} }
} }
} }

View File

@ -50,6 +50,10 @@ struct RenderDataRD {
CameraMatrix cam_projection = CameraMatrix(); CameraMatrix cam_projection = CameraMatrix();
bool cam_ortogonal = false; bool cam_ortogonal = false;
// For stereo rendering
uint32_t view_count = 1;
CameraMatrix view_projection[RendererSceneRender::MAX_RENDER_VIEWS];
float z_near = 0.0; float z_near = 0.0;
float z_far = 0.0; float z_far = 0.0;
@ -87,7 +91,7 @@ protected:
double time_step = 0; double time_step = 0;
struct RenderBufferData { struct RenderBufferData {
virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa) = 0; virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa, uint32_t p_view_count) = 0;
virtual ~RenderBufferData() {} virtual ~RenderBufferData() {}
}; };
virtual RenderBufferData *_create_render_buffer_data() = 0; virtual RenderBufferData *_create_render_buffer_data() = 0;
@ -411,6 +415,7 @@ private:
RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED;
RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED; RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED;
bool use_debanding = false; bool use_debanding = false;
uint32_t view_count = 1;
RID render_target; RID render_target;
@ -1118,7 +1123,7 @@ public:
/* render buffers */ /* render buffers */
RID render_buffers_create(); RID render_buffers_create();
void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding); void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding, uint32_t p_view_count);
void gi_set_use_half_resolution(bool p_enable); void gi_set_use_half_resolution(bool p_enable);
RID render_buffers_get_ao_texture(RID p_render_buffers); RID render_buffers_get_ao_texture(RID p_render_buffers);
@ -1147,7 +1152,7 @@ public:
float render_buffers_get_volumetric_fog_end(RID p_render_buffers); float render_buffers_get_volumetric_fog_end(RID p_render_buffers);
float render_buffers_get_volumetric_fog_detail_spread(RID p_render_buffers); float render_buffers_get_volumetric_fog_detail_spread(RID p_render_buffers);
void render_scene(RID p_render_buffers, const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr); void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr);
void render_material(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region); void render_material(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region);

View File

@ -32,6 +32,7 @@
#include "core/config/project_settings.h" #include "core/config/project_settings.h"
#include "core/math/math_defs.h" #include "core/math/math_defs.h"
#include "renderer_scene_render_rd.h" #include "renderer_scene_render_rd.h"
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
#include "servers/rendering/rendering_server_default.h" #include "servers/rendering/rendering_server_default.h"
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -694,7 +695,18 @@ void RendererSceneSkyRD::init(RendererStorageRD *p_storage) {
sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n"); // Cubemap sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n"); // Cubemap
sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n#define USE_HALF_RES_PASS\n"); // Half Res Cubemap sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n#define USE_HALF_RES_PASS\n"); // Half Res Cubemap
sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n#define USE_QUARTER_RES_PASS\n"); // Quarter res Cubemap sky_modes.push_back("\n#define USE_CUBEMAP_PASS\n#define USE_QUARTER_RES_PASS\n"); // Quarter res Cubemap
sky_modes.push_back("\n#define USE_MULTIVIEW\n"); // Full size multiview
sky_modes.push_back("\n#define USE_HALF_RES_PASS\n#define USE_MULTIVIEW\n"); // Half Res multiview
sky_modes.push_back("\n#define USE_QUARTER_RES_PASS\n#define USE_MULTIVIEW\n"); // Quarter res multiview
sky_shader.shader.initialize(sky_modes, defines); sky_shader.shader.initialize(sky_modes, defines);
if (!RendererCompositorRD::singleton->is_xr_enabled()) {
sky_shader.shader.set_variant_enabled(SKY_VERSION_BACKGROUND_MULTIVIEW, false);
sky_shader.shader.set_variant_enabled(SKY_VERSION_HALF_RES_MULTIVIEW, false);
sky_shader.shader.set_variant_enabled(SKY_VERSION_QUARTER_RES_MULTIVIEW, false);
}
} }
// register our shader funds // register our shader funds
@ -1140,7 +1152,7 @@ void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraM
RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES, sky_shader.default_shader_rd); RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES, sky_shader.default_shader_rd);
cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[2].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[2].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
storage->get_effects()->render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, cm, local_view.basis, multiplier, p_transform.origin); storage->get_effects()->render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view.basis, multiplier, p_transform.origin);
RD::get_singleton()->draw_list_end(); RD::get_singleton()->draw_list_end();
} }
} }
@ -1158,7 +1170,7 @@ void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraM
RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP_HALF_RES, sky_shader.default_shader_rd); RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP_HALF_RES, sky_shader.default_shader_rd);
cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[1].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[1].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
storage->get_effects()->render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, cm, local_view.basis, multiplier, p_transform.origin); storage->get_effects()->render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view.basis, multiplier, p_transform.origin);
RD::get_singleton()->draw_list_end(); RD::get_singleton()->draw_list_end();
} }
} }
@ -1172,7 +1184,7 @@ void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraM
RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP, sky_shader.default_shader_rd); RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_CUBEMAP, sky_shader.default_shader_rd);
cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD);
storage->get_effects()->render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, cm, local_view.basis, multiplier, p_transform.origin); storage->get_effects()->render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view.basis, multiplier, p_transform.origin);
RD::get_singleton()->draw_list_end(); RD::get_singleton()->draw_list_end();
} }
@ -1213,9 +1225,12 @@ void RendererSceneSkyRD::update(RendererSceneEnvironmentRD *p_env, const CameraM
} }
} }
void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, const CameraMatrix &p_projection, const Transform3D &p_transform, double p_time) { void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time) {
ERR_FAIL_COND(!p_env); ERR_FAIL_COND(!p_env);
ERR_FAIL_COND(p_view_count == 0);
ERR_FAIL_COND(p_view_count > RendererSceneRender::MAX_RENDER_VIEWS);
Sky *sky = get_sky(p_env->sky); Sky *sky = get_sky(p_env->sky);
ERR_FAIL_COND(!sky); ERR_FAIL_COND(!sky);
@ -1257,24 +1272,28 @@ void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_cont
float multiplier = p_env->bg_energy; float multiplier = p_env->bg_energy;
float custom_fov = p_env->sky_custom_fov; float custom_fov = p_env->sky_custom_fov;
// Camera // Camera
CameraMatrix camera; CameraMatrix camera;
uint32_t view_count = p_view_count;
const CameraMatrix *projections = p_projections;
if (custom_fov) { if (custom_fov) {
float near_plane = p_projection.get_z_near(); // With custom fov we don't support stereo...
float far_plane = p_projection.get_z_far(); float near_plane = p_projections[0].get_z_near();
float aspect = p_projection.get_aspect(); float far_plane = p_projections[0].get_z_far();
float aspect = p_projections[0].get_aspect();
camera.set_perspective(custom_fov, aspect, near_plane, far_plane); camera.set_perspective(custom_fov, aspect, near_plane, far_plane);
} else { view_count = 1;
camera = p_projection; projections = &camera;
} }
sky_transform = p_transform.basis * sky_transform; sky_transform = p_transform.basis * sky_transform;
if (shader_data->uses_quarter_res) { if (shader_data->uses_quarter_res) {
PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_QUARTER_RES]; PipelineCacheRD *pipeline = &shader_data->pipelines[view_count > 1 ? SKY_VERSION_QUARTER_RES_MULTIVIEW : SKY_VERSION_QUARTER_RES];
RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_QUARTER_RES, sky_shader.default_shader_rd); RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_QUARTER_RES, sky_shader.default_shader_rd);
@ -1282,12 +1301,12 @@ void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_cont
clear_colors.push_back(Color(0.0, 0.0, 0.0)); clear_colors.push_back(Color(0.0, 0.0, 0.0));
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->quarter_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->quarter_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
storage->get_effects()->render_sky(draw_list, p_time, sky->quarter_res_framebuffer, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, camera, sky_transform, multiplier, p_transform.origin); storage->get_effects()->render_sky(draw_list, p_time, sky->quarter_res_framebuffer, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin);
RD::get_singleton()->draw_list_end(); RD::get_singleton()->draw_list_end();
} }
if (shader_data->uses_half_res) { if (shader_data->uses_half_res) {
PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_HALF_RES]; PipelineCacheRD *pipeline = &shader_data->pipelines[view_count > 1 ? SKY_VERSION_HALF_RES_MULTIVIEW : SKY_VERSION_HALF_RES];
RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_HALF_RES, sky_shader.default_shader_rd); RID texture_uniform_set = sky->get_textures(storage, SKY_TEXTURE_SET_HALF_RES, sky_shader.default_shader_rd);
@ -1295,11 +1314,11 @@ void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_cont
clear_colors.push_back(Color(0.0, 0.0, 0.0)); clear_colors.push_back(Color(0.0, 0.0, 0.0));
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->half_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->half_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors);
storage->get_effects()->render_sky(draw_list, p_time, sky->half_res_framebuffer, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, camera, sky_transform, multiplier, p_transform.origin); storage->get_effects()->render_sky(draw_list, p_time, sky->half_res_framebuffer, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin);
RD::get_singleton()->draw_list_end(); RD::get_singleton()->draw_list_end();
} }
PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_BACKGROUND]; PipelineCacheRD *pipeline = &shader_data->pipelines[view_count > 1 ? SKY_VERSION_BACKGROUND_MULTIVIEW : SKY_VERSION_BACKGROUND];
RID texture_uniform_set; RID texture_uniform_set;
if (sky) { if (sky) {
@ -1309,7 +1328,7 @@ void RendererSceneSkyRD::draw(RendererSceneEnvironmentRD *p_env, bool p_can_cont
} }
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_fb, RD::INITIAL_ACTION_CONTINUE, p_can_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, p_can_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_fb, RD::INITIAL_ACTION_CONTINUE, p_can_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, p_can_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ);
storage->get_effects()->render_sky(draw_list, p_time, p_fb, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, camera, sky_transform, multiplier, p_transform.origin); storage->get_effects()->render_sky(draw_list, p_time, p_fb, sky_scene_state.uniform_set, sky_scene_state.fog_uniform_set, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin);
RD::get_singleton()->draw_list_end(); RD::get_singleton()->draw_list_end();
} }

View File

@ -72,6 +72,11 @@ public:
SKY_VERSION_CUBEMAP, SKY_VERSION_CUBEMAP,
SKY_VERSION_CUBEMAP_HALF_RES, SKY_VERSION_CUBEMAP_HALF_RES,
SKY_VERSION_CUBEMAP_QUARTER_RES, SKY_VERSION_CUBEMAP_QUARTER_RES,
SKY_VERSION_BACKGROUND_MULTIVIEW,
SKY_VERSION_HALF_RES_MULTIVIEW,
SKY_VERSION_QUARTER_RES_MULTIVIEW,
SKY_VERSION_MAX SKY_VERSION_MAX
}; };
@ -270,7 +275,7 @@ public:
void setup(RendererSceneEnvironmentRD *p_env, RID p_render_buffers, const CameraMatrix &p_projection, const Transform3D &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render); void setup(RendererSceneEnvironmentRD *p_env, RID p_render_buffers, const CameraMatrix &p_projection, const Transform3D &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render);
void update(RendererSceneEnvironmentRD *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform, double p_time); void update(RendererSceneEnvironmentRD *p_env, const CameraMatrix &p_projection, const Transform3D &p_transform, double p_time);
void draw(RendererSceneEnvironmentRD *p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, const CameraMatrix &p_projection, const Transform3D &p_transform, double p_time); void draw(RendererSceneEnvironmentRD *p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, double p_time);
void invalidate_sky(Sky *p_sky); void invalidate_sky(Sky *p_sky);
void update_dirty_skys(); void update_dirty_skys();

View File

@ -6911,9 +6911,13 @@ void RendererStorageRD::_update_render_target(RenderTarget *rt) {
rd_format.width = rt->size.width; rd_format.width = rt->size.width;
rd_format.height = rt->size.height; rd_format.height = rt->size.height;
rd_format.depth = 1; rd_format.depth = 1;
rd_format.array_layers = 1; rd_format.array_layers = rt->view_count; // for stereo we create two (or more) layers, need to see if we can make fallback work like this too if we don't have multiview
rd_format.mipmaps = 1; rd_format.mipmaps = 1;
rd_format.texture_type = RD::TEXTURE_TYPE_2D; if (rd_format.array_layers > 1) { // why are we not using rt->texture_type ??
rd_format.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
} else {
rd_format.texture_type = RD::TEXTURE_TYPE_2D;
}
rd_format.samples = RD::TEXTURE_SAMPLES_1; rd_format.samples = RD::TEXTURE_SAMPLES_1;
rd_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; rd_format.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
rd_format.shareable_formats.push_back(rt->color_format); rd_format.shareable_formats.push_back(rt->color_format);
@ -6925,7 +6929,7 @@ void RendererStorageRD::_update_render_target(RenderTarget *rt) {
Vector<RID> fb_textures; Vector<RID> fb_textures;
fb_textures.push_back(rt->color); fb_textures.push_back(rt->color);
rt->framebuffer = RD::get_singleton()->framebuffer_create(fb_textures); rt->framebuffer = RD::get_singleton()->framebuffer_create(fb_textures, RenderingDevice::INVALID_ID, rt->view_count);
if (rt->framebuffer.is_null()) { if (rt->framebuffer.is_null()) {
_clear_render_target(rt); _clear_render_target(rt);
ERR_FAIL_COND(rt->framebuffer.is_null()); ERR_FAIL_COND(rt->framebuffer.is_null());
@ -7039,12 +7043,15 @@ void RendererStorageRD::render_target_set_position(RID p_render_target, int p_x,
//unused for this render target //unused for this render target
} }
void RendererStorageRD::render_target_set_size(RID p_render_target, int p_width, int p_height) { void RendererStorageRD::render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) {
RenderTarget *rt = render_target_owner.getornull(p_render_target); RenderTarget *rt = render_target_owner.getornull(p_render_target);
ERR_FAIL_COND(!rt); ERR_FAIL_COND(!rt);
rt->size.x = p_width; if (rt->size.x != p_width || rt->size.y != p_height || rt->view_count != p_view_count) {
rt->size.y = p_height; rt->size.x = p_width;
_update_render_target(rt); rt->size.y = p_height;
rt->view_count = p_view_count;
_update_render_target(rt);
}
} }
RID RendererStorageRD::render_target_get_texture(RID p_render_target) { RID RendererStorageRD::render_target_get_texture(RID p_render_target) {

View File

@ -1125,6 +1125,7 @@ private:
struct RenderTarget { struct RenderTarget {
Size2i size; Size2i size;
uint32_t view_count;
RID framebuffer; RID framebuffer;
RID color; RID color;
@ -2282,7 +2283,7 @@ public:
RID render_target_create(); RID render_target_create();
void render_target_set_position(RID p_render_target, int p_x, int p_y); void render_target_set_position(RID p_render_target, int p_x, int p_y);
void render_target_set_size(RID p_render_target, int p_width, int p_height); void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count);
RID render_target_get_texture(RID p_render_target); RID render_target_get_texture(RID p_render_target);
void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id); void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id);
void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value); void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value);

View File

@ -96,6 +96,18 @@ layout(location = 8) out float dp_clip;
#endif #endif
#ifdef USE_MULTIVIEW
#ifdef has_VK_KHR_multiview
#define ViewIndex gl_ViewIndex
#else
// !BAS! This needs to become an input once we implement our fallback!
#define ViewIndex 0
#endif
#else
// Set to zero, not supported in non stereo
#define ViewIndex 0
#endif //USE_MULTIVIEW
invariant gl_Position; invariant gl_Position;
#GLOBALS #GLOBALS
@ -234,7 +246,13 @@ void main() {
vec4 position; vec4 position;
#endif #endif
#ifdef USE_MULTIVIEW
mat4 projection_matrix = scene_data.projection_matrix_view[ViewIndex];
mat4 inv_projection_matrix = scene_data.inv_projection_matrix_view[ViewIndex];
#else
mat4 projection_matrix = scene_data.projection_matrix; mat4 projection_matrix = scene_data.projection_matrix;
mat4 inv_projection_matrix = scene_data.inv_projection_matrix;
#endif //USE_MULTIVIEW
//using world coordinates //using world coordinates
#if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED) #if !defined(SKIP_TRANSFORM_USED) && defined(VERTEX_WORLD_COORDS_USED)
@ -386,10 +404,26 @@ layout(location = 8) in float dp_clip;
#endif #endif
#ifdef USE_MULTIVIEW
#ifdef has_VK_KHR_multiview
#define ViewIndex gl_ViewIndex
#else
// !BAS! This needs to become an input once we implement our fallback!
#define ViewIndex 0
#endif
#else
// Set to zero, not supported in non stereo
#define ViewIndex 0
#endif //USE_MULTIVIEW
//defines to keep compatibility with vertex //defines to keep compatibility with vertex
#define world_matrix draw_call.transform #define world_matrix draw_call.transform
#ifdef USE_MULTIVIEW
#define projection_matrix scene_data.projection_matrix_view[ViewIndex]
#else
#define projection_matrix scene_data.projection_matrix #define projection_matrix scene_data.projection_matrix
#endif
#if defined(ENABLE_SSS) && defined(ENABLE_TRANSMITTANCE) #if defined(ENABLE_SSS) && defined(ENABLE_TRANSMITTANCE)
//both required for transmittance to be enabled //both required for transmittance to be enabled

View File

@ -1,4 +1,9 @@
#define M_PI 3.14159265359 #define M_PI 3.14159265359
#define MAX_VIEWS 2
#if defined(USE_MULTIVIEW) && defined(has_VK_KHR_multiview)
#extension GL_EXT_multiview : enable
#endif
#include "decal_data_inc.glsl" #include "decal_data_inc.glsl"
@ -121,10 +126,13 @@ global_variables;
layout(set = 1, binding = 0, std140) uniform SceneData { layout(set = 1, binding = 0, std140) uniform SceneData {
mat4 projection_matrix; mat4 projection_matrix;
mat4 inv_projection_matrix; mat4 inv_projection_matrix;
mat4 camera_matrix; mat4 camera_matrix;
mat4 inv_camera_matrix; mat4 inv_camera_matrix;
// only used for multiview
mat4 projection_matrix_view[MAX_VIEWS];
mat4 inv_projection_matrix_view[MAX_VIEWS];
vec2 viewport_size; vec2 viewport_size;
vec2 screen_pixel_size; vec2 screen_pixel_size;

View File

@ -4,11 +4,17 @@
#VERSION_DEFINES #VERSION_DEFINES
#define MAX_VIEWS 2
#if defined(USE_MULTIVIEW) && defined(has_VK_KHR_multiview)
#extension GL_EXT_multiview : enable
#endif
layout(location = 0) out vec2 uv_interp; layout(location = 0) out vec2 uv_interp;
layout(push_constant, binding = 1, std430) uniform Params { layout(push_constant, binding = 1, std430) uniform Params {
mat3 orientation; mat3 orientation;
vec4 proj; vec4 projections[MAX_VIEWS];
vec4 position_multiplier; vec4 position_multiplier;
float time; float time;
} }
@ -26,15 +32,29 @@ void main() {
#VERSION_DEFINES #VERSION_DEFINES
#ifdef USE_MULTIVIEW
#ifdef has_VK_KHR_multiview
#extension GL_EXT_multiview : enable
#define ViewIndex gl_ViewIndex
#else // has_VK_KHR_multiview
// !BAS! This needs to become an input once we implement our fallback!
#define ViewIndex 0
#endif // has_VK_KHR_multiview
#else // USE_MULTIVIEW
// Set to zero, not supported in non stereo
#define ViewIndex 0
#endif //USE_MULTIVIEW
#define M_PI 3.14159265359 #define M_PI 3.14159265359
#define MAX_VIEWS 2
layout(location = 0) in vec2 uv_interp; layout(location = 0) in vec2 uv_interp;
layout(push_constant, binding = 1, std430) uniform Params { layout(push_constant, binding = 1, std430) uniform Params {
mat3 orientation; mat3 orientation;
vec4 proj; vec4 projections[MAX_VIEWS];
vec4 position_multiplier; vec4 position_multiplier;
float time; //TODO consider adding vec2 screen res, and float radiance size float time;
} }
params; params;
@ -85,7 +105,6 @@ struct DirectionalLightData {
layout(set = 0, binding = 3, std140) uniform DirectionalLights { layout(set = 0, binding = 3, std140) uniform DirectionalLights {
DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS]; DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS];
} }
directional_lights; directional_lights;
#ifdef MATERIAL_UNIFORMS_USED #ifdef MATERIAL_UNIFORMS_USED
@ -154,8 +173,8 @@ vec4 fog_process(vec3 view, vec3 sky_color) {
void main() { void main() {
vec3 cube_normal; vec3 cube_normal;
cube_normal.z = -1.0; cube_normal.z = -1.0;
cube_normal.x = (cube_normal.z * (-uv_interp.x - params.proj.x)) / params.proj.y; cube_normal.x = (cube_normal.z * (-uv_interp.x - params.projections[ViewIndex].x)) / params.projections[ViewIndex].y;
cube_normal.y = -(cube_normal.z * (-uv_interp.y - params.proj.z)) / params.proj.w; cube_normal.y = -(cube_normal.z * (-uv_interp.y - params.projections[ViewIndex].z)) / params.projections[ViewIndex].w;
cube_normal = mat3(params.orientation) * cube_normal; cube_normal = mat3(params.orientation) * cube_normal;
cube_normal.z = -cube_normal.z; cube_normal.z = -cube_normal.z;
cube_normal = normalize(cube_normal); cube_normal = normalize(cube_normal);

View File

@ -4,6 +4,12 @@
#VERSION_DEFINES #VERSION_DEFINES
#ifdef MULTIVIEW
#ifdef has_VK_KHR_multiview
#extension GL_EXT_multiview : enable
#endif
#endif
layout(location = 0) out vec2 uv_interp; layout(location = 0) out vec2 uv_interp;
void main() { void main() {
@ -18,9 +24,22 @@ void main() {
#VERSION_DEFINES #VERSION_DEFINES
#ifdef MULTIVIEW
#ifdef has_VK_KHR_multiview
#extension GL_EXT_multiview : enable
#define ViewIndex gl_ViewIndex
#else // has_VK_KHR_multiview
#define ViewIndex 0
#endif // has_VK_KHR_multiview
#endif //MULTIVIEW
layout(location = 0) in vec2 uv_interp; layout(location = 0) in vec2 uv_interp;
#ifdef MULTIVIEW
layout(set = 0, binding = 0) uniform sampler2DArray source_color;
#else
layout(set = 0, binding = 0) uniform sampler2D source_color; layout(set = 0, binding = 0) uniform sampler2D source_color;
#endif
layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure; layout(set = 1, binding = 0) uniform sampler2D source_auto_exposure;
layout(set = 2, binding = 0) uniform sampler2D source_glow; layout(set = 2, binding = 0) uniform sampler2D source_glow;
#ifdef USE_1D_LUT #ifdef USE_1D_LUT
@ -277,10 +296,17 @@ vec3 do_fxaa(vec3 color, float exposure, vec2 uv_interp) {
const float FXAA_REDUCE_MUL = (1.0 / 8.0); const float FXAA_REDUCE_MUL = (1.0 / 8.0);
const float FXAA_SPAN_MAX = 8.0; const float FXAA_SPAN_MAX = 8.0;
#ifdef MULTIVIEW
vec3 rgbNW = textureLod(source_color, vec3(uv_interp + vec2(-1.0, -1.0) * params.pixel_size, ViewIndex), 0.0).xyz * exposure;
vec3 rgbNE = textureLod(source_color, vec3(uv_interp + vec2(1.0, -1.0) * params.pixel_size, ViewIndex), 0.0).xyz * exposure;
vec3 rgbSW = textureLod(source_color, vec3(uv_interp + vec2(-1.0, 1.0) * params.pixel_size, ViewIndex), 0.0).xyz * exposure;
vec3 rgbSE = textureLod(source_color, vec3(uv_interp + vec2(1.0, 1.0) * params.pixel_size, ViewIndex), 0.0).xyz * exposure;
#else
vec3 rgbNW = textureLod(source_color, uv_interp + vec2(-1.0, -1.0) * params.pixel_size, 0.0).xyz * exposure; vec3 rgbNW = textureLod(source_color, uv_interp + vec2(-1.0, -1.0) * params.pixel_size, 0.0).xyz * exposure;
vec3 rgbNE = textureLod(source_color, uv_interp + vec2(1.0, -1.0) * params.pixel_size, 0.0).xyz * exposure; vec3 rgbNE = textureLod(source_color, uv_interp + vec2(1.0, -1.0) * params.pixel_size, 0.0).xyz * exposure;
vec3 rgbSW = textureLod(source_color, uv_interp + vec2(-1.0, 1.0) * params.pixel_size, 0.0).xyz * exposure; vec3 rgbSW = textureLod(source_color, uv_interp + vec2(-1.0, 1.0) * params.pixel_size, 0.0).xyz * exposure;
vec3 rgbSE = textureLod(source_color, uv_interp + vec2(1.0, 1.0) * params.pixel_size, 0.0).xyz * exposure; vec3 rgbSE = textureLod(source_color, uv_interp + vec2(1.0, 1.0) * params.pixel_size, 0.0).xyz * exposure;
#endif
vec3 rgbM = color; vec3 rgbM = color;
vec3 luma = vec3(0.299, 0.587, 0.114); vec3 luma = vec3(0.299, 0.587, 0.114);
float lumaNW = dot(rgbNW, luma); float lumaNW = dot(rgbNW, luma);
@ -305,8 +331,13 @@ vec3 do_fxaa(vec3 color, float exposure, vec2 uv_interp) {
dir * rcpDirMin)) * dir * rcpDirMin)) *
params.pixel_size; params.pixel_size;
#ifdef MULTIVIEW
vec3 rgbA = 0.5 * exposure * (textureLod(source_color, vec3(uv_interp + dir * (1.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz + textureLod(source_color, vec3(uv_interp + dir * (2.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz);
vec3 rgbB = rgbA * 0.5 + 0.25 * exposure * (textureLod(source_color, vec3(uv_interp + dir * -0.5, ViewIndex), 0.0).xyz + textureLod(source_color, vec3(uv_interp + dir * 0.5, ViewIndex), 0.0).xyz);
#else
vec3 rgbA = 0.5 * exposure * (textureLod(source_color, uv_interp + dir * (1.0 / 3.0 - 0.5), 0.0).xyz + textureLod(source_color, uv_interp + dir * (2.0 / 3.0 - 0.5), 0.0).xyz); vec3 rgbA = 0.5 * exposure * (textureLod(source_color, uv_interp + dir * (1.0 / 3.0 - 0.5), 0.0).xyz + textureLod(source_color, uv_interp + dir * (2.0 / 3.0 - 0.5), 0.0).xyz);
vec3 rgbB = rgbA * 0.5 + 0.25 * exposure * (textureLod(source_color, uv_interp + dir * -0.5, 0.0).xyz + textureLod(source_color, uv_interp + dir * 0.5, 0.0).xyz); vec3 rgbB = rgbA * 0.5 + 0.25 * exposure * (textureLod(source_color, uv_interp + dir * -0.5, 0.0).xyz + textureLod(source_color, uv_interp + dir * 0.5, 0.0).xyz);
#endif
float lumaB = dot(rgbB, luma); float lumaB = dot(rgbB, luma);
if ((lumaB < lumaMin) || (lumaB > lumaMax)) { if ((lumaB < lumaMin) || (lumaB > lumaMax)) {
@ -329,7 +360,11 @@ vec3 screen_space_dither(vec2 frag_coord) {
} }
void main() { void main() {
#ifdef MULTIVIEW
vec3 color = textureLod(source_color, vec3(uv_interp, ViewIndex), 0.0f).rgb;
#else
vec3 color = textureLod(source_color, uv_interp, 0.0f).rgb; vec3 color = textureLod(source_color, uv_interp, 0.0f).rgb;
#endif
// Exposure // Exposure

View File

@ -189,7 +189,7 @@ public:
virtual RID render_buffers_create() = 0; virtual RID render_buffers_create() = 0;
virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding) = 0; virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding, uint32_t p_view_count) = 0;
virtual void gi_set_use_half_resolution(bool p_enable) = 0; virtual void gi_set_use_half_resolution(bool p_enable) = 0;
@ -201,8 +201,7 @@ public:
virtual void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) = 0; virtual void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) = 0;
virtual void render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas) = 0; virtual void render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas) = 0;
virtual void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_lod_threshold, RID p_shadow_atlas) = 0; virtual void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_lod_threshold, RID p_shadow_atlas, Ref<XRInterface> &p_xr_interface) = 0;
virtual void render_camera(RID p_render_buffers, Ref<XRInterface> &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_lod_threshold, RID p_shadow_atlas) = 0;
virtual void update() = 0; virtual void update() = 0;
virtual void render_probes() = 0; virtual void render_probes() = 0;

View File

@ -2186,143 +2186,92 @@ bool RendererSceneCull::_light_instance_update_shadow(Instance *p_instance, cons
return animated_material_found; return animated_material_found;
} }
void RendererSceneCull::render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas) { void RendererSceneCull::render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas, Ref<XRInterface> &p_xr_interface) {
// render to mono camera
#ifndef _3D_DISABLED #ifndef _3D_DISABLED
Camera *camera = camera_owner.getornull(p_camera); Camera *camera = camera_owner.getornull(p_camera);
ERR_FAIL_COND(!camera); ERR_FAIL_COND(!camera);
/* STEP 1 - SETUP CAMERA */ RendererSceneRender::CameraData camera_data;
CameraMatrix camera_matrix;
bool ortho = false;
switch (camera->type) { // Setup Camera(s)
case Camera::ORTHOGONAL: { if (p_xr_interface.is_null()) {
camera_matrix.set_orthogonal( // Normal camera
camera->size, Transform3D transform = camera->transform;
p_viewport_size.width / (float)p_viewport_size.height, CameraMatrix projection;
camera->znear, bool vaspect = camera->vaspect;
camera->zfar, bool is_ortogonal = false;
camera->vaspect);
ortho = true;
} break;
case Camera::PERSPECTIVE: {
camera_matrix.set_perspective(
camera->fov,
p_viewport_size.width / (float)p_viewport_size.height,
camera->znear,
camera->zfar,
camera->vaspect);
ortho = false;
} break; switch (camera->type) {
case Camera::FRUSTUM: { case Camera::ORTHOGONAL: {
camera_matrix.set_frustum( projection.set_orthogonal(
camera->size, camera->size,
p_viewport_size.width / (float)p_viewport_size.height, p_viewport_size.width / (float)p_viewport_size.height,
camera->offset, camera->znear,
camera->znear, camera->zfar,
camera->zfar, camera->vaspect);
camera->vaspect); is_ortogonal = true;
ortho = false; } break;
} break; case Camera::PERSPECTIVE: {
projection.set_perspective(
camera->fov,
p_viewport_size.width / (float)p_viewport_size.height,
camera->znear,
camera->zfar,
camera->vaspect);
} break;
case Camera::FRUSTUM: {
projection.set_frustum(
camera->size,
p_viewport_size.width / (float)p_viewport_size.height,
camera->offset,
camera->znear,
camera->zfar,
camera->vaspect);
} break;
}
camera_data.set_camera(transform, projection, is_ortogonal, vaspect);
} else {
// Setup our camera for our XR interface.
// We can support multiple views here each with their own camera
Transform3D transforms[RendererSceneRender::MAX_RENDER_VIEWS];
CameraMatrix projections[RendererSceneRender::MAX_RENDER_VIEWS];
uint32_t view_count = p_xr_interface->get_view_count();
ERR_FAIL_COND_MSG(view_count > RendererSceneRender::MAX_RENDER_VIEWS, "Requested view count is not supported");
float aspect = p_viewport_size.width / (float)p_viewport_size.height;
Transform3D world_origin = XRServer::get_singleton()->get_world_origin();
// We ignore our camera position, it will have been positioned with a slightly old tracking position.
// Instead we take our origin point and have our XR interface add fresh tracking data! Whoohoo!
for (uint32_t v = 0; v < view_count; v++) {
transforms[v] = p_xr_interface->get_transform_for_view(v, world_origin);
projections[v] = p_xr_interface->get_projection_for_view(v, aspect, camera->znear, camera->zfar);
}
if (view_count == 1) {
camera_data.set_camera(transforms[0], projections[0], false, camera->vaspect);
} else if (view_count == 2) {
camera_data.set_multiview_camera(view_count, transforms, projections, false, camera->vaspect);
} else {
// this won't be called (see fail check above) but keeping this comment to indicate we may support more then 2 views in the future...
}
} }
RID environment = _render_get_environment(p_camera, p_scenario); RID environment = _render_get_environment(p_camera, p_scenario);
RENDER_TIMESTAMP("Update occlusion buffer") RENDER_TIMESTAMP("Update occlusion buffer")
RendererSceneOcclusionCull::get_singleton()->buffer_update(p_viewport, camera->transform, camera_matrix, ortho, RendererThreadPool::singleton->thread_work_pool); // For now just cull on the first camera
RendererSceneOcclusionCull::get_singleton()->buffer_update(p_viewport, camera_data.main_transform, camera_data.main_projection, camera_data.is_ortogonal, RendererThreadPool::singleton->thread_work_pool);
_render_scene(camera->transform, camera_matrix, ortho, camera->vaspect, p_render_buffers, environment, camera->effects, camera->visible_layers, p_scenario, p_viewport, p_shadow_atlas, RID(), -1, p_screen_lod_threshold); _render_scene(&camera_data, p_render_buffers, environment, camera->effects, camera->visible_layers, p_scenario, p_viewport, p_shadow_atlas, RID(), -1, p_screen_lod_threshold);
#endif #endif
} }
void RendererSceneCull::render_camera(RID p_render_buffers, Ref<XRInterface> &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas) {
// render for AR/VR interface
#if 0
Camera *camera = camera_owner.getornull(p_camera);
ERR_FAIL_COND(!camera);
/* SETUP CAMERA, we are ignoring type and FOV here */
float aspect = p_viewport_size.width / (float)p_viewport_size.height;
CameraMatrix camera_matrix = p_interface->get_projection_for_eye(p_eye, aspect, camera->znear, camera->zfar);
// We also ignore our camera position, it will have been positioned with a slightly old tracking position.
// Instead we take our origin point and have our ar/vr interface add fresh tracking data! Whoohoo!
Transform3D world_origin = XRServer::get_singleton()->get_world_origin();
Transform3D cam_transform = p_interface->get_transform_for_eye(p_eye, world_origin);
RID environment = _render_get_environment(p_camera, p_scenario);
// For stereo render we only prepare for our left eye and then reuse the outcome for our right eye
if (p_eye == XRInterface::EYE_LEFT) {
// Center our transform, we assume basis is equal.
Transform3D mono_transform = cam_transform;
Transform3D right_transform = p_interface->get_transform_for_eye(XRInterface::EYE_RIGHT, world_origin);
mono_transform.origin += right_transform.origin;
mono_transform.origin *= 0.5;
// We need to combine our projection frustums for culling.
// Ideally we should use our clipping planes for this and combine them,
// however our shadow map logic uses our projection matrix.
// Note: as our left and right frustums should be mirrored, we don't need our right projection matrix.
// - get some base values we need
float eye_dist = (mono_transform.origin - cam_transform.origin).length();
float z_near = camera_matrix.get_z_near(); // get our near plane
float z_far = camera_matrix.get_z_far(); // get our far plane
float width = (2.0 * z_near) / camera_matrix.matrix[0][0];
float x_shift = width * camera_matrix.matrix[2][0];
float height = (2.0 * z_near) / camera_matrix.matrix[1][1];
float y_shift = height * camera_matrix.matrix[2][1];
// printf("Eye_dist = %f, Near = %f, Far = %f, Width = %f, Shift = %f\n", eye_dist, z_near, z_far, width, x_shift);
// - calculate our near plane size (horizontal only, right_near is mirrored)
float left_near = -eye_dist - ((width - x_shift) * 0.5);
// - calculate our far plane size (horizontal only, right_far is mirrored)
float left_far = -eye_dist - (z_far * (width - x_shift) * 0.5 / z_near);
float left_far_right_eye = eye_dist - (z_far * (width + x_shift) * 0.5 / z_near);
if (left_far > left_far_right_eye) {
// on displays smaller then double our iod, the right eye far frustrum can overtake the left eyes.
left_far = left_far_right_eye;
}
// - figure out required z-shift
float slope = (left_far - left_near) / (z_far - z_near);
float z_shift = (left_near / slope) - z_near;
// - figure out new vertical near plane size (this will be slightly oversized thanks to our z-shift)
float top_near = (height - y_shift) * 0.5;
top_near += (top_near / z_near) * z_shift;
float bottom_near = -(height + y_shift) * 0.5;
bottom_near += (bottom_near / z_near) * z_shift;
// printf("Left_near = %f, Left_far = %f, Top_near = %f, Bottom_near = %f, Z_shift = %f\n", left_near, left_far, top_near, bottom_near, z_shift);
// - generate our frustum
CameraMatrix combined_matrix;
combined_matrix.set_frustum(left_near, -left_near, bottom_near, top_near, z_near + z_shift, z_far + z_shift);
// and finally move our camera back
Transform3D apply_z_shift;
apply_z_shift.origin = Vector3(0.0, 0.0, z_shift); // z negative is forward so this moves it backwards
mono_transform *= apply_z_shift;
// now prepare our scene with our adjusted transform projection matrix
_prepare_scene(mono_transform, combined_matrix, false, false, p_render_buffers, environment, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), p_screen_lod_threshold);
} else if (p_eye == XRInterface::EYE_MONO) {
// For mono render, prepare as per usual
_prepare_scene(cam_transform, camera_matrix, false, false, p_render_buffers, environment, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), p_screen_lod_threshold);
}
// And render our scene...
_render_scene(p_render_buffers, cam_transform, camera_matrix, false, environment, camera->effects, p_scenario, p_shadow_atlas, RID(), -1, p_screen_lod_threshold);
#endif
};
void RendererSceneCull::_frustum_cull_threaded(uint32_t p_thread, CullData *cull_data) { void RendererSceneCull::_frustum_cull_threaded(uint32_t p_thread, CullData *cull_data) {
uint32_t cull_total = cull_data->scenario->instance_data.size(); uint32_t cull_total = cull_data->scenario->instance_data.size();
uint32_t total_threads = RendererThreadPool::singleton->thread_work_pool.get_thread_count(); uint32_t total_threads = RendererThreadPool::singleton->thread_work_pool.get_thread_count();
@ -2544,11 +2493,7 @@ void RendererSceneCull::_frustum_cull(CullData &cull_data, FrustumCullResult &cu
} }
} }
void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, bool p_using_shadows) { void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_camera_data, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, bool p_using_shadows) {
// Note, in stereo rendering:
// - p_cam_transform will be a transform in the middle of our two eyes
// - p_cam_projection is a wider frustrum that encompasses both eyes
Instance *render_reflection_probe = instance_owner.getornull(p_reflection_probe); //if null, not rendering to it Instance *render_reflection_probe = instance_owner.getornull(p_reflection_probe); //if null, not rendering to it
Scenario *scenario = scenario_owner.getornull(p_scenario); Scenario *scenario = scenario_owner.getornull(p_scenario);
@ -2559,16 +2504,16 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const
if (p_render_buffers.is_valid()) { if (p_render_buffers.is_valid()) {
//no rendering code here, this is only to set up what needs to be done, request regions, etc. //no rendering code here, this is only to set up what needs to be done, request regions, etc.
scene_render->sdfgi_update(p_render_buffers, p_environment, p_cam_transform.origin); //update conditions for SDFGI (whether its used or not) scene_render->sdfgi_update(p_render_buffers, p_environment, p_camera_data->main_transform.origin); //update conditions for SDFGI (whether its used or not)
} }
RENDER_TIMESTAMP("Frustum Culling"); RENDER_TIMESTAMP("Frustum Culling");
//rasterizer->set_camera(camera->transform, camera_matrix,ortho); //rasterizer->set_camera(p_camera_data->main_transform, p_camera_data.main_projection, p_camera_data.is_ortogonal);
Vector<Plane> planes = p_cam_projection.get_projection_planes(p_cam_transform); Vector<Plane> planes = p_camera_data->main_projection.get_projection_planes(p_camera_data->main_transform);
Plane near_plane(p_cam_transform.origin, -p_cam_transform.basis.get_axis(2).normalized()); Plane near_plane(p_camera_data->main_transform.origin, -p_camera_data->main_transform.basis.get_axis(2).normalized());
/* STEP 2 - CULL */ /* STEP 2 - CULL */
@ -2606,7 +2551,7 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const
scene_render->set_directional_shadow_count(lights_with_shadow.size()); scene_render->set_directional_shadow_count(lights_with_shadow.size());
for (int i = 0; i < lights_with_shadow.size(); i++) { for (int i = 0; i < lights_with_shadow.size(); i++) {
_light_instance_setup_directional_shadow(i, lights_with_shadow[i], p_cam_transform, p_cam_projection, p_cam_orthogonal, p_cam_vaspect); _light_instance_setup_directional_shadow(i, lights_with_shadow[i], p_camera_data->main_transform, p_camera_data->main_projection, p_camera_data->is_ortogonal, p_camera_data->vaspect);
} }
} }
@ -2647,11 +2592,11 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const
cull_data.cull = &cull; cull_data.cull = &cull;
cull_data.scenario = scenario; cull_data.scenario = scenario;
cull_data.shadow_atlas = p_shadow_atlas; cull_data.shadow_atlas = p_shadow_atlas;
cull_data.cam_transform = p_cam_transform; cull_data.cam_transform = p_camera_data->main_transform;
cull_data.visible_layers = p_visible_layers; cull_data.visible_layers = p_visible_layers;
cull_data.render_reflection_probe = render_reflection_probe; cull_data.render_reflection_probe = render_reflection_probe;
cull_data.occlusion_buffer = RendererSceneOcclusionCull::get_singleton()->buffer_get_ptr(p_viewport); cull_data.occlusion_buffer = RendererSceneOcclusionCull::get_singleton()->buffer_get_ptr(p_viewport);
cull_data.camera_matrix = &p_cam_projection; cull_data.camera_matrix = &p_camera_data->main_projection;
//#define DEBUG_CULL_TIME //#define DEBUG_CULL_TIME
#ifdef DEBUG_CULL_TIME #ifdef DEBUG_CULL_TIME
uint64_t time_from = OS::get_singleton()->get_ticks_usec(); uint64_t time_from = OS::get_singleton()->get_ticks_usec();
@ -2726,12 +2671,12 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const
{ //compute coverage { //compute coverage
Transform3D cam_xf = p_cam_transform; Transform3D cam_xf = p_camera_data->main_transform;
float zn = p_cam_projection.get_z_near(); float zn = p_camera_data->main_projection.get_z_near();
Plane p(cam_xf.origin + cam_xf.basis.get_axis(2) * -zn, -cam_xf.basis.get_axis(2)); //camera near plane Plane p(cam_xf.origin + cam_xf.basis.get_axis(2) * -zn, -cam_xf.basis.get_axis(2)); //camera near plane
// near plane half width and height // near plane half width and height
Vector2 vp_half_extents = p_cam_projection.get_viewport_half_extents(); Vector2 vp_half_extents = p_camera_data->main_projection.get_viewport_half_extents();
switch (RSG::storage->light_get_type(ins->base)) { switch (RSG::storage->light_get_type(ins->base)) {
case RS::LIGHT_OMNI: { case RS::LIGHT_OMNI: {
@ -2743,7 +2688,7 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const
ins->transform.origin + cam_xf.basis.get_axis(0) * radius ins->transform.origin + cam_xf.basis.get_axis(0) * radius
}; };
if (!p_cam_orthogonal) { if (!p_camera_data->is_ortogonal) {
//if using perspetive, map them to near plane //if using perspetive, map them to near plane
for (int j = 0; j < 2; j++) { for (int j = 0; j < 2; j++) {
if (p.distance_to(points[j]) < 0) { if (p.distance_to(points[j]) < 0) {
@ -2771,7 +2716,7 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const
base + cam_xf.basis.get_axis(0) * w base + cam_xf.basis.get_axis(0) * w
}; };
if (!p_cam_orthogonal) { if (!p_camera_data->is_ortogonal) {
//if using perspetive, map them to near plane //if using perspetive, map them to near plane
for (int j = 0; j < 2; j++) { for (int j = 0; j < 2; j++) {
if (p.distance_to(points[j]) < 0) { if (p.distance_to(points[j]) < 0) {
@ -2802,7 +2747,7 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const
if (redraw && max_shadows_used < MAX_UPDATE_SHADOWS) { if (redraw && max_shadows_used < MAX_UPDATE_SHADOWS) {
//must redraw! //must redraw!
RENDER_TIMESTAMP(">Rendering Light " + itos(i)); RENDER_TIMESTAMP(">Rendering Light " + itos(i));
light->shadow_dirty = _light_instance_update_shadow(ins, p_cam_transform, p_cam_projection, p_cam_orthogonal, p_cam_vaspect, p_shadow_atlas, scenario, p_screen_lod_threshold); light->shadow_dirty = _light_instance_update_shadow(ins, p_camera_data->main_transform, p_camera_data->main_projection, p_camera_data->is_ortogonal, p_camera_data->vaspect, p_shadow_atlas, scenario, p_screen_lod_threshold);
RENDER_TIMESTAMP("<Rendering Light " + itos(i)); RENDER_TIMESTAMP("<Rendering Light " + itos(i));
} else { } else {
light->shadow_dirty = redraw; light->shadow_dirty = redraw;
@ -2864,7 +2809,7 @@ void RendererSceneCull::_render_scene(const Transform3D &p_cam_transform, const
} }
RENDER_TIMESTAMP("Render Scene "); RENDER_TIMESTAMP("Render Scene ");
scene_render->render_scene(p_render_buffers, p_cam_transform, p_cam_projection, p_cam_orthogonal, frustum_cull_result.geometry_instances, frustum_cull_result.light_instances, frustum_cull_result.reflections, frustum_cull_result.voxel_gi_instances, frustum_cull_result.decals, frustum_cull_result.lightmaps, p_environment, camera_effects, p_shadow_atlas, occluders_tex, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass, p_screen_lod_threshold, render_shadow_data, max_shadows_used, render_sdfgi_data, cull.sdfgi.region_count, &sdfgi_update_data); scene_render->render_scene(p_render_buffers, p_camera_data, frustum_cull_result.geometry_instances, frustum_cull_result.light_instances, frustum_cull_result.reflections, frustum_cull_result.voxel_gi_instances, frustum_cull_result.decals, frustum_cull_result.lightmaps, p_environment, camera_effects, p_shadow_atlas, occluders_tex, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass, p_screen_lod_threshold, render_shadow_data, max_shadows_used, render_sdfgi_data, cull.sdfgi.region_count, &sdfgi_update_data);
for (uint32_t i = 0; i < max_shadows_used; i++) { for (uint32_t i = 0; i < max_shadows_used; i++) {
render_shadow_data[i].instances.clear(); render_shadow_data[i].instances.clear();
@ -2911,7 +2856,11 @@ void RendererSceneCull::render_empty_scene(RID p_render_buffers, RID p_scenario,
environment = scenario->fallback_environment; environment = scenario->fallback_environment;
} }
RENDER_TIMESTAMP("Render Empty Scene "); RENDER_TIMESTAMP("Render Empty Scene ");
scene_render->render_scene(p_render_buffers, Transform3D(), CameraMatrix(), true, PagedArray<RendererSceneRender::GeometryInstance *>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), RID(), RID(), p_shadow_atlas, RID(), scenario->reflection_atlas, RID(), 0, 0, nullptr, 0, nullptr, 0, nullptr);
RendererSceneRender::CameraData camera_data;
camera_data.set_camera(Transform3D(), CameraMatrix(), true, false);
scene_render->render_scene(p_render_buffers, &camera_data, PagedArray<RendererSceneRender::GeometryInstance *>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), RID(), RID(), p_shadow_atlas, RID(), scenario->reflection_atlas, RID(), 0, 0, nullptr, 0, nullptr, 0, nullptr);
#endif #endif
} }
@ -2981,7 +2930,10 @@ bool RendererSceneCull::_render_reflection_probe_step(Instance *p_instance, int
} }
RENDER_TIMESTAMP("Render Reflection Probe, Step " + itos(p_step)); RENDER_TIMESTAMP("Render Reflection Probe, Step " + itos(p_step));
_render_scene(xform, cm, false, false, RID(), environment, RID(), RSG::storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, RID(), shadow_atlas, reflection_probe->instance, p_step, lod_threshold, use_shadows); RendererSceneRender::CameraData camera_data;
camera_data.set_camera(xform, cm, false, false);
_render_scene(&camera_data, RID(), environment, RID(), RSG::storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, RID(), shadow_atlas, reflection_probe->instance, p_step, lod_threshold, use_shadows);
} else { } else {
//do roughness postprocess step until it believes it's done //do roughness postprocess step until it believes it's done

View File

@ -952,11 +952,10 @@ public:
void _frustum_cull(CullData &cull_data, FrustumCullResult &cull_result, uint64_t p_from, uint64_t p_to); void _frustum_cull(CullData &cull_data, FrustumCullResult &cull_result, uint64_t p_from, uint64_t p_to);
bool _render_reflection_probe_step(Instance *p_instance, int p_step); bool _render_reflection_probe_step(Instance *p_instance, int p_step);
void _render_scene(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, bool p_using_shadows = true); void _render_scene(const RendererSceneRender::CameraData *p_camera_data, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, bool p_using_shadows = true);
void render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas); void render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas);
void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas); void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas, Ref<XRInterface> &p_xr_interface);
void render_camera(RID p_render_buffers, Ref<XRInterface> &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, float p_screen_lod_threshold, RID p_shadow_atlas);
void update_dirty_instances(); void update_dirty_instances();
void render_particle_colliders(); void render_particle_colliders();
@ -1054,7 +1053,7 @@ public:
/* Render Buffers */ /* Render Buffers */
PASS0R(RID, render_buffers_create) PASS0R(RID, render_buffers_create)
PASS7(render_buffers_configure, RID, RID, int, int, RS::ViewportMSAA, RS::ViewportScreenSpaceAA, bool) PASS8(render_buffers_configure, RID, RID, int, int, RS::ViewportMSAA, RS::ViewportScreenSpaceAA, bool, uint32_t)
PASS1(gi_set_use_half_resolution, bool) PASS1(gi_set_use_half_resolution, bool)
/* Shadow Atlas */ /* Shadow Atlas */

View File

@ -29,3 +29,153 @@
/*************************************************************************/ /*************************************************************************/
#include "renderer_scene_render.h" #include "renderer_scene_render.h"
void RendererSceneRender::CameraData::set_camera(const Transform3D p_transform, const CameraMatrix p_projection, bool p_is_ortogonal, bool p_vaspect) {
view_count = 1;
is_ortogonal = p_is_ortogonal;
vaspect = p_vaspect;
main_transform = p_transform;
main_projection = p_projection;
view_offset[0] = Transform3D();
view_projection[0] = p_projection;
}
void RendererSceneRender::CameraData::set_multiview_camera(uint32_t p_view_count, const Transform3D *p_transforms, const CameraMatrix *p_projections, bool p_is_ortogonal, bool p_vaspect) {
ERR_FAIL_COND_MSG(p_view_count != 2, "Incorrect view count for stereoscopic view");
view_count = p_view_count;
is_ortogonal = p_is_ortogonal;
vaspect = p_vaspect;
Vector<Plane> planes[2];
/////////////////////////////////////////////////////////////////////////////
// Figure out our center transform
// 1. obtain our planes
for (uint32_t v = 0; v < view_count; v++) {
planes[v] = p_projections[v].get_projection_planes(p_transforms[v]);
}
// 2. average and normalize plane normals to obtain z vector, cross them to obtain y vector, and from there the x vector for combined camera basis.
Vector3 n0 = planes[0][CameraMatrix::PLANE_LEFT].normal;
Vector3 n1 = planes[1][CameraMatrix::PLANE_RIGHT].normal;
Vector3 z = (n0 + n1).normalized();
Vector3 y = n0.cross(n1).normalized();
Vector3 x = y.cross(z).normalized();
y = z.cross(x).normalized();
main_transform.basis.set(x, y, z);
// 3. create a horizon plane with one of the eyes and the up vector as normal.
Plane horizon(p_transforms[0].origin, y);
// 4. Intersect horizon, left and right to obtain the combined camera origin.
ERR_FAIL_COND_MSG(
!horizon.intersect_3(planes[0][CameraMatrix::PLANE_LEFT], planes[1][CameraMatrix::PLANE_RIGHT], &main_transform.origin), "Can't determine camera origin");
// handy to have the inverse of the transform we just build
Transform3D main_transform_inv = main_transform.inverse();
// 5. figure out far plane, this could use some improvement, we may have our far plane too close like this, not sure if this matters
Vector3 far_center = (planes[0][CameraMatrix::PLANE_FAR].center() + planes[1][CameraMatrix::PLANE_FAR].center()) * 0.5;
Plane far(far_center, -z);
/////////////////////////////////////////////////////////////////////////////
// Figure out our top/bottom planes
// 6. Intersect far and left planes with top planes from both eyes, save the point with highest y as top_left.
Vector3 top_left, other;
ERR_FAIL_COND_MSG(
!far.intersect_3(planes[0][CameraMatrix::PLANE_LEFT], planes[0][CameraMatrix::PLANE_TOP], &top_left), "Can't determine left camera far/left/top vector");
ERR_FAIL_COND_MSG(
!far.intersect_3(planes[1][CameraMatrix::PLANE_LEFT], planes[1][CameraMatrix::PLANE_TOP], &other), "Can't determine right camera far/left/top vector");
if (y.dot(top_left) < y.dot(other)) {
top_left = other;
}
// 7. Intersect far and left planes with bottom planes from both eyes, save the point with lowest y as bottom_left.
Vector3 bottom_left;
ERR_FAIL_COND_MSG(
!far.intersect_3(planes[0][CameraMatrix::PLANE_LEFT], planes[0][CameraMatrix::PLANE_BOTTOM], &bottom_left), "Can't determine left camera far/left/bottom vector");
ERR_FAIL_COND_MSG(
!far.intersect_3(planes[1][CameraMatrix::PLANE_LEFT], planes[1][CameraMatrix::PLANE_BOTTOM], &other), "Can't determine right camera far/left/bottom vector");
if (y.dot(other) < y.dot(bottom_left)) {
bottom_left = other;
}
// 8. Intersect far and right planes with top planes from both eyes, save the point with highest y as top_right.
Vector3 top_right;
ERR_FAIL_COND_MSG(
!far.intersect_3(planes[0][CameraMatrix::PLANE_RIGHT], planes[0][CameraMatrix::PLANE_TOP], &top_right), "Can't determine left camera far/right/top vector");
ERR_FAIL_COND_MSG(
!far.intersect_3(planes[1][CameraMatrix::PLANE_RIGHT], planes[1][CameraMatrix::PLANE_TOP], &other), "Can't determine right camera far/right/top vector");
if (y.dot(top_right) < y.dot(other)) {
top_right = other;
}
// 9. Intersect far and right planes with bottom planes from both eyes, save the point with lowest y as bottom_right.
Vector3 bottom_right;
ERR_FAIL_COND_MSG(
!far.intersect_3(planes[0][CameraMatrix::PLANE_RIGHT], planes[0][CameraMatrix::PLANE_BOTTOM], &bottom_right), "Can't determine left camera far/right/bottom vector");
ERR_FAIL_COND_MSG(
!far.intersect_3(planes[1][CameraMatrix::PLANE_RIGHT], planes[1][CameraMatrix::PLANE_BOTTOM], &other), "Can't determine right camera far/right/bottom vector");
if (y.dot(other) < y.dot(bottom_right)) {
bottom_right = other;
}
// 10. Create top plane with these points: camera origin, top_left, top_right
Plane top(main_transform.origin, top_left, top_right);
// 11. Create bottom plane with these points: camera origin, bottom_left, bottom_right
Plane bottom(main_transform.origin, bottom_left, bottom_right);
/////////////////////////////////////////////////////////////////////////////
// Figure out our near plane points
// 12. Create a near plane using -camera z and the eye further along in that axis.
Plane near;
Vector3 neg_z = -z;
if (neg_z.dot(p_transforms[1].origin) < neg_z.dot(p_transforms[0].origin)) {
near = Plane(p_transforms[0].origin, neg_z);
} else {
near = Plane(p_transforms[1].origin, neg_z);
}
// 13. Intersect near plane with bottm/left planes, to obtain min_vec then top/right to obtain max_vec
Vector3 min_vec;
ERR_FAIL_COND_MSG(
!near.intersect_3(bottom, planes[0][CameraMatrix::PLANE_LEFT], &min_vec), "Can't determine left camera near/left/bottom vector");
ERR_FAIL_COND_MSG(
!near.intersect_3(bottom, planes[1][CameraMatrix::PLANE_LEFT], &other), "Can't determine right camera near/left/bottom vector");
if (x.dot(other) < x.dot(min_vec)) {
min_vec = other;
}
Vector3 max_vec;
ERR_FAIL_COND_MSG(
!near.intersect_3(top, planes[0][CameraMatrix::PLANE_RIGHT], &max_vec), "Can't determine left camera near/right/top vector");
ERR_FAIL_COND_MSG(
!near.intersect_3(top, planes[1][CameraMatrix::PLANE_RIGHT], &other), "Can't determine right camera near/right/top vector");
if (x.dot(max_vec) < x.dot(other)) {
max_vec = other;
}
// 14. transform these points by the inverse camera to obtain local_min_vec and local_max_vec
Vector3 local_min_vec = main_transform_inv.xform(min_vec);
Vector3 local_max_vec = main_transform_inv.xform(max_vec);
// 15. get x and y from these to obtain left, top, right bottom for the frustum. Get the distance from near plane to camera origin to obtain near, and the distance from the far plane to the camer origin to obtain far.
float z_near = -near.distance_to(main_transform.origin);
float z_far = -far.distance_to(main_transform.origin);
// 16. Use this to build the combined camera matrix.
main_projection.set_frustum(local_min_vec.x, local_max_vec.x, local_min_vec.y, local_max_vec.y, z_near, z_far);
/////////////////////////////////////////////////////////////////////////////
// 3. Copy our view data
for (uint32_t v = 0; v < view_count; v++) {
view_offset[v] = p_transforms[v] * main_transform_inv;
view_projection[v] = p_projections[v] * CameraMatrix(view_offset[v]);
}
}

View File

@ -39,7 +39,8 @@ class RendererSceneRender {
public: public:
enum { enum {
MAX_DIRECTIONAL_LIGHTS = 8, MAX_DIRECTIONAL_LIGHTS = 8,
MAX_DIRECTIONAL_LIGHT_CASCADES = 4 MAX_DIRECTIONAL_LIGHT_CASCADES = 4,
MAX_RENDER_VIEWS = 2
}; };
struct GeometryInstance { struct GeometryInstance {
@ -216,7 +217,24 @@ public:
uint32_t positional_light_count; uint32_t positional_light_count;
}; };
virtual void render_scene(RID p_render_buffers, const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr) = 0; struct CameraData {
// flags
uint32_t view_count;
bool is_ortogonal;
bool vaspect;
// Main/center projection
Transform3D main_transform;
CameraMatrix main_projection;
Transform3D view_offset[RendererSceneRender::MAX_RENDER_VIEWS];
CameraMatrix view_projection[RendererSceneRender::MAX_RENDER_VIEWS];
void set_camera(const Transform3D p_transform, const CameraMatrix p_projection, bool p_is_ortogonal, bool p_vaspect);
void set_multiview_camera(uint32_t p_view_count, const Transform3D *p_transforms, const CameraMatrix *p_projections, bool p_is_ortogonal, bool p_vaspect);
};
virtual void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const PagedArray<GeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr) = 0;
virtual void render_material(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) = 0; virtual void render_material(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, const PagedArray<GeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) = 0;
virtual void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<GeometryInstance *> &p_instances) = 0; virtual void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<GeometryInstance *> &p_instances) = 0;
@ -226,7 +244,7 @@ public:
virtual void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) = 0; virtual void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) = 0;
virtual RID render_buffers_create() = 0; virtual RID render_buffers_create() = 0;
virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding) = 0; virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding, uint32_t p_view_count) = 0;
virtual void gi_set_use_half_resolution(bool p_enable) = 0; virtual void gi_set_use_half_resolution(bool p_enable) = 0;
virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_limit) = 0; virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_limit) = 0;

View File

@ -590,7 +590,7 @@ public:
virtual RID render_target_create() = 0; virtual RID render_target_create() = 0;
virtual void render_target_set_position(RID p_render_target, int p_x, int p_y) = 0; virtual void render_target_set_position(RID p_render_target, int p_x, int p_y) = 0;
virtual void render_target_set_size(RID p_render_target, int p_width, int p_height) = 0; virtual void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) = 0;
virtual RID render_target_get_texture(RID p_render_target) = 0; virtual RID render_target_get_texture(RID p_render_target) = 0;
virtual void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) = 0; virtual void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) = 0;
virtual void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) = 0; virtual void render_target_set_flag(RID p_render_target, RenderTargetFlags p_flag, bool p_value) = 0;

View File

@ -71,11 +71,11 @@ static Transform2D _canvas_get_transform(RendererViewport::Viewport *p_viewport,
return xf; return xf;
} }
void RendererViewport::_draw_3d(Viewport *p_viewport, XRInterface::Eyes p_eye) { void RendererViewport::_draw_3d(Viewport *p_viewport) {
RENDER_TIMESTAMP(">Begin Rendering 3D Scene"); RENDER_TIMESTAMP(">Begin Rendering 3D Scene");
Ref<XRInterface> xr_interface; Ref<XRInterface> xr_interface;
if (XRServer::get_singleton() != nullptr) { if (p_viewport->use_xr && XRServer::get_singleton() != nullptr) {
xr_interface = XRServer::get_singleton()->get_primary_interface(); xr_interface = XRServer::get_singleton()->get_primary_interface();
} }
@ -95,15 +95,12 @@ void RendererViewport::_draw_3d(Viewport *p_viewport, XRInterface::Eyes p_eye) {
} }
float screen_lod_threshold = p_viewport->lod_threshold / float(p_viewport->size.width); float screen_lod_threshold = p_viewport->lod_threshold / float(p_viewport->size.width);
if (p_viewport->use_xr && xr_interface.is_valid()) { RSG::scene->render_camera(p_viewport->render_buffers, p_viewport->camera, p_viewport->scenario, p_viewport->self, p_viewport->size, screen_lod_threshold, p_viewport->shadow_atlas, xr_interface);
RSG::scene->render_camera(p_viewport->render_buffers, xr_interface, p_eye, p_viewport->camera, p_viewport->scenario, p_viewport->self, p_viewport->size, screen_lod_threshold, p_viewport->shadow_atlas);
} else {
RSG::scene->render_camera(p_viewport->render_buffers, p_viewport->camera, p_viewport->scenario, p_viewport->self, p_viewport->size, screen_lod_threshold, p_viewport->shadow_atlas);
}
RENDER_TIMESTAMP("<End Rendering 3D Scene"); RENDER_TIMESTAMP("<End Rendering 3D Scene");
} }
void RendererViewport::_draw_viewport(Viewport *p_viewport, XRInterface::Eyes p_eye) { void RendererViewport::_draw_viewport(Viewport *p_viewport, uint32_t p_view_count) {
if (p_viewport->measure_render_time) { if (p_viewport->measure_render_time) {
String rt_id = "vp_begin_" + itos(p_viewport->self.get_id()); String rt_id = "vp_begin_" + itos(p_viewport->self.get_id());
RSG::storage->capture_timestamp(rt_id); RSG::storage->capture_timestamp(rt_id);
@ -139,13 +136,13 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport, XRInterface::Eyes p_
if ((scenario_draw_canvas_bg || can_draw_3d) && !p_viewport->render_buffers.is_valid()) { if ((scenario_draw_canvas_bg || can_draw_3d) && !p_viewport->render_buffers.is_valid()) {
//wants to draw 3D but there is no render buffer, create //wants to draw 3D but there is no render buffer, create
p_viewport->render_buffers = RSG::scene->render_buffers_create(); p_viewport->render_buffers = RSG::scene->render_buffers_create();
RSG::scene->render_buffers_configure(p_viewport->render_buffers, p_viewport->render_target, p_viewport->size.width, p_viewport->size.height, p_viewport->msaa, p_viewport->screen_space_aa, p_viewport->use_debanding); RSG::scene->render_buffers_configure(p_viewport->render_buffers, p_viewport->render_target, p_viewport->size.width, p_viewport->size.height, p_viewport->msaa, p_viewport->screen_space_aa, p_viewport->use_debanding, p_view_count);
} }
RSG::storage->render_target_request_clear(p_viewport->render_target, bgcolor); RSG::storage->render_target_request_clear(p_viewport->render_target, bgcolor);
if (!scenario_draw_canvas_bg && can_draw_3d) { if (!scenario_draw_canvas_bg && can_draw_3d) {
_draw_3d(p_viewport, p_eye); _draw_3d(p_viewport);
} }
if (!p_viewport->hide_canvas) { if (!p_viewport->hide_canvas) {
@ -392,7 +389,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport, XRInterface::Eyes p_
if (!can_draw_3d) { if (!can_draw_3d) {
RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas); RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas);
} else { } else {
_draw_3d(p_viewport, p_eye); _draw_3d(p_viewport);
} }
scenario_draw_canvas_bg = false; scenario_draw_canvas_bg = false;
} }
@ -433,7 +430,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport, XRInterface::Eyes p_
if (!can_draw_3d) { if (!can_draw_3d) {
RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas); RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas);
} else { } else {
_draw_3d(p_viewport, p_eye); _draw_3d(p_viewport);
} }
scenario_draw_canvas_bg = false; scenario_draw_canvas_bg = false;
@ -444,7 +441,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport, XRInterface::Eyes p_
if (!can_draw_3d) { if (!can_draw_3d) {
RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas); RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas);
} else { } else {
_draw_3d(p_viewport, p_eye); _draw_3d(p_viewport);
} }
} }
} }
@ -481,7 +478,7 @@ void RendererViewport::draw_viewports() {
//sort viewports //sort viewports
active_viewports.sort_custom<ViewportSort>(); active_viewports.sort_custom<ViewportSort>();
Map<DisplayServer::WindowID, Vector<RendererCompositor::BlitToScreen>> blit_to_screen_list; Map<DisplayServer::WindowID, Vector<BlitToScreen>> blit_to_screen_list;
//draw viewports //draw viewports
RENDER_TIMESTAMP(">Render Viewports"); RENDER_TIMESTAMP(">Render Viewports");
@ -535,52 +532,54 @@ void RendererViewport::draw_viewports() {
RENDER_TIMESTAMP(">Rendering Viewport " + itos(i)); RENDER_TIMESTAMP(">Rendering Viewport " + itos(i));
RSG::storage->render_target_set_as_unused(vp->render_target); RSG::storage->render_target_set_as_unused(vp->render_target);
#if 0
// TODO fix up this code after we change our commit_for_eye to accept our new render targets
if (vp->use_xr && xr_interface.is_valid()) { if (vp->use_xr && xr_interface.is_valid()) {
// override our size, make sure it matches our required size // override our size, make sure it matches our required size and is created as a stereo target
vp->size = xr_interface->get_render_targetsize(); vp->size = xr_interface->get_render_targetsize();
RSG::storage->render_target_set_size(vp->render_target, vp->size.x, vp->size.y); uint32_t view_count = xr_interface->get_view_count();
RSG::storage->render_target_set_size(vp->render_target, vp->size.x, vp->size.y, view_count);
// render mono or left eye first // check for an external texture destination (disabled for now, not yet supported)
XRInterface::Eyes leftOrMono = xr_interface->is_stereo() ? XRInterface::EYE_LEFT : XRInterface::EYE_MONO; // RSG::storage->render_target_set_external_texture(vp->render_target, xr_interface->get_external_texture_for_eye(leftOrMono));
RSG::storage->render_target_set_external_texture(vp->render_target, 0);
// check for an external texture destination for our left eye/mono // render...
// TODO investigate how we're going to make external textures work RSG::scene->set_debug_draw_mode(vp->debug_draw);
RSG::storage->render_target_set_external_texture(vp->render_target, xr_interface->get_external_texture_for_eye(leftOrMono)); RSG::storage->render_info_begin_capture();
// set our render target as current // and draw viewport
RSG::rasterizer->set_current_render_target(vp->render_target); _draw_viewport(vp, view_count);
// and draw left eye/mono // measure
_draw_viewport(vp, leftOrMono); RSG::storage->render_info_end_capture();
xr_interface->commit_for_eye(leftOrMono, vp->render_target, vp->viewport_to_screen_rect); vp->render_info[RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_OBJECTS_IN_FRAME);
vp->render_info[RS::VIEWPORT_RENDER_INFO_VERTICES_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_VERTICES_IN_FRAME);
vp->render_info[RS::VIEWPORT_RENDER_INFO_MATERIAL_CHANGES_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_MATERIAL_CHANGES_IN_FRAME);
vp->render_info[RS::VIEWPORT_RENDER_INFO_SHADER_CHANGES_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_SHADER_CHANGES_IN_FRAME);
vp->render_info[RS::VIEWPORT_RENDER_INFO_SURFACE_CHANGES_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_SURFACE_CHANGES_IN_FRAME);
vp->render_info[RS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_DRAW_CALLS_IN_FRAME);
// render right eye // commit our eyes
if (leftOrMono == XRInterface::EYE_LEFT) { Vector<BlitToScreen> blits = xr_interface->commit_views(vp->render_target, vp->viewport_to_screen_rect);
// check for an external texture destination for our right eye if (vp->viewport_to_screen != DisplayServer::INVALID_WINDOW_ID && blits.size() > 0) {
RSG::storage->render_target_set_external_texture(vp->render_target, xr_interface->get_external_texture_for_eye(XRInterface::EYE_RIGHT)); if (!blit_to_screen_list.has(vp->viewport_to_screen)) {
blit_to_screen_list[vp->viewport_to_screen] = Vector<BlitToScreen>();
}
// commit for eye may have changed the render target for (int b = 0; b < blits.size(); b++) {
RSG::rasterizer->set_current_render_target(vp->render_target); blit_to_screen_list[vp->viewport_to_screen].push_back(blits[b]);
}
_draw_viewport(vp, XRInterface::EYE_RIGHT);
xr_interface->commit_for_eye(XRInterface::EYE_RIGHT, vp->render_target, vp->viewport_to_screen_rect);
} }
// and for our frame timing, mark when we've finished committing our eyes // and for our frame timing, mark when we've finished committing our eyes
XRServer::get_singleton()->_mark_commit(); XRServer::get_singleton()->_mark_commit();
} else { } else {
#endif
{
RSG::storage->render_target_set_external_texture(vp->render_target, 0); RSG::storage->render_target_set_external_texture(vp->render_target, 0);
RSG::scene->set_debug_draw_mode(vp->debug_draw); RSG::scene->set_debug_draw_mode(vp->debug_draw);
RSG::storage->render_info_begin_capture(); RSG::storage->render_info_begin_capture();
// render standard mono camera // render standard mono camera
_draw_viewport(vp); _draw_viewport(vp, 1);
RSG::storage->render_info_end_capture(); RSG::storage->render_info_end_capture();
vp->render_info[RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_OBJECTS_IN_FRAME); vp->render_info[RS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] = RSG::storage->get_captured_render_info(RS::INFO_OBJECTS_IN_FRAME);
@ -592,7 +591,7 @@ void RendererViewport::draw_viewports() {
if (vp->viewport_to_screen != DisplayServer::INVALID_WINDOW_ID && (!vp->viewport_render_direct_to_screen || !RSG::rasterizer->is_low_end())) { if (vp->viewport_to_screen != DisplayServer::INVALID_WINDOW_ID && (!vp->viewport_render_direct_to_screen || !RSG::rasterizer->is_low_end())) {
//copy to screen if set as such //copy to screen if set as such
RendererCompositor::BlitToScreen blit; BlitToScreen blit;
blit.render_target = vp->render_target; blit.render_target = vp->render_target;
if (vp->viewport_to_screen_rect != Rect2()) { if (vp->viewport_to_screen_rect != Rect2()) {
blit.rect = vp->viewport_to_screen_rect; blit.rect = vp->viewport_to_screen_rect;
@ -602,7 +601,7 @@ void RendererViewport::draw_viewports() {
} }
if (!blit_to_screen_list.has(vp->viewport_to_screen)) { if (!blit_to_screen_list.has(vp->viewport_to_screen)) {
blit_to_screen_list[vp->viewport_to_screen] = Vector<RendererCompositor::BlitToScreen>(); blit_to_screen_list[vp->viewport_to_screen] = Vector<BlitToScreen>();
} }
blit_to_screen_list[vp->viewport_to_screen].push_back(blit); blit_to_screen_list[vp->viewport_to_screen].push_back(blit);
@ -621,7 +620,7 @@ void RendererViewport::draw_viewports() {
//this needs to be called to make screen swapping more efficient //this needs to be called to make screen swapping more efficient
RSG::rasterizer->prepare_for_blitting_render_targets(); RSG::rasterizer->prepare_for_blitting_render_targets();
for (Map<int, Vector<RendererCompositor::BlitToScreen>>::Element *E = blit_to_screen_list.front(); E; E = E->next()) { for (Map<int, Vector<BlitToScreen>>::Element *E = blit_to_screen_list.front(); E; E = E->next()) {
RSG::rasterizer->blit_render_targets_to_screen(E->key(), E->get().ptr(), E->get().size()); RSG::rasterizer->blit_render_targets_to_screen(E->key(), E->get().ptr(), E->get().size());
} }
} }
@ -646,7 +645,29 @@ void RendererViewport::viewport_set_use_xr(RID p_viewport, bool p_use_xr) {
Viewport *viewport = viewport_owner.getornull(p_viewport); Viewport *viewport = viewport_owner.getornull(p_viewport);
ERR_FAIL_COND(!viewport); ERR_FAIL_COND(!viewport);
if (viewport->use_xr == p_use_xr) {
return;
}
viewport->use_xr = p_use_xr; viewport->use_xr = p_use_xr;
if (viewport->render_buffers.is_valid()) {
RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, viewport->screen_space_aa, viewport->use_debanding, viewport->get_view_count());
}
}
uint32_t RendererViewport::Viewport::get_view_count() {
uint32_t view_count = 1;
if (use_xr && XRServer::get_singleton() != nullptr) {
Ref<XRInterface> xr_interface;
xr_interface = XRServer::get_singleton()->get_primary_interface();
if (xr_interface.is_valid()) {
view_count = xr_interface->get_view_count();
}
}
return view_count;
} }
void RendererViewport::viewport_set_size(RID p_viewport, int p_width, int p_height) { void RendererViewport::viewport_set_size(RID p_viewport, int p_width, int p_height) {
@ -656,13 +677,14 @@ void RendererViewport::viewport_set_size(RID p_viewport, int p_width, int p_heig
ERR_FAIL_COND(!viewport); ERR_FAIL_COND(!viewport);
viewport->size = Size2(p_width, p_height); viewport->size = Size2(p_width, p_height);
RSG::storage->render_target_set_size(viewport->render_target, p_width, p_height); uint32_t view_count = viewport->get_view_count();
RSG::storage->render_target_set_size(viewport->render_target, p_width, p_height, view_count);
if (viewport->render_buffers.is_valid()) { if (viewport->render_buffers.is_valid()) {
if (p_width == 0 || p_height == 0) { if (p_width == 0 || p_height == 0) {
RSG::scene->free(viewport->render_buffers); RSG::scene->free(viewport->render_buffers);
viewport->render_buffers = RID(); viewport->render_buffers = RID();
} else { } else {
RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, viewport->screen_space_aa, viewport->use_debanding); RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, viewport->screen_space_aa, viewport->use_debanding, view_count);
} }
} }
@ -704,7 +726,7 @@ void RendererViewport::viewport_attach_to_screen(RID p_viewport, const Rect2 &p_
// If using GLES2 we can optimize this operation by rendering directly to system_fbo // If using GLES2 we can optimize this operation by rendering directly to system_fbo
// instead of rendering to fbo and copying to system_fbo after // instead of rendering to fbo and copying to system_fbo after
if (RSG::rasterizer->is_low_end() && viewport->viewport_render_direct_to_screen) { if (RSG::rasterizer->is_low_end() && viewport->viewport_render_direct_to_screen) {
RSG::storage->render_target_set_size(viewport->render_target, p_rect.size.x, p_rect.size.y); RSG::storage->render_target_set_size(viewport->render_target, p_rect.size.x, p_rect.size.y, viewport->get_view_count());
RSG::storage->render_target_set_position(viewport->render_target, p_rect.position.x, p_rect.position.y); RSG::storage->render_target_set_position(viewport->render_target, p_rect.position.x, p_rect.position.y);
} }
@ -714,7 +736,7 @@ void RendererViewport::viewport_attach_to_screen(RID p_viewport, const Rect2 &p_
// if render_direct_to_screen was used, reset size and position // if render_direct_to_screen was used, reset size and position
if (RSG::rasterizer->is_low_end() && viewport->viewport_render_direct_to_screen) { if (RSG::rasterizer->is_low_end() && viewport->viewport_render_direct_to_screen) {
RSG::storage->render_target_set_position(viewport->render_target, 0, 0); RSG::storage->render_target_set_position(viewport->render_target, 0, 0);
RSG::storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y); RSG::storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y, viewport->get_view_count());
} }
viewport->viewport_to_screen_rect = Rect2(); viewport->viewport_to_screen_rect = Rect2();
@ -733,7 +755,7 @@ void RendererViewport::viewport_set_render_direct_to_screen(RID p_viewport, bool
// if disabled, reset render_target size and position // if disabled, reset render_target size and position
if (!p_enable) { if (!p_enable) {
RSG::storage->render_target_set_position(viewport->render_target, 0, 0); RSG::storage->render_target_set_position(viewport->render_target, 0, 0);
RSG::storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y); RSG::storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y, viewport->get_view_count());
} }
RSG::storage->render_target_set_flag(viewport->render_target, RendererStorage::RENDER_TARGET_DIRECT_TO_SCREEN, p_enable); RSG::storage->render_target_set_flag(viewport->render_target, RendererStorage::RENDER_TARGET_DIRECT_TO_SCREEN, p_enable);
@ -741,7 +763,7 @@ void RendererViewport::viewport_set_render_direct_to_screen(RID p_viewport, bool
// if attached to screen already, setup screen size and position, this needs to happen after setting flag to avoid an unnecessary buffer allocation // if attached to screen already, setup screen size and position, this needs to happen after setting flag to avoid an unnecessary buffer allocation
if (RSG::rasterizer->is_low_end() && viewport->viewport_to_screen_rect != Rect2() && p_enable) { if (RSG::rasterizer->is_low_end() && viewport->viewport_to_screen_rect != Rect2() && p_enable) {
RSG::storage->render_target_set_size(viewport->render_target, viewport->viewport_to_screen_rect.size.x, viewport->viewport_to_screen_rect.size.y); RSG::storage->render_target_set_size(viewport->render_target, viewport->viewport_to_screen_rect.size.x, viewport->viewport_to_screen_rect.size.y, viewport->get_view_count());
RSG::storage->render_target_set_position(viewport->render_target, viewport->viewport_to_screen_rect.position.x, viewport->viewport_to_screen_rect.position.y); RSG::storage->render_target_set_position(viewport->render_target, viewport->viewport_to_screen_rect.position.x, viewport->viewport_to_screen_rect.position.y);
} }
} }
@ -892,7 +914,7 @@ void RendererViewport::viewport_set_msaa(RID p_viewport, RS::ViewportMSAA p_msaa
} }
viewport->msaa = p_msaa; viewport->msaa = p_msaa;
if (viewport->render_buffers.is_valid()) { if (viewport->render_buffers.is_valid()) {
RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, p_msaa, viewport->screen_space_aa, viewport->use_debanding); RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, p_msaa, viewport->screen_space_aa, viewport->use_debanding, viewport->get_view_count());
} }
} }
@ -905,7 +927,7 @@ void RendererViewport::viewport_set_screen_space_aa(RID p_viewport, RS::Viewport
} }
viewport->screen_space_aa = p_mode; viewport->screen_space_aa = p_mode;
if (viewport->render_buffers.is_valid()) { if (viewport->render_buffers.is_valid()) {
RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, p_mode, viewport->use_debanding); RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, p_mode, viewport->use_debanding, viewport->get_view_count());
} }
} }
@ -918,7 +940,7 @@ void RendererViewport::viewport_set_use_debanding(RID p_viewport, bool p_use_deb
} }
viewport->use_debanding = p_use_debanding; viewport->use_debanding = p_use_debanding;
if (viewport->render_buffers.is_valid()) { if (viewport->render_buffers.is_valid()) {
RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, viewport->screen_space_aa, p_use_debanding); RSG::scene->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, viewport->screen_space_aa, p_use_debanding, viewport->get_view_count());
} }
} }

View File

@ -164,6 +164,8 @@ public:
time_gpu_begin = 0; time_gpu_begin = 0;
time_gpu_end = 0; time_gpu_end = 0;
} }
uint32_t get_view_count();
}; };
HashMap<String, RID> timestamp_vp_map; HashMap<String, RID> timestamp_vp_map;
@ -187,8 +189,8 @@ public:
Vector<Viewport *> active_viewports; Vector<Viewport *> active_viewports;
private: private:
void _draw_3d(Viewport *p_viewport, XRInterface::Eyes p_eye); void _draw_3d(Viewport *p_viewport);
void _draw_viewport(Viewport *p_viewport, XRInterface::Eyes p_eye = XRInterface::EYE_MONO); void _draw_viewport(Viewport *p_viewport, uint32_t p_view_count = 1);
int occlusion_rays_per_thread = 512; int occlusion_rays_per_thread = 512;

View File

@ -516,11 +516,11 @@ public:
typedef int64_t FramebufferFormatID; typedef int64_t FramebufferFormatID;
// This ID is warranted to be unique for the same formats, does not need to be freed // This ID is warranted to be unique for the same formats, does not need to be freed
virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format) = 0; virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format, uint32_t p_view_count = 1) = 0;
virtual FramebufferFormatID framebuffer_format_create_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1) = 0; virtual FramebufferFormatID framebuffer_format_create_empty(TextureSamples p_samples = TEXTURE_SAMPLES_1) = 0;
virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format) = 0; virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format) = 0;
virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID) = 0; virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID, uint32_t p_view_count = 1) = 0;
virtual RID framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples = TEXTURE_SAMPLES_1, FramebufferFormatID p_format_check = INVALID_ID) = 0; virtual RID framebuffer_create_empty(const Size2i &p_size, TextureSamples p_samples = TEXTURE_SAMPLES_1, FramebufferFormatID p_format_check = INVALID_ID) = 0;
virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer) = 0; virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer) = 0;

View File

@ -92,6 +92,10 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VIEWPORT_SIZE"] = constt(ShaderLanguage::TYPE_VEC2); shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VIEWPORT_SIZE"] = constt(ShaderLanguage::TYPE_VEC2);
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL); shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL);
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VIEW_INDEX"] = constt(ShaderLanguage::TYPE_INT);
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VIEW_MONO_LEFT"] = constt(ShaderLanguage::TYPE_INT);
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VIEW_RIGHT"] = constt(ShaderLanguage::TYPE_INT);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VERTEX"] = constt(ShaderLanguage::TYPE_VEC3); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VERTEX"] = constt(ShaderLanguage::TYPE_VEC3);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["FRAGCOORD"] = constt(ShaderLanguage::TYPE_VEC4); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["FRAGCOORD"] = constt(ShaderLanguage::TYPE_VEC4);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["FRONT_FACING"] = constt(ShaderLanguage::TYPE_BOOL); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["FRONT_FACING"] = constt(ShaderLanguage::TYPE_BOOL);
@ -131,6 +135,10 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["SCREEN_UV"] = constt(ShaderLanguage::TYPE_VEC2); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["SCREEN_UV"] = constt(ShaderLanguage::TYPE_VEC2);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["POINT_COORD"] = constt(ShaderLanguage::TYPE_VEC2); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["POINT_COORD"] = constt(ShaderLanguage::TYPE_VEC2);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VIEW_INDEX"] = constt(ShaderLanguage::TYPE_INT);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VIEW_MONO_LEFT"] = constt(ShaderLanguage::TYPE_INT);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["VIEW_RIGHT"] = constt(ShaderLanguage::TYPE_INT);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["WORLD_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4); shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["WORLD_MATRIX"] = constt(ShaderLanguage::TYPE_MAT4);

View File

@ -2420,6 +2420,8 @@ RenderingServer::RenderingServer() {
GLOBAL_DEF("rendering/limits/cluster_builder/max_clustered_elements", 512); GLOBAL_DEF("rendering/limits/cluster_builder/max_clustered_elements", 512);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/cluster_builder/max_clustered_elements", PropertyInfo(Variant::FLOAT, "rendering/limits/cluster_builder/max_clustered_elements", PROPERTY_HINT_RANGE, "32,8192,1")); ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/cluster_builder/max_clustered_elements", PropertyInfo(Variant::FLOAT, "rendering/limits/cluster_builder/max_clustered_elements", PROPERTY_HINT_RANGE, "32,8192,1"));
GLOBAL_DEF_RST("rendering/xr/enabled", false);
} }
RenderingServer::~RenderingServer() { RenderingServer::~RenderingServer() {

View File

@ -29,6 +29,7 @@
/*************************************************************************/ /*************************************************************************/
#include "xr_interface.h" #include "xr_interface.h"
#include "servers/rendering/renderer_compositor.h"
void XRInterface::_bind_methods() { void XRInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_name"), &XRInterface::get_name); ClassDB::bind_method(D_METHOD("get_name"), &XRInterface::get_name);
@ -45,7 +46,7 @@ void XRInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tracking_status"), &XRInterface::get_tracking_status); ClassDB::bind_method(D_METHOD("get_tracking_status"), &XRInterface::get_tracking_status);
ClassDB::bind_method(D_METHOD("get_render_targetsize"), &XRInterface::get_render_targetsize); ClassDB::bind_method(D_METHOD("get_render_targetsize"), &XRInterface::get_render_targetsize);
ClassDB::bind_method(D_METHOD("is_stereo"), &XRInterface::is_stereo); ClassDB::bind_method(D_METHOD("get_view_count"), &XRInterface::get_view_count);
ADD_GROUP("Interface", "interface_"); ADD_GROUP("Interface", "interface_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interface_is_primary"), "set_is_primary", "is_primary"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interface_is_primary"), "set_is_primary", "is_primary");

View File

@ -35,6 +35,9 @@
#include "core/os/thread_safe.h" #include "core/os/thread_safe.h"
#include "servers/xr_server.h" #include "servers/xr_server.h"
// forward declaration
struct BlitToScreen;
/** /**
@author Bastiaan Olij <mux213@gmail.com> @author Bastiaan Olij <mux213@gmail.com>
@ -105,17 +108,22 @@ public:
/** rendering and internal **/ /** rendering and internal **/
virtual Size2 get_render_targetsize() = 0; /* returns the recommended render target size per eye for this device */ virtual Size2 get_render_targetsize() = 0; /* returns the recommended render target size per eye for this device */
virtual bool is_stereo() = 0; /* returns true if this interface requires stereo rendering (for VR HMDs) or mono rendering (for mobile AR) */ virtual uint32_t get_view_count() = 0; /* returns the view count we need (1 is monoscopic, 2 is stereoscopic but can be more) */
virtual Transform3D get_transform_for_eye(XRInterface::Eyes p_eye, const Transform3D &p_cam_transform) = 0; /* get each eyes camera transform, also implement EYE_MONO */ virtual Transform3D get_camera_transform() = 0; /* returns the position of our camera for updating our camera node. For monoscopic this is equal to the views transform, for stereoscopic this should be an average */
virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) = 0; /* get each eyes projection matrix */ virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) = 0; /* get each views transform */
virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye); /* if applicable return external texture to render to */ virtual CameraMatrix get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) = 0; /* get each view projection matrix */
virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) = 0; /* output the left or right eye */
virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) = 0; /* commit rendered views to the XR interface */
virtual void process() = 0; virtual void process() = 0;
virtual void notification(int p_what) = 0; virtual void notification(int p_what) = 0;
XRInterface(); XRInterface();
~XRInterface(); ~XRInterface();
// deprecated
virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye); /* if applicable return external texture to render to */
virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) = 0; /* output the left or right eye */
}; };
VARIANT_ENUM_CAST(XRInterface::Capabilities); VARIANT_ENUM_CAST(XRInterface::Capabilities);

View File

@ -119,7 +119,7 @@ void XRServer::center_on_hmd(RotationMode p_rotation_mode, bool p_keep_height) {
reference_frame = Transform3D(); reference_frame = Transform3D();
// requesting our EYE_MONO transform should return our current HMD position // requesting our EYE_MONO transform should return our current HMD position
Transform3D new_reference_frame = primary_interface->get_transform_for_eye(XRInterface::EYE_MONO, Transform3D()); Transform3D new_reference_frame = primary_interface->get_camera_transform();
// remove our tilt // remove our tilt
if (p_rotation_mode == 1) { if (p_rotation_mode == 1) {
@ -148,7 +148,7 @@ void XRServer::center_on_hmd(RotationMode p_rotation_mode, bool p_keep_height) {
Transform3D XRServer::get_hmd_transform() { Transform3D XRServer::get_hmd_transform() {
Transform3D hmd_transform; Transform3D hmd_transform;
if (primary_interface != nullptr) { if (primary_interface != nullptr) {
hmd_transform = primary_interface->get_transform_for_eye(XRInterface::EYE_MONO, hmd_transform); hmd_transform = primary_interface->get_camera_transform();
}; };
return hmd_transform; return hmd_transform;
}; };