Skip to content

Commit a1b28e7

Browse files
Apply anti-aliasing in VR mode (OpenXR) (#91)
This reduces jagged edges in the VR view by using MSAA anti-aliasing in VR mode. Implemented with assistance from ChatGPT as an experiment!
1 parent 2b45ca8 commit a1b28e7

2 files changed

Lines changed: 125 additions & 42 deletions

File tree

src/VRInterface.cpp

Lines changed: 115 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,19 @@ int VRInterface::load(SimulationModel* model) {
198198
std::cout << "glDeleteRenderbuffers not available" << std::endl;
199199
return 1;
200200
}
201+
202+
glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)wglGetProcAddress("glRenderbufferStorageMultisample");
203+
if (glRenderbufferStorageMultisample == 0) {
204+
std::cout << "glRenderbufferStorageMultisample not available" << std::endl;
205+
return 1;
206+
}
207+
208+
glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)wglGetProcAddress("glBlitFramebuffer");
209+
if (glBlitFramebuffer == 0) {
210+
std::cout << "glBlitFramebuffer not available" << std::endl;
211+
return 1;
212+
}
213+
201214
#elif defined __linux__
202215
// glXGetProcAddress never returns Null, so no point in checking
203216
glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)glXGetProcAddress((const GLubyte *)"glGenFramebuffers");
@@ -210,6 +223,8 @@ int VRInterface::load(SimulationModel* model) {
210223
glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)glXGetProcAddress((const GLubyte *)"glFramebufferRenderbuffer");
211224
glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)glXGetProcAddress((const GLubyte *)"glDeleteFramebuffers");
212225
glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)glXGetProcAddress((const GLubyte *)"glDeleteRenderbuffers");
226+
glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)glXGetProcAddress((const GLubyte*)"glRenderbufferStorageMultisample");
227+
glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)glXGetProcAddress((const GLubyte*)"glBlitFramebuffer");
213228
#endif
214229

