├── .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 | 
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
--------------------------------------------------------------------------------