├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── THIRD_PARTY ├── XR_APILAYER_NOVENDOR_steamvr_passthrough ├── XR_APILAYER_NOVENDOR_steamvr_passthrough.json ├── XR_APILAYER_NOVENDOR_steamvr_passthrough.rc ├── XR_APILAYER_NOVENDOR_steamvr_passthrough.vcxproj ├── XR_APILAYER_NOVENDOR_steamvr_passthrough.vcxproj.filters ├── camera_enumerator.cpp ├── camera_enumerator.h ├── camera_manager.h ├── camera_manager_opencv.cpp ├── camera_manager_openvr.cpp ├── check.h ├── config_manager.cpp ├── config_manager.h ├── dashboard_menu.cpp ├── dashboard_menu.h ├── depth_reconstruction.cpp ├── depth_reconstruction.h ├── fonts │ ├── cousine_regular.cpp │ └── roboto_medium.cpp ├── framework │ ├── dispatch.cpp │ ├── dispatch.gen.cpp │ ├── dispatch.gen.h │ ├── dispatch.h │ ├── dispatch_generator.py │ ├── entry.cpp │ ├── layer_apis.py │ ├── log.cpp │ ├── log.h │ └── util.h ├── layer.cpp ├── layer.h ├── mathutil.h ├── mesh.cpp ├── mesh.h ├── openvr_manager.cpp ├── openvr_manager.h ├── passthrough_icon.png ├── passthrough_renderer.h ├── passthrough_renderer_dx11.cpp ├── passthrough_renderer_dx11_interop.cpp ├── passthrough_renderer_dx12.cpp ├── passthrough_renderer_vulkan.cpp ├── pch.cpp ├── pch.h ├── prebuild_commands.bat ├── resource.h ├── shaders │ ├── alpha_copy_masked_ps.hlsl │ ├── alpha_prepass_masked_fullscreen_ps.hlsl │ ├── alpha_prepass_masked_ps.hlsl │ ├── alpha_prepass_ps.hlsl │ ├── common_ps.hlsl │ ├── common_vs.hlsl │ ├── depth_write_ps.hlsl │ ├── depth_write_temporal_ps.hlsl │ ├── fill_holes_cs.hlsl │ ├── fullscreen_passthrough_composite_ps.hlsl │ ├── fullscreen_passthrough_composite_temporal_ps.hlsl │ ├── fullscreen_passthrough_ps.hlsl │ ├── fullscreen_passthrough_temporal_ps.hlsl │ ├── fullscreen_quad_vs.hlsl │ ├── fullscreen_util.hlsl │ ├── mesh_rigid_vs.hlsl │ ├── passthrough_ps.hlsl │ ├── passthrough_read_depth_vs.hlsl │ ├── passthrough_stereo_composite_ps.hlsl │ ├── passthrough_stereo_composite_temporal_ps.hlsl │ ├── passthrough_stereo_temporal_vs.hlsl │ ├── passthrough_stereo_vs.hlsl │ ├── passthrough_temporal_ps.hlsl │ ├── passthrough_vs.hlsl │ ├── util.hlsl │ └── vs_outputs.hlsl ├── testpattern.png ├── testpattern_old.png └── testpattern_vert.png ├── camera-calibration ├── camera-calibration.cpp ├── camera-calibration.h ├── camera-calibration.rc ├── camera-calibration.vcxproj ├── camera-calibration.vcxproj.filters └── resource.h ├── openxr-steamvr-passthrough.sln ├── passthrough-setup ├── passthrough-setup.cpp ├── passthrough-setup.rc ├── passthrough-setup.vcxproj ├── passthrough-setup.vcxproj.filters ├── passthrough_icon_opaque.ico └── resource.h ├── readme.md ├── scripts ├── Install-Layer.ps1 ├── Tracing.wprp └── Uninstall-Layer.ps1 └── settings_menu.png /.gitattributes: -------------------------------------------------------------------------------- 1 | *.gen.* text eol=lf 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: Rectus -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | packages/ 3 | bin/ 4 | **/x64/ 5 | **/obj/ 6 | **/__pycache__/ 7 | *.pyc 8 | *.vcxproj.user 9 | *.csproj.user 10 | XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/*.h 11 | external/opencv/ 12 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/OpenXR-SDK-Source"] 2 | path = external/OpenXR-SDK-Source 3 | url = https://github.com/KhronosGroup/OpenXR-SDK-Source.git 4 | 5 | [submodule "external/OpenXR-SDK"] 6 | path = external/OpenXR-SDK 7 | url = https://github.com/KhronosGroup/OpenXR-SDK.git 8 | 9 | [submodule "external/openvr"] 10 | path = external/openvr 11 | url = https://github.com/ValveSoftware/openvr.git 12 | 13 | [submodule "external/simpleini"] 14 | path = external/simpleini 15 | url = https://github.com/Rectus/simpleini.git 16 | 17 | [submodule "external/imgui"] 18 | path = external/imgui 19 | url = https://github.com/ocornut/imgui.git 20 | 21 | [submodule "external/lodepng"] 22 | path = external/lodepng 23 | url = https://github.com/lvandeve/lodepng.git 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Rectus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/XR_APILAYER_NOVENDOR_steamvr_passthrough.json: -------------------------------------------------------------------------------- 1 | { 2 | "file_format_version" : "1.0.0", 3 | "api_layer": { 4 | "name": "XR_APILAYER_NOVENDOR_steamvr_passthrough", 5 | "library_path": ".\\XR_APILAYER_NOVENDOR_steamvr_passthrough.dll", 6 | "api_version": "1.0", 7 | "implementation_version": "1", 8 | "description": "Passthrough Support for SteamVR", 9 | "functions": { 10 | "xrNegotiateLoaderApiLayerInterface": "xrNegotiateLoaderApiLayerInterface" 11 | }, 12 | "instance_extensions": [ 13 | { 14 | "name": "XR_VARJO_composition_layer_depth_test", 15 | "extension_version": "2", 16 | "entrypoints": [] 17 | }, 18 | { 19 | "name": "XR_VARJO_environment_depth_estimation", 20 | "extension_version": "1", 21 | "entrypoints": [] 22 | } 23 | ], 24 | "disable_environment": "DISABLE_XR_APILAYER_NOVENDOR_steamvr_passthrough" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/XR_APILAYER_NOVENDOR_steamvr_passthrough.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "winres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // Swedish (Sweden) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_SVE) 19 | LANGUAGE LANG_SWEDISH, SUBLANG_SWEDISH 20 | #pragma code_page(1252) 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Version 51 | // 52 | 53 | VS_VERSION_INFO VERSIONINFO 54 | FILEVERSION 0,3,3,0 55 | PRODUCTVERSION 0,3,3,0 56 | FILEFLAGSMASK 0x3fL 57 | #ifdef _DEBUG 58 | FILEFLAGS 0x1L 59 | #else 60 | FILEFLAGS 0x0L 61 | #endif 62 | FILEOS 0x40004L 63 | FILETYPE 0x2L 64 | FILESUBTYPE 0x0L 65 | BEGIN 66 | BLOCK "StringFileInfo" 67 | BEGIN 68 | BLOCK "040904b0" 69 | BEGIN 70 | VALUE "CompanyName", "N/A" 71 | VALUE "FileDescription", "OpenXR SteamVR Passthrough API Layer" 72 | VALUE "FileVersion", "0.3.3.0" 73 | VALUE "InternalName", "XR_APILAYER_NOVENDOR_steamvr_passthrough.dll" 74 | VALUE "LegalCopyright", "Copyright (C) Rectus 2025" 75 | VALUE "OriginalFilename", "XR_APILAYER_NOVENDOR_steamvr_passthrough.dll" 76 | VALUE "ProductName", "OpenXR SteamVR Passthrough API Layer" 77 | VALUE "ProductVersion", "0.3.3" 78 | END 79 | END 80 | BLOCK "VarFileInfo" 81 | BEGIN 82 | VALUE "Translation", 0x409, 1200 83 | END 84 | END 85 | 86 | 87 | ///////////////////////////////////////////////////////////////////////////// 88 | // 89 | // PNG 90 | // 91 | 92 | IDB_PNG_DASHBOARD_ICON PNG "passthrough_icon.png" 93 | 94 | IDB_PNG_TESTPATTERN PNG "testpattern.png" 95 | 96 | #endif // Swedish (Sweden) resources 97 | ///////////////////////////////////////////////////////////////////////////// 98 | 99 | 100 | 101 | #ifndef APSTUDIO_INVOKED 102 | ///////////////////////////////////////////////////////////////////////////// 103 | // 104 | // Generated from the TEXTINCLUDE 3 resource. 105 | // 106 | 107 | 108 | ///////////////////////////////////////////////////////////////////////////// 109 | #endif // not APSTUDIO_INVOKED 110 | 111 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/XR_APILAYER_NOVENDOR_steamvr_passthrough.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {060fbbc6-44b1-4494-b904-cb9719cec138} 14 | 15 | 16 | {077e54ab-58a7-4c28-84ce-2a722465e3d9} 17 | 18 | 19 | {7fb9ef4f-5887-4810-b86f-3b4724e1c8e2} 20 | 21 | 22 | 23 | 24 | Header Files 25 | 26 | 27 | Header Files 28 | 29 | 30 | Framework 31 | 32 | 33 | Framework 34 | 35 | 36 | Framework 37 | 38 | 39 | Framework 40 | 41 | 42 | Header Files 43 | 44 | 45 | Header Files 46 | 47 | 48 | Header Files 49 | 50 | 51 | Header Files 52 | 53 | 54 | Libraries 55 | 56 | 57 | Libraries 58 | 59 | 60 | Libraries 61 | 62 | 63 | Libraries 64 | 65 | 66 | Libraries 67 | 68 | 69 | Libraries 70 | 71 | 72 | Header Files 73 | 74 | 75 | Header Files 76 | 77 | 78 | Header Files 79 | 80 | 81 | Header Files 82 | 83 | 84 | Header Files 85 | 86 | 87 | Header Files 88 | 89 | 90 | Header Files 91 | 92 | 93 | 94 | 95 | Source Files 96 | 97 | 98 | Source Files 99 | 100 | 101 | Framework 102 | 103 | 104 | Framework 105 | 106 | 107 | Framework 108 | 109 | 110 | Framework 111 | 112 | 113 | Source Files 114 | 115 | 116 | Source Files 117 | 118 | 119 | Source Files 120 | 121 | 122 | Source Files 123 | 124 | 125 | Source Files 126 | 127 | 128 | Libraries 129 | 130 | 131 | Libraries 132 | 133 | 134 | Libraries 135 | 136 | 137 | Libraries 138 | 139 | 140 | Libraries 141 | 142 | 143 | Libraries 144 | 145 | 146 | Libraries 147 | 148 | 149 | Source Files 150 | 151 | 152 | Source Files 153 | 154 | 155 | Source Files 156 | 157 | 158 | Source Files 159 | 160 | 161 | Source Files 162 | 163 | 164 | Source Files 165 | 166 | 167 | Source Files 168 | 169 | 170 | 171 | 172 | Framework 173 | 174 | 175 | Framework 176 | 177 | 178 | Shaders 179 | 180 | 181 | Shaders 182 | 183 | 184 | Shaders 185 | 186 | 187 | Shaders 188 | 189 | 190 | Shaders 191 | 192 | 193 | 194 | 195 | Shaders 196 | 197 | 198 | Shaders 199 | 200 | 201 | Shaders 202 | 203 | 204 | Shaders 205 | 206 | 207 | Shaders 208 | 209 | 210 | Shaders 211 | 212 | 213 | Shaders 214 | 215 | 216 | Shaders 217 | 218 | 219 | Shaders 220 | 221 | 222 | Shaders 223 | 224 | 225 | Shaders 226 | 227 | 228 | Shaders 229 | 230 | 231 | Shaders 232 | 233 | 234 | Shaders 235 | 236 | 237 | Shaders 238 | 239 | 240 | Shaders 241 | 242 | 243 | Shaders 244 | 245 | 246 | Shaders 247 | 248 | 249 | Shaders 250 | 251 | 252 | Shaders 253 | 254 | 255 | Shaders 256 | 257 | 258 | 259 | 260 | Libraries 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/camera_enumerator.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "camera_enumerator.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | void CameraEnumerator::EnumerateCameras(std::vector& deviceList) 10 | { 11 | uint32_t deviceCount = 0; 12 | IMFAttributes* attributes = NULL; 13 | IMFActivate** devices = NULL; 14 | 15 | deviceList.clear(); 16 | 17 | HRESULT result = MFCreateAttributes(&attributes, 1); 18 | 19 | if (FAILED(result)) 20 | { 21 | return; 22 | } 23 | 24 | result = attributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID); 25 | 26 | 27 | if (FAILED(result)) 28 | { 29 | attributes->Release(); 30 | return; 31 | } 32 | 33 | result = MFEnumDeviceSources(attributes, &devices, &deviceCount); 34 | 35 | if (FAILED(result)) 36 | { 37 | CoTaskMemFree(devices); 38 | attributes->Release(); 39 | return; 40 | } 41 | 42 | for (uint32_t i = 0; i < deviceCount; i++) 43 | { 44 | WCHAR* displayName = NULL; 45 | char buffer[128] = { 0 }; 46 | 47 | UINT32 nameLength; 48 | HRESULT result = devices[i]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &displayName, &nameLength); 49 | 50 | if (SUCCEEDED(result) && displayName) 51 | { 52 | WideCharToMultiByte(CP_UTF8, 0, displayName, nameLength, buffer, 127, NULL, NULL); 53 | deviceList.push_back(buffer); 54 | } 55 | else 56 | { 57 | deviceList.push_back("ERROR"); 58 | } 59 | CoTaskMemFree(displayName); 60 | } 61 | 62 | 63 | for (uint32_t i = 0; i < deviceCount; i++) 64 | { 65 | devices[i]->Release(); 66 | } 67 | CoTaskMemFree(devices); 68 | attributes->Release(); 69 | } -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/camera_enumerator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | 5 | 6 | class CameraEnumerator 7 | { 8 | public: 9 | static void EnumerateCameras(std::vector& deviceList); 10 | 11 | }; -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/check.h: -------------------------------------------------------------------------------- 1 | // From OpenXR SDK hello_xr 2 | // Copyright (c) 2017-2021, The Khronos Group Inc. 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | #pragma once 7 | 8 | #include "pch.h" 9 | 10 | #include 11 | 12 | // Macro to generate stringify functions for OpenXR enumerations based data provided in openxr_reflection.h 13 | // clang-format off 14 | #define ENUM_CASE_STR(name, val) case name: return #name; 15 | #define MAKE_TO_STRING_FUNC(enumType) \ 16 | inline const char* to_string(enumType e) { \ 17 | switch (e) { \ 18 | XR_LIST_ENUM_##enumType(ENUM_CASE_STR) \ 19 | default: return "Unknown " #enumType; \ 20 | } \ 21 | } 22 | // clang-format on 23 | 24 | MAKE_TO_STRING_FUNC(XrResult); 25 | 26 | #define CHK_STRINGIFY(x) #x 27 | #define TOSTRING(x) CHK_STRINGIFY(x) 28 | #define FILE_AND_LINE __FILE__ ":" TOSTRING(__LINE__) 29 | 30 | [[noreturn]] inline void Throw(std::string failureMessage, const char* originator = nullptr, const char* sourceLocation = nullptr) { 31 | if (originator != nullptr) { 32 | failureMessage += std::format("\n Origin: %s", originator); 33 | } 34 | if (sourceLocation != nullptr) { 35 | failureMessage += std::format("\n Source: %s", sourceLocation); 36 | } 37 | 38 | throw std::logic_error(failureMessage); 39 | } 40 | 41 | #define THROW(msg) Throw(msg, nullptr, FILE_AND_LINE); 42 | #define CHECK(exp) \ 43 | { \ 44 | if (!(exp)) { \ 45 | Throw("Check failed", #exp, FILE_AND_LINE); \ 46 | } \ 47 | } 48 | #define CHECK_MSG(exp, msg) \ 49 | { \ 50 | if (!(exp)) { \ 51 | Throw(msg, #exp, FILE_AND_LINE); \ 52 | } \ 53 | } 54 | 55 | [[noreturn]] inline void ThrowXrResult(XrResult res, const char* originator = nullptr, const char* sourceLocation = nullptr) { 56 | Throw(std::format("XrResult failure [%s]", to_string(res)), originator, sourceLocation); 57 | } 58 | 59 | inline XrResult CheckXrResult(XrResult res, const char* originator = nullptr, const char* sourceLocation = nullptr) { 60 | if (XR_FAILED(res)) { 61 | ThrowXrResult(res, originator, sourceLocation); 62 | } 63 | 64 | return res; 65 | } 66 | 67 | #define THROW_XR(xr, cmd) ThrowXrResult(xr, #cmd, FILE_AND_LINE); 68 | #define CHECK_XRCMD(cmd) CheckXrResult(cmd, #cmd, FILE_AND_LINE); 69 | #define CHECK_XRRESULT(res, cmdStr) CheckXrResult(res, cmdStr, FILE_AND_LINE); 70 | 71 | #ifdef XR_USE_PLATFORM_WIN32 72 | 73 | [[noreturn]] inline void ThrowHResult(HRESULT hr, const char* originator = nullptr, const char* sourceLocation = nullptr) { 74 | Throw(std::format("HRESULT failure [%x]", hr), originator, sourceLocation); 75 | } 76 | 77 | inline HRESULT CheckHResult(HRESULT hr, const char* originator = nullptr, const char* sourceLocation = nullptr) { 78 | if (FAILED(hr)) { 79 | ThrowHResult(hr, originator, sourceLocation); 80 | } 81 | 82 | return hr; 83 | } 84 | 85 | #define THROW_HR(hr, cmd) ThrowHResult(hr, #cmd, FILE_AND_LINE); 86 | #define CHECK_HRCMD(cmd) CheckHResult(cmd, #cmd, FILE_AND_LINE); 87 | #define CHECK_HRESULT(res, cmdStr) CheckHResult(res, cmdStr, FILE_AND_LINE); 88 | 89 | #endif -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/dashboard_menu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "layer.h" 7 | #include "config_manager.h" 8 | #include "openvr_manager.h" 9 | #include "imgui.h" 10 | 11 | using Microsoft::WRL::ComPtr; 12 | 13 | 14 | #define DASHBOARD_OVERLAY_KEY "XR_APILAYER_NOVENDOR_steamvr_passthrough.{}.dashboard" 15 | 16 | #define OVERLAY_RES_WIDTH 1200 17 | #define OVERLAY_RES_HEIGHT 700 18 | 19 | enum EMenuTab 20 | { 21 | TabMain, 22 | TabApplication, 23 | TabStereo, 24 | TabOverrides, 25 | TabCamera, 26 | TabDebug 27 | }; 28 | 29 | struct MenuDisplayValues 30 | { 31 | bool bSessionActive = false; 32 | bool bDepthBlendingActive = false; 33 | ERenderAPI renderAPI = None; 34 | ERenderAPI appRenderAPI = None; 35 | std::string currentApplication; 36 | int frameBufferWidth = 0; 37 | int frameBufferHeight = 0; 38 | XrCompositionLayerFlags frameBufferFlags = 0; 39 | int64_t frameBufferFormat = 0; 40 | int64_t depthBufferFormat = 0; 41 | float frameToRenderLatencyMS = 0.0f; 42 | float frameToPhotonsLatencyMS = 0.0f; 43 | float renderTimeMS = 0.0f; 44 | float stereoReconstructionTimeMS = 0.0f; 45 | float frameRetrievalTimeMS = 0.0f; 46 | 47 | bool bCorePassthroughActive = false; 48 | int CoreCurrentMode = 0; 49 | 50 | bool bVarjoDepthEstimationExtensionActive = false; 51 | bool bVarjoDepthCompositionExtensionActive = false; 52 | 53 | uint32_t CameraFrameWidth = 0; 54 | uint32_t CameraFrameHeight = 0; 55 | float CameraFrameRate = 0.0f; 56 | std::string CameraAPI; 57 | }; 58 | 59 | 60 | class DashboardMenu 61 | { 62 | public: 63 | 64 | DashboardMenu(HMODULE dllModule, std::shared_ptr configManager, std::shared_ptr openVRManager); 65 | 66 | ~DashboardMenu(); 67 | 68 | MenuDisplayValues& GetDisplayValues() { return m_displayValues; } 69 | 70 | private: 71 | 72 | void CreateOverlay(); 73 | void DestroyOverlay(); 74 | void CreateThumbnail(); 75 | 76 | void RunThread(); 77 | void HandleEvents(); 78 | void TickMenu(); 79 | 80 | void SetupDX11(); 81 | 82 | void TextDescription(const char* fmt, ...); 83 | void TextDescriptionSpaced(const char* fmt, ...); 84 | 85 | std::shared_ptr m_configManager; 86 | std::shared_ptr m_openVRManager; 87 | HMODULE m_dllModule; 88 | 89 | vr::VROverlayHandle_t m_overlayHandle; 90 | vr::VROverlayHandle_t m_thumbnailHandle; 91 | 92 | std::thread m_menuThread; 93 | bool m_bRunThread; 94 | 95 | ComPtr m_d3d11Device; 96 | ComPtr m_d3d11DeviceContext; 97 | ComPtr m_d3d11Texture[2]; 98 | ComPtr m_d3d11RTV[2]; 99 | int m_frameIndex = 0; 100 | 101 | bool m_bMenuIsVisible; 102 | MenuDisplayValues m_displayValues; 103 | EMenuTab m_activeTab; 104 | 105 | ImFont* m_mainFont; 106 | ImFont* m_smallFont; 107 | ImFont* m_fixedFont; 108 | 109 | std::vector m_deviceDebugProps; 110 | int m_currentDebugDevice; 111 | 112 | std::vector m_deviceIdentProps; 113 | int m_currentIdentDevice; 114 | 115 | std::vector m_cameraDevices; 116 | 117 | bool m_cameraTabBeenOpened = false; 118 | bool m_debugTabBeenOpened = false; 119 | 120 | bool m_bIsKeyboardOpen = false; 121 | }; 122 | 123 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/depth_reconstruction.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "layer.h" 4 | #include "openvr_manager.h" 5 | #include "config_manager.h" 6 | #include "camera_manager.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | class DepthReconstruction 14 | { 15 | public: 16 | DepthReconstruction(std::shared_ptr configManager, std::shared_ptr openVRManager, std::shared_ptr cameraManager); 17 | ~DepthReconstruction(); 18 | 19 | std::shared_ptr GetDepthFrame(); 20 | UVDistortionParameters& GetDistortionParameters() 21 | { 22 | return m_distortionParams; 23 | } 24 | float GetReconstructionPerfTime() { return m_averageReconstructionTime; } 25 | 26 | private: 27 | void InitReconstruction(); 28 | void RunThread(); 29 | void CreateDistortionMap(); 30 | 31 | std::thread m_thread; 32 | std::atomic_bool m_bRunThread; 33 | std::mutex m_serveMutex; 34 | 35 | std::shared_ptr m_configManager; 36 | std::shared_ptr m_openVRManager; 37 | std::shared_ptr m_cameraManager; 38 | 39 | std::shared_ptr m_servedDepthFrame; 40 | std::shared_ptr m_depthFrame; 41 | std::shared_ptr m_underConstructionDepthFrame; 42 | 43 | UVDistortionParameters m_distortionParams; 44 | 45 | XrVector2f m_cameraCenter[2]; 46 | XrVector2f m_cameraFocalLength[2]; 47 | uint32_t m_cameraTextureWidth; 48 | uint32_t m_cameraTextureHeight; 49 | uint32_t m_cameraFrameWidth; 50 | uint32_t m_cameraFrameHeight; 51 | uint32_t m_cvImageWidth; 52 | uint32_t m_cvImageHeight; 53 | XrMatrix4x4f m_cameraLeftToRightTransform; 54 | EStereoFrameLayout m_frameLayout; 55 | 56 | uint32_t m_lastFrameSequence; 57 | uint32_t m_downscaleFactor; 58 | float m_fovScale; 59 | float m_depthOffsetCalibration; 60 | int m_maxDisparity; 61 | bool m_bUseMulticore; 62 | bool m_bUseColor; 63 | bool m_bDisparityBothEyes; 64 | 65 | cv::Mat m_intrinsicsLeft; 66 | cv::Mat m_intrinsicsRight; 67 | cv::Mat m_distortionParamsLeft; 68 | cv::Mat m_distortionParamsRight; 69 | 70 | cv::Mat m_leftMap1; 71 | cv::Mat m_leftMap2; 72 | cv::Mat m_rightMap1; 73 | cv::Mat m_rightMap2; 74 | 75 | XrMatrix4x4f m_disparityToDepth; 76 | 77 | XrMatrix4x4f m_rectifiedRotationLeft; 78 | XrMatrix4x4f m_rectifiedRotationRight; 79 | 80 | XrMatrix4x4f m_fishEyeProjectionLeft; 81 | XrMatrix4x4f m_fishEyeProjectionRight; 82 | 83 | cv::Ptr m_stereoLeftMatcher; 84 | cv::Ptr m_stereoRightMatcher; 85 | 86 | cv::Ptr m_wlsFilterLeft; 87 | cv::Ptr m_wlsFilterRight; 88 | 89 | cv::Mat m_inputFrame; 90 | cv::Mat m_inputFrameLeft; 91 | cv::Mat m_inputFrameRight; 92 | cv::Mat m_inputAlphaLeft; 93 | cv::Mat m_inputAlphaRight; 94 | 95 | cv::Mat m_rectifiedFrameLeft; 96 | cv::Mat m_rectifiedFrameRight; 97 | cv::Mat m_scaledFrameLeft; 98 | cv::Mat m_scaledFrameRight; 99 | 100 | cv::Mat m_scaledExtFrameLeft; 101 | cv::Mat m_scaledExtFrameRight; 102 | 103 | cv::Mat m_rawDisparityLeft; 104 | cv::Mat m_rawDisparityRight; 105 | cv::Mat m_filteredDisparityLeft; 106 | cv::Mat m_filteredDisparityRight; 107 | 108 | cv::Mat m_confidenceLeft; 109 | cv::Mat m_confidenceRight; 110 | cv::Mat m_bilateralDisparityLeft; 111 | cv::Mat m_bilateralDisparityRight; 112 | 113 | cv::Mat m_outputDisparity; 114 | cv::Mat m_outputDisparityLeft; 115 | cv::Mat m_outputDisparityRight; 116 | 117 | std::deque m_reconstructionTimes; 118 | float m_averageReconstructionTime; 119 | 120 | cv::Mat m_colorRectifyInput; 121 | cv::Mat m_colorRectifyLeft; 122 | cv::Mat m_colorRectifyRight; 123 | cv::Mat m_colorRectifiedLeft; 124 | cv::Mat m_colorRectifiedRight; 125 | cv::Mat m_colorRectifiedOutput; 126 | 127 | cv::Mat m_maskMat; 128 | }; 129 | 130 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/fonts/cousine_regular.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rectus/openxr-steamvr-passthrough/7c11ecd44c311c78a9e9aed7e7fc1b94e7e03af6/XR_APILAYER_NOVENDOR_steamvr_passthrough/fonts/cousine_regular.cpp -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/fonts/roboto_medium.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rectus/openxr-steamvr-passthrough/7c11ecd44c311c78a9e9aed7e7fc1b94e7e03af6/XR_APILAYER_NOVENDOR_steamvr_passthrough/fonts/roboto_medium.cpp -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/framework/dispatch.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #ifndef LAYER_NAMESPACE 26 | #error Must define LAYER_NAMESPACE 27 | #endif 28 | 29 | namespace LAYER_NAMESPACE { 30 | 31 | XrResult xrGetInstanceProcAddr(XrInstance instance, 32 | const char* name, 33 | PFN_xrVoidFunction* function); 34 | XrResult xrDestroyInstance(XrInstance instance); 35 | XrResult xrCreateApiLayerInstance(const XrInstanceCreateInfo* instanceCreateInfo, 36 | const struct XrApiLayerCreateInfo* apiLayerInfo, 37 | XrInstance* instance); 38 | 39 | } // namespace LAYER_NAMESPACE 40 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/framework/entry.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2021-2022 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "pch.h" 24 | 25 | #include 26 | 27 | #include "dispatch.h" 28 | #include "log.h" 29 | 30 | #ifndef LAYER_NAMESPACE 31 | #error Must define LAYER_NAMESPACE 32 | #endif 33 | 34 | namespace LAYER_NAMESPACE { 35 | // The path where the DLL is loaded from (eg: to load data files). 36 | std::filesystem::path dllHome; 37 | 38 | // The path that is writable (eg: to store logs). 39 | std::filesystem::path localAppData; 40 | 41 | namespace log { 42 | // The file logger. 43 | std::ofstream logStream; 44 | } // namespace log 45 | } // namespace LAYER_NAMESPACE 46 | 47 | using namespace LAYER_NAMESPACE; 48 | using namespace LAYER_NAMESPACE::log; 49 | 50 | extern "C" { 51 | 52 | // Entry point for the loader. 53 | XrResult __declspec(dllexport) XRAPI_CALL 54 | xrNegotiateLoaderApiLayerInterface(const XrNegotiateLoaderInfo* const loaderInfo, 55 | const char* const apiLayerName, 56 | XrNegotiateApiLayerRequest* const apiLayerRequest) { 57 | #if USE_TRACELOGGING 58 | TraceLoggingWrite(g_traceProvider, "xrNegotiateLoaderApiLayerInterface"); 59 | #endif 60 | 61 | // Retrieve the path of the DLL. 62 | if (dllHome.empty()) { 63 | HMODULE module; 64 | if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 65 | (LPCSTR)&dllHome, 66 | &module)) { 67 | char path[_MAX_PATH]; 68 | GetModuleFileNameA(module, path, sizeof(path)); 69 | dllHome = std::filesystem::path(path).parent_path(); 70 | } 71 | } 72 | 73 | // Start logging to file. 74 | if (!logStream.is_open()) { 75 | std::string logFile = (std::filesystem::path(getenv("LOCALAPPDATA")) / (LayerName + ".log")).string(); 76 | logStream.open(logFile, std::ios_base::ate); 77 | } 78 | 79 | DebugLog("--> xrNegotiateLoaderApiLayerInterface\n"); 80 | 81 | if (apiLayerName && apiLayerName != LayerName) { 82 | ErrorLog("Invalid apiLayerName \"%s\"\n", apiLayerName); 83 | return XR_ERROR_INITIALIZATION_FAILED; 84 | } 85 | 86 | if (!loaderInfo || !apiLayerRequest || loaderInfo->structType != XR_LOADER_INTERFACE_STRUCT_LOADER_INFO || 87 | loaderInfo->structVersion != XR_LOADER_INFO_STRUCT_VERSION || 88 | loaderInfo->structSize != sizeof(XrNegotiateLoaderInfo) || 89 | apiLayerRequest->structType != XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST || 90 | apiLayerRequest->structVersion != XR_API_LAYER_INFO_STRUCT_VERSION || 91 | apiLayerRequest->structSize != sizeof(XrNegotiateApiLayerRequest) || 92 | loaderInfo->minInterfaceVersion > XR_CURRENT_LOADER_API_LAYER_VERSION || 93 | loaderInfo->maxInterfaceVersion < XR_CURRENT_LOADER_API_LAYER_VERSION || 94 | loaderInfo->maxInterfaceVersion > XR_CURRENT_LOADER_API_LAYER_VERSION || 95 | loaderInfo->maxApiVersion < XR_CURRENT_API_VERSION || loaderInfo->minApiVersion > XR_CURRENT_API_VERSION) { 96 | ErrorLog("xrNegotiateLoaderApiLayerInterface validation failed\n"); 97 | return XR_ERROR_INITIALIZATION_FAILED; 98 | } 99 | 100 | // Setup our layer to intercept OpenXR calls. 101 | apiLayerRequest->layerInterfaceVersion = XR_CURRENT_LOADER_API_LAYER_VERSION; 102 | apiLayerRequest->layerApiVersion = XR_CURRENT_API_VERSION; 103 | apiLayerRequest->getInstanceProcAddr = reinterpret_cast(xrGetInstanceProcAddr); 104 | apiLayerRequest->createApiLayerInstance = reinterpret_cast(xrCreateApiLayerInstance); 105 | 106 | DebugLog("<-- xrNegotiateLoaderApiLayerInterface\n"); 107 | 108 | Log("%s layer (%s) is active\n", LayerName.c_str(), VersionString.c_str()); 109 | 110 | #if USE_TRACELOGGING 111 | TraceLoggingWrite(g_traceProvider, "xrNegotiateLoaderApiLayerInterface_Complete"); 112 | #endif 113 | 114 | return XR_SUCCESS; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/framework/layer_apis.py: -------------------------------------------------------------------------------- 1 | # The list of OpenXR functions our layer will override. 2 | override_functions = [ 3 | "xrGetSystem", 4 | "xrGetVulkanDeviceExtensionsKHR", 5 | "xrCreateVulkanDeviceKHR", 6 | "xrCreateSession", 7 | "xrDestroySession", 8 | "xrEnumerateEnvironmentBlendModes", 9 | "xrCreateReferenceSpace", 10 | "xrDestroySpace", 11 | "xrCreateSwapchain", 12 | "xrDestroySwapchain", 13 | "xrAcquireSwapchainImage", 14 | "xrWaitSwapchainImage", 15 | "xrReleaseSwapchainImage", 16 | "xrBeginFrame", 17 | "xrEndFrame", 18 | "xrSetEnvironmentDepthEstimationVARJO" 19 | ] 20 | 21 | # The list of OpenXR functions our layer will use from the runtime. 22 | # Might repeat entries from override_functions above. 23 | requested_functions = [ 24 | "xrGetInstanceProperties", 25 | "xrGetSystemProperties", 26 | "xrEnumerateSwapchainFormats", 27 | "xrEnumerateViewConfigurationViews", 28 | "xrLocateViews", 29 | "xrLocateSpace", 30 | "xrCreateReferenceSpace", 31 | "xrDestroySpace", 32 | "xrCreateSwapchain", 33 | "xrDestroySwapchain", 34 | "xrEnumerateSwapchainImages", 35 | "xrAcquireSwapchainImage", 36 | "xrWaitSwapchainImage", 37 | "xrReleaseSwapchainImage", 38 | "xrBeginFrame", 39 | "xrEndFrame", 40 | "xrConvertTimeToWin32PerformanceCounterKHR" 41 | ] 42 | 43 | # The list of OpenXR extensions our layer will either override or use. 44 | extensions = [ 45 | "XR_KHR_vulkan_enable", 46 | "XR_KHR_vulkan_enable2", 47 | "XR_KHR_win32_convert_performance_counter_time", 48 | "XR_VARJO_composition_layer_depth_test", 49 | "XR_VARJO_environment_depth_estimation" 50 | ] 51 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/framework/log.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "pch.h" 24 | 25 | namespace { 26 | constexpr uint32_t k_maxLoggedErrors = 100; 27 | uint32_t g_globalErrorCount = 0; 28 | constexpr uint32_t k_maxBufferedLines = 200; 29 | std::deque g_logBuffer; 30 | std::shared_timed_mutex g_logBufferMutex; 31 | } // namespace 32 | 33 | namespace LAYER_NAMESPACE::log { 34 | extern std::ofstream logStream; 35 | 36 | #if USE_TRACELOGGING 37 | // {030e3bd6-23f4-442a-b87e-f1fe947c123e} 38 | TRACELOGGING_DEFINE_PROVIDER(g_traceProvider, 39 | "OpenXRSteamVRPassthrough", 40 | (0x030e3bd6, 0x23f4, 0x442a, 0xb8, 0x7e, 0xf1, 0xfe, 0x94, 0x7c, 0x12, 0x3e)); 41 | 42 | TraceLoggingActivity g_traceActivity; 43 | #endif 44 | 45 | namespace { 46 | 47 | void BufferLog(const char* buf) 48 | { 49 | std::unique_lock writeLock(g_logBufferMutex, std::chrono::milliseconds(1)); 50 | 51 | if (!writeLock.owns_lock()) 52 | { 53 | return; 54 | } 55 | 56 | g_logBuffer.emplace_back(buf); 57 | 58 | if (g_logBuffer.size() >= k_maxBufferedLines) 59 | { 60 | g_logBuffer.pop_front(); 61 | } 62 | } 63 | 64 | // Utility logging function. 65 | void InternalLog(const char* fmt, va_list va) { 66 | const std::time_t now = std::time(nullptr); 67 | 68 | char buf[1024]; 69 | size_t offset = std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %z: ", std::localtime(&now)); 70 | vsnprintf_s(buf + offset, sizeof(buf) - offset, _TRUNCATE, fmt, va); 71 | 72 | BufferLog(buf); 73 | OutputDebugStringA(buf); 74 | if (logStream.is_open()) { 75 | logStream << buf; 76 | logStream.flush(); 77 | } 78 | } 79 | } // namespace 80 | 81 | void Log(const char* fmt, ...) { 82 | va_list va; 83 | va_start(va, fmt); 84 | InternalLog(fmt, va); 85 | va_end(va); 86 | } 87 | 88 | void ErrorLog(const char* fmt, ...) { 89 | if (g_globalErrorCount++ < k_maxLoggedErrors) { 90 | va_list va; 91 | va_start(va, fmt); 92 | InternalLog(fmt, va); 93 | va_end(va); 94 | if (g_globalErrorCount == k_maxLoggedErrors) { 95 | Log("Maximum number of errors logged. Going silent."); 96 | } 97 | } 98 | } 99 | 100 | void DebugLog(const char* fmt, ...) { 101 | #ifdef _DEBUG 102 | va_list va; 103 | va_start(va, fmt); 104 | InternalLog(fmt, va); 105 | va_end(va); 106 | #endif 107 | } 108 | 109 | void ReadLogBuffer(void (*printFunc)(std::deque& logBuffer)) 110 | { 111 | std::shared_lock readLock(g_logBufferMutex, std::chrono::milliseconds(1)); 112 | 113 | if (!readLock.owns_lock()) 114 | { 115 | return; 116 | } 117 | 118 | printFunc(g_logBuffer); 119 | } 120 | 121 | } // namespace LAYER_NAMESPACE::log 122 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/framework/log.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #include "pch.h" 26 | 27 | namespace LAYER_NAMESPACE::log { 28 | 29 | #if USE_TRACELOGGING 30 | TRACELOGGING_DECLARE_PROVIDER(g_traceProvider); 31 | 32 | extern TraceLoggingActivity g_traceGlobal; 33 | 34 | #define IsTraceEnabled() TraceLoggingProviderEnabled(g_traceProvider, 0, 0) 35 | 36 | #define TraceLocalActivity(activity) TraceLoggingActivity activity; 37 | 38 | #define TLArg(var, ...) TraceLoggingValue(var, ##__VA_ARGS__) 39 | #define TLPArg(var, ...) TraceLoggingPointer(var, ##__VA_ARGS__) 40 | 41 | #endif 42 | 43 | // General logging function. 44 | void Log(const char* fmt, ...); 45 | 46 | // Debug logging function. Can make things very slow (only enabled on Debug builds). 47 | void DebugLog(const char* fmt, ...); 48 | 49 | // Error logging function. Goes silent after too many errors. 50 | void ErrorLog(const char* fmt, ...); 51 | 52 | // Read buffer of log lines through passed function for display. 53 | void ReadLogBuffer(void (*printFunc)(std::deque& logBuffer)); 54 | 55 | } // namespace LAYER_NAMESPACE::log 56 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/framework/util.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022 Matthieu Bucchianeri 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #include "pch.h" 26 | #include 27 | 28 | namespace xr { 29 | 30 | static inline std::string ToString(XrVersion version) { 31 | return std::format("{}.{}.{}", XR_VERSION_MAJOR(version), XR_VERSION_MINOR(version), XR_VERSION_PATCH(version)); 32 | } 33 | 34 | static inline std::string ToString(XrPosef pose) { 35 | return std::format("p: ({:.3f}, {:.3f}, {:.3f}), o:({:.3f}, {:.3f}, {:.3f}, {:.3f})", 36 | pose.position.x, 37 | pose.position.y, 38 | pose.position.z, 39 | pose.orientation.x, 40 | pose.orientation.y, 41 | pose.orientation.z, 42 | pose.orientation.w); 43 | } 44 | 45 | static inline std::string ToString(XrFovf fov) { 46 | return std::format( 47 | "(l:{:.3f}, r:{:.3f}, u:{:.3f}, d:{:.3f})", fov.angleLeft, fov.angleRight, fov.angleUp, fov.angleDown); 48 | } 49 | 50 | static inline std::string ToString(XrRect2Di rect) { 51 | return std::format("x:{}, y:{} w:{} h:{}", rect.offset.x, rect.offset.y, rect.extent.width, rect.extent.height); 52 | } 53 | 54 | static inline std::string ToString(XrRect2Df rect) { 55 | return std::format("x:{}, y:{} w:{} h:{}", rect.offset.x, rect.offset.y, rect.extent.width, rect.extent.height); 56 | } 57 | 58 | } // namespace xr -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/mathutil.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define _USE_MATH_DEFINES 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | struct EulerAngles 13 | { 14 | double X; 15 | double Y; 16 | double Z; 17 | }; 18 | 19 | inline double RadToDeg(double r) 20 | { 21 | return r * 180.0 / M_PI; 22 | } 23 | 24 | inline double DegToRad(double d) 25 | { 26 | return d * M_PI / 180.0; 27 | } 28 | 29 | inline float RadToDeg(float r) 30 | { 31 | return r * 180.0f / (float)M_PI; 32 | } 33 | 34 | inline float DegToRad(float d) 35 | { 36 | return d * (float)M_PI / 180.0f; 37 | } 38 | 39 | inline EulerAngles HMDMatRotationToEuler(vr::HmdMatrix34_t& R) 40 | { 41 | double sy = sqrt(R.m[0][0] * R.m[0][0] + R.m[1][0] * R.m[1][0]); 42 | 43 | EulerAngles angles; 44 | if (sy > 1e-6) 45 | { 46 | angles.X = RadToDeg(atan2(R.m[2][1], R.m[2][2])); 47 | angles.Y = RadToDeg(atan2(-R.m[2][0], sy)); 48 | angles.Z = RadToDeg(atan2(R.m[1][0], R.m[0][0])); 49 | } 50 | else 51 | { 52 | angles.X = RadToDeg(atan2(-R.m[1][2], R.m[1][1])); 53 | angles.Y = RadToDeg(atan2(-R.m[2][0], sy)); 54 | angles.Z = RadToDeg(0.0); 55 | } 56 | 57 | return angles; 58 | } 59 | 60 | inline EulerAngles CVMatRotationToEuler(cv::Mat& R) 61 | { 62 | double sy = sqrt(R.at(0, 0) * R.at(0, 0) + R.at(1, 0) * R.at(1, 0)); 63 | 64 | EulerAngles angles; 65 | if (sy > 1e-6) 66 | { 67 | angles.X = RadToDeg(atan2(R.at(2, 1), R.at(2, 2))); 68 | angles.Y= RadToDeg(atan2(-R.at(2, 0), sy)); 69 | angles.Z = RadToDeg(atan2(R.at(1, 0), R.at(0, 0))); 70 | } 71 | else 72 | { 73 | angles.X = RadToDeg(atan2(-R.at(1, 2), R.at(1, 1))); 74 | angles.Y = RadToDeg(atan2(-R.at(2, 0), sy)); 75 | angles.Z = RadToDeg(0.0); 76 | } 77 | 78 | return angles; 79 | } 80 | 81 | inline cv::Mat EulerToCVMatRotation(EulerAngles angles) 82 | { 83 | double rx = DegToRad(angles.X); 84 | double ry = DegToRad(angles.Y); 85 | double rz = DegToRad(angles.Z); 86 | 87 | cv::Mat X = (cv::Mat_(3, 3) << 1, 0, 0, 0, cos(rx), -sin(rx), 0, sin(rx), cos(rx)); 88 | cv::Mat Y = (cv::Mat_(3, 3) << cos(ry), 0, sin(ry), 0, 1, 0, -sin(ry), 0, cos(ry)); 89 | cv::Mat Z = (cv::Mat_(3, 3) << cos(rz), -sin(rz), 0, sin(rz), cos(rz), 0, 0, 0, 1); 90 | 91 | return Z * Y * X; 92 | } 93 | 94 | inline XrMatrix4x4f ToXRMatrix4x4(vr::HmdMatrix44_t& input) 95 | { 96 | XrMatrix4x4f output = 97 | { 98 | input.m[0][0], input.m[1][0], input.m[2][0], input.m[3][0], 99 | input.m[0][1], input.m[1][1], input.m[2][1], input.m[3][1], 100 | input.m[0][2], input.m[1][2], input.m[2][2], input.m[3][2], 101 | input.m[0][3], input.m[1][3], input.m[2][3], input.m[3][3] 102 | }; 103 | return output; 104 | } 105 | 106 | inline XrMatrix4x4f ToXRMatrix4x4(vr::HmdMatrix34_t& input) 107 | { 108 | XrMatrix4x4f output = 109 | { 110 | input.m[0][0], input.m[1][0], input.m[2][0], 0, 111 | input.m[0][1], input.m[1][1], input.m[2][1], 0, 112 | input.m[0][2], input.m[1][2], input.m[2][2], 0, 113 | input.m[0][3], input.m[1][3], input.m[2][3], 1 114 | }; 115 | return output; 116 | } 117 | 118 | inline XrMatrix4x4f ToXRMatrix4x4Inverted(vr::HmdMatrix44_t& input) 119 | { 120 | XrMatrix4x4f temp = 121 | { 122 | input.m[0][0], input.m[1][0], input.m[2][0], input.m[3][0], 123 | input.m[0][1], input.m[1][1], input.m[2][1], input.m[3][1], 124 | input.m[0][2], input.m[1][2], input.m[2][2], input.m[3][2], 125 | input.m[0][3], input.m[1][3], input.m[2][3], input.m[3][3] 126 | }; 127 | XrMatrix4x4f output; 128 | XrMatrix4x4f_Invert(&output, &temp); 129 | return output; 130 | } 131 | 132 | 133 | inline XrMatrix4x4f ToXRMatrix4x4Inverted(vr::HmdMatrix34_t& input) 134 | { 135 | XrMatrix4x4f temp = 136 | { 137 | input.m[0][0], input.m[1][0], input.m[2][0], 0, 138 | input.m[0][1], input.m[1][1], input.m[2][1], 0, 139 | input.m[0][2], input.m[1][2], input.m[2][2], 0, 140 | input.m[0][3], input.m[1][3], input.m[2][3], 1 141 | }; 142 | XrMatrix4x4f output; 143 | XrMatrix4x4f_Invert(&output, &temp); 144 | return output; 145 | } 146 | 147 | 148 | inline XrMatrix4x4f CVMatToXrMatrix(cv::Mat& inMatrix) 149 | { 150 | XrMatrix4x4f outMatrix; 151 | XrMatrix4x4f_CreateIdentity(&outMatrix); 152 | for (int y = 0; y < inMatrix.rows; y++) 153 | { 154 | for (int x = 0; x < inMatrix.cols; x++) 155 | { 156 | outMatrix.m[x + y * 4] = (float)inMatrix.at(y, x); 157 | } 158 | } 159 | return outMatrix; 160 | } -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/mesh.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "pch.h" 3 | #include "mesh.h" 4 | 5 | 6 | #define BORDER_SIZE 3 7 | 8 | 9 | #define clamp(val, min, max) ((val < min ? min : (val > max ? max : val))) 10 | 11 | 12 | // Generate a cylinder with radius and height 1. 13 | void MeshCreateCylinder(Mesh& mesh, int numBoundaryVertices) 14 | { 15 | uint32_t numVertices = numBoundaryVertices * 2 + 2; 16 | 17 | mesh.vertices.resize(0); 18 | mesh.vertices.reserve(numVertices); 19 | mesh.triangles.resize(0); 20 | mesh.triangles.reserve(numBoundaryVertices * 4); 21 | 22 | float radianStep = -2.0f * MATH_PI / (float)numBoundaryVertices; 23 | 24 | mesh.vertices.emplace_back(0.0f, 1.0f, 0.0f); 25 | 26 | uint32_t index = 1; 27 | 28 | for (int i = 0; i < numBoundaryVertices; i++) 29 | { 30 | int32_t nextSliceIndex = (i == numBoundaryVertices - 1) ? 1 : index + 2; 31 | 32 | mesh.vertices.emplace_back(cosf(radianStep * i), 1.0f, sinf(radianStep * i)); 33 | mesh.vertices.emplace_back(cosf(radianStep * i), 0.0f, sinf(radianStep * i)); 34 | 35 | mesh.triangles.emplace_back(0, index, nextSliceIndex); 36 | mesh.triangles.emplace_back(index, index + 1, nextSliceIndex); 37 | mesh.triangles.emplace_back(index + 1, nextSliceIndex + 1, nextSliceIndex); 38 | mesh.triangles.emplace_back(index + 1, numVertices - 1, nextSliceIndex + 1); 39 | 40 | index += 2; 41 | } 42 | 43 | mesh.vertices.emplace_back(0.0f, 0.0f, 0.0f); 44 | 45 | } 46 | 47 | 48 | // Grid with vertices at the center of pixels, with extra clamped vertices at the edges. 49 | void MeshCreateGrid(Mesh& mesh, int width, int height) 50 | { 51 | int numVertsX = width + 2; 52 | int numVertsY = height + 2; 53 | 54 | mesh.vertices.resize(0); 55 | mesh.vertices.reserve(numVertsX * numVertsY); 56 | mesh.triangles.resize(0); 57 | mesh.triangles.reserve(numVertsX * numVertsY * 2); 58 | 59 | float stepX = 1.0f / (float)width; 60 | float stepY = 1.0f / (float)height; 61 | 62 | uint32_t index = 0; 63 | 64 | for (int y = 0; y < numVertsY; y++) 65 | { 66 | for (int x = 0; x < numVertsX; x++) 67 | { 68 | float xPos = clamp((x - 0.5f) * stepX, 0.0f, 1.0f); 69 | float yPos = clamp((y - 0.5f) * stepY, 0.0f, 1.0f); 70 | 71 | // Mark border vertices 72 | float zPos = 0.0f; 73 | 74 | if (x < BORDER_SIZE || x >= (numVertsX - BORDER_SIZE) || y < BORDER_SIZE || y >= (numVertsY - BORDER_SIZE)) 75 | { 76 | float size = (float)BORDER_SIZE; 77 | 78 | float low = fmaxf((size - x) / size, (size - y) / size); 79 | float high = -fmin(0.0f, fminf((numVertsX - size - x - 1) / size, (numVertsY - size - y - 1) / size)); 80 | 81 | zPos = fmaxf(low, high); 82 | } 83 | 84 | mesh.vertices.emplace_back(xPos, yPos, zPos); 85 | 86 | if (x < numVertsX - 1 && y < numVertsY - 1) 87 | { 88 | mesh.triangles.emplace_back(index, index + 1, index + numVertsX + 1); 89 | mesh.triangles.emplace_back(index, index + numVertsX + 1, index + numVertsX); 90 | } 91 | 92 | index++; 93 | } 94 | } 95 | } 96 | 97 | 98 | void MeshCreateHexGrid(Mesh& mesh, int width, int height) 99 | { 100 | mesh.vertices.resize(0); 101 | mesh.vertices.reserve((width + 1) * (height + 1)); 102 | mesh.triangles.resize(0); 103 | mesh.triangles.reserve(width * height * 2); 104 | 105 | float stepX = 1.0f / (float)width; 106 | float stepY = 1.0f / (float)height; 107 | 108 | uint32_t index = 0; 109 | 110 | for (int y = 0; y < height; y++) 111 | { 112 | for (int x = 0; x < width; x++) 113 | { 114 | // Mark border vertices 115 | float z = 0.0f; 116 | 117 | if (x < BORDER_SIZE || x >= (width - BORDER_SIZE) || y < BORDER_SIZE || y >= (height - BORDER_SIZE)) 118 | { 119 | float size = (float)BORDER_SIZE; 120 | 121 | float low = fmaxf((size - x) / size, (size - y) / size); 122 | float high = -fmin(0.0f, fminf((width - size - x - 1) / size, (height - size - y - 1) / size)); 123 | 124 | z = fmaxf(low, high); 125 | } 126 | 127 | if (y % 2 == 0) 128 | { 129 | mesh.vertices.emplace_back(x * stepX, y * stepY, z); 130 | 131 | if (x < width - 1 && y < height - 1) 132 | { 133 | mesh.triangles.emplace_back(index, index + 1, index + width); 134 | mesh.triangles.emplace_back(index + 1, index + width + 1, index + width); 135 | } 136 | } 137 | else 138 | { 139 | mesh.vertices.emplace_back(x * stepX + 0.5f * stepX, y * stepY, z); 140 | 141 | if (x < width - 1 && y < height - 1) 142 | { 143 | mesh.triangles.emplace_back(index, index + 1, index + width + 1); 144 | mesh.triangles.emplace_back(index, index + width + 1, index + width); 145 | } 146 | } 147 | 148 | index++; 149 | } 150 | } 151 | } 152 | 153 | void MeshCreateRenderModel(Mesh& mesh, vr::RenderModel_t* renderModel) 154 | { 155 | mesh.vertices.resize(renderModel->unVertexCount); 156 | mesh.triangles.resize(renderModel->unTriangleCount); 157 | 158 | for (unsigned int i = 0; i < renderModel->unVertexCount; i++) 159 | { 160 | mesh.vertices[i].position[0] = renderModel->rVertexData[i].vPosition.v[0]; 161 | mesh.vertices[i].position[1] = renderModel->rVertexData[i].vPosition.v[1]; 162 | mesh.vertices[i].position[2] = renderModel->rVertexData[i].vPosition.v[2]; 163 | } 164 | 165 | for (unsigned int i = 0; i < renderModel->unTriangleCount; i++) 166 | { 167 | mesh.triangles[i].a = renderModel->rIndexData[i * 3]; 168 | mesh.triangles[i].b = renderModel->rIndexData[i * 3 + 1]; 169 | mesh.triangles[i].c = renderModel->rIndexData[i * 3 + 2]; 170 | } 171 | } -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/mesh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | struct VertexFormatBasic 5 | { 6 | VertexFormatBasic() 7 | { 8 | VertexFormatBasic(0, 0, 0); 9 | } 10 | 11 | VertexFormatBasic(float x, float y, float z) 12 | { 13 | position[0] = x; 14 | position[1] = y; 15 | position[2] = z; 16 | } 17 | 18 | float position[3]; 19 | }; 20 | 21 | struct MeshTriangle 22 | { 23 | MeshTriangle() 24 | : a(0) 25 | , b(0) 26 | , c(0) 27 | {} 28 | 29 | MeshTriangle(uint32_t inA, uint32_t inB, uint32_t inC) 30 | : a(inA) 31 | , b(inB) 32 | , c(inC) 33 | {} 34 | 35 | uint32_t a; 36 | uint32_t b; 37 | uint32_t c; 38 | }; 39 | 40 | template 41 | struct Mesh 42 | { 43 | std::vector vertices; 44 | std::vector triangles; 45 | }; 46 | 47 | 48 | void MeshCreateCylinder(Mesh& mesh, int numBoundaryVertices); 49 | void MeshCreateGrid(Mesh& mesh, int width, int height); 50 | void MeshCreateHexGrid(Mesh& mesh, int width, int height); 51 | void MeshCreateRenderModel(Mesh& mesh, vr::RenderModel_t* renderModel); -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/openvr_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "openvr_manager.h" 3 | #include 4 | #include "layer.h" 5 | 6 | 7 | using namespace steamvr_passthrough; 8 | using namespace steamvr_passthrough::log; 9 | 10 | 11 | OpenVRManager::OpenVRManager() 12 | : m_bRuntimeInitialized(false) 13 | , m_hmdDeviceId(-1) 14 | { 15 | InitRuntime(); 16 | } 17 | 18 | OpenVRManager::~OpenVRManager() 19 | { 20 | std::lock_guard lock(m_runtimeMutex); 21 | if (m_bRuntimeInitialized) 22 | { 23 | m_vrSystem = nullptr; 24 | m_vrCompositor = nullptr; 25 | m_vrTrackedCamera = nullptr; 26 | m_vrOverlay = nullptr; 27 | m_vrRenderModels = nullptr; 28 | vr::VR_Shutdown(); 29 | } 30 | } 31 | 32 | bool OpenVRManager::InitRuntime() 33 | { 34 | std::lock_guard lock(m_runtimeMutex); 35 | 36 | if (m_bRuntimeInitialized) { return true; } 37 | 38 | if (!vr::VR_IsRuntimeInstalled()) 39 | { 40 | ErrorLog("SteamVR installation not detected!\n"); 41 | return false; 42 | } 43 | 44 | vr::EVRInitError error; 45 | vr::IVRSystem* system = vr::VR_Init(&error, vr::EVRApplicationType::VRApplication_Overlay); 46 | 47 | if (error != vr::EVRInitError::VRInitError_None) 48 | { 49 | ErrorLog("Failed to initialize SteamVR runtime, error %i\n", error); 50 | return false; 51 | } 52 | 53 | if (!vr::VRSystem() || !vr::VRCompositor()) 54 | { 55 | ErrorLog("Invalid SteamVR interface handle!\n"); 56 | vr::VR_Shutdown(); 57 | return false; 58 | } 59 | 60 | for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) 61 | { 62 | if (vr::VRSystem()->GetTrackedDeviceClass(i) == vr::TrackedDeviceClass_HMD) 63 | { 64 | m_hmdDeviceId = i; 65 | break; 66 | } 67 | } 68 | 69 | if (m_hmdDeviceId < 0) 70 | { 71 | ErrorLog("HMD device ID not found!\n"); 72 | vr::VR_Shutdown(); 73 | return false; 74 | } 75 | 76 | m_bRuntimeInitialized = true; 77 | m_vrSystem = system; 78 | m_vrCompositor = vr::VRCompositor(); 79 | m_vrTrackedCamera = vr::VRTrackedCamera(); 80 | m_vrOverlay = vr::VROverlay(); 81 | m_vrRenderModels = vr::VRRenderModels(); 82 | 83 | return true; 84 | } 85 | 86 | void OpenVRManager::GetCameraDebugProperties(std::vector& properties) 87 | { 88 | properties.clear(); 89 | 90 | vr::IVRSystem* vrSystem = GetVRSystem(); 91 | vr::IVRTrackedCamera* trackedCamera = GetVRTrackedCamera(); 92 | 93 | char stringPropBuffer[256]; 94 | 95 | for (uint32_t deviceId = 0; deviceId < vr::k_unMaxTrackedDeviceCount; deviceId++) 96 | { 97 | if (vrSystem->GetTrackedDeviceClass(deviceId) == vr::TrackedDeviceClass_Invalid) 98 | { 99 | continue; 100 | } 101 | 102 | properties.push_back(DeviceDebugProperties()); 103 | DeviceDebugProperties& deviceProps = properties.at(properties.size() - 1); 104 | 105 | deviceProps.DeviceClass = vrSystem->GetTrackedDeviceClass(deviceId); 106 | deviceProps.DeviceId = deviceId; 107 | 108 | 109 | deviceProps.bHasCamera = vrSystem->GetBoolTrackedDeviceProperty(deviceId, vr::Prop_HasCamera_Bool); 110 | deviceProps.NumCameras = vrSystem->GetInt32TrackedDeviceProperty(deviceId, vr::Prop_NumCameras_Int32); 111 | 112 | memset(stringPropBuffer, 0, sizeof(stringPropBuffer)); 113 | uint32_t numChars = vrSystem->GetStringTrackedDeviceProperty(deviceId, vr::Prop_ManufacturerName_String, stringPropBuffer, sizeof(stringPropBuffer)); 114 | deviceProps.DeviceName.assign(stringPropBuffer); 115 | 116 | memset(stringPropBuffer, 0, sizeof(stringPropBuffer)); 117 | numChars = vrSystem->GetStringTrackedDeviceProperty(deviceId, vr::Prop_ModelNumber_String, stringPropBuffer, sizeof(stringPropBuffer)); 118 | deviceProps.DeviceName.append(" "); 119 | deviceProps.DeviceName.append(stringPropBuffer); 120 | 121 | vr::HmdMatrix34_t cameraToHeadmatrices[4]; 122 | uint32_t numBytes = vrSystem->GetArrayTrackedDeviceProperty(deviceId, vr::Prop_CameraToHeadTransforms_Matrix34_Array, vr::k_unHmdMatrix34PropertyTag, &cameraToHeadmatrices, sizeof(cameraToHeadmatrices)); 123 | 124 | int32_t cameraDistortionFunctions[4]; 125 | numBytes = vrSystem->GetArrayTrackedDeviceProperty(deviceId, vr::Prop_CameraDistortionFunction_Int32_Array, vr::k_unInt32PropertyTag, &cameraDistortionFunctions, sizeof(cameraDistortionFunctions)); 126 | 127 | double cameraDistortionCoeffs[4][vr::k_unMaxDistortionFunctionParameters]; 128 | numBytes = vrSystem->GetArrayTrackedDeviceProperty(deviceId, vr::Prop_CameraDistortionCoefficients_Float_Array, vr::k_unFloatPropertyTag, &cameraDistortionCoeffs, sizeof(cameraDistortionCoeffs)); 129 | 130 | vr::HmdVector4_t whiteBalance[4]; 131 | numBytes = vrSystem->GetArrayTrackedDeviceProperty(deviceId, vr::Prop_CameraWhiteBalance_Vector4_Array, vr::k_unHmdVector4PropertyTag, &whiteBalance, sizeof(whiteBalance)); 132 | 133 | uint32_t fbSize; 134 | 135 | trackedCamera->GetCameraFrameSize(deviceId, vr::VRTrackedCameraFrameType_Distorted, &deviceProps.DistortedFrameWidth, &deviceProps.DistortedFrameHeight, &fbSize); 136 | trackedCamera->GetCameraFrameSize(deviceId, vr::VRTrackedCameraFrameType_Undistorted, &deviceProps.UndistortedFrameWidth, &deviceProps.UndistortedFrameHeight, &fbSize); 137 | trackedCamera->GetCameraFrameSize(deviceId, vr::VRTrackedCameraFrameType_MaximumUndistorted, &deviceProps.MaximumUndistortedFrameWidth, &deviceProps.MaximumUndistortedFrameHeight, &fbSize); 138 | 139 | for (uint32_t cameraId = 0; cameraId < deviceProps.NumCameras; cameraId++) 140 | { 141 | CameraDebugProperties& cameraProps = deviceProps.CameraProps[cameraId]; 142 | 143 | trackedCamera->GetCameraIntrinsics(deviceId, cameraId, vr::VRTrackedCameraFrameType_Distorted, &cameraProps.DistortedFocalLength, &cameraProps.DistortedOpticalCenter); 144 | trackedCamera->GetCameraIntrinsics(deviceId, cameraId, vr::VRTrackedCameraFrameType_Undistorted, &cameraProps.UndistortedFocalLength, &cameraProps.UndistortedOpticalCenter); 145 | trackedCamera->GetCameraIntrinsics(deviceId, cameraId, vr::VRTrackedCameraFrameType_MaximumUndistorted, &cameraProps.MaximumUndistortedFocalLength, &cameraProps.MaximumUndistortedOpticalCenter); 146 | 147 | trackedCamera->GetCameraProjection(deviceId, cameraId, vr::VRTrackedCameraFrameType_Undistorted, 0.1f, 1.0f, &cameraProps.UndistortedProjecton); 148 | trackedCamera->GetCameraProjection(deviceId, cameraId, vr::VRTrackedCameraFrameType_MaximumUndistorted, 0.1f, 1.0f, &cameraProps.MaximumUndistortedProjecton); 149 | 150 | memcpy(&cameraProps.CameraToHeadTransform, &cameraToHeadmatrices[cameraId], sizeof(vr::HmdMatrix34_t)); 151 | cameraProps.DistortionFunction = (vr::EVRDistortionFunctionType)cameraDistortionFunctions[cameraId]; 152 | 153 | for (uint32_t coeff = 0; coeff < vr::k_unMaxDistortionFunctionParameters; coeff++) 154 | { 155 | cameraProps.DistortionCoefficients[coeff] = cameraDistortionCoeffs[cameraId][coeff]; 156 | } 157 | cameraProps.WhiteBalance = whiteBalance[cameraId]; 158 | } 159 | 160 | deviceProps.CameraFrameLayout = (vr::EVRTrackedCameraFrameLayout)vrSystem->GetInt32TrackedDeviceProperty(deviceId, vr::Prop_CameraFrameLayout_Int32); 161 | deviceProps.CameraStreamFormat = vrSystem->GetInt32TrackedDeviceProperty(deviceId, vr::Prop_CameraStreamFormat_Int32); 162 | deviceProps.CameraToHeadTransform = vrSystem->GetMatrix34TrackedDeviceProperty(deviceId, vr::Prop_CameraToHeadTransform_Matrix34); 163 | deviceProps.CameraFirmwareVersion = vrSystem->GetUint64TrackedDeviceProperty(deviceId, vr::Prop_CameraFirmwareVersion_Uint64); 164 | 165 | memset(stringPropBuffer, 0, sizeof(stringPropBuffer)); 166 | numChars = vrSystem->GetStringTrackedDeviceProperty(deviceId, vr::Prop_CameraFirmwareDescription_String, stringPropBuffer, sizeof(stringPropBuffer)); 167 | deviceProps.CameraFirmwareDescription.assign(stringPropBuffer); 168 | 169 | deviceProps.CameraCompatibilityMode = vrSystem->GetInt32TrackedDeviceProperty(deviceId, vr::Prop_CameraCompatibilityMode_Int32); 170 | deviceProps.bCameraSupportsCompatibilityModes = vrSystem->GetBoolTrackedDeviceProperty(deviceId, vr::Prop_CameraSupportsCompatibilityModes_Bool); 171 | deviceProps.CameraExposureTime = vrSystem->GetFloatTrackedDeviceProperty(deviceId, vr::Prop_CameraExposureTime_Float); 172 | deviceProps.CameraGlobalGain = vrSystem->GetFloatTrackedDeviceProperty(deviceId, vr::Prop_CameraGlobalGain_Float); 173 | } 174 | } 175 | 176 | void OpenVRManager::GetDeviceIdentProperties(std::vector& properties) 177 | { 178 | properties.clear(); 179 | 180 | vr::IVRSystem* vrSystem = GetVRSystem(); 181 | 182 | char stringPropBuffer[256]; 183 | 184 | for (uint32_t deviceId = 0; deviceId < vr::k_unMaxTrackedDeviceCount; deviceId++) 185 | { 186 | if (vrSystem->GetTrackedDeviceClass(deviceId) == vr::TrackedDeviceClass_Invalid) 187 | { 188 | continue; 189 | } 190 | 191 | properties.push_back(DeviceIdentProperties()); 192 | DeviceIdentProperties& deviceProps = properties.at(properties.size() - 1); 193 | 194 | deviceProps.DeviceId = deviceId; 195 | 196 | memset(stringPropBuffer, 0, sizeof(stringPropBuffer)); 197 | uint32_t numChars = vrSystem->GetStringTrackedDeviceProperty(deviceId, vr::Prop_ManufacturerName_String, stringPropBuffer, sizeof(stringPropBuffer)); 198 | deviceProps.DeviceName.assign(stringPropBuffer); 199 | 200 | memset(stringPropBuffer, 0, sizeof(stringPropBuffer)); 201 | numChars = vrSystem->GetStringTrackedDeviceProperty(deviceId, vr::Prop_ModelNumber_String, stringPropBuffer, sizeof(stringPropBuffer)); 202 | deviceProps.DeviceName.append(" "); 203 | deviceProps.DeviceName.append(stringPropBuffer); 204 | 205 | memset(stringPropBuffer, 0, sizeof(stringPropBuffer)); 206 | numChars = vrSystem->GetStringTrackedDeviceProperty(deviceId, vr::Prop_SerialNumber_String, stringPropBuffer, sizeof(stringPropBuffer)); 207 | deviceProps.DeviceSerial.assign(stringPropBuffer); 208 | } 209 | } -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/openvr_manager.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include "layer.h" 7 | 8 | 9 | class OpenVRManager 10 | { 11 | public: 12 | 13 | OpenVRManager(); 14 | ~OpenVRManager(); 15 | 16 | inline vr::IVRSystem* GetVRSystem() 17 | { 18 | if (!CheckRuntimeIntialized()) { return nullptr; } 19 | return m_vrSystem; 20 | } 21 | 22 | inline vr::IVRCompositor* GetVRCompositor() 23 | { 24 | if (!CheckRuntimeIntialized()) { return nullptr; } 25 | return m_vrCompositor; 26 | } 27 | 28 | inline vr::IVRTrackedCamera* GetVRTrackedCamera() 29 | { 30 | if (!CheckRuntimeIntialized()) { return nullptr; } 31 | return m_vrTrackedCamera; 32 | } 33 | 34 | inline vr::IVROverlay* GetVROverlay() 35 | { 36 | if (!CheckRuntimeIntialized()) { return nullptr; } 37 | return m_vrOverlay; 38 | } 39 | 40 | inline vr::IVRRenderModels* GetVRRenderModels() 41 | { 42 | if (!CheckRuntimeIntialized()) { return nullptr; } 43 | return m_vrRenderModels; 44 | } 45 | 46 | int GetHMDDeviceId() const 47 | { 48 | return m_hmdDeviceId; 49 | } 50 | 51 | void GetCameraDebugProperties(std::vector& properties); 52 | void GetDeviceIdentProperties(std::vector& properties); 53 | 54 | 55 | private: 56 | bool InitRuntime(); 57 | 58 | inline bool CheckRuntimeIntialized() 59 | { 60 | if (!m_bRuntimeInitialized) 61 | { 62 | return InitRuntime(); 63 | } 64 | 65 | return true; 66 | } 67 | 68 | bool m_bRuntimeInitialized; 69 | int m_hmdDeviceId; 70 | 71 | std::mutex m_runtimeMutex; 72 | 73 | vr::IVRSystem* m_vrSystem; 74 | vr::IVRCompositor* m_vrCompositor; 75 | vr::IVRTrackedCamera* m_vrTrackedCamera; 76 | vr::IVROverlay* m_vrOverlay; 77 | vr::IVRRenderModels* m_vrRenderModels; 78 | }; 79 | 80 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/passthrough_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rectus/openxr-steamvr-passthrough/7c11ecd44c311c78a9e9aed7e7fc1b94e7e03af6/XR_APILAYER_NOVENDOR_steamvr_passthrough/passthrough_icon.png -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/pch.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022 Rectus 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "pch.h" 24 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/pch.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2022 Rectus 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this softwareand associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and /or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright noticeand this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | using namespace std::chrono_literals; 42 | 43 | 44 | #define XR_NO_PROTOTYPES 45 | #define XR_USE_PLATFORM_WIN32 46 | 47 | 48 | #ifdef XR_USE_PLATFORM_WIN32 49 | 50 | #define XR_USE_GRAPHICS_API_D3D11 51 | #define XR_USE_GRAPHICS_API_D3D12 52 | #define WIN32_LEAN_AND_MEAN 53 | 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | 61 | using Microsoft::WRL::ComPtr; 62 | 63 | #endif 64 | 65 | 66 | 67 | #define XR_USE_GRAPHICS_API_VULKAN 68 | 69 | #ifdef XR_USE_GRAPHICS_API_VULKAN 70 | 71 | #ifdef XR_USE_PLATFORM_WIN32 72 | #define VK_USE_PLATFORM_WIN32_KHR 73 | #endif 74 | #include 75 | 76 | #endif 77 | 78 | 79 | #include 80 | #include 81 | #include 82 | #include 83 | 84 | // Link OpenVR as a static library for compatibility with utilities 85 | // that use modified versions of openvr_api.dll, such as OpenComposite. 86 | #define OPENVR_BUILD_STATIC 87 | #include 88 | 89 | #include "check.h" 90 | 91 | #define USE_TRACELOGGING 0 92 | 93 | #if USE_TRACELOGGING 94 | #include 95 | #include 96 | #include 97 | #include 98 | 99 | #include 100 | #endif 101 | 102 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/prebuild_commands.bat: -------------------------------------------------------------------------------- 1 | python framework\dispatch_generator.py 2 | 3 | %VULKAN_SDK%\Bin\glslangValidator.exe -D -V -S vert -e main --vn g_FullscreenQuadVS shaders/fullscreen_quad_vs.hlsl -o shaders\fullscreen_quad_vs.spv.h 4 | %VULKAN_SDK%\Bin\glslangValidator.exe -D -V -S vert -e main --vn g_PassthroughVS shaders/passthrough_vs.hlsl -o shaders\passthrough_vs.spv.h 5 | %VULKAN_SDK%\Bin\glslangValidator.exe -D -V -S vert -e main --vn g_PassthroughStereoVS shaders/passthrough_stereo_vs.hlsl -o shaders\passthrough_stereo_vs.spv.h 6 | 7 | %VULKAN_SDK%\Bin\glslangValidator.exe -D -V -S frag -e main --vn g_AlphaPrepassPS shaders/alpha_prepass_ps.hlsl -o shaders\alpha_prepass_ps.spv.h 8 | %VULKAN_SDK%\Bin\glslangValidator.exe -D -V -S frag -e main --vn g_AlphaPrepassMaskedPS shaders/alpha_prepass_masked_ps.hlsl -o shaders\alpha_prepass_masked_ps.spv.h 9 | %VULKAN_SDK%\Bin\glslangValidator.exe -D -V -S frag -e main --vn g_AlphaCopyMaskedPS shaders/alpha_copy_masked_ps.hlsl -o shaders\alpha_copy_masked_ps.spv.h 10 | %VULKAN_SDK%\Bin\glslangValidator.exe -D -V -S frag -e main --vn g_PassthroughPS shaders/passthrough_ps.hlsl -o shaders\passthrough_ps.spv.h 11 | 12 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by XR_APILAYER_NOVENDOR_steamvr_passthrough.rc 4 | // 5 | #define IDB_PNG_DASHBOARD_ICON 101 6 | #define IDB_PNG_TESTPATTERN 102 7 | 8 | // Next default values for new objects 9 | // 10 | #ifdef APSTUDIO_INVOKED 11 | #ifndef APSTUDIO_READONLY_SYMBOLS 12 | #define _APS_NEXT_RESOURCE_VALUE 103 13 | #define _APS_NEXT_COMMAND_VALUE 40001 14 | #define _APS_NEXT_CONTROL_VALUE 1001 15 | #define _APS_NEXT_SYMED_VALUE 101 16 | #endif 17 | #endif 18 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/alpha_copy_masked_ps.hlsl: -------------------------------------------------------------------------------- 1 | 2 | #include "common_ps.hlsl" 3 | #include "vs_outputs.hlsl" 4 | #include "util.hlsl" 5 | 6 | #ifdef VULKAN 7 | 8 | SamplerState g_samplerState : register(s6); 9 | Texture2D g_blendMask : register(t6); 10 | 11 | #else 12 | 13 | SamplerState g_samplerState : register(s0); 14 | Texture2D g_blendMask : register(t2); 15 | 16 | #endif 17 | 18 | 19 | float4 main(VS_OUTPUT input) : SV_TARGET 20 | { 21 | float2 screenUvs = input.screenPos.xy; 22 | screenUvs = screenUvs * float2(0.5, -0.5) + float2(0.5, 0.5); 23 | screenUvs = screenUvs * (g_uvPrepassBounds.zw - g_uvPrepassBounds.xy) + g_uvPrepassBounds.xy; 24 | screenUvs = clamp(screenUvs, g_uvPrepassBounds.xy, g_uvPrepassBounds.zw); 25 | 26 | float alpha = g_blendMask.Sample(g_samplerState, screenUvs); 27 | 28 | return float4(0, 0, 0, alpha); 29 | } 30 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/alpha_prepass_masked_fullscreen_ps.hlsl: -------------------------------------------------------------------------------- 1 | 2 | #include "common_ps.hlsl" 3 | #include "vs_outputs.hlsl" 4 | #include "util.hlsl" 5 | #include "fullscreen_util.hlsl" 6 | 7 | 8 | SamplerState g_samplerState : register(s0); 9 | Texture2DArray g_texture : register(t0); 10 | Texture2D g_fisheyeCorrectionTexture : register(t1); 11 | Texture2D g_cameraValidation : register(t2); 12 | Texture2D g_depthMap : register(t3); 13 | Texture2D g_crossDepthMap : register(t4); 14 | 15 | 16 | struct PS_Output 17 | { 18 | float color : SV_Target; 19 | float depth : SV_Depth; 20 | }; 21 | 22 | 23 | PS_Output main(VS_OUTPUT input) 24 | { 25 | float4 color; 26 | 27 | float2 screenUvs = Remap(input.screenPos.xy, float2(-1.0, -1.0), float2(1.0, 1.0), float2(0.0, 1.0), float2(1.0, 0.0)); 28 | 29 | float depth = g_depthMap.Sample(g_samplerState, screenUvs); 30 | float cameraBlend = 0.0; 31 | 32 | [branch] 33 | if(g_bIsCutoutEnabled) 34 | { 35 | float4 cameraValidation = g_cameraValidation.Sample(g_samplerState, screenUvs); 36 | float2 cameraBlendValidity = cameraValidation.zw; 37 | 38 | bool selectMainCamera = cameraBlendValidity.x >= cameraBlendValidity.y; 39 | bool blendCameras = cameraBlendValidity.x > 0.1 && cameraBlendValidity.y > 0.1; 40 | 41 | cameraBlend = blendCameras ? (1 - saturate(cameraBlendValidity.x + 1 - cameraBlendValidity.y)) : (selectMainCamera ? 0.0 : 1.0); 42 | 43 | float crossDepth = g_crossDepthMap.Sample(g_samplerState, screenUvs); 44 | 45 | bool bIsDiscontinuityFiltered = false; 46 | bool bIsCrossDiscontinuityFiltered = false; 47 | 48 | [branch] 49 | if (cameraBlend < 1.0 && g_depthContourStrength > 0) 50 | { 51 | depth = sobel_discontinuity_adjust(g_depthMap, g_samplerState, depth, screenUvs, bIsDiscontinuityFiltered); 52 | } 53 | 54 | [branch] 55 | if (cameraBlend > 0.0 && g_depthContourStrength > 0) 56 | { 57 | crossDepth = sobel_discontinuity_adjust(g_crossDepthMap, g_samplerState, crossDepth, screenUvs, bIsCrossDiscontinuityFiltered); 58 | } 59 | 60 | depth = lerp(depth, crossDepth, cameraBlend); 61 | } 62 | else 63 | { 64 | [branch] 65 | if (g_depthContourStrength > 0) 66 | { 67 | bool bIsDiscontinuityFiltered = false; 68 | depth = sobel_discontinuity_adjust(g_depthMap, g_samplerState, depth, screenUvs, bIsDiscontinuityFiltered); 69 | } 70 | } 71 | 72 | bool bInvertOutput = g_bMaskedInvert; 73 | 74 | if (g_bMaskedUseCamera) 75 | { 76 | float4 clipSpacePos = float4(input.screenPos.xy, depth, 1.0); 77 | float4 worldProjectionPos = mul(g_HMDProjectionToWorld, clipSpacePos); 78 | float4 cameraClipSpacePos = mul((g_cameraViewIndex == 0) == (cameraBlend < 0.5) ? g_worldToCameraFrameProjectionLeft : g_worldToCameraFrameProjectionRight, worldProjectionPos); 79 | 80 | float2 outUvs = Remap(cameraClipSpacePos.xy / cameraClipSpacePos.w, -1.0, 1.0, 0.0, 1.0); 81 | 82 | if(cameraBlend < 0.5) 83 | { 84 | outUvs = Remap(outUvs, 0.0, 1.0, g_uvBounds.xy, g_uvBounds.zw); 85 | outUvs = clamp(outUvs, g_uvBounds.xy, g_uvBounds.zw); 86 | } 87 | else 88 | { 89 | outUvs = Remap(outUvs, 0.0, 1.0, g_crossUVBounds.xy, g_crossUVBounds.zw); 90 | outUvs = clamp(outUvs, g_crossUVBounds.xy, g_crossUVBounds.zw); 91 | } 92 | 93 | 94 | if (g_bUseFisheyeCorrection) 95 | { 96 | float2 correction = g_fisheyeCorrectionTexture.Sample(g_samplerState, outUvs); 97 | outUvs += correction; 98 | } 99 | else 100 | { 101 | outUvs.y = 1 - outUvs.y; 102 | } 103 | 104 | color = g_texture.Sample(g_samplerState, float3(outUvs.xy, 0)); 105 | bInvertOutput = !bInvertOutput; 106 | } 107 | else 108 | { 109 | float2 screenUvs = Remap(input.screenPos.xy, float2(-1.0, -1.0), float2(1.0, 1.0), float2(0.0, 1.0), float2(1.0, 0.0)); 110 | 111 | depth = g_depthMap.Sample(g_samplerState, screenUvs); 112 | 113 | float2 outUvs = Remap(input.screenPos.xy / input.screenPos.w, float2(-1.0, -1.0), float2(1.0, 1.0), float2(0.0, 1.0), float2(1.0, 0.0)); 114 | 115 | color = g_texture.Sample(g_samplerState, 116 | float3((outUvs * (g_uvPrepassBounds.zw - g_uvPrepassBounds.xy) + g_uvPrepassBounds.xy), float(g_arrayIndex))); 117 | } 118 | 119 | float3 difference = LinearRGBtoLAB_D65(color.xyz) - LinearRGBtoLAB_D65(g_maskedKey); 120 | 121 | float2 distChromaSqr = float2(pow(difference.y, 2), pow(difference.z, 2)); 122 | float fracChromaSqr = pow(g_maskedFracChroma, 2); 123 | 124 | float distChroma = smoothstep(fracChromaSqr, fracChromaSqr + pow(g_maskedSmooth, 2), (distChromaSqr.x + distChromaSqr.y)); 125 | float distLuma = smoothstep(g_maskedFracLuma, g_maskedFracLuma + g_maskedSmooth, abs(difference.x)); 126 | 127 | PS_Output output; 128 | 129 | output.color = bInvertOutput ? 1.0 - max(distChroma, distLuma) : max(distChroma, distLuma); 130 | 131 | output.depth = output.color > 0.0 ? 0.0 : 1.0; 132 | 133 | return output; 134 | } 135 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/alpha_prepass_masked_ps.hlsl: -------------------------------------------------------------------------------- 1 | 2 | #include "common_ps.hlsl" 3 | #include "vs_outputs.hlsl" 4 | #include "util.hlsl" 5 | 6 | 7 | #ifdef VULKAN 8 | 9 | SamplerState g_samplerState : register(s7); 10 | Texture2DArray g_texture : register(t7); 11 | Texture2D g_fisheyeCorrectionTexture : register(t8); 12 | 13 | #else 14 | 15 | SamplerState g_samplerState : register(s0); 16 | Texture2DArray g_texture : register(t0); 17 | Texture2D g_fisheyeCorrectionTexture : register(t1); 18 | 19 | #endif 20 | 21 | 22 | [earlydepthstencil] 23 | float main(VS_OUTPUT input) : SV_TARGET 24 | { 25 | float4 color; 26 | 27 | bool bInvertOutput = g_bMaskedInvert; 28 | 29 | if (g_bMaskedUseCamera) 30 | { 31 | float2 outUvs = Remap(input.cameraReprojectedPos.xy / input.cameraReprojectedPos.w, -1.0, 1.0, g_uvBounds.xy, g_uvBounds.zw); 32 | 33 | outUvs = clamp(outUvs, g_uvBounds.xy, g_uvBounds.zw); 34 | 35 | if (g_bUseFisheyeCorrection) 36 | { 37 | float2 correction = g_fisheyeCorrectionTexture.Sample(g_samplerState, outUvs); 38 | outUvs += correction; 39 | } 40 | else 41 | { 42 | outUvs.y = 1 - outUvs.y; 43 | } 44 | 45 | color = g_texture.Sample(g_samplerState, float3(outUvs.xy, 0)); 46 | bInvertOutput = !bInvertOutput; 47 | } 48 | else 49 | { 50 | float2 outUvs = Remap(input.screenPos.xy / input.screenPos.w, float2(-1.0, -1.0), float2(1.0, 1.0), float2(0.0, 1.0), float2(1.0, 0.0)); 51 | 52 | color = g_texture.Sample(g_samplerState, 53 | float3((outUvs * (g_uvPrepassBounds.zw - g_uvPrepassBounds.xy) + g_uvPrepassBounds.xy), float(g_arrayIndex))); 54 | } 55 | 56 | float3 difference = LinearRGBtoLAB_D65(color.xyz) - LinearRGBtoLAB_D65(g_maskedKey); 57 | 58 | float2 distChromaSqr = float2(pow(difference.y, 2), pow(difference.z, 2)); 59 | float fracChromaSqr = pow(g_maskedFracChroma, 2); 60 | 61 | float distChroma = smoothstep(fracChromaSqr, fracChromaSqr + pow(g_maskedSmooth, 2), (distChromaSqr.x + distChromaSqr.y)); 62 | float distLuma = smoothstep(g_maskedFracLuma, g_maskedFracLuma + g_maskedSmooth, abs(difference.x)); 63 | 64 | return bInvertOutput ? 1.0 - max(distChroma, distLuma) : max(distChroma, distLuma); 65 | } 66 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/alpha_prepass_ps.hlsl: -------------------------------------------------------------------------------- 1 | 2 | #include "common_ps.hlsl" 3 | #include "vs_outputs.hlsl" 4 | 5 | 6 | float4 main(VS_OUTPUT input) : SV_TARGET 7 | { 8 | clip(input.projectionConfidence.x); 9 | 10 | if (g_bUseDepthCutoffRange) 11 | { 12 | clip(input.screenPos.w - g_depthCutoffRange.x); 13 | clip(g_depthCutoffRange.y - input.screenPos.w); 14 | } 15 | 16 | return float4(0, 0, 0, 1.0 - g_opacity); 17 | } 18 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/common_ps.hlsl: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _COMMON_PS_INCLUDED 3 | #define _COMMON_PS_INCLUDED 4 | 5 | 6 | 7 | #ifdef VULKAN 8 | #define REGISTER_PSVIEW register(b2) 9 | #define REGISTER_PSPASS register(b3) 10 | #define REGISTER_PSMASKED register(b4) 11 | #else 12 | #define REGISTER_PSVIEW register(b1) 13 | #define REGISTER_PSPASS register(b0) 14 | #define REGISTER_PSMASKED register(b2) 15 | #endif 16 | 17 | 18 | cbuffer psViewConstantBuffer : REGISTER_PSVIEW 19 | { 20 | float4x4 g_worldToHMDProjection; 21 | float4x4 g_HMDProjectionToWorld; 22 | float4x4 g_prevHMDFrame_WorldToHMDProjection; 23 | float4x4 g_prevCameraFrame_WorldToHMDProjection; 24 | 25 | float4 g_uvBounds; 26 | float4 g_crossUVBounds; 27 | float4 g_uvPrepassBounds; 28 | uint g_arrayIndex; 29 | int g_cameraViewIndex; 30 | bool g_doCutout; 31 | bool g_bPremultiplyAlpha; 32 | bool g_bUseFullscreenQuad; 33 | }; 34 | 35 | 36 | cbuffer psPassConstantBuffer : REGISTER_PSPASS 37 | { 38 | float4x4 g_worldToCameraFrameProjectionLeft; 39 | float4x4 g_worldToCameraFrameProjectionRight; 40 | float4x4 g_worldToPrevCameraFrameProjectionLeft; 41 | float4x4 g_worldToPrevCameraFrameProjectionRight; 42 | 43 | float2 g_depthRange; 44 | float2 g_depthCutoffRange; 45 | float g_opacity; 46 | float g_brightness; 47 | float g_contrast; 48 | float g_saturation; 49 | float g_sharpness; 50 | int g_temporalFilteringSampling; 51 | float g_temporalFilteringFactor; 52 | float g_temporalFilteringColorRangeCutoff; 53 | float g_cutoutCombineFactor; 54 | float g_depthTemporalFilterFactor; 55 | float g_depthTemporalFilterDistance; 56 | float g_depthContourStrength; 57 | float g_depthContourTreshold; 58 | int g_depthContourFilterWidth; 59 | uint g_debugOverlay; 60 | bool g_bDoColorAdjustment; 61 | bool g_bDebugDepth; 62 | bool g_bUseFisheyeCorrection; 63 | bool g_bIsFirstRenderOfCameraFrame; 64 | bool g_bUseDepthCutoffRange; 65 | bool g_bClampCameraFrame; 66 | bool g_bIsCutoutEnabled; 67 | }; 68 | 69 | 70 | cbuffer psMaskedConstantBuffer : REGISTER_PSMASKED 71 | { 72 | float3 g_maskedKey; 73 | float g_maskedFracChroma; 74 | float g_maskedFracLuma; 75 | float g_maskedSmooth; 76 | bool g_bMaskedUseCamera; 77 | bool g_bMaskedInvert; 78 | }; 79 | 80 | 81 | #endif //_COMMON_PS_INCLUDED 82 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/common_vs.hlsl: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _COMMON_VS_INCLUDED 3 | #define _COMMON_VS_INCLUDED 4 | 5 | cbuffer vsViewConstantBuffer : register(b0) 6 | { 7 | float4x4 g_worldToHMDProjection; 8 | float4x4 g_HMDProjectionToWorld; 9 | float4x4 g_prevHMDFrame_WorldToHMDProjection; 10 | float4x4 g_prevCameraFrame_WorldToHMDProjection; 11 | float4 g_disparityUVBounds; 12 | float3 g_projectionOriginWorld; 13 | float g_projectionDistance; 14 | float g_floorHeightOffset; 15 | float g_cameraBlendWeight; 16 | int g_cameraViewIndex; 17 | bool g_bWriteDisparityFilter; 18 | }; 19 | 20 | cbuffer vsPassConstantBuffer : register(b1) 21 | { 22 | float4x4 g_worldToCameraFrameProjectionLeft; 23 | float4x4 g_worldToCameraFrameProjectionRight; 24 | float4x4 g_worldToPrevCameraFrameProjectionLeft; 25 | float4x4 g_worldToPrevCameraFrameProjectionRight; 26 | float4x4 g_worldToPrevDepthFrameProjectionLeft; 27 | float4x4 g_worldToPrevDepthFrameProjectionRight; 28 | float4x4 g_depthFrameViewToWorldLeft; 29 | float4x4 g_depthFrameViewToWorldRight; 30 | float4x4 g_prevDepthFrameViewToWorldLeft; 31 | float4x4 g_prevDepthFrameViewToWorldRight; 32 | 33 | float4x4 g_disparityToDepth; 34 | uint2 g_disparityTextureSize; 35 | float g_minDisparity; 36 | float g_maxDisparity; 37 | float g_disparityDownscaleFactor; 38 | float g_cutoutFactor; 39 | float g_cutoutOffset; 40 | float g_cutoutFilterWidth; 41 | int g_disparityFilterWidth; 42 | bool g_bProjectBorders; 43 | bool g_bFindDiscontinuities; 44 | bool g_bUseDisparityTemporalFilter; 45 | bool g_bBlendDepthMaps; 46 | float g_disparityTemporalFilterStrength; 47 | float g_disparityTemporalFilterDistance; 48 | float g_depthContourStrength; 49 | float g_depthContourTreshold; 50 | }; 51 | 52 | #endif //_COMMON_VS_INCLUDED 53 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/depth_write_ps.hlsl: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "common_ps.hlsl" 4 | #include "vs_outputs.hlsl" 5 | #include "util.hlsl" 6 | 7 | 8 | float4 main(VS_OUTPUT input) : SV_Target 9 | { 10 | float outProjectionConfidence = input.projectionConfidence.x; 11 | float outBlendValidity = input.cameraBlendConfidence.x; 12 | 13 | // Written channels are selected with the pipeline. 14 | return float4(outProjectionConfidence, outProjectionConfidence, outBlendValidity, outBlendValidity); 15 | } 16 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/depth_write_temporal_ps.hlsl: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "common_ps.hlsl" 4 | #include "vs_outputs.hlsl" 5 | #include "util.hlsl" 6 | 7 | 8 | SamplerState g_samplerState : register(s0); 9 | Texture2D g_prevDepthMap : register(t0); 10 | Texture2D g_prevCameraValidation : register(t1); 11 | 12 | 13 | float sobel_discontinuity_adjust(in Texture2D depthTex, in SamplerState texSampler, in float depth, in float2 uvs, float2 texSize) 14 | { 15 | float outDepth = depth; 16 | 17 | uint2 pixelPos = floor(saturate(uvs) * texSize); 18 | 19 | float dispU = depthTex.Load(int3(pixelPos + uint2(0, -1), 0)); 20 | float dispD = depthTex.Load(int3(pixelPos + uint2(0, 1), 0)); 21 | float dispL = depthTex.Load(int3(pixelPos + uint2(-1, 0), 0)); 22 | float dispR = depthTex.Load(int3(pixelPos + uint2(1, 0), 0)); 23 | 24 | float dispUL = depthTex.Load(int3(pixelPos + uint2(-1, -1), 0)); 25 | float dispDL = depthTex.Load(int3(pixelPos + uint2(-1, 1), 0)); 26 | float dispUR = depthTex.Load(int3(pixelPos + uint2(1, -1), 0)); 27 | float dispDR = depthTex.Load(int3(pixelPos + uint2(1, 1), 0)); 28 | 29 | float filterX = dispUL + dispL * 2 + dispDL - dispUR - dispR * 2 - dispDR; 30 | float filterY = dispUL + dispU * 2 + dispUR - dispDL - dispD * 2 - dispDR; 31 | 32 | float minDepth = min(depth, min(dispU, min(dispD, min(dispL, min(dispR, min(dispUL, min(dispDL, min(dispUR, dispDR)))))))); 33 | float maxDepth = max(depth, max(dispU, max(dispD, max(dispL, max(dispR, max(dispUL, max(dispDL, max(dispUR, dispDR)))))))); 34 | 35 | float magnitude = length(float2(filterX, filterY)); 36 | 37 | if(magnitude > g_depthContourTreshold) 38 | { 39 | bool inForeground = ((maxDepth - depth) > (depth - minDepth)); 40 | 41 | float offsetFactor = saturate(g_depthContourStrength * 10.0 * magnitude); 42 | 43 | outDepth = lerp(depth, inForeground ? minDepth : maxDepth, offsetFactor); 44 | } 45 | return outDepth; 46 | } 47 | 48 | 49 | float4 main(VS_OUTPUT input, out float outDepth : SV_Depth ) : SV_Target 50 | { 51 | float outProjectionConfidence = input.projectionConfidence.x; 52 | 53 | float outBlendValidity = input.cameraBlendConfidence.x; 54 | 55 | outDepth = input.position.z; 56 | 57 | if(input.projectionConfidence.x < 0.5 || input.cameraBlendConfidence.x < 0.5) 58 | { 59 | float2 prevScreenUvs = Remap(input.prevHMDFrameScreenPos.xy / input.prevHMDFrameScreenPos.w, float2(-1.0, -1.0), float2(1.0, 1.0), float2(0.0, 1.0), float2(1.0, 0.0)); 60 | 61 | bool bPrevUVsValid = prevScreenUvs.x >= 0.0 && prevScreenUvs.y >= 0.0 && prevScreenUvs.x <= 1.0 && prevScreenUvs.y <= 1.0; 62 | 63 | float2 historyTextureSize; 64 | g_prevDepthMap.GetDimensions(historyTextureSize.x, historyTextureSize.y); 65 | 66 | float prevDepth = bicubic_b_spline_4tap(g_prevDepthMap, g_samplerState, prevScreenUvs, historyTextureSize); 67 | float4 prevValid4 = bicubic_b_spline_4tap(g_prevCameraValidation, g_samplerState, prevScreenUvs, historyTextureSize); 68 | 69 | // Create sharp edges so that the discontinuity adjust in the main pass works properly. 70 | prevDepth = sobel_discontinuity_adjust(g_prevDepthMap, g_samplerState, prevDepth, prevScreenUvs, historyTextureSize); 71 | 72 | float prevProjectionConfidence = g_doCutout ? prevValid4.y : prevValid4.x; 73 | float prevBlendConfidence = g_doCutout ? prevValid4.w : prevValid4.z; 74 | 75 | float depthDiff = abs((prevDepth - input.position.z) / (g_depthRange.y - g_depthRange.x)); 76 | 77 | if(bPrevUVsValid && prevProjectionConfidence >= outProjectionConfidence && depthDiff <= g_depthTemporalFilterDistance) 78 | { 79 | float lerpFactor = min(g_depthTemporalFilterFactor, 1.0);//0.9999); 80 | outDepth = lerp(outDepth, prevDepth, lerpFactor); 81 | outProjectionConfidence = lerp(outProjectionConfidence, prevProjectionConfidence, lerpFactor); 82 | 83 | outBlendValidity = lerp(input.cameraBlendConfidence.x, prevBlendConfidence, lerpFactor); 84 | } 85 | } 86 | 87 | // Written channels are selected with the pipeline. 88 | return float4(outProjectionConfidence, outProjectionConfidence, outBlendValidity, outBlendValidity); 89 | } 90 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/fill_holes_cs.hlsl: -------------------------------------------------------------------------------- 1 | 2 | cbuffer csConstantBuffer : register(b0) 3 | { 4 | uint g_disparityFrameWidth; 5 | bool g_bHoleFillLastPass; 6 | float g_minDisparity; 7 | float g_maxDisparity; 8 | } 9 | 10 | RWTexture2D g_disparityTexture: register(u0); 11 | 12 | [numthreads(16, 16, 1)] 13 | void main(uint3 pos : SV_DispatchThreadID) 14 | { 15 | float2 dispConf = g_disparityTexture.Load(pos.xy); 16 | 17 | // Filter large invalid disparites from the right edge of the image. 18 | int pixelsFromRightEdge = (pos.x > g_disparityFrameWidth) ? g_disparityFrameWidth * 2 - pos.x : g_disparityFrameWidth - pos.x; 19 | float maxDisp = lerp(g_minDisparity, g_maxDisparity, min(1, pixelsFromRightEdge / ((g_maxDisparity - g_minDisparity) * 2048.0))); 20 | 21 | if (pixelsFromRightEdge < 2) 22 | { 23 | return; 24 | } 25 | 26 | // Sample neighbors and temporarily store furthest disparity in the confidence map as a negative value. 27 | if (dispConf.y <= 0.0 && (dispConf.x <= g_minDisparity || dispConf.x >= maxDisp)) 28 | { 29 | float2 dispConfU = g_disparityTexture.Load(pos.xy + uint2(0, -1)); 30 | float2 dispConfD = g_disparityTexture.Load(pos.xy + uint2(0, 1)); 31 | float2 dispConfL = g_disparityTexture.Load(pos.xy + uint2(-1, 0)); 32 | float2 dispConfR = g_disparityTexture.Load(pos.xy + uint2(1, 0)); 33 | 34 | if (dispConf.y == 0.0) 35 | { 36 | if (dispConfU.x > g_minDisparity && dispConfU.x < maxDisp) 37 | { 38 | dispConf.y = dispConfU.x * -1.0; 39 | } 40 | else if (dispConfD.x > g_minDisparity && dispConfD.x < maxDisp) 41 | { 42 | dispConf.y = dispConfD.x * -1.0; 43 | } 44 | else if (dispConfL.x > g_minDisparity && dispConfL.x < maxDisp) 45 | { 46 | dispConf.y = dispConfL.x * -1.0; 47 | } 48 | else if (dispConfR.x > g_minDisparity && dispConfR.x < maxDisp) 49 | { 50 | dispConf.y = dispConfR.x * -1.0; 51 | } 52 | else if (dispConfU.y < 0.0) 53 | { 54 | dispConf.y = dispConfU.y; 55 | } 56 | else if (dispConfD.y < 0.0) 57 | { 58 | dispConf.y = dispConfD.y; 59 | } 60 | else if (dispConfL.y < 0.0) 61 | { 62 | dispConf.y = dispConfL.y; 63 | } 64 | else if (dispConfR.y < 0.0) 65 | { 66 | dispConf.y = dispConfR.y; 67 | } 68 | } 69 | 70 | if (dispConfU.y < 0.0 && dispConfU.y > dispConf.y) 71 | { 72 | dispConf.y = dispConfU.y; 73 | } 74 | if (dispConfD.y < 0.0 && dispConfD.y > dispConf.y) 75 | { 76 | dispConf.y = dispConfD.y; 77 | } 78 | if (dispConfL.y < 0.0 && dispConfL.y > dispConf.y) 79 | { 80 | dispConf.y = dispConfL.y; 81 | } 82 | if (dispConfR.y < 0.0 && dispConfR.y > dispConf.y) 83 | { 84 | dispConf.y = dispConfR.y; 85 | } 86 | 87 | // Return the stored disparity value if one exists. 88 | if (g_bHoleFillLastPass) 89 | { 90 | if (dispConf.y < 0.5 && dispConf.y >= 0.0) 91 | { 92 | dispConf.x = g_minDisparity; 93 | dispConf.y = -1.0; 94 | 95 | } 96 | else 97 | { 98 | dispConf.x = -dispConf.y; 99 | dispConf.y = 0.0; 100 | } 101 | } 102 | 103 | g_disparityTexture[pos.xy] = dispConf; 104 | } 105 | else if (g_bHoleFillLastPass) 106 | { 107 | //dispConf.y = 0.4; 108 | g_disparityTexture[pos.xy] = dispConf; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/fullscreen_passthrough_composite_ps.hlsl: -------------------------------------------------------------------------------- 1 | 2 | #include "common_ps.hlsl" 3 | #include "vs_outputs.hlsl" 4 | #include "util.hlsl" 5 | #include "fullscreen_util.hlsl" 6 | 7 | 8 | SamplerState g_samplerState : register(s0); 9 | Texture2D g_cameraFrameTexture : register(t0); 10 | Texture2D g_fisheyeCorrectionTexture : register(t1); 11 | Texture2D g_cameraValidation : register(t2); 12 | Texture2D g_depthMap : register(t3); 13 | Texture2D g_crossDepthMap : register(t4); 14 | 15 | struct PS_Output 16 | { 17 | float4 color : SV_Target; 18 | float depth : SV_Depth; 19 | }; 20 | 21 | 22 | 23 | 24 | PS_Output main(VS_OUTPUT input) 25 | { 26 | float2 screenUvs = Remap(input.screenPos.xy, float2(-1.0, -1.0), float2(1.0, 1.0), float2(0.0, 1.0), float2(1.0, 0.0)); 27 | 28 | float depth = g_depthMap.Sample(g_samplerState, screenUvs); 29 | float crossDepth = g_crossDepthMap.Sample(g_samplerState, screenUvs); 30 | float4 cameraValidation = g_cameraValidation.Sample(g_samplerState, screenUvs); 31 | 32 | float2 projectionConfidence = cameraValidation.xy; 33 | float2 cameraBlendValidity = cameraValidation.zw; 34 | 35 | bool selectMainCamera = cameraBlendValidity.x >= cameraBlendValidity.y; 36 | bool blendCameras = cameraBlendValidity.x > 0.1 && cameraBlendValidity.y > 0.1; 37 | 38 | float cameraBlend = blendCameras ? (1 - saturate(cameraBlendValidity.x + 1 - cameraBlendValidity.y)) : (selectMainCamera ? 0.0 : 1.0); 39 | 40 | 41 | bool bIsDiscontinuityFiltered = false; 42 | bool bIsCrossDiscontinuityFiltered = false; 43 | 44 | [branch] 45 | if (cameraBlend < 1.0 && g_depthContourStrength > 0) 46 | { 47 | depth = sobel_discontinuity_adjust(g_depthMap, g_samplerState, depth, screenUvs, bIsDiscontinuityFiltered); 48 | } 49 | 50 | [branch] 51 | if (cameraBlend > 0.0 && g_depthContourStrength > 0) 52 | { 53 | crossDepth = sobel_discontinuity_adjust(g_crossDepthMap, g_samplerState, crossDepth, screenUvs, bIsCrossDiscontinuityFiltered); 54 | } 55 | 56 | float4 clipSpacePos = float4(input.screenPos.xy, depth, 1.0); 57 | float4 worldProjectionPos = mul(g_HMDProjectionToWorld, clipSpacePos); 58 | float4 cameraClipSpacePos = mul((g_cameraViewIndex == 0) ? g_worldToCameraFrameProjectionLeft : g_worldToCameraFrameProjectionRight, worldProjectionPos); 59 | 60 | float4 crossClipSpacePos = float4(input.screenPos.xy, crossDepth, 1.0); 61 | float4 crossWorldProjectionPos = mul(g_HMDProjectionToWorld, crossClipSpacePos); 62 | float4 cameraCrossClipSpacePos = mul((g_cameraViewIndex == 0) ? g_worldToCameraFrameProjectionRight : g_worldToCameraFrameProjectionLeft, crossWorldProjectionPos); 63 | 64 | if (g_bUseDepthCutoffRange) 65 | { 66 | float4 worldHMDEyePos = mul(g_HMDProjectionToWorld, float4(0, 0, 0, 1)); 67 | float depthMeters = distance(lerp(worldProjectionPos.xyz / worldProjectionPos.w, crossWorldProjectionPos.xyz / crossWorldProjectionPos.w, cameraBlend), worldHMDEyePos.xyz / worldHMDEyePos.w); 68 | clip(depthMeters - g_depthCutoffRange.x); 69 | clip(g_depthCutoffRange.y - depthMeters); 70 | } 71 | 72 | // Convert from homogenous clip space coordinates to 0-1. 73 | float2 outUvs = Remap(cameraClipSpacePos.xy / cameraClipSpacePos.w, -1.0, 1.0, 0.0, 1.0); 74 | float2 crossUvs = Remap(cameraCrossClipSpacePos.xy / cameraCrossClipSpacePos.w, -1.0, 1.0, 0.0, 1.0); 75 | 76 | if (g_bClampCameraFrame) 77 | { 78 | clip(cameraClipSpacePos.z); 79 | clip(outUvs); 80 | clip(1 - outUvs); 81 | } 82 | 83 | float2 correction = 0; 84 | 85 | if (g_bUseFisheyeCorrection) 86 | { 87 | // Remap and clamp to frame UV bounds. 88 | outUvs = Remap(outUvs, 0.0, 1.0, g_uvBounds.xy, g_uvBounds.zw); 89 | outUvs = clamp(outUvs, g_uvBounds.xy, g_uvBounds.zw); 90 | 91 | correction = g_fisheyeCorrectionTexture.Sample(g_samplerState, outUvs); 92 | outUvs += correction; 93 | 94 | crossUvs = Remap(crossUvs, 0.0, 1.0, g_crossUVBounds.xy, g_crossUVBounds.zw); 95 | crossUvs = clamp(crossUvs, g_crossUVBounds.xy, g_crossUVBounds.zw); 96 | 97 | correction = g_fisheyeCorrectionTexture.Sample(g_samplerState, crossUvs); 98 | crossUvs += correction; 99 | } 100 | else 101 | { 102 | // Remap and clamp to frame UV bounds. 103 | outUvs = Remap(outUvs, float2(0.0, 1.0), float2(1.0, 0.0), g_uvBounds.xy, g_uvBounds.zw); 104 | outUvs = clamp(outUvs, g_uvBounds.xy, g_uvBounds.zw); 105 | 106 | crossUvs = Remap(crossUvs, float2(0.0, 1.0), float2(1.0, 0.0), g_crossUVBounds.xy, g_crossUVBounds.zw); 107 | crossUvs = clamp(crossUvs, g_crossUVBounds.xy, g_crossUVBounds.zw); 108 | } 109 | 110 | float3 rgbColor = g_cameraFrameTexture.Sample(g_samplerState, outUvs).xyz; 111 | 112 | float3 minColor = rgbColor; 113 | float3 maxColor = rgbColor; 114 | 115 | float3 crossRGBColor = g_cameraFrameTexture.Sample(g_samplerState, crossUvs).xyz; 116 | 117 | 118 | rgbColor *= (1 + g_sharpness * 4); 119 | 120 | float3 textureSize; 121 | g_cameraFrameTexture.GetDimensions(0, textureSize.x, textureSize.y, textureSize.z); 122 | float3 sample = g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(-1, 0) / textureSize.xy).xyz; 123 | rgbColor -= sample * g_sharpness; 124 | minColor = min(minColor, sample); 125 | maxColor = max(maxColor, sample); 126 | 127 | sample = g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(1, 0) / textureSize.xy).xyz; 128 | rgbColor -= sample * g_sharpness; 129 | minColor = min(minColor, sample); 130 | maxColor = max(maxColor, sample); 131 | 132 | sample = g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(0, -1) / textureSize.xy).xyz; 133 | rgbColor -= sample * g_sharpness; 134 | minColor = min(minColor, sample); 135 | maxColor = max(maxColor, sample); 136 | 137 | sample = g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(0, 1) / textureSize.xy).xyz; 138 | rgbColor -= sample * g_sharpness; 139 | minColor = min(minColor, sample); 140 | maxColor = max(maxColor, sample); 141 | 142 | crossRGBColor *= 1 + g_sharpness * 4; 143 | crossRGBColor -= g_cameraFrameTexture.Sample(g_samplerState, crossUvs + float2(-1, 0) / textureSize.xy).xyz * g_sharpness; 144 | crossRGBColor -= g_cameraFrameTexture.Sample(g_samplerState, crossUvs + float2(1, 0) / textureSize.xy).xyz * g_sharpness; 145 | crossRGBColor -= g_cameraFrameTexture.Sample(g_samplerState, crossUvs + float2(0, -1) / textureSize.xy).xyz * g_sharpness; 146 | crossRGBColor -= g_cameraFrameTexture.Sample(g_samplerState, crossUvs + float2(0, 1) / textureSize.xy).xyz * g_sharpness; 147 | 148 | 149 | float3 crossRGBColorClamped = min(maxColor, max(crossRGBColor, minColor)); 150 | 151 | uint texW, texH; 152 | g_cameraFrameTexture.GetDimensions(texW, texH); 153 | int2 cameraFrameRes = uint2(texW, texH); 154 | 155 | float2 camTexCoords = outUvs * cameraFrameRes; 156 | uint2 camPixel = floor(camTexCoords); 157 | 158 | float2 crossCamTexCoords = crossUvs * cameraFrameRes; 159 | uint2 crossCamPixel = floor(crossCamTexCoords); 160 | 161 | // How far the current pixel is to the sampled one 162 | float distanceFactor = abs(camTexCoords.x - camPixel.x - 0.5) + abs(camTexCoords.y - camPixel.y - 0.5); 163 | float crossDistanceFactor = abs(crossCamTexCoords.x - crossCamPixel.x - 0.5) + abs(crossCamTexCoords.y - crossCamPixel.y - 0.5); 164 | 165 | float pixelDistanceBlend = distanceFactor + (1 - crossDistanceFactor); 166 | 167 | float depthFactor = saturate(1 - (abs(depth - crossDepth) * 1000)); 168 | 169 | float combineFactor = g_cutoutCombineFactor * depthFactor * projectionConfidence.x * projectionConfidence.y; 170 | 171 | 172 | float finalFactor = lerp(cameraBlend, pixelDistanceBlend, combineFactor); 173 | 174 | // Blend together both cameras based on which ones are valid and have the closest pixels. 175 | rgbColor = lerp(rgbColor, lerp(crossRGBColor, crossRGBColorClamped, combineFactor), finalFactor); 176 | 177 | float finalDepth = lerp(depth, crossDepth, finalFactor); 178 | 179 | if (g_bDoColorAdjustment) 180 | { 181 | // Using CIELAB D65 to match the EXT_FB_passthrough adjustments. 182 | float3 labColor = LinearRGBtoLAB_D65(rgbColor); 183 | float LPrime = clamp((labColor.x - 50.0) * g_contrast + 50.0, 0.0, 100.0); 184 | float LBis = clamp(LPrime + g_brightness, 0.0, 100.0); 185 | float2 ab = labColor.yz * g_saturation; 186 | 187 | rgbColor = LABtoLinearRGB_D65(float3(LBis, ab.xy)); 188 | } 189 | 190 | if (g_bDebugDepth) 191 | { 192 | float debugDepth = pow(abs(finalDepth), g_depthRange.y * 5.0); 193 | rgbColor = float3(debugDepth, debugDepth, debugDepth); 194 | } 195 | if (g_debugOverlay == 1) // Confidence 196 | { 197 | if (projectionConfidence.x < 0.0 && projectionConfidence.y < 0.0) 198 | { 199 | rgbColor.r += 0.5; 200 | } 201 | else 202 | { 203 | if (projectionConfidence.x > 0.0) 204 | { 205 | rgbColor.g += projectionConfidence.x * 0.25; 206 | } 207 | if (projectionConfidence.y > 0.0) 208 | { 209 | rgbColor.b += projectionConfidence.y * 0.25; 210 | } 211 | } 212 | } 213 | else if (g_debugOverlay == 2) // Camera selection 214 | { 215 | rgbColor.g += finalFactor; 216 | } 217 | else if (g_debugOverlay == 5) // Discontinuity filtering 218 | { 219 | if (bIsDiscontinuityFiltered) 220 | { 221 | rgbColor.g += 1.0; 222 | } 223 | if (bIsCrossDiscontinuityFiltered) 224 | { 225 | rgbColor.b += 1.0; 226 | } 227 | } 228 | 229 | PS_Output output; 230 | 231 | rgbColor = g_bPremultiplyAlpha ? rgbColor * g_opacity : rgbColor; 232 | 233 | output.color = float4(rgbColor, g_opacity); 234 | output.depth = finalDepth; 235 | 236 | return output; 237 | } 238 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/fullscreen_passthrough_ps.hlsl: -------------------------------------------------------------------------------- 1 | 2 | #include "common_ps.hlsl" 3 | #include "vs_outputs.hlsl" 4 | #include "util.hlsl" 5 | #include "fullscreen_util.hlsl" 6 | 7 | 8 | SamplerState g_samplerState : register(s0); 9 | Texture2D g_cameraFrameTexture : register(t0); 10 | Texture2D g_fisheyeCorrectionTexture : register(t1); 11 | Texture2D g_cameraValidation : register(t2); 12 | Texture2D g_depthMap : register(t3); 13 | 14 | struct PS_Output 15 | { 16 | float4 color : SV_Target; 17 | float depth : SV_Depth; 18 | }; 19 | 20 | PS_Output main(VS_OUTPUT input) 21 | { 22 | float2 screenUvs = Remap(input.screenPos.xy, float2(-1.0, -1.0), float2(1.0, 1.0), float2(0.0, 1.0), float2(1.0, 0.0)); 23 | 24 | float depth = g_depthMap.Sample(g_samplerState, screenUvs); 25 | float4 cameraValidation = g_cameraValidation.Sample(g_samplerState, screenUvs); 26 | 27 | float projectionConfidence = cameraValidation.x; 28 | 29 | 30 | bool bIsDiscontinuityFiltered = false; 31 | 32 | [branch] 33 | if(g_depthContourStrength > 0) 34 | { 35 | depth = sobel_discontinuity_adjust(g_depthMap, g_samplerState, depth, screenUvs, bIsDiscontinuityFiltered); 36 | } 37 | 38 | float4 clipSpacePos = float4(input.screenPos.xy, depth, 1.0); 39 | 40 | float4 worldProjectionPos = mul(g_HMDProjectionToWorld, clipSpacePos); 41 | 42 | float4 cameraClipSpacePos = mul((g_cameraViewIndex == 0) ? g_worldToCameraFrameProjectionLeft : g_worldToCameraFrameProjectionRight, worldProjectionPos); 43 | 44 | if (g_bUseDepthCutoffRange) 45 | { 46 | float4 worldHMDEyePos = mul(g_HMDProjectionToWorld, float4(0, 0, 0, 1)); 47 | float depthMeters = distance(worldProjectionPos.xyz / worldProjectionPos.w, worldHMDEyePos.xyz / worldHMDEyePos.w); 48 | clip(depthMeters - g_depthCutoffRange.x); 49 | clip(g_depthCutoffRange.y - depthMeters); 50 | } 51 | 52 | // Convert from homogenous clip space coordinates to 0-1. 53 | float2 outUvs = Remap(cameraClipSpacePos.xy / cameraClipSpacePos.w, -1.0, 1.0, 0.0, 1.0); 54 | 55 | if (g_bClampCameraFrame) 56 | { 57 | clip(cameraClipSpacePos.z); 58 | clip(outUvs); 59 | clip(1 - outUvs); 60 | } 61 | 62 | float2 correction = 0; 63 | 64 | if (g_bUseFisheyeCorrection) 65 | { 66 | // Remap and clamp to frame UV bounds. 67 | outUvs = Remap(outUvs, 0.0, 1.0, g_uvBounds.xy, g_uvBounds.zw); 68 | outUvs = clamp(outUvs, g_uvBounds.xy, g_uvBounds.zw); 69 | 70 | correction = g_fisheyeCorrectionTexture.Sample(g_samplerState, outUvs); 71 | outUvs += correction; 72 | } 73 | else 74 | { 75 | // Remap and clamp to frame UV bounds. 76 | outUvs = Remap(outUvs, float2(0.0, 1.0), float2(1.0, 0.0), g_uvBounds.xy, g_uvBounds.zw); 77 | outUvs = clamp(outUvs, g_uvBounds.xy, g_uvBounds.zw); 78 | } 79 | 80 | float3 rgbColor = g_cameraFrameTexture.Sample(g_samplerState, outUvs).xyz; 81 | 82 | if (g_sharpness != 0.0) 83 | { 84 | float3 textureSize; 85 | g_cameraFrameTexture.GetDimensions(0, textureSize.x, textureSize.y, textureSize.z); 86 | rgbColor *= 1 + g_sharpness * 4; 87 | rgbColor -= g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(-1, 0) / textureSize.xy).xyz * g_sharpness; 88 | rgbColor -= g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(1, 0) / textureSize.xy).xyz * g_sharpness; 89 | rgbColor -= g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(0, -1) / textureSize.xy).xyz * g_sharpness; 90 | rgbColor -= g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(0, 1) / textureSize.xy).xyz * g_sharpness; 91 | } 92 | 93 | if (g_bDoColorAdjustment) 94 | { 95 | // Using CIELAB D65 to match the EXT_FB_passthrough adjustments. 96 | float3 labColor = LinearRGBtoLAB_D65(rgbColor); 97 | float LPrime = clamp((labColor.x - 50.0) * g_contrast + 50.0, 0.0, 100.0); 98 | float LBis = clamp(LPrime + g_brightness, 0.0, 100.0); 99 | float2 ab = labColor.yz * g_saturation; 100 | 101 | rgbColor = LABtoLinearRGB_D65(float3(LBis, ab.xy)); 102 | } 103 | 104 | if (g_bDebugDepth) 105 | { 106 | float debugDepth = pow(abs(depth), g_depthRange.y * 5.0); 107 | rgbColor = float3(debugDepth, debugDepth, debugDepth); 108 | } 109 | if (g_debugOverlay == 1) // Confidence 110 | { 111 | if (projectionConfidence < 0.0) 112 | { 113 | rgbColor.r += 0.5; 114 | } 115 | else if (projectionConfidence > 0.0) 116 | { 117 | rgbColor.g += projectionConfidence * 0.25; 118 | } 119 | else 120 | { 121 | rgbColor.b += 0.25; 122 | } 123 | } 124 | else if (g_debugOverlay == 5) // Discontinuity filtering 125 | { 126 | if (bIsDiscontinuityFiltered) 127 | { 128 | rgbColor.b += 1.0; 129 | } 130 | } 131 | 132 | PS_Output output; 133 | 134 | rgbColor = g_bPremultiplyAlpha ? rgbColor * g_opacity : rgbColor; 135 | 136 | output.color = float4(rgbColor, g_opacity); 137 | output.depth = depth; 138 | 139 | return output; 140 | } 141 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/fullscreen_passthrough_temporal_ps.hlsl: -------------------------------------------------------------------------------- 1 | 2 | #include "common_ps.hlsl" 3 | #include "vs_outputs.hlsl" 4 | #include "util.hlsl" 5 | #include "fullscreen_util.hlsl" 6 | 7 | 8 | SamplerState g_samplerState : register(s0); 9 | Texture2D g_cameraFrameTexture : register(t0); 10 | Texture2D g_fisheyeCorrectionTexture : register(t1); 11 | Texture2D g_cameraValidation : register(t2); 12 | Texture2D g_depthMap : register(t3); 13 | Texture2D g_prevCameraHistory : register(t4); 14 | 15 | RWTexture2D g_cameraHistory : register(u1); 16 | 17 | struct PS_Output 18 | { 19 | float4 color : SV_Target; 20 | float depth : SV_Depth; 21 | }; 22 | 23 | 24 | 25 | 26 | PS_Output main(VS_OUTPUT input) 27 | { 28 | float2 screenUvs = Remap(input.screenPos.xy, float2(-1.0, -1.0), float2(1.0, 1.0), float2(0.0, 1.0), float2(1.0, 0.0)); 29 | 30 | float depth = g_depthMap.Sample(g_samplerState, screenUvs); 31 | float4 cameraValidation = g_cameraValidation.Sample(g_samplerState, screenUvs); 32 | 33 | float projectionConfidence = cameraValidation.x; 34 | 35 | bool bIsDiscontinuityFiltered = false; 36 | 37 | [branch] 38 | if(g_depthContourStrength > 0) 39 | { 40 | depth = sobel_discontinuity_adjust(g_depthMap, g_samplerState, depth, screenUvs, bIsDiscontinuityFiltered); 41 | } 42 | 43 | float4 clipSpacePos = float4(input.screenPos.xy, depth, 1.0); 44 | float4 worldProjectionPos = mul(g_HMDProjectionToWorld, clipSpacePos); 45 | float4 cameraClipSpacePos = mul((g_cameraViewIndex == 0) ? g_worldToCameraFrameProjectionLeft : g_worldToCameraFrameProjectionRight, worldProjectionPos); 46 | 47 | float4 prevCameraFrameScreenPos = mul(g_prevCameraFrame_WorldToHMDProjection, worldProjectionPos); 48 | 49 | // Convert from homogenous clip space coordinates to 0-1. 50 | float2 outUvs = Remap(cameraClipSpacePos.xy / cameraClipSpacePos.w, -1.0, 1.0, 0.0, 1.0); 51 | 52 | if (g_bUseDepthCutoffRange) 53 | { 54 | float4 worldHMDEyePos = mul(g_HMDProjectionToWorld, float4(0, 0, 0, 1)); 55 | float depthMeters = distance(worldProjectionPos.xyz / worldProjectionPos.w, worldHMDEyePos.xyz / worldHMDEyePos.w); 56 | clip(depthMeters - g_depthCutoffRange.x); 57 | clip(g_depthCutoffRange.y - depthMeters); 58 | } 59 | 60 | float2 correction = 0; 61 | 62 | if (g_bUseFisheyeCorrection) 63 | { 64 | // Remap and clamp to frame UV bounds. 65 | outUvs = Remap(outUvs, 0.0, 1.0, g_uvBounds.xy, g_uvBounds.zw); 66 | outUvs = clamp(outUvs, g_uvBounds.xy, g_uvBounds.zw); 67 | 68 | correction = g_fisheyeCorrectionTexture.Sample(g_samplerState, outUvs); 69 | outUvs += correction; 70 | } 71 | else 72 | { 73 | // Remap and clamp to frame UV bounds. 74 | outUvs = Remap(outUvs, float2(0.0, 1.0), float2(1.0, 0.0), g_uvBounds.xy, g_uvBounds.zw); 75 | outUvs = clamp(outUvs, g_uvBounds.xy, g_uvBounds.zw); 76 | } 77 | 78 | float3 rgbColor = g_cameraFrameTexture.Sample(g_samplerState, outUvs).rgb; 79 | float3 minColor = rgbColor; 80 | float3 maxColor = rgbColor; 81 | 82 | float sharpness = g_sharpness + 0.5; 83 | 84 | rgbColor *= (1 + sharpness * 4); 85 | 86 | float2 cameraFrameRes; 87 | g_cameraFrameTexture.GetDimensions(cameraFrameRes.x, cameraFrameRes.y); 88 | 89 | float3 sample = g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(-1, 0) / cameraFrameRes).rgb; 90 | rgbColor -= sample * sharpness; 91 | minColor = min(minColor, sample); 92 | maxColor = max(maxColor, sample); 93 | 94 | sample = g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(1, 0) / cameraFrameRes).rgb; 95 | rgbColor -= sample * sharpness; 96 | minColor = min(minColor, sample); 97 | maxColor = max(maxColor, sample); 98 | 99 | sample = g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(0, -1) / cameraFrameRes).rgb; 100 | rgbColor -= sample * sharpness; 101 | minColor = min(minColor, sample); 102 | maxColor = max(maxColor, sample); 103 | 104 | sample = g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(0, 1) / cameraFrameRes).rgb; 105 | rgbColor -= sample * sharpness; 106 | minColor = min(minColor, sample); 107 | maxColor = max(maxColor, sample); 108 | 109 | float2 camTexCoords = outUvs * cameraFrameRes; 110 | uint2 camPixel = floor(camTexCoords); 111 | 112 | // How far the current pixel is to the sampled one 113 | float pixelDistanceFactor = abs(camTexCoords.x - camPixel.x - 0.5) + abs(camTexCoords.y - camPixel.y - 0.5); 114 | 115 | float2 historyTextureSize; 116 | g_prevCameraHistory.GetDimensions(historyTextureSize.x, historyTextureSize.y); 117 | 118 | float2 prevScreenUvs = Remap((prevCameraFrameScreenPos.xy / prevCameraFrameScreenPos.w), float2(-1.0, -1.0), float2(1.0, 1.0), float2(0.0, 1.0), float2(1.0, 0.0)); 119 | 120 | float2 prevTexCoords = prevScreenUvs * historyTextureSize; 121 | uint2 prevPixel = floor(prevTexCoords); 122 | 123 | float4 historyColor; 124 | 125 | [branch] 126 | if (g_temporalFilteringSampling == 1) 127 | { 128 | historyColor = g_prevCameraHistory.SampleLevel(g_samplerState, prevScreenUvs, 0); 129 | } 130 | else if (g_temporalFilteringSampling == 2) 131 | { 132 | historyColor = bicubic_b_spline_4tap(g_prevCameraHistory, g_samplerState, prevScreenUvs, historyTextureSize); 133 | } 134 | else if (g_temporalFilteringSampling == 3) 135 | { 136 | historyColor = catmull_rom_9tap(g_prevCameraHistory, g_samplerState, prevScreenUvs, historyTextureSize); 137 | } 138 | else if (g_temporalFilteringSampling == 4) 139 | { 140 | historyColor = lanczos2(g_prevCameraHistory, prevScreenUvs, historyTextureSize); 141 | } 142 | else 143 | { 144 | historyColor = g_prevCameraHistory.Load(uint3(prevPixel, 0)); 145 | } 146 | 147 | 148 | float prevPixelDistanceFactor = clamp((abs(prevTexCoords.x - prevPixel.x - 0.5) + abs(prevTexCoords.y - prevPixel.y - 0.5)), 0.05, 1) / 2.0; 149 | float combinedPixelDistanceBlend = lerp(pixelDistanceFactor, 1.0 - prevPixelDistanceFactor, g_temporalFilteringFactor); 150 | 151 | // If the current frame has a very close pixel, discard history. 152 | if(pixelDistanceFactor < 0.01) { combinedPixelDistanceBlend = 0; } 153 | 154 | float finalHistoryFactor = clamp(min(projectionConfidence * 5.0, combinedPixelDistanceBlend), 0.0, g_temporalFilteringFactor); 155 | 156 | if (projectionConfidence < 0.1 || bIsDiscontinuityFiltered) 157 | { 158 | finalHistoryFactor = 0; 159 | } 160 | 161 | // Clip history color to AABB of neighborhood color values + some configurable leeway. 162 | float3 historyColorClipped = min(maxColor * (1.0 + g_temporalFilteringColorRangeCutoff), max(historyColor.rgb, minColor * (1.0 - g_temporalFilteringColorRangeCutoff))); 163 | 164 | float clipFactor = any(historyColor.rgb - historyColorClipped) ? 1.0 : 0.0; 165 | 166 | if(historyColor.a > 0.0) 167 | { 168 | clipFactor = lerp(clipFactor, historyColor.a, finalHistoryFactor); 169 | } 170 | 171 | historyColor.rgb = lerp(historyColor.rgb, historyColorClipped, clipFactor); 172 | 173 | rgbColor = lerp(rgbColor, historyColor.rgb, finalHistoryFactor); 174 | 175 | 176 | if(g_bIsFirstRenderOfCameraFrame) 177 | { 178 | g_cameraHistory[floor(screenUvs * historyTextureSize)] = float4(rgbColor, clipFactor); 179 | } 180 | 181 | 182 | if (g_bDoColorAdjustment) 183 | { 184 | // Using CIELAB D65 to match the EXT_FB_passthrough adjustments. 185 | float3 labColor = LinearRGBtoLAB_D65(rgbColor); 186 | float LPrime = clamp((labColor.x - 50.0) * g_contrast + 50.0, 0.0, 100.0); 187 | float LBis = clamp(LPrime + g_brightness, 0.0, 100.0); 188 | float2 ab = labColor.yz * g_saturation; 189 | 190 | rgbColor = LABtoLinearRGB_D65(float3(LBis, ab.xy)); 191 | } 192 | 193 | if (g_bDebugDepth) 194 | { 195 | float debugDepth = pow(abs(depth), g_depthRange.y * 5.0); 196 | rgbColor = float3(debugDepth, debugDepth, debugDepth); 197 | } 198 | if (g_debugOverlay == 1) // Confidence 199 | { 200 | if (projectionConfidence < 0.0) 201 | { 202 | rgbColor.r += 0.5; 203 | } 204 | else if (projectionConfidence > 0.0) 205 | { 206 | rgbColor.g += projectionConfidence.x * 0.25; 207 | } 208 | else 209 | { 210 | rgbColor.b += 0.5; 211 | } 212 | } 213 | else if (g_debugOverlay == 3) // Temporal blend 214 | { 215 | if (finalHistoryFactor >= g_temporalFilteringFactor) 216 | { 217 | rgbColor.g += 1.0; 218 | } 219 | else if (finalHistoryFactor <= 0.0) 220 | { 221 | rgbColor.r += 1.0; 222 | } 223 | else 224 | { 225 | rgbColor.b += finalHistoryFactor; 226 | } 227 | } 228 | else if (g_debugOverlay == 4) // Temporal clipping 229 | { 230 | if(clipFactor >= 1.0) 231 | { 232 | rgbColor.r += 0.5; 233 | } 234 | else 235 | { 236 | rgbColor.b += clipFactor; 237 | } 238 | } 239 | else if (g_debugOverlay == 5) // Discontinuity filtering 240 | { 241 | if (bIsDiscontinuityFiltered) 242 | { 243 | rgbColor.g += 1.0; 244 | } 245 | } 246 | 247 | PS_Output output; 248 | 249 | rgbColor = g_bPremultiplyAlpha ? rgbColor * g_opacity : rgbColor; 250 | 251 | output.color = float4(rgbColor, g_opacity); 252 | output.depth = depth; 253 | 254 | return output; 255 | } 256 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/fullscreen_quad_vs.hlsl: -------------------------------------------------------------------------------- 1 | 2 | //#include "common_vs.hlsl" 3 | #include "vs_outputs.hlsl" 4 | 5 | VS_OUTPUT main(float3 inPosition : POSITION, uint vertexID : SV_VertexID) 6 | { 7 | VS_OUTPUT output; 8 | 9 | // Single triangle from (0, 0) to (2, 2). 10 | float2 clipCoords = float2((vertexID >> 1) * 2, (vertexID & 1) * 2) * 2 - 1; 11 | 12 | output.position = float4(clipCoords, 0, 1); 13 | 14 | output.cameraReprojectedPos = float4(clipCoords, 1, 1); 15 | output.screenPos = float4(clipCoords, 1, 1); 16 | output.projectionConfidence = 1; 17 | 18 | #ifdef VULKAN 19 | output.position.y *= -1.0; 20 | #endif 21 | 22 | output.cameraBlendConfidence = 1.0; 23 | output.crossCameraReprojectedPos = 0; 24 | output.prevHMDFrameScreenPos = 0; 25 | output.prevCameraFrameScreenPos = 0; 26 | output.prevCameraFrameVelocity = 0; 27 | output.cameraDepth = 0; 28 | 29 | return output; 30 | } 31 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/fullscreen_util.hlsl: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _FULLSCREEN_UTIL_INCLUDED 3 | #define _FULLSCREEN_UTIL_INCLUDED 4 | 5 | #include "common_ps.hlsl" 6 | #include "vs_outputs.hlsl" 7 | #include "util.hlsl" 8 | 9 | 10 | 11 | float gaussian(float2 value, float filterWidth) 12 | { 13 | return exp(-0.5 * dot(value /= (filterWidth * 2.0 * 0.25), value)) / 14 | (2.0 * PI * pow(filterWidth * 2.0 * 0.25, 2)); 15 | } 16 | 17 | // Finds depth map discontinuities using a Sobel filter, and moves each pixel either to the background or foreground 18 | // while generating a smooth contour. 19 | // Optionally uses a gaussian filter to smooth the input depth to a futher distance than bilinear sampling can handle. 20 | float sobel_discontinuity_adjust(in Texture2D depthTex, in SamplerState texSampler, in float depth, in float2 uvs, out bool bWasFiltered) 21 | { 22 | bWasFiltered = false; 23 | float outDepth = depth; 24 | 25 | uint texW, texH; 26 | depthTex.GetDimensions(texW, texH); 27 | float2 invTexSize = 1.0 / float2(texW, texH); 28 | 29 | float2 texturePos = saturate(uvs) * float2(texW, texH); 30 | uint2 pixelPos = floor(texturePos); 31 | 32 | float dispU = depthTex.Load(int3(pixelPos + uint2(0, -1), 0)); 33 | float dispD = depthTex.Load(int3(pixelPos + uint2(0, 1), 0)); 34 | float dispL = depthTex.Load(int3(pixelPos + uint2(-1, 0), 0)); 35 | float dispR = depthTex.Load(int3(pixelPos + uint2(1, 0), 0)); 36 | 37 | float dispUL = depthTex.Load(int3(pixelPos + uint2(-1, -1), 0)); 38 | float dispDL = depthTex.Load(int3(pixelPos + uint2(-1, 1), 0)); 39 | float dispUR = depthTex.Load(int3(pixelPos + uint2(1, -1), 0)); 40 | float dispDR = depthTex.Load(int3(pixelPos + uint2(1, 1), 0)); 41 | 42 | float filterX = dispUL + dispL * 2 + dispDL - dispUR - dispR * 2 - dispDR; 43 | float filterY = dispUL + dispU * 2 + dispUR - dispDL - dispD * 2 - dispDR; 44 | 45 | float minDepth = min(depth, min(dispU, min(dispD, min(dispL, min(dispR, min(dispUL, min(dispDL, min(dispUR, dispDR)))))))); 46 | float maxDepth = max(depth, max(dispU, max(dispD, max(dispL, max(dispR, max(dispUL, max(dispDL, max(dispUR, dispDR)))))))); 47 | 48 | float magnitude = length(float2(filterX, filterY)); 49 | 50 | if(magnitude > g_depthContourTreshold) 51 | { 52 | float totalWeight = 0; 53 | float smoothedDepth = depth; 54 | 55 | [branch] 56 | if (g_depthContourFilterWidth > 0) 57 | { 58 | smoothedDepth = 0; 59 | 60 | // Filter with an output pixel-centered gaussian blur to get a smooth contour over the low res depth map pixels. 61 | for (int x = -g_depthContourFilterWidth; x <= g_depthContourFilterWidth; x++) 62 | { 63 | for (int y = -g_depthContourFilterWidth; y <= g_depthContourFilterWidth; y++) 64 | { 65 | float weight = gaussian(float2(x, y), g_depthContourFilterWidth); 66 | totalWeight += weight; 67 | smoothedDepth += depthTex.SampleLevel(texSampler, uvs + float2(x, y) * invTexSize, 0) * weight; 68 | } 69 | } 70 | 71 | smoothedDepth /= totalWeight; 72 | } 73 | 74 | bool inForeground = ((maxDepth - smoothedDepth) > (smoothedDepth - minDepth)); 75 | 76 | float offsetFactor = saturate(g_depthContourStrength * 10.0 * magnitude); 77 | 78 | bWasFiltered = true; 79 | outDepth = lerp(depth, inForeground ? minDepth : maxDepth, offsetFactor); 80 | } 81 | return outDepth; 82 | } 83 | 84 | #endif //_FULLSCREEN_UTIL_INCLUDED 85 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/mesh_rigid_vs.hlsl: -------------------------------------------------------------------------------- 1 | 2 | #include "common_vs.hlsl" 3 | #include "vs_outputs.hlsl" 4 | 5 | 6 | cbuffer vsMeshConstantBuffer : register(b2) 7 | { 8 | float4x4 g_meshToWorldTransform; 9 | }; 10 | 11 | 12 | VS_OUTPUT main(float3 inPosition : POSITION, uint vertexID : SV_VertexID) 13 | { 14 | VS_OUTPUT output; 15 | 16 | float4 worldPos = mul(g_meshToWorldTransform, float4(inPosition, 1.0)); 17 | 18 | float4 clipSpacePos = mul((g_disparityUVBounds.x < 0.5) ? g_worldToCameraFrameProjectionLeft : g_worldToCameraFrameProjectionRight, worldPos); 19 | output.cameraReprojectedPos = clipSpacePos; 20 | 21 | output.position = mul(g_worldToHMDProjection, worldPos); 22 | output.screenPos = output.position; 23 | output.prevCameraFrameScreenPos = mul(g_prevCameraFrame_WorldToHMDProjection, worldPos); 24 | output.prevHMDFrameScreenPos = mul(g_prevHMDFrame_WorldToHMDProjection, worldPos); 25 | output.projectionConfidence = 1.0; 26 | 27 | float4 prevOutCoords = mul((g_disparityUVBounds.x < 0.5) ? g_worldToPrevCameraFrameProjectionLeft : g_worldToPrevCameraFrameProjectionRight, worldPos); 28 | 29 | output.prevCameraFrameVelocity = clipSpacePos.xyz / clipSpacePos.w - prevOutCoords.xyz / prevOutCoords.w; 30 | 31 | output.cameraBlendConfidence = 1.0; 32 | output.crossCameraReprojectedPos = 0; 33 | output.cameraDepth = 0; 34 | 35 | return output; 36 | } 37 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/passthrough_ps.hlsl: -------------------------------------------------------------------------------- 1 | 2 | #include "common_ps.hlsl" 3 | #include "vs_outputs.hlsl" 4 | #include "util.hlsl" 5 | 6 | 7 | #ifdef VULKAN 8 | 9 | SamplerState g_samplerState : register(s5); 10 | Texture2D g_cameraFrameTexture : register(t5); 11 | Texture2D g_fisheyeCorrectionTexture : register(t6); 12 | 13 | #else 14 | 15 | SamplerState g_samplerState : register(s0); 16 | Texture2D g_cameraFrameTexture : register(t0); 17 | Texture2D g_fisheyeCorrectionTexture : register(t1); 18 | 19 | #endif 20 | 21 | 22 | [earlydepthstencil] 23 | float4 main(VS_OUTPUT input) : SV_TARGET 24 | { 25 | float alpha = 1.0; 26 | 27 | if (g_doCutout) 28 | { 29 | clip(input.cameraBlendConfidence.x); 30 | alpha = saturate(input.cameraBlendConfidence.x); 31 | } 32 | 33 | if (g_bUseDepthCutoffRange) 34 | { 35 | float depth = (input.screenPos.z / input.screenPos.w);// * (g_depthRange.y - g_depthRange.x) + g_depthRange.x; 36 | clip(depth - g_depthCutoffRange.x); 37 | clip(g_depthCutoffRange.y - depth); 38 | } 39 | 40 | // Convert from homogenous clip space coordinates to 0-1. 41 | float2 outUvs = (input.cameraReprojectedPos.xy / input.cameraReprojectedPos.w) * float2(0.5, 0.5) + float2(0.5, 0.5); 42 | 43 | if (g_bClampCameraFrame) 44 | { 45 | clip(input.cameraReprojectedPos.z); 46 | clip(outUvs); 47 | clip(1 - outUvs); 48 | } 49 | 50 | float2 correction = 0; 51 | 52 | if (g_bUseFisheyeCorrection) 53 | { 54 | // Remap and clamp to frame UV bounds. 55 | outUvs = outUvs * (g_uvBounds.zw - g_uvBounds.xy) + g_uvBounds.xy; 56 | outUvs = clamp(outUvs, g_uvBounds.xy, g_uvBounds.zw); 57 | 58 | correction = g_fisheyeCorrectionTexture.Sample(g_samplerState, outUvs); 59 | outUvs += correction; 60 | } 61 | else 62 | { 63 | outUvs.y = 1 - outUvs.y; 64 | 65 | // Remap and clamp to frame UV bounds. 66 | outUvs = outUvs * (g_uvBounds.zw - g_uvBounds.xy) + g_uvBounds.xy; 67 | outUvs = clamp(outUvs, g_uvBounds.xy, g_uvBounds.zw); 68 | } 69 | 70 | float3 rgbColor = g_cameraFrameTexture.Sample(g_samplerState, outUvs).xyz; 71 | 72 | if (g_sharpness != 0.0) 73 | { 74 | float3 textureSize; 75 | g_cameraFrameTexture.GetDimensions(0, textureSize.x, textureSize.y, textureSize.z); 76 | rgbColor *= 1 + g_sharpness * 4; 77 | rgbColor -= g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(-1, 0) / textureSize.xy).xyz * g_sharpness; 78 | rgbColor -= g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(1, 0) / textureSize.xy).xyz * g_sharpness; 79 | rgbColor -= g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(0, -1) / textureSize.xy).xyz * g_sharpness; 80 | rgbColor -= g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(0, 1) / textureSize.xy).xyz * g_sharpness; 81 | } 82 | 83 | if (g_bDoColorAdjustment) 84 | { 85 | // Using CIELAB D65 to match the EXT_FB_passthrough adjustments. 86 | float3 labColor = LinearRGBtoLAB_D65(rgbColor); 87 | float LPrime = clamp((labColor.x - 50.0) * g_contrast + 50.0, 0.0, 100.0); 88 | float LBis = clamp(LPrime + g_brightness, 0.0, 100.0); 89 | float2 ab = labColor.yz * g_saturation; 90 | 91 | rgbColor = LABtoLinearRGB_D65(float3(LBis, ab.xy)); 92 | } 93 | 94 | if (g_bDebugDepth) 95 | { 96 | float depth = saturate((input.screenPos.z / input.screenPos.w) / (g_depthRange.y - g_depthRange.x) - g_depthRange.x); 97 | rgbColor = float3(depth, depth, depth); 98 | } 99 | if (g_debugOverlay == 1) // Confidence 100 | { 101 | if (input.projectionConfidence.x < 0.0) 102 | { 103 | rgbColor.r += 0.5; 104 | } 105 | else if (input.projectionConfidence.x > 0.0) 106 | { 107 | rgbColor.g += input.projectionConfidence.x * 0.25; 108 | } 109 | else 110 | { 111 | rgbColor.b += 0.25; 112 | } 113 | } 114 | else if (g_debugOverlay == 2) // Camera selection 115 | { 116 | if (!g_doCutout) 117 | { 118 | rgbColor.g += 1.0; 119 | } 120 | } 121 | 122 | 123 | rgbColor = g_bPremultiplyAlpha ? rgbColor * g_opacity * alpha : rgbColor; 124 | 125 | return float4(rgbColor, g_opacity * alpha); 126 | } 127 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/passthrough_read_depth_vs.hlsl: -------------------------------------------------------------------------------- 1 | 2 | #include "common_vs.hlsl" 3 | #include "vs_outputs.hlsl" 4 | #include "util.hlsl" 5 | 6 | SamplerState g_samplerState : register(s0); 7 | Texture2D g_depthMap : register(t0); 8 | Texture2D g_crossDepthMap : register(t1); 9 | Texture2D g_cameraValidation : register(t2); 10 | 11 | 12 | float2 sobel_discontinuity_contour(in Texture2D tex, inout float depth, in float2 uvs, in float confidence) 13 | { 14 | uint texW, texH; 15 | tex.GetDimensions(texW, texH); 16 | float2 invTexSize = 1.0 / float2(texW, texH); 17 | 18 | uint2 pixelPos = saturate(uvs) * float2(texW, texH); 19 | 20 | float dispU = tex.Load(int3(pixelPos + uint2(0, -1), 0)); 21 | float dispD = tex.Load(int3(pixelPos + uint2(0, 1), 0)); 22 | float dispL = tex.Load(int3(pixelPos + uint2(-1, 0), 0)); 23 | float dispR = tex.Load(int3(pixelPos + uint2(1, 0), 0)); 24 | 25 | float dispUL = tex.Load(int3(pixelPos + uint2(-1, -1), 0)); 26 | float dispDL = tex.Load(int3(pixelPos + uint2(-1, 1), 0)); 27 | float dispUR = tex.Load(int3(pixelPos + uint2(1, -1), 0)); 28 | float dispDR = tex.Load(int3(pixelPos + uint2(1, 1), 0)); 29 | 30 | float filterX = dispUL + dispL * 2 + dispDL - dispUR - dispR * 2 - dispDR; 31 | float filterY = dispUL + dispU * 2 + dispUR - dispDL - dispD * 2 - dispDR; 32 | 33 | float minDepth = min(depth, min(dispU, min(dispD, min(dispL, min(dispR, min(dispUL, min(dispDL, min(dispUR, dispDR)))))))); 34 | float maxDepth = max(depth, max(dispU, max(dispD, max(dispL, max(dispR, max(dispUL, max(dispDL, max(dispUR, dispDR)))))))); 35 | 36 | if((maxDepth - minDepth) > g_depthContourTreshold * 0.01) 37 | { 38 | float contourFactor = saturate(g_depthContourStrength * 10.0 * length(float2(filterX, filterY))); 39 | 40 | bool inForeground = ((maxDepth - depth) > (depth - minDepth)); 41 | 42 | depth = lerp(depth, inForeground ? minDepth : depth, contourFactor); 43 | 44 | float2 maxOffset = 1.0 * invTexSize; 45 | 46 | float2 offset = clamp((inForeground ? float2(filterX, filterY) : float2(-filterX, -filterY)) * maxOffset * g_depthContourStrength, -maxOffset, maxOffset); 47 | 48 | return lerp(float2(0, 0), offset, contourFactor); 49 | } 50 | return float2(0, 0); 51 | } 52 | 53 | 54 | VS_OUTPUT main(float3 inPosition : POSITION, uint vertexID : SV_VertexID) 55 | { 56 | VS_OUTPUT output; 57 | 58 | float depth = LoadTextureNearestClamped(g_depthMap, inPosition.xy); 59 | 60 | float4 cameraValidation = LoadTextureNearestClamped(g_cameraValidation, inPosition.xy); 61 | 62 | float2 projectionConfidence = cameraValidation.xy; 63 | float2 cameraBlendValidity = cameraValidation.zw; 64 | 65 | float crossDepth = depth; 66 | float activeDepth = depth; 67 | 68 | float2 vertexOffset = float2(0, 0); 69 | 70 | [branch] 71 | if(g_bBlendDepthMaps) 72 | { 73 | crossDepth = LoadTextureNearestClamped(g_crossDepthMap, inPosition.xy); 74 | 75 | bool selectMainCamera = cameraBlendValidity.x >= cameraBlendValidity.y; 76 | bool blendCameras = cameraBlendValidity.x > 0.1 && cameraBlendValidity.y > 0.1; 77 | 78 | float cameraBlendFactor = blendCameras ? (1 - saturate(cameraBlendValidity.x + 1 - cameraBlendValidity.y)) : (selectMainCamera ? 0.0 : 1.0); 79 | 80 | cameraBlendValidity = float2(cameraBlendFactor, 1 - cameraBlendFactor); 81 | 82 | [branch] 83 | if (projectionConfidence.x < 0.9 || projectionConfidence.y < 0.9) 84 | { 85 | // Smooth out mesh contours at discontinuities. 86 | vertexOffset = lerp(sobel_discontinuity_contour(g_depthMap, depth, inPosition.xy, projectionConfidence.x), 87 | sobel_discontinuity_contour(g_crossDepthMap, crossDepth, inPosition.xy, projectionConfidence.y), 88 | cameraBlendFactor); 89 | } 90 | 91 | activeDepth = lerp(depth, crossDepth, cameraBlendFactor); 92 | } 93 | else if (projectionConfidence.x < 0.9) 94 | { 95 | vertexOffset = sobel_discontinuity_contour(g_depthMap, activeDepth, inPosition.xy, projectionConfidence.x); 96 | } 97 | 98 | inPosition.xy += vertexOffset; 99 | float4 clipSpacePos = float4((inPosition.xy * float2(2.0, -2.0) + float2(-1, 1)), activeDepth, 1.0); 100 | 101 | float4 worldProjectionPos = mul(g_HMDProjectionToWorld, clipSpacePos); 102 | 103 | // Hack to get consistent homogenous coordinates, shouldn't matter for rendering but makes debugging the depth map easier. 104 | clipSpacePos = mul(g_worldToHMDProjection, worldProjectionPos / worldProjectionPos.w); 105 | 106 | 107 | float4 cameraClipSpacePos = mul((g_cameraViewIndex == 0) ? g_worldToCameraFrameProjectionLeft : g_worldToCameraFrameProjectionRight, worldProjectionPos); 108 | float4 cameraCrossClipSpacePos = mul((g_cameraViewIndex == 0) ? g_worldToCameraFrameProjectionRight : g_worldToCameraFrameProjectionLeft, worldProjectionPos); 109 | 110 | output.position = clipSpacePos; 111 | output.screenPos = clipSpacePos; 112 | output.screenPos.z *= output.screenPos.w; //Linearize depth 113 | 114 | output.projectionConfidence = projectionConfidence; 115 | output.cameraBlendConfidence = cameraBlendValidity; 116 | 117 | output.cameraReprojectedPos = cameraClipSpacePos; 118 | output.crossCameraReprojectedPos = cameraCrossClipSpacePos; 119 | output.prevHMDFrameScreenPos = mul(g_prevHMDFrame_WorldToHMDProjection, worldProjectionPos); 120 | output.prevCameraFrameScreenPos = mul(g_prevCameraFrame_WorldToHMDProjection, worldProjectionPos); 121 | 122 | output.cameraDepth = float2(depth, crossDepth); 123 | 124 | #ifndef VULKAN 125 | float4 prevOutCoords = mul((g_cameraViewIndex == 0) ? g_worldToPrevCameraFrameProjectionLeft : g_worldToPrevCameraFrameProjectionRight, worldProjectionPos); 126 | 127 | output.prevCameraFrameVelocity = cameraClipSpacePos.xyz / cameraClipSpacePos.w - prevOutCoords.xyz / prevOutCoords.w; 128 | #endif 129 | 130 | #ifdef VULKAN 131 | output.position.z *= 0.1; // Vulkan is currently fucky with depth. 132 | output.position.y *= -1.0; 133 | #endif 134 | 135 | return output; 136 | } 137 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/passthrough_stereo_composite_ps.hlsl: -------------------------------------------------------------------------------- 1 | 2 | #include "common_ps.hlsl" 3 | #include "vs_outputs.hlsl" 4 | #include "util.hlsl" 5 | 6 | 7 | SamplerState g_samplerState : register(s0); 8 | Texture2D g_cameraFrameTexture : register(t0); 9 | Texture2D g_fisheyeCorrectionTexture : register(t1); 10 | 11 | 12 | 13 | [earlydepthstencil] 14 | float4 main(VS_OUTPUT input) : SV_TARGET 15 | { 16 | float alpha = 1.0; 17 | 18 | if (g_doCutout) 19 | { 20 | clip(input.cameraBlendConfidence.x); 21 | alpha = saturate(input.projectionConfidence.x); 22 | } 23 | 24 | if (g_bUseDepthCutoffRange) 25 | { 26 | float depth = (input.screenPos.z / input.screenPos.w);// * (g_depthRange.y - g_depthRange.x) + g_depthRange.x; 27 | clip(depth - g_depthCutoffRange.x); 28 | clip(g_depthCutoffRange.y - depth); 29 | } 30 | 31 | // Convert from homogenous clip space coordinates to 0-1. 32 | float2 outUvs = (input.cameraReprojectedPos.xy / input.cameraReprojectedPos.w) * float2(0.5, 0.5) + float2(0.5, 0.5); 33 | float2 crossUvs = (input.crossCameraReprojectedPos.xy / input.crossCameraReprojectedPos.w) * float2(0.5, 0.5) + float2(0.5, 0.5); 34 | 35 | if (g_bClampCameraFrame) 36 | { 37 | clip(input.cameraReprojectedPos.z); 38 | clip(outUvs); 39 | clip(1 - outUvs); 40 | } 41 | 42 | float2 correction = 0; 43 | 44 | if (g_bUseFisheyeCorrection) 45 | { 46 | // Remap and clamp to frame UV bounds. 47 | outUvs = outUvs * (g_uvBounds.zw - g_uvBounds.xy) + g_uvBounds.xy; 48 | outUvs = clamp(outUvs, g_uvBounds.xy, g_uvBounds.zw); 49 | 50 | correction = g_fisheyeCorrectionTexture.Sample(g_samplerState, outUvs); 51 | outUvs += correction; 52 | 53 | crossUvs = crossUvs * (g_crossUVBounds.zw - g_crossUVBounds.xy) + g_crossUVBounds.xy; 54 | crossUvs = clamp(crossUvs, g_crossUVBounds.xy, g_crossUVBounds.zw); 55 | 56 | correction = g_fisheyeCorrectionTexture.Sample(g_samplerState, crossUvs); 57 | crossUvs += correction; 58 | } 59 | else 60 | { 61 | outUvs.y = 1 - outUvs.y; 62 | 63 | // Remap and clamp to frame UV bounds. 64 | outUvs = outUvs * (g_uvBounds.zw - g_uvBounds.xy) + g_uvBounds.xy; 65 | outUvs = clamp(outUvs, g_uvBounds.xy, g_uvBounds.zw); 66 | 67 | crossUvs = crossUvs * (g_crossUVBounds.zw - g_crossUVBounds.xy) + g_crossUVBounds.xy; 68 | crossUvs = clamp(crossUvs, g_crossUVBounds.xy, g_crossUVBounds.zw); 69 | } 70 | 71 | float3 rgbColor = g_cameraFrameTexture.Sample(g_samplerState, outUvs).xyz; 72 | 73 | float3 minColor = rgbColor; 74 | float3 maxColor = rgbColor; 75 | 76 | float3 crossRGBColor = g_cameraFrameTexture.Sample(g_samplerState, crossUvs).xyz; 77 | 78 | 79 | rgbColor *= (1 + g_sharpness * 4); 80 | 81 | float3 textureSize; 82 | g_cameraFrameTexture.GetDimensions(0, textureSize.x, textureSize.y, textureSize.z); 83 | float3 sample = g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(-1, 0) / textureSize.xy).xyz; 84 | rgbColor -= sample * g_sharpness; 85 | minColor = min(minColor, sample); 86 | maxColor = max(maxColor, sample); 87 | 88 | sample = g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(1, 0) / textureSize.xy).xyz; 89 | rgbColor -= sample * g_sharpness; 90 | minColor = min(minColor, sample); 91 | maxColor = max(maxColor, sample); 92 | 93 | sample = g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(0, -1) / textureSize.xy).xyz; 94 | rgbColor -= sample * g_sharpness; 95 | minColor = min(minColor, sample); 96 | maxColor = max(maxColor, sample); 97 | 98 | sample = g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(0, 1) / textureSize.xy).xyz; 99 | rgbColor -= sample * g_sharpness; 100 | minColor = min(minColor, sample); 101 | maxColor = max(maxColor, sample); 102 | 103 | crossRGBColor *= 1 + g_sharpness * 4; 104 | crossRGBColor -= g_cameraFrameTexture.Sample(g_samplerState, crossUvs + float2(-1, 0) / textureSize.xy).xyz * g_sharpness; 105 | crossRGBColor -= g_cameraFrameTexture.Sample(g_samplerState, crossUvs + float2(1, 0) / textureSize.xy).xyz * g_sharpness; 106 | crossRGBColor -= g_cameraFrameTexture.Sample(g_samplerState, crossUvs + float2(0, -1) / textureSize.xy).xyz * g_sharpness; 107 | crossRGBColor -= g_cameraFrameTexture.Sample(g_samplerState, crossUvs + float2(0, 1) / textureSize.xy).xyz * g_sharpness; 108 | 109 | 110 | float3 crossRGBColorClamped = min(maxColor, max(crossRGBColor, minColor)); 111 | 112 | uint texW, texH; 113 | g_cameraFrameTexture.GetDimensions(texW, texH); 114 | int2 cameraFrameRes = uint2(texW, texH); 115 | 116 | float2 camTexCoords = outUvs * cameraFrameRes; 117 | uint2 camPixel = floor(camTexCoords); 118 | 119 | float2 crossCamTexCoords = crossUvs * cameraFrameRes; 120 | uint2 crossCamPixel = floor(crossCamTexCoords); 121 | 122 | // How far the current pixel is to the sampled one 123 | float distanceFactor = abs(camTexCoords.x - camPixel.x - 0.5) + abs(camTexCoords.y - camPixel.y - 0.5); 124 | float crossDistanceFactor = abs(crossCamTexCoords.x - crossCamPixel.x - 0.5) + abs(crossCamTexCoords.y - crossCamPixel.y - 0.5); 125 | 126 | float pixelDistanceBlend = distanceFactor + (1 - crossDistanceFactor); 127 | 128 | float depthFactor = saturate(1 - (abs(input.cameraDepth.x - input.cameraDepth.y) * 1000)); 129 | 130 | float combineFactor = g_cutoutCombineFactor * depthFactor * input.projectionConfidence.x * input.projectionConfidence.y; 131 | 132 | 133 | float finalFactor = lerp(input.cameraBlendConfidence.x, pixelDistanceBlend, combineFactor); 134 | 135 | // Blend together both cameras based on which ones are valid and have the closest pixels. 136 | rgbColor = lerp(rgbColor, lerp(crossRGBColor, crossRGBColorClamped, combineFactor), finalFactor); 137 | 138 | if (g_bDoColorAdjustment) 139 | { 140 | // Using CIELAB D65 to match the EXT_FB_passthrough adjustments. 141 | float3 labColor = LinearRGBtoLAB_D65(rgbColor); 142 | float LPrime = clamp((labColor.x - 50.0) * g_contrast + 50.0, 0.0, 100.0); 143 | float LBis = clamp(LPrime + g_brightness, 0.0, 100.0); 144 | float2 ab = labColor.yz * g_saturation; 145 | 146 | rgbColor = LABtoLinearRGB_D65(float3(LBis, ab.xy)); 147 | } 148 | 149 | if (g_bDebugDepth) 150 | { 151 | float depth = saturate((input.screenPos.z / input.screenPos.w) / (g_depthRange.y - g_depthRange.x) - g_depthRange.x); 152 | rgbColor = float3(depth, depth, depth); 153 | } 154 | if (g_debugOverlay == 1) // Confidence 155 | { 156 | if (input.projectionConfidence.x < 0.0 && input.projectionConfidence.y < 0.0) 157 | { 158 | rgbColor.r += 0.5; 159 | } 160 | else 161 | { 162 | if (input.projectionConfidence.x > 0.0) 163 | { 164 | rgbColor.g += input.projectionConfidence.x * 0.25; 165 | } 166 | if (input.projectionConfidence.y > 0.0) 167 | { 168 | rgbColor.b += input.projectionConfidence.y * 0.25; 169 | } 170 | } 171 | } 172 | else if (g_debugOverlay == 2) // Camera selection 173 | { 174 | rgbColor.g += finalFactor; 175 | } 176 | 177 | 178 | rgbColor = g_bPremultiplyAlpha ? rgbColor * g_opacity * alpha : rgbColor; 179 | 180 | return float4(rgbColor, g_opacity * alpha); 181 | } 182 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/passthrough_stereo_vs.hlsl: -------------------------------------------------------------------------------- 1 | 2 | #include "common_vs.hlsl" 3 | #include "vs_outputs.hlsl" 4 | #include "util.hlsl" 5 | 6 | 7 | SamplerState g_samplerState : register(s0); 8 | Texture2D g_disparityTexture : register(t0); 9 | 10 | 11 | float gaussian(float2 value) 12 | { 13 | return exp(-0.5 * dot(value /= (g_disparityFilterWidth * 2 * 0.25), value)) / 14 | (2 * PI * pow(g_disparityFilterWidth * 2 * 0.25, 2)); 15 | } 16 | 17 | 18 | float4 DisparityToWorldCoords(float disparity, float2 clipCoords) 19 | { 20 | float2 texturePos = clipCoords * g_disparityTextureSize * float2(0.5, 1) * g_disparityDownscaleFactor; 21 | 22 | // Convert to int16 range with 4 bit fixed decimal: 65536 / 2 / 16 23 | float scaledDisp = disparity * 2048.0 * g_disparityDownscaleFactor; 24 | float4 viewSpaceCoords = mul(g_disparityToDepth, float4(texturePos, scaledDisp, 1.0)); 25 | viewSpaceCoords.y = 1 - viewSpaceCoords.y; 26 | viewSpaceCoords.z *= -1; 27 | viewSpaceCoords /= viewSpaceCoords.w; 28 | viewSpaceCoords.z = sign(viewSpaceCoords.z) * min(abs(viewSpaceCoords.z), g_projectionDistance); 29 | 30 | return mul((g_disparityUVBounds.x < 0.5) ? g_depthFrameViewToWorldLeft : g_depthFrameViewToWorldRight, viewSpaceCoords); 31 | } 32 | 33 | 34 | VS_OUTPUT main(float3 inPosition : POSITION, uint vertexID : SV_VertexID) 35 | { 36 | VS_OUTPUT output; 37 | 38 | float2 disparityUVs = inPosition.xy * (g_disparityUVBounds.zw - g_disparityUVBounds.xy) + g_disparityUVBounds.xy; 39 | uint3 uvPos = uint3(floor(disparityUVs * g_disparityTextureSize), 0); 40 | 41 | float2 dispConf = g_disparityTexture.SampleLevel(g_samplerState, disparityUVs, 0); 42 | 43 | // Disparity at the max projection distance 44 | float minDisparity = max(g_minDisparity, g_disparityToDepth[2][3] / 45 | (g_projectionDistance * 2048.0 * g_disparityDownscaleFactor * g_disparityToDepth[3][2])); 46 | 47 | float defaultDisparity = g_disparityToDepth[2][3] / 48 | (min(2.0, g_projectionDistance) * 2048.0 * g_disparityDownscaleFactor * g_disparityToDepth[3][2]); 49 | 50 | uint maxFilterWidth = max(g_disparityFilterWidth, (int)ceil(g_cutoutFilterWidth)); 51 | 52 | float disparity = clamp(dispConf.x, minDisparity, g_maxDisparity); 53 | float confidence = dispConf.y; 54 | 55 | output.projectionConfidence = confidence; 56 | output.cameraBlendConfidence = confidence; 57 | 58 | float2 disparityOffset = 0.0; 59 | 60 | if (dispConf.x > g_maxDisparity || dispConf.x < g_minDisparity) 61 | { 62 | disparity = defaultDisparity; 63 | output.projectionConfidence.x = -10000; 64 | output.cameraBlendConfidence.x = -10000; 65 | } 66 | // Prevent filtering if it would sample across the image edge 67 | else if (uvPos.x < maxFilterWidth || uvPos.x >= g_disparityTextureSize.x - maxFilterWidth || 68 | uvPos.y < maxFilterWidth || uvPos.y >= g_disparityTextureSize.y - maxFilterWidth) 69 | { 70 | output.projectionConfidence = 0; 71 | } 72 | else 73 | { 74 | // Sample neighboring pixels using a modified clamped Sobel filter, and mask out any areas with discontinuities. 75 | [branch] 76 | if (g_bFindDiscontinuities) 77 | { 78 | float2 fac = g_cutoutFilterWidth / g_disparityTextureSize; 79 | 80 | float dispU = g_disparityTexture.SampleLevel(g_samplerState, disparityUVs + float2(0, -1) * fac, 0).x; 81 | float dispD = g_disparityTexture.SampleLevel(g_samplerState, disparityUVs + float2(0, 1) * fac, 0).x; 82 | float dispL = g_disparityTexture.SampleLevel(g_samplerState, disparityUVs + float2(-1, 0) * fac, 0).x; 83 | float dispR = g_disparityTexture.SampleLevel(g_samplerState, disparityUVs + float2(1, 0) * fac, 0).x; 84 | 85 | float dispUL = g_disparityTexture.SampleLevel(g_samplerState, disparityUVs + float2(-1, -1) * fac, 0).x; 86 | float dispDL = g_disparityTexture.SampleLevel(g_samplerState, disparityUVs + float2(-1, 1) * fac, 0).x; 87 | float dispUR = g_disparityTexture.SampleLevel(g_samplerState, disparityUVs + float2(1, -1) * fac, 0).x; 88 | float dispDR = g_disparityTexture.SampleLevel(g_samplerState, disparityUVs + float2(1, 1) * fac, 0).x; 89 | 90 | // Clamp the max disparity tested to the sampled pixel disparity in order to not filter foreground pixels. 91 | float filterX = min(disparity, dispUL) + min(disparity, dispL) * 2 + min(disparity, dispDL) - min(disparity, dispUR) - min(disparity, dispR) * 2 - min(disparity, dispDR); 92 | 93 | float filterY = min(disparity, dispUL) + min(disparity, dispU) * 2 + min(disparity, dispUR) - min(disparity, dispDL) - min(disparity, dispD) * 2 - min(disparity, dispDR); 94 | 95 | float filter = sqrt(pow(filterX, 2) + pow(filterY, 2)); 96 | 97 | // Filter only the occluded side for camera selection. Assumes left and right cameras. 98 | float filterCamX = (g_disparityUVBounds.x < 0.5) ? 99 | max(0, min(disparity, dispUL) + min(disparity, dispL) * 2 + min(disparity, dispDL) - dispUR - dispR * 2 - dispDR) : 100 | min(0, dispUL + dispL * 2 + dispDL - min(disparity, dispUR) - min(disparity, dispR) * 2 - min(disparity, dispDR)); 101 | 102 | float filterCam = sqrt(pow(filterCamX, 2) + pow(filterY, 2)); 103 | 104 | // Output optimistic values for camera composition to only filter occlusions 105 | output.cameraBlendConfidence = (1 + g_cutoutOffset + confidence - 100.0 * g_cutoutFactor * filterCam) * g_cameraBlendWeight; 106 | 107 | // Output pessimistic values for depth temporal filter to force invalidation on movement 108 | output.projectionConfidence = min(confidence, 1 + g_cutoutOffset - 100.0 * g_cutoutFactor * filter); 109 | 110 | float dfilterX = dispUL + dispL * 2 + dispDL - dispUR - dispR * 2 - dispDR; 111 | float dfilterY = dispUL + dispU * 2 + dispUR - dispDL - dispD * 2 - dispDR; 112 | 113 | float minDisp = min(disparity, min(dispU, min(dispD, min(dispL, min(dispR, min(dispUL, min(dispDL, min(dispUR, dispDR)))))))); 114 | float maxDisp = max(disparity, max(dispU, max(dispD, max(dispL, max(dispR, max(dispUL, max(dispDL, max(dispUR, dispDR)))))))); 115 | 116 | 117 | if ((maxDisp - minDisp) > g_depthContourTreshold * (g_maxDisparity - minDisparity) * 0.01) 118 | { 119 | float contourFactor = saturate(g_depthContourStrength * 4 * length(float2(dfilterX, dfilterY))); 120 | 121 | bool inForeground = ((disparity - minDisp) > (maxDisp - disparity)); 122 | 123 | float2 maxOffset = 1.0 / g_disparityTextureSize; 124 | 125 | float2 offset = clamp((inForeground ? float2(-dfilterX, dfilterY) : float2(dfilterX, -dfilterY)) * maxOffset * g_depthContourStrength * 2, -maxOffset, maxOffset); 126 | 127 | disparityOffset = lerp(float2(0, 0), offset, contourFactor); 128 | } 129 | } 130 | 131 | // Filter any uncertain areas with a gaussian blur. 132 | [branch] 133 | if (g_disparityFilterWidth > 0 && confidence < 0.5) 134 | { 135 | float totalWeight = 0; 136 | float outDisp = 0; 137 | float disparityDelta = 0; 138 | 139 | for (int x = -g_disparityFilterWidth; x <= g_disparityFilterWidth; x++) 140 | { 141 | for (int y = -g_disparityFilterWidth; y <= g_disparityFilterWidth; y++) 142 | { 143 | float2 offset = float2(x, y) / (float) g_disparityTextureSize; 144 | float sampleDisp = g_disparityTexture.SampleLevel(g_samplerState, disparityUVs + offset, 0).x; 145 | float weight = gaussian(float2(x, y)); 146 | totalWeight += weight; 147 | outDisp += clamp(sampleDisp, minDisparity, g_maxDisparity) * weight; 148 | } 149 | } 150 | 151 | disparity = outDisp / totalWeight; 152 | } 153 | } 154 | 155 | float4 worldSpacePoint = DisparityToWorldCoords(disparity, inPosition.xy); 156 | 157 | // Clamp positions to floor height 158 | if ((worldSpacePoint.y / worldSpacePoint.w) < g_floorHeightOffset) 159 | { 160 | float3 ray = normalize(worldSpacePoint.xyz / worldSpacePoint.w - g_projectionOriginWorld); 161 | 162 | float num = (dot(float3(0, 1, 0), float3(0, g_floorHeightOffset, 0)) - dot(float3(0, 1, 0), g_projectionOriginWorld)); 163 | float denom = dot(float3(0, 1, 0), ray); 164 | 165 | if (denom < 0) 166 | { 167 | worldSpacePoint = float4(g_projectionOriginWorld + ray * num / denom, 1); 168 | } 169 | } 170 | 171 | output.position = mul(g_worldToHMDProjection, worldSpacePoint); 172 | output.position.xy += disparityOffset; 173 | 174 | output.screenPos = output.position; 175 | output.screenPos.z *= output.screenPos.w; //Linearize depth 176 | 177 | #ifndef VULKAN 178 | float4 outCoords = mul((g_cameraViewIndex == 0) ? g_worldToCameraFrameProjectionLeft : g_worldToCameraFrameProjectionRight, worldSpacePoint); 179 | output.cameraReprojectedPos = outCoords; 180 | 181 | float4 prevOutCoords = mul((g_cameraViewIndex == 0) ? g_worldToPrevCameraFrameProjectionLeft : g_worldToPrevCameraFrameProjectionRight, worldSpacePoint); 182 | 183 | output.prevCameraFrameScreenPos = mul(g_prevCameraFrame_WorldToHMDProjection, worldSpacePoint); 184 | output.prevHMDFrameScreenPos = mul(g_prevHMDFrame_WorldToHMDProjection, worldSpacePoint); 185 | 186 | output.prevCameraFrameVelocity = outCoords.xyz / outCoords.w - prevOutCoords.xyz / prevOutCoords.w; 187 | #endif 188 | 189 | #ifdef VULKAN 190 | output.position.y *= -1.0; 191 | #endif 192 | 193 | output.crossCameraReprojectedPos = 0; 194 | output.cameraDepth = 0; 195 | 196 | return output; 197 | } 198 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/passthrough_temporal_ps.hlsl: -------------------------------------------------------------------------------- 1 | 2 | #include "common_ps.hlsl" 3 | #include "vs_outputs.hlsl" 4 | #include "util.hlsl" 5 | 6 | 7 | SamplerState g_samplerState : register(s0); 8 | Texture2D g_cameraFrameTexture : register(t0); 9 | Texture2D g_fisheyeCorrectionTexture : register(t1); 10 | 11 | Texture2D g_prevCameraHistory : register(t2); 12 | RWTexture2D g_cameraHistory : register(u1); 13 | 14 | 15 | 16 | 17 | //[earlydepthstencil] 18 | float4 main(VS_OUTPUT input) : SV_TARGET 19 | { 20 | float alpha = 1.0; 21 | 22 | if (g_doCutout) 23 | { 24 | alpha = saturate(input.cameraBlendConfidence.x); 25 | clip(input.cameraBlendConfidence.x); 26 | } 27 | 28 | if (g_bUseDepthCutoffRange) 29 | { 30 | clip(input.screenPos.w - g_depthCutoffRange.x); 31 | clip(g_depthCutoffRange.y - input.screenPos.w); 32 | } 33 | 34 | // Convert from homogenous clip space coordinates to 0-1. 35 | float2 outUvs = (input.cameraReprojectedPos.xy / input.cameraReprojectedPos.w) * float2(0.5, 0.5) + float2(0.5, 0.5); 36 | 37 | if (g_bClampCameraFrame) 38 | { 39 | clip(input.cameraReprojectedPos.z); 40 | clip(outUvs); 41 | clip(1 - outUvs); 42 | } 43 | 44 | // Remap and clamp to frame UV bounds. 45 | outUvs = outUvs * (g_uvBounds.zw - g_uvBounds.xy) + g_uvBounds.xy; 46 | outUvs = clamp(outUvs, g_uvBounds.xy, g_uvBounds.zw); 47 | 48 | float2 correction = 0; 49 | 50 | if (g_bUseFisheyeCorrection) 51 | { 52 | correction = g_fisheyeCorrectionTexture.Sample(g_samplerState, outUvs); 53 | outUvs += correction; 54 | } 55 | else 56 | { 57 | outUvs.y = 1 - outUvs.y; 58 | } 59 | 60 | uint texW, texH; 61 | g_cameraFrameTexture.GetDimensions(texW, texH); 62 | int2 cameraFrameRes = uint2(texW, texH); 63 | 64 | g_prevCameraHistory.GetDimensions(texW, texH); 65 | int2 outputFrameRes = uint2(texW, texH); 66 | 67 | 68 | float3 rgbColor = 0; 69 | float3 minColor = 1; 70 | float3 maxColor = 0; 71 | 72 | float sharpness = g_sharpness + 0.5; 73 | 74 | float3 sample = g_cameraFrameTexture.Sample(g_samplerState, outUvs).xyz; 75 | 76 | minColor = sample; 77 | maxColor = sample; 78 | 79 | float dist = 1; 80 | 81 | rgbColor = sample * (1 + sharpness * 4); 82 | sample = g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(-dist, 0) / cameraFrameRes).xyz; 83 | rgbColor -= sample * sharpness; 84 | minColor = min(minColor, sample); 85 | maxColor = max(maxColor, sample); 86 | 87 | sample = g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(dist, 0) / cameraFrameRes).xyz; 88 | rgbColor -= sample * sharpness; 89 | minColor = min(minColor, sample); 90 | maxColor = max(maxColor, sample); 91 | 92 | sample = g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(0, -dist) / cameraFrameRes).xyz; 93 | rgbColor -= sample * sharpness; 94 | minColor = min(minColor, sample); 95 | maxColor = max(maxColor, sample); 96 | 97 | sample = g_cameraFrameTexture.Sample(g_samplerState, outUvs + float2(0, dist) / cameraFrameRes).xyz; 98 | rgbColor -= sample * sharpness; 99 | minColor = min(minColor, sample); 100 | maxColor = max(maxColor, sample); 101 | 102 | 103 | float2 prevScreenUvs = (input.prevCameraFrameScreenPos.xy / input.prevCameraFrameScreenPos.w) * float2(0.5, 0.5) + float2(0.5, 0.5); 104 | prevScreenUvs.y = 1 - prevScreenUvs.y; 105 | 106 | float2 newScreenUvs = (input.screenPos.xy / input.screenPos.w) * float2(0.5, 0.5) + float2(0.5, 0.5); 107 | newScreenUvs.y = 1 - newScreenUvs.y; 108 | 109 | float2 prevTexCoords = prevScreenUvs * outputFrameRes; 110 | uint2 prevPixel = floor(prevTexCoords); 111 | 112 | float4 filtered; 113 | 114 | [branch] 115 | if (g_temporalFilteringSampling == 0) 116 | { 117 | filtered = g_prevCameraHistory.Load(uint3(prevPixel, 0)); 118 | } 119 | else if (g_temporalFilteringSampling == 1) 120 | { 121 | filtered = g_prevCameraHistory.SampleLevel(g_samplerState, prevScreenUvs, 0); 122 | } 123 | else if (g_temporalFilteringSampling == 2) 124 | { 125 | filtered = bicubic_b_spline_4tap(g_prevCameraHistory, g_samplerState, prevScreenUvs, outputFrameRes); 126 | } 127 | else if (g_temporalFilteringSampling == 3) 128 | { 129 | filtered = catmull_rom_9tap(g_prevCameraHistory, g_samplerState, prevScreenUvs, outputFrameRes); 130 | } 131 | else 132 | { 133 | filtered = lanczos2(g_prevCameraHistory, prevScreenUvs, outputFrameRes); 134 | } 135 | 136 | //filtered.a = g_prevCameraHistory.Load(uint3(prevPixel, 0)).a; 137 | 138 | 139 | // Clip history color to AABB of neighborhood color values + some configurable leeway. 140 | 141 | float3 filteredClipped = min(maxColor * (1.0 + g_temporalFilteringColorRangeCutoff), max(filtered.xyz, minColor * (1.0 - g_temporalFilteringColorRangeCutoff))); 142 | 143 | // Flicker reduction attempt based on: Callum Glover - Temporal Anti Aliasing Implementation and Extensions 144 | // https://static1.squarespace.com/static/5a3beb72692ebe77330b5118/t/5c9d4f5be2c483f0c4108eca/1553813352302/report.pdf 145 | 146 | float isClipped = any(filtered.xyz - filteredClipped) ? 1 : 0; 147 | 148 | filtered.xyz = isClipped != 0 ? lerp(filtered.xyz, filteredClipped, filtered.a) : filtered.xyz; 149 | 150 | float2 camTexCoords = outUvs * cameraFrameRes; 151 | uint2 camPixel = floor(camTexCoords); 152 | 153 | // How far the current pixel is to the sampled one 154 | float confidenceInv = abs(camTexCoords.x - camPixel.x - 0.5) + abs(camTexCoords.y - camPixel.y - 0.5); 155 | 156 | float prevConfidenceInv = clamp((abs(prevTexCoords.x - prevPixel.x - 0.5) + abs(prevTexCoords.y - prevPixel.y - 0.5)), 157 | 0.05, 1); 158 | 159 | float vLenSq = dot(input.prevCameraFrameVelocity, input.prevCameraFrameVelocity); 160 | float factor = saturate(min(g_temporalFilteringFactor, 1 - vLenSq * 500)); 161 | float confidence = confidenceInv + (1 - prevConfidenceInv); 162 | 163 | float finalFactor = clamp(factor * confidence, 0, g_temporalFilteringFactor); 164 | 165 | rgbColor = lerp(rgbColor, filtered.xyz, finalFactor); 166 | 167 | float clipHistory = (filtered.a == 0) ? isClipped : lerp(isClipped, filtered.a, finalFactor); 168 | 169 | if(g_bIsFirstRenderOfCameraFrame) 170 | { 171 | g_cameraHistory[floor(newScreenUvs * outputFrameRes)] = float4(rgbColor, input.projectionConfidence.x >= 0 ? clipHistory : 1); 172 | } 173 | 174 | if (g_bDoColorAdjustment) 175 | { 176 | // Using CIELAB D65 to match the EXT_FB_passthrough adjustments. 177 | float3 labColor = LinearRGBtoLAB_D65(rgbColor); 178 | float LPrime = clamp((labColor.x - 50.0) * g_contrast + 50.0, 0.0, 100.0); 179 | float LBis = clamp(LPrime + g_brightness, 0.0, 100.0); 180 | float2 ab = labColor.yz * g_saturation; 181 | 182 | rgbColor = LABtoLinearRGB_D65(float3(LBis, ab.xy)); 183 | } 184 | 185 | if (g_bDebugDepth) 186 | { 187 | float depth = saturate((input.screenPos.z / input.screenPos.w) / (g_depthRange.y - g_depthRange.x) - g_depthRange.x); 188 | rgbColor = float3(depth, depth, depth); 189 | } 190 | if (g_debugOverlay == 1) // Confidence 191 | { 192 | if (input.projectionConfidence.x < 0.0) 193 | { 194 | rgbColor.r += 0.5; 195 | } 196 | else if (input.projectionConfidence.x > 0.0) 197 | { 198 | rgbColor.g += input.projectionConfidence.x * 0.25; 199 | } 200 | else 201 | { 202 | rgbColor.b += 0.25; 203 | } 204 | } 205 | else if (g_debugOverlay == 2) // Camera selection 206 | { 207 | if (!g_doCutout) 208 | { 209 | rgbColor.g += 1.0; 210 | } 211 | } 212 | else if (g_debugOverlay == 3) // Temporal blend 213 | { 214 | rgbColor.g += finalFactor; 215 | } 216 | else if (g_debugOverlay == 4) // Temporal clipping 217 | { 218 | rgbColor.b += clipHistory; 219 | } 220 | 221 | rgbColor = g_bPremultiplyAlpha ? rgbColor * g_opacity * alpha : rgbColor; 222 | 223 | return float4(rgbColor, g_opacity * alpha); 224 | } 225 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/passthrough_vs.hlsl: -------------------------------------------------------------------------------- 1 | 2 | #include "common_vs.hlsl" 3 | #include "vs_outputs.hlsl" 4 | 5 | 6 | VS_OUTPUT main(float3 inPosition : POSITION, uint vertexID : SV_VertexID) 7 | { 8 | VS_OUTPUT output; 9 | 10 | float heightOffset = min(g_floorHeightOffset, g_projectionOriginWorld.y); 11 | float4 worldProjectionPos = float4(inPosition, 1.0); 12 | worldProjectionPos.xz *= g_projectionDistance; 13 | worldProjectionPos.xz += g_projectionOriginWorld.xz; 14 | worldProjectionPos.y *= max(g_projectionDistance * 2.0, g_projectionOriginWorld.y + g_projectionDistance - heightOffset); 15 | worldProjectionPos.y += min(heightOffset, g_projectionOriginWorld.y - 0.1); 16 | 17 | float4 cameraClipSpacePos = mul((g_disparityUVBounds.x < 0.5) ? g_worldToCameraFrameProjectionLeft : g_worldToCameraFrameProjectionRight, worldProjectionPos); 18 | 19 | output.cameraReprojectedPos = cameraClipSpacePos; 20 | output.prevCameraFrameScreenPos = mul(g_prevCameraFrame_WorldToHMDProjection, worldProjectionPos); 21 | output.prevHMDFrameScreenPos = mul(g_prevHMDFrame_WorldToHMDProjection, worldProjectionPos); 22 | output.position = mul(g_worldToHMDProjection, worldProjectionPos); 23 | output.screenPos = output.position; 24 | output.projectionConfidence = 1.0; 25 | output.cameraBlendConfidence = 1.0; 26 | 27 | #ifndef VULKAN 28 | float4 prevOutCoords = mul((g_cameraViewIndex == 0) ? g_worldToPrevCameraFrameProjectionLeft : g_worldToPrevCameraFrameProjectionRight, worldProjectionPos); 29 | 30 | output.prevCameraFrameVelocity = cameraClipSpacePos.xyz / cameraClipSpacePos.w - prevOutCoords.xyz / prevOutCoords.w; 31 | #endif 32 | 33 | #ifdef VULKAN 34 | output.position.z *= 0.1; // Vulkan is currently fucky with depth. 35 | output.position.y *= -1.0; 36 | #endif 37 | 38 | output.crossCameraReprojectedPos = 0; 39 | output.cameraDepth = 0; 40 | 41 | return output; 42 | } 43 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/shaders/vs_outputs.hlsl: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _VS_OUTPUTS_INCLUDED 3 | #define _VS_OUTPUTS_INCLUDED 4 | 5 | struct VS_OUTPUT 6 | { 7 | float4 position : SV_POSITION; 8 | float4 screenPos : TEXCOORD0; 9 | float2 projectionConfidence : TEXCOORD1; 10 | float2 cameraBlendConfidence : TEXCOORD2; 11 | float4 cameraReprojectedPos : TEXCOORD3; 12 | float4 crossCameraReprojectedPos : TEXCOORD4; 13 | float4 prevHMDFrameScreenPos : TEXCOORD5; 14 | float4 prevCameraFrameScreenPos : TEXCOORD6; 15 | float3 prevCameraFrameVelocity : TEXCOORD7; 16 | float2 cameraDepth : TEXCOORD8; 17 | }; 18 | 19 | #endif //_VS_OUTPUTS_INCLUDED 20 | -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/testpattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rectus/openxr-steamvr-passthrough/7c11ecd44c311c78a9e9aed7e7fc1b94e7e03af6/XR_APILAYER_NOVENDOR_steamvr_passthrough/testpattern.png -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/testpattern_old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rectus/openxr-steamvr-passthrough/7c11ecd44c311c78a9e9aed7e7fc1b94e7e03af6/XR_APILAYER_NOVENDOR_steamvr_passthrough/testpattern_old.png -------------------------------------------------------------------------------- /XR_APILAYER_NOVENDOR_steamvr_passthrough/testpattern_vert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rectus/openxr-steamvr-passthrough/7c11ecd44c311c78a9e9aed7e7fc1b94e7e03af6/XR_APILAYER_NOVENDOR_steamvr_passthrough/testpattern_vert.png -------------------------------------------------------------------------------- /camera-calibration/camera-calibration.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "resource.h" 4 | -------------------------------------------------------------------------------- /camera-calibration/camera-calibration.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #define APSTUDIO_HIDDEN_SYMBOLS 11 | #include "windows.h" 12 | #undef APSTUDIO_HIDDEN_SYMBOLS 13 | 14 | ///////////////////////////////////////////////////////////////////////////// 15 | #undef APSTUDIO_READONLY_SYMBOLS 16 | 17 | ///////////////////////////////////////////////////////////////////////////// 18 | // English (United States) resources 19 | 20 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 21 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 22 | #pragma code_page(1252) 23 | 24 | ///////////////////////////////////////////////////////////////////////////// 25 | // 26 | // Icon 27 | // 28 | 29 | // Icon with lowest ID value placed first to ensure application icon 30 | // remains consistent on all systems. 31 | IDI_PASSTHROUGH_ICON ICON "..\\passthrough-setup\\passthrough_icon_opaque.ico" 32 | 33 | 34 | #ifdef APSTUDIO_INVOKED 35 | ///////////////////////////////////////////////////////////////////////////// 36 | // 37 | // TEXTINCLUDE 38 | // 39 | 40 | 1 TEXTINCLUDE 41 | BEGIN 42 | "resource.h\0" 43 | END 44 | 45 | 2 TEXTINCLUDE 46 | BEGIN 47 | "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" 48 | "#include ""windows.h""\r\n" 49 | "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" 50 | "\0" 51 | END 52 | 53 | 3 TEXTINCLUDE 54 | BEGIN 55 | "\r\n" 56 | "\0" 57 | END 58 | 59 | #endif // APSTUDIO_INVOKED 60 | 61 | #endif // English (United States) resources 62 | ///////////////////////////////////////////////////////////////////////////// 63 | 64 | 65 | ///////////////////////////////////////////////////////////////////////////// 66 | // Swedish (Sweden) resources 67 | 68 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_SVE) 69 | LANGUAGE LANG_SWEDISH, SUBLANG_SWEDISH 70 | #pragma code_page(1252) 71 | 72 | ///////////////////////////////////////////////////////////////////////////// 73 | // 74 | // Version 75 | // 76 | 77 | VS_VERSION_INFO VERSIONINFO 78 | FILEVERSION 0,3,3,0 79 | PRODUCTVERSION 0,3,3,0 80 | FILEFLAGSMASK 0x3fL 81 | #ifdef _DEBUG 82 | FILEFLAGS 0x1L 83 | #else 84 | FILEFLAGS 0x0L 85 | #endif 86 | FILEOS 0x40004L 87 | FILETYPE 0x1L 88 | FILESUBTYPE 0x0L 89 | BEGIN 90 | BLOCK "StringFileInfo" 91 | BEGIN 92 | BLOCK "040904b0" 93 | BEGIN 94 | VALUE "CompanyName", "N/A" 95 | VALUE "FileDescription", "Passthrough Camera Calibration Utility" 96 | VALUE "FileVersion", "0.3.3.0" 97 | VALUE "InternalName", "camera-calibration.exe" 98 | VALUE "LegalCopyright", "Copyright (C) Rectus 2025" 99 | VALUE "OriginalFilename", "camera-calibration.exe" 100 | VALUE "ProductName", "OpenXR SteamVR Passthrough API Layer" 101 | VALUE "ProductVersion", "0.3.3" 102 | END 103 | END 104 | BLOCK "VarFileInfo" 105 | BEGIN 106 | VALUE "Translation", 0x409, 1200 107 | END 108 | END 109 | 110 | #endif // Swedish (Sweden) resources 111 | ///////////////////////////////////////////////////////////////////////////// 112 | 113 | 114 | 115 | #ifndef APSTUDIO_INVOKED 116 | ///////////////////////////////////////////////////////////////////////////// 117 | // 118 | // Generated from the TEXTINCLUDE 3 resource. 119 | // 120 | 121 | 122 | ///////////////////////////////////////////////////////////////////////////// 123 | #endif // not APSTUDIO_INVOKED 124 | 125 | -------------------------------------------------------------------------------- /camera-calibration/camera-calibration.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {35f05883-9563-4ada-911e-506e27ec6269} 25 | cameracalibration 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | $(SolutionDir)\bin\$(Platform)\$(Configuration)\utils 75 | $(SolutionDir)\obj\camera-calibration\$(Platform)\$(Configuration)\ 76 | 77 | 78 | $(SolutionDir)\bin\$(Platform)\$(Configuration)\utils 79 | $(SolutionDir)\obj\camera-calibration\$(Platform)\$(Configuration)\ 80 | 81 | 82 | 83 | Level3 84 | true 85 | WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) 86 | true 87 | 88 | 89 | Windows 90 | true 91 | 92 | 93 | 94 | 95 | Level3 96 | true 97 | true 98 | true 99 | WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 100 | true 101 | 102 | 103 | Windows 104 | true 105 | true 106 | true 107 | 108 | 109 | 110 | 111 | Level3 112 | true 113 | _DEBUG;_WINDOWS;%(PreprocessorDefinitions) 114 | true 115 | stdcpp20 116 | $(SolutionDir)\external\imgui\backends;$(SolutionDir)\external\imgui;$(SolutionDir)\external\opencv\build\include;$(SolutionDir)\external\simpleini;$(SolutionDir)\external\openvr\headers 117 | 118 | 119 | Windows 120 | true 121 | d3d11.lib;d3dcompiler.lib;dxgi.lib;pathcch.lib;shlwapi.lib;mfplat.lib;mf.lib;mfreadwrite.lib;mfuuid.lib;opencv_world4100.lib;openvr_api64.lib;%(AdditionalDependencies) 122 | $(SolutionDir)\external\openvr\lib\win64;$(SolutionDir)\external\opencv\build\x64\vc16\lib;%(AdditionalLibraryDirectories) 123 | 124 | 125 | 126 | 127 | Level3 128 | true 129 | true 130 | true 131 | NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 132 | true 133 | stdcpp20 134 | $(SolutionDir)\external\imgui\backends;$(SolutionDir)\external\imgui;$(SolutionDir)\external\opencv\build\include;$(SolutionDir)\external\simpleini;$(SolutionDir)\external\openvr\headers 135 | 136 | 137 | Windows 138 | true 139 | true 140 | true 141 | d3d11.lib;d3dcompiler.lib;dxgi.lib;pathcch.lib;shlwapi.lib;mfplat.lib;mf.lib;mfreadwrite.lib;mfuuid.lib;opencv_world4100.lib;openvr_api64.lib;%(AdditionalDependencies) 142 | $(SolutionDir)\external\openvr\lib\win64;$(SolutionDir)\external\opencv\build\x64\vc16\lib;%(AdditionalLibraryDirectories) 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /camera-calibration/camera-calibration.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {04c810dc-81c8-4f79-bc72-ca933704c38d} 18 | 19 | 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Libraries 29 | 30 | 31 | Libraries 32 | 33 | 34 | Libraries 35 | 36 | 37 | Libraries 38 | 39 | 40 | Libraries 41 | 42 | 43 | 44 | 45 | Source Files 46 | 47 | 48 | Libraries 49 | 50 | 51 | Libraries 52 | 53 | 54 | Libraries 55 | 56 | 57 | Libraries 58 | 59 | 60 | Libraries 61 | 62 | 63 | Libraries 64 | 65 | 66 | Libraries 67 | 68 | 69 | 70 | 71 | Resource Files 72 | 73 | 74 | 75 | 76 | Resource Files 77 | 78 | 79 | -------------------------------------------------------------------------------- /camera-calibration/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by camera-calibration.rc 4 | // 5 | #define IDI_PASSTHROUGH_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NO_MFC 1 12 | #define _APS_NEXT_RESOURCE_VALUE 102 13 | #define _APS_NEXT_COMMAND_VALUE 32771 14 | #define _APS_NEXT_CONTROL_VALUE 1000 15 | #define _APS_NEXT_SYMED_VALUE 110 16 | #endif 17 | #endif 18 | -------------------------------------------------------------------------------- /openxr-steamvr-passthrough.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32929.385 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XR_APILAYER_NOVENDOR_steamvr_passthrough", "XR_APILAYER_NOVENDOR_steamvr_passthrough\XR_APILAYER_NOVENDOR_steamvr_passthrough.vcxproj", "{93D573D0-634F-4BA0-8FE0-FB63D7D00A05}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Files", "Solution Files", "{A53ED6CB-95D3-4833-8A16-C6A588F16F6E}" 9 | ProjectSection(SolutionItems) = preProject 10 | .gitattributes = .gitattributes 11 | .gitignore = .gitignore 12 | LICENSE = LICENSE 13 | EndProjectSection 14 | EndProject 15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{BA775DE9-671E-4E3B-92AC-9828C2AAF490}" 16 | ProjectSection(SolutionItems) = preProject 17 | scripts\Install-Layer.ps1 = scripts\Install-Layer.ps1 18 | scripts\Tracing.wprp = scripts\Tracing.wprp 19 | scripts\Uninstall-Layer.ps1 = scripts\Uninstall-Layer.ps1 20 | EndProjectSection 21 | EndProject 22 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "passthrough-setup", "passthrough-setup\passthrough-setup.vcxproj", "{22AEAC56-2B71-421B-95B6-8C54D9CBCD42}" 23 | EndProject 24 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "camera-calibration", "camera-calibration\camera-calibration.vcxproj", "{35F05883-9563-4ADA-911E-506E27EC6269}" 25 | EndProject 26 | Global 27 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 28 | Debug|x64 = Debug|x64 29 | Release|x64 = Release|x64 30 | EndGlobalSection 31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 32 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Debug|x64.ActiveCfg = Debug|x64 33 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Debug|x64.Build.0 = Debug|x64 34 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Release|x64.ActiveCfg = Release|x64 35 | {93D573D0-634F-4BA0-8FE0-FB63D7D00A05}.Release|x64.Build.0 = Release|x64 36 | {22AEAC56-2B71-421B-95B6-8C54D9CBCD42}.Debug|x64.ActiveCfg = Debug|x64 37 | {22AEAC56-2B71-421B-95B6-8C54D9CBCD42}.Debug|x64.Build.0 = Debug|x64 38 | {22AEAC56-2B71-421B-95B6-8C54D9CBCD42}.Release|x64.ActiveCfg = Release|x64 39 | {22AEAC56-2B71-421B-95B6-8C54D9CBCD42}.Release|x64.Build.0 = Release|x64 40 | {35F05883-9563-4ADA-911E-506E27EC6269}.Debug|x64.ActiveCfg = Debug|x64 41 | {35F05883-9563-4ADA-911E-506E27EC6269}.Debug|x64.Build.0 = Debug|x64 42 | {35F05883-9563-4ADA-911E-506E27EC6269}.Release|x64.ActiveCfg = Release|x64 43 | {35F05883-9563-4ADA-911E-506E27EC6269}.Release|x64.Build.0 = Release|x64 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(NestedProjects) = preSolution 49 | {BA775DE9-671E-4E3B-92AC-9828C2AAF490} = {A53ED6CB-95D3-4833-8A16-C6A588F16F6E} 50 | EndGlobalSection 51 | GlobalSection(ExtensibilityGlobals) = postSolution 52 | SolutionGuid = {07E77829-9766-4585-AC6C-0A28BA014E77} 53 | EndGlobalSection 54 | EndGlobal 55 | -------------------------------------------------------------------------------- /passthrough-setup/passthrough-setup.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "winres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // Swedish (Sweden) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_SVE) 19 | LANGUAGE LANG_SWEDISH, SUBLANG_SWEDISH 20 | #pragma code_page(1252) 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Version 51 | // 52 | 53 | VS_VERSION_INFO VERSIONINFO 54 | FILEVERSION 0,3,3,0 55 | PRODUCTVERSION 0,3,3,0 56 | FILEFLAGSMASK 0x3fL 57 | #ifdef _DEBUG 58 | FILEFLAGS 0x1L 59 | #else 60 | FILEFLAGS 0x0L 61 | #endif 62 | FILEOS 0x40004L 63 | FILETYPE 0x1L 64 | FILESUBTYPE 0x0L 65 | BEGIN 66 | BLOCK "StringFileInfo" 67 | BEGIN 68 | BLOCK "040904b0" 69 | BEGIN 70 | VALUE "CompanyName", "N/A" 71 | VALUE "FileDescription", "Passthrough API Layer Setup" 72 | VALUE "FileVersion", "0.3.3.0" 73 | VALUE "InternalName", "passthrough-setup.exe" 74 | VALUE "LegalCopyright", "Copyright (C) Rectus 2025" 75 | VALUE "OriginalFilename", "passthrough-setup.exe" 76 | VALUE "ProductName", "OpenXR SteamVR Passthrough API Layer" 77 | VALUE "ProductVersion", "0.3.3" 78 | END 79 | END 80 | BLOCK "VarFileInfo" 81 | BEGIN 82 | VALUE "Translation", 0x409, 1200 83 | END 84 | END 85 | 86 | 87 | ///////////////////////////////////////////////////////////////////////////// 88 | // 89 | // Icon 90 | // 91 | 92 | // Icon with lowest ID value placed first to ensure application icon 93 | // remains consistent on all systems. 94 | IDI_PASSTHROUGH_ICON ICON "passthrough_icon_opaque.ico" 95 | 96 | #endif // Swedish (Sweden) resources 97 | ///////////////////////////////////////////////////////////////////////////// 98 | 99 | 100 | 101 | #ifndef APSTUDIO_INVOKED 102 | ///////////////////////////////////////////////////////////////////////////// 103 | // 104 | // Generated from the TEXTINCLUDE 3 resource. 105 | // 106 | 107 | 108 | ///////////////////////////////////////////////////////////////////////////// 109 | #endif // not APSTUDIO_INVOKED 110 | 111 | -------------------------------------------------------------------------------- /passthrough-setup/passthrough-setup.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | 16.0 15 | Win32Proj 16 | {22aeac56-2b71-421b-95b6-8c54d9cbcd42} 17 | passthroughsetup 18 | 10.0 19 | 20 | 21 | 22 | Application 23 | true 24 | v143 25 | Unicode 26 | 27 | 28 | Application 29 | false 30 | v143 31 | true 32 | Unicode 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | $(SolutionDir)\obj\passthrough-setup\$(Platform)\$(Configuration)\ 48 | $(SolutionDir)\bin\$(Platform)\$(Configuration)\ 49 | 50 | 51 | $(SolutionDir)\bin\$(Platform)\$(Configuration)\ 52 | $(SolutionDir)\obj\passthrough-setup\$(Platform)\$(Configuration)\ 53 | 54 | 55 | 56 | Level3 57 | true 58 | _DEBUG;_WINDOWS;%(PreprocessorDefinitions) 59 | true 60 | stdcpp20 61 | $(SolutionDir)\external\imgui\backends;$(SolutionDir)\external\imgui;%(AdditionalIncludeDirectories) 62 | 63 | 64 | Windows 65 | true 66 | $(SolutionDir)\external\imgui\backends;%(AdditionalLibraryDirectories) 67 | d3d11.lib;d3dcompiler.lib;dxgi.lib;pathcch.lib;shlwapi.lib;%(AdditionalDependencies) 68 | RequireAdministrator 69 | 70 | 71 | 72 | 73 | Level3 74 | true 75 | true 76 | true 77 | NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 78 | true 79 | stdcpp20 80 | $(SolutionDir)\external\imgui\backends;$(SolutionDir)\external\imgui;%(AdditionalIncludeDirectories) 81 | 82 | 83 | Windows 84 | true 85 | true 86 | false 87 | d3d11.lib;d3dcompiler.lib;dxgi.lib;pathcch.lib;shlwapi.lib;%(AdditionalDependencies) 88 | $(SolutionDir)\external\imgui\backends;%(AdditionalLibraryDirectories) 89 | RequireAdministrator 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /passthrough-setup/passthrough-setup.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {8d90eec9-97a2-4585-8a95-162d60ed6541} 18 | 19 | 20 | 21 | 22 | Libraries 23 | 24 | 25 | Libraries 26 | 27 | 28 | Libraries 29 | 30 | 31 | Libraries 32 | 33 | 34 | Libraries 35 | 36 | 37 | Header Files 38 | 39 | 40 | 41 | 42 | Source Files 43 | 44 | 45 | Libraries 46 | 47 | 48 | Libraries 49 | 50 | 51 | Libraries 52 | 53 | 54 | Libraries 55 | 56 | 57 | Libraries 58 | 59 | 60 | Libraries 61 | 62 | 63 | Libraries 64 | 65 | 66 | 67 | 68 | Resource Files 69 | 70 | 71 | 72 | 73 | Resource Files 74 | 75 | 76 | -------------------------------------------------------------------------------- /passthrough-setup/passthrough_icon_opaque.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rectus/openxr-steamvr-passthrough/7c11ecd44c311c78a9e9aed7e7fc1b94e7e03af6/passthrough-setup/passthrough_icon_opaque.ico -------------------------------------------------------------------------------- /passthrough-setup/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by passthrough-setup.rc 4 | // 5 | #define IDI_PASSTHROUGH_ICON 104 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 105 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | OpenXR SteamVR Passthrough API Layer 2 | --- 3 | 4 | This OpenXR API layer adds camera passthrough (Mixed Reality) support to the SteamVR OpenXR runtime. It allows OpenXR applications that use the OpenXR passthrough feature to enable it when using the SteamVR runtime. 5 | 6 | The SteamVR runtime itself does not currently support any OpenXR passthrough features, but provides access to the camera video feeds and projection data through the proprietary OpenVR interface. This layer acts as a compositor in-between the application and runtime, retrieves the passthrough data from OpenVR (or a USB webcam), and renders it on the frames submitted by the application before passing them on to the runtime. 7 | 8 | Please report any issues! Any comments and suggestions are also appreciated. 9 | 10 | ### DISCLAIMER ### 11 | This is an experimental release. Please be careful when using the passthrough. 12 | 13 | This software is distributed as-is, without any warranties or conditions of any kind. Use at your own risk! 14 | 15 | Using the 3D stereo mode may induce heavy flickering on the display. Exercise caution if you are photosensitive. 16 | 17 | 18 | ### Features ### 19 | 20 | - Supports both application selectable environment blend modes in the OpenXR core specification: Alpha Blended and Additive. 21 | - Configuration menu available in the SteamVR dashboard. 22 | - User adjustable color parameters and opacity. 23 | - Override mode for applying passthrough to applications that do not support it. The passthrough view can be blended using chroma keying. 24 | - The floor projection height can be shifted up to get correct projection on an horizontal surface such as a desk. 25 | - EXPERIMENTAL: Supports 3D stereo reconstruction to estimate projection depth, using OpenCV. Includes support for Weighted Least Squares disparity filtering, and Fast Bilateral Solver filtering. 26 | - Supports custom fisheye lens rectification instead of using the OpenVR pre-rectified output. 27 | - Supports compositing the passthrough based on scene depth, for applications that supply depth buffers. 28 | - EXPERIMENTAL: Support for USB camera input. This can be used alone or in conjunction with the depth provided by a stereoscopic HMD camera. The camera can either be attached to a tracked SteamVR device, or be set up in a static position. Manual calibration is required. 29 | 30 | 31 | ### Limitations ### 32 | 33 | - Only the SteamVR runtime is supported. 34 | - Only headsets that provide the passthrough camera feed to SteamVR are supported. If the SteamVR Room View does not work, this will not work. 35 | - OpenVR applications are not supported. 36 | - Only applications that use the core specification passthrough by submitting frames with `environmentBlendMode` set are supported. 37 | - Applications using the Meta `XR_FB_passthrough` extension are not currently supported. 38 | - The default passthrough view only supports a fixed depth reconstruction, while clamping the projection depth to the floor plane of the playspace. This works the same as the the SteamVR 2D Room View mode. 39 | - The depth reconstruction from the 3D Room View is not supported. It is not currently accessible to developers. A custom depth reconstruction is used instead. 40 | - The passthrough view has higher latency than the SteamVR compositor. 41 | - OpenGL applications are not currently supported. 42 | - The Vulkan renderer interop does not support the the old XR_KHR_vulkan_enable extension. 43 | - Depth blending requires the application to submit depth buffers using the `XR_KHR_composition_layer_depth` extension, which only a few do. 44 | - USB webcam frames can not be accurately timed. This will cause hitching in the image, especially if the frame rate jitters. 45 | 46 | ### Supported Headsets ### 47 | 48 | - Valve Index - Supported 49 | - HTC Vive - Untested, driver only provides correct pose data in 60 Hz mode. 50 | - HTC Vive Pro - Untested 51 | - HTC Vive Pro 2 - Untested 52 | - Other HTC headsets - Unknown 53 | - Varjo XR headsets - Unknown 54 | - Windows Mixed Reality headsets - Unsupported, no passthrough support in driver 55 | - Oculus/Meta headsets - Unsupported, no passthrough support in driver 56 | - PSVR2 - Unsupported, no passthrough support in driver 57 | 58 | The SteamVR settings UI will misconfigure the original HTC Vive camera if the frame rate is not set the right way. To correctly set it, click the right end of the slider instead of dragging it. The USB drivers may need to be reset if the camera is incorrectly configured. 59 | 60 | The Valve Index camera poses may be poorly calibrated from the factory. The options menu allows adjusting the distance between cameras for better depth-perception when using the custom projection modes. 61 | 62 | 63 | ### Installation ### 64 | 65 | 1. Download and install the [Visual Studio C++ Redistributable (64 bit) ](https://aka.ms/vs/17/release/vc_redist.x64.exe) 66 | 2. Download the API layer release from the GitHub Releases page, and extract the files to the location you want to keep them in. 67 | 3. Run the `passthrough-setup.exe` utility, and select the Install option to install the API layer. Note that if you want to move the files, you will need to run the utility again. 68 | 4. If you want to disable or uninstall the API layer, run the `passthrough-setup.exe` utility and select the Uninstall option. 69 | 70 | 71 | ### Usage ### 72 | Starting an OpenXR application will automatically start the API layer. If the application natively supports passthrough, the API layer will by default notify the application that additional environment blend modes are available. If the application does not support the OpenXR passthrough features, it is still possible to enable limited passthrough modes (see below). 73 | 74 | While an application is running, the SteamVR dashboard will have an additional button in the bottom left, pressing it will open the settings overlay. 75 | 76 | ![Settings menu](https://github.com/Rectus/openxr-steamvr-passthrough/blob/main/settings_menu.png?raw=true) 77 | 78 | The options under the OpenXR Core allow setting what passthrough modes are available for the application to use, as well as what mode it should prefer. Some applications may automatically switch to the preferred mode even though they don't support passthrough. 79 | 80 | The options under Overrides allow forcing the passthrough mode regardless of whether the application has support for passthrough. Note that the Alpha Blend mode will show nothing unless the application submits alpha channels to the compositor. 81 | 82 | The Additive mode will blend the passthrough on top of the view. 83 | 84 | The Opaque mode will replace the application view with the passthrough. 85 | 86 | The Masked mode allows setting a chroma key color that gets replaced with the passthrough view, as well as range for how similar colors get replaced, and a smoothness of the transition. 87 | 88 | The settings can also be edited from `%APPDATA%\OpenXR SteamVR Passthrough\config.ini` 89 | 90 | See the project [Wiki](https://github.com/Rectus/openxr-steamvr-passthrough/wiki) for more information. 91 | 92 | ### Building from source ### 93 | The following are required: 94 | - Visual Studio 2022 95 | - The MSVC build tools, and the Windows 10 SDK (installed via the Visual Studio Installer as "Desktop development with C++"). 96 | - Python 3 interpreter (installed via the Visual Studio Installer or externally available in your PATH). 97 | - [OpenXR SDK Source](https://github.com/KhronosGroup/OpenXR-SDK-Source) (Included as Git submodule) 98 | - [OpenXR SDK](https://github.com/KhronosGroup/OpenXR-SDK) (Included as Git submodule) 99 | - [OpenVR](https://github.com/ValveSoftware/openvr) (Included as Git submodule, the project is setup for static linking by default - requires custom source build) 100 | - [LodePNG](https://github.com/lvandeve/lodepng) (Included as Git submodule) 101 | - [SimpleINI](https://github.com/brofield/simpleini) (Included as Git submodule) 102 | - [Dear ImGui](https://github.com/ocornut/imgui) (Included as Git submodule) 103 | - [Vulkan SDK](https://vulkan.lunarg.com/sdk/home) (Uses the VULKAN_SDK environment variable) 104 | - [OpenCV 4.10.0](https://github.com/opencv/opencv) (The project is setup for static linking by default - requires custom source build) 105 | - [OpenCV-Contrib](https://github.com/opencv/opencv_contrib) (The ximgproc module needs to be built along with OpenCV for WLS and FBS filtering support.) 106 | 107 | ### Possible improvements ### 108 | 109 | - Add partial support for the `XR_FB_passthrough` extension 110 | - OpenGL support 111 | - Add edge shader modes 112 | - Hand depth projection + masking on 3D mode 113 | - Improvements to 3D reconstruction 114 | - `XR_HTC_passthrough` extension support (no headsets or applications use this yet) 115 | - Linux support (does passthrough work on Linux?) 116 | - Passthrough override support for OpenVR apps (better as independent application) 117 | 118 | 119 | 120 | ### Notes ### 121 | Based on [OpenXR-Layer-Template](https://github.com/mbucchia/OpenXR-Layer-Template) 122 | 123 | -------------------------------------------------------------------------------- /scripts/Install-Layer.ps1: -------------------------------------------------------------------------------- 1 | $RegistryPath = "HKLM:\Software\Khronos\OpenXR\1\ApiLayers\Implicit" 2 | $JsonPath = Join-Path "$PSScriptRoot" "XR_APILAYER_NOVENDOR_steamvr_passthrough.json" 3 | Start-Process -FilePath powershell.exe -Verb RunAs -Wait -ArgumentList @" 4 | & { 5 | If (-not (Test-Path $RegistryPath)) { 6 | New-Item -Path $RegistryPath -Force | Out-Null 7 | } 8 | New-ItemProperty -Path $RegistryPath -Name '$jsonPath' -PropertyType DWord -Value 0 -Force | Out-Null 9 | } 10 | "@ 11 | -------------------------------------------------------------------------------- /scripts/Tracing.wprp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /scripts/Uninstall-Layer.ps1: -------------------------------------------------------------------------------- 1 | $JsonPath = Join-Path "$PSScriptRoot" "XR_APILAYER_NOVENDOR_steamvr_passthrough.json" 2 | Start-Process -FilePath powershell.exe -Verb RunAs -Wait -ArgumentList @" 3 | & { 4 | Remove-ItemProperty -Path HKLM:\Software\Khronos\OpenXR\1\ApiLayers\Implicit -Name '$jsonPath' -Force | Out-Null 5 | } 6 | "@ 7 | -------------------------------------------------------------------------------- /settings_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rectus/openxr-steamvr-passthrough/7c11ecd44c311c78a9e9aed7e7fc1b94e7e03af6/settings_menu.png --------------------------------------------------------------------------------