215230
// Changing to HANDHELD_DISPLAY or a future form factor may work, but has not been tested.
@@ -231,7 +246,6 @@ int VRInterface::load(SimulationModel* model) {
231246

232247
// each graphics API requires the use of a specialized struct
233248
#ifdef _WIN32
234-
//XrGraphicsBindingOpenGLWin32KHR graphics_binding_gl = { 0 };
235249
XrGraphicsBindingOpenGLWin32KHR graphics_binding_gl;
236250
graphics_binding_gl.type = XR_TYPE_UNKNOWN;
237251
graphics_binding_gl.next = NULL;
@@ -316,7 +330,6 @@ int VRInterface::load(SimulationModel* model) {
316330
return 1;
317331
}
318332

319-
//XrExtensionProperties* ext_props = malloc(sizeof(XrExtensionProperties) * ext_count);
320333
XrExtensionProperties* ext_props = new XrExtensionProperties[ext_count];
321334

322335
for (uint16_t i = 0; i < ext_count; i++) {
@@ -344,7 +357,6 @@ int VRInterface::load(SimulationModel* model) {
344357
depth.supported = true;
345358
}
346359
}
347-
//free(ext_props);
348360
delete[] ext_props;
349361

350362
// A graphics extension like OpenGL is required to draw anything in VR
@@ -412,7 +424,6 @@ int VRInterface::load(SimulationModel* model) {
412424
if (!xr_check(instance, result, "Failed to get view configuration view count!"))
413425
return 1;
414426

415-
//viewconfig_views = malloc(sizeof(XrViewConfigurationView) * view_count);
416427
viewconfig_views = new XrViewConfigurationView[view_count];
417428

418429
for (uint32_t i = 0; i < view_count; i++) {
@@ -506,11 +517,8 @@ int VRInterface::load(SimulationModel* model) {
506517
// --- Create swapchain for main VR rendering
507518

508519
// In the frame loop we render into OpenGL textures we receive from the runtime here.
509-
//swapchains = malloc(sizeof(XrSwapchain) * view_count);
510520
swapchains = new XrSwapchain[view_count];
511-
//swapchain_lengths = malloc(sizeof(uint32_t) * view_count);
512521
swapchain_lengths = new uint32_t[view_count];
513-
//images = malloc(sizeof(XrSwapchainImageOpenGLKHR*) * view_count);
514522
images = new XrSwapchainImageOpenGLKHR*[view_count];
515523
for (uint32_t i = 0; i < view_count; i++) {
516524
XrSwapchainCreateInfo swapchain_create_info;
@@ -536,7 +544,6 @@ int VRInterface::load(SimulationModel* model) {
536544
if (!xr_check(instance, result, "Failed to enumerate swapchains"))
537545
return 1;
538546

539-
//images[i] = malloc(sizeof(XrSwapchainImageOpenGLKHR) * swapchain_lengths[i]);
540547
images[i] = new XrSwapchainImageOpenGLKHR[swapchain_lengths[i]];
541548
for (uint32_t j = 0; j < swapchain_lengths[i]; j++) {
542549
images[i][j].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR;
@@ -551,19 +558,89 @@ int VRInterface::load(SimulationModel* model) {
551558
// Store image width and height, assumed to be same for both views
552559
swapchainImageWidth = swapchain_create_info.width;
553560
swapchainImageHeight = swapchain_create_info.height;
561+
}
554562

563+
// Create framebuffers
564+
framebuffers = new GLuint * [view_count];
565+
depthbuffers = new GLuint * [view_count];
566+
for (uint32_t i = 0; i < view_count; i++) {
567+
framebuffers[i] = new GLuint[swapchain_lengths[i]];
568+
depthbuffers[i] = new GLuint[swapchain_lengths[i]];
569+
}
570+
571+
for (uint32_t i = 0; i < view_count; i++) {
572+
// ---------------------------------------------------------
573+
// Create per-swapchain-image FBO (attach once!)
574+
// ---------------------------------------------------------
575+
for (uint32_t j = 0; j < swapchain_lengths[i]; ++j)
576+
{
577+
glGenFramebuffers(1, &framebuffers[i][j]);
578+
glBindFramebuffer(GL_FRAMEBUFFER, framebuffers[i][j]);
579+
580+
glFramebufferTexture2D(
581+
GL_FRAMEBUFFER,
582+
GL_COLOR_ATTACHMENT0,
583+
GL_TEXTURE_2D,
584+
images[i][j].image,
585+
0
586+
);
587+
588+
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
589+
std::cerr << "Swapchain FBO incomplete!" << std::endl;
590+
}
591+
}
592+
593+
glBindFramebuffer(GL_FRAMEBUFFER, 0);
594+
595+
// ---------------------------------------------------------
596+
// Create MSAA framebuffer for this eye
597+
// ---------------------------------------------------------
598+
int width = viewconfig_views[i].recommendedImageRectWidth;
599+
int height = viewconfig_views[i].recommendedImageRectHeight;
600+
601+
glGenFramebuffers(1, &msaaFBO[i]);
602+
glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO[i]);
603+
604+
// --- Multisample color ---
605+
glGenRenderbuffers(1, &msaaColor[i]);
606+
glBindRenderbuffer(GL_RENDERBUFFER, msaaColor[i]);
607+
glRenderbufferStorageMultisample(
608+
GL_RENDERBUFFER,
609+
MSAA_SAMPLES,
610+
color_format,
611+
width,
612+
height
613+
);
614+
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
615+
GL_RENDERBUFFER, msaaColor[i]);
616+
617+
// --- Multisample depth ---
618+
glGenRenderbuffers(1, &msaaDepth[i]);
619+
glBindRenderbuffer(GL_RENDERBUFFER, msaaDepth[i]);
620+
glRenderbufferStorageMultisample(
621+
GL_RENDERBUFFER,
622+
MSAA_SAMPLES,
623+
GL_DEPTH24_STENCIL8,
624+
width,
625+
height
626+
);
627+
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
628+
GL_RENDERBUFFER, msaaDepth[i]);
629+
630+
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
631+
std::cerr << "MSAA FBO incomplete!" << std::endl;
632+
}
633+
634+
glBindFramebuffer(GL_FRAMEBUFFER, 0);
555635
}
556636

557637
// Do not allocate these every frame to save some resources
558-
//views = (XrView*)malloc(sizeof(XrView) * view_count);
559638
views = new XrView[view_count];
560639
for (uint32_t i = 0; i < view_count; i++) {
561640
views[i].type = XR_TYPE_VIEW;
562641
views[i].next = NULL;
563642
}
564643

565-
//projection_views = (XrCompositionLayerProjectionView*)malloc(
566-
// sizeof(XrCompositionLayerProjectionView) * view_count);
567644
projection_views = new XrCompositionLayerProjectionView[view_count];
568645
for (uint32_t i = 0; i < view_count; i++) {
569646
projection_views[i].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
@@ -581,15 +658,7 @@ int VRInterface::load(SimulationModel* model) {
581658
// projection_views[i].{pose, fov} have to be filled every frame in frame loop
582659
};
583660

584-
// Create framebuffers
585-
framebuffers = new GLuint* [view_count];
586-
depthbuffers = new GLuint * [view_count];
587-
for (uint32_t i = 0; i < view_count; i++) {
588-
framebuffers[i] = new GLuint[swapchain_lengths[i]];
589-
depthbuffers[i] = new GLuint[swapchain_lengths[i]];
590-
glGenFramebuffers(swapchain_lengths[i], framebuffers[i]);
591-
glGenRenderbuffers(swapchain_lengths[i], depthbuffers[i]);
592-
}
661+
593662

594663
// Set up controllers
595664
// --- Set up input (actions)
@@ -1347,29 +1416,34 @@ int VRInterface::update() {
13471416
int w = viewconfig_views[i].recommendedImageRectWidth;
13481417
int h = viewconfig_views[i].recommendedImageRectHeight;
13491418

1350-
// Render into swapchain images here (for left or right eye), into images[i][acquired_index].image
1351-
glBindFramebuffer(GL_FRAMEBUFFER, framebuffers[i][acquired_index]);
1419+
// ---------------------------------------------------------
1420+
// 1) Render scene into MSAA buffer
1421+
// ---------------------------------------------------------
1422+
glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO[i]);
13521423

1353-
glBindRenderbuffer(GL_RENDERBUFFER, depthbuffers[i][acquired_index]);
1354-
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w, h);
1355-
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthbuffers[1][acquired_index]);
1356-
13571424
glViewport(0, 0, w, h);
1358-
glScissor(0, 0, w, h);
1359-
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, images[i][acquired_index].image, 0);
1360-
glClearColor(0.0f, 0.0f, 0.2f, 1.0f);
1425+
13611426
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1362-
1363-
// Check
1364-
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
1365-
std::cout << "glCheckFramebufferStatus: " << glCheckFramebufferStatus(GL_FRAMEBUFFER) << std::endl;
1366-
}
1367-
1368-
// Render
13691427
smgr->drawAll();
13701428

1371-
// Return to framebuffer
1372-
glBindFramebuffer(GL_FRAMEBUFFER, 0);
1429+
// ---------------------------------------------------------
1430+
// 2) Resolve MSAA -> swapchain image
1431+
// ---------------------------------------------------------
1432+
1433+
glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFBO[i]);
1434+
glBindFramebuffer(GL_DRAW_FRAMEBUFFER,
1435+
framebuffers[i][acquired_index]);
1436+
1437+
glBlitFramebuffer(
1438+
0, 0,
1439+
viewconfig_views[i].recommendedImageRectWidth,
1440+
viewconfig_views[i].recommendedImageRectHeight,
1441+
0, 0,
1442+
viewconfig_views[i].recommendedImageRectWidth,
1443+
viewconfig_views[i].recommendedImageRectHeight,
1444+
GL_COLOR_BUFFER_BIT,
1445+
GL_NEAREST
1446+
);
13731447

13741448
XrSwapchainImageReleaseInfo release_info;
13751449
release_info.type = XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO;
@@ -1412,6 +1486,9 @@ int VRInterface::update() {
14121486
if (!xr_check(instance, result, "failed to end frame!"))
14131487
return 1;
14141488

1489+
// Return to normal framebuffer
1490+
glBindFramebuffer(GL_FRAMEBUFFER, 0);
1491+
14151492
// Do ray/mesh intersection here if 'select' is active and HUD is shown
14161493
if (showHUD) {
14171494
if (selectState[HAND_LEFT_INDEX] || selectState[HAND_RIGHT_INDEX]) {
@@ -1712,7 +1789,6 @@ void VRInterface::print_api_layers()
17121789
if (count == 0)
17131790
return;
17141791

1715-
//XrApiLayerProperties* props = malloc(count * sizeof(XrApiLayerProperties));
17161792
XrApiLayerProperties* props = new XrApiLayerProperties[count];
17171793
for (uint32_t i = 0; i < count; i++) {
17181794
props[i].type = XR_TYPE_API_LAYER_PROPERTIES;
@@ -1728,7 +1804,6 @@ void VRInterface::print_api_layers()
17281804
printf("\t%s v%d: %s\n", props[i].layerName, props[i].layerVersion, props[i].description);
17291805
}
17301806

1731-
//free(props)
17321807
delete[] props;
17331808
#else
17341809
std::cout << "VR interface not implemented" << std::endl;
@@ -1809,7 +1884,6 @@ int64_t VRInterface::get_swapchain_format(XrInstance instance,
18091884
return -1;
18101885

18111886
printf("Runtime supports %d swapchain formats\n", swapchain_format_count);
1812-
//int64_t* swapchain_formats = malloc(sizeof(int64_t) * swapchain_format_count);
18131887
int64_t* swapchain_formats = new int64_t[swapchain_format_count];
18141888
result = xrEnumerateSwapchainFormats(session, swapchain_format_count, &swapchain_format_count,
18151889
swapchain_formats);
@@ -1830,7 +1904,6 @@ int64_t VRInterface::get_swapchain_format(XrInstance instance,
18301904
printf("Falling back to non preferred swapchain format %#lx\n", chosen_format);
18311905
}
18321906

1833-
//free(swapchain_formats);
18341907
delete[] swapchain_formats;
18351908

18361909
return chosen_format;

src/VRInterface.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ class VRInterface {
133133
PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer;
134134
PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers;
135135
PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers;
136+
PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample;
137+
PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer;
136138
#endif
137139

138140
bool quit_mainloop;
@@ -181,6 +183,14 @@ class VRInterface {
181183
bool previousSelectState[HAND_COUNT];
182184
irr::s32 raySelectScreenX;
183185
irr::s32 raySelectScreenY;
186+
187+
// --- MSAA ---
188+
static const int MSAA_SAMPLES = 4;
189+
190+
GLuint msaaFBO[2] = { 0, 0 };
191+
GLuint msaaColor[2] = { 0, 0 };
192+
GLuint msaaDepth[2] = { 0, 0 };
193+
184194
};
185195

186196
#endif

0 commit comments

Comments
 (0)