From 1271b4458f9c774f48328efe577fcb607868461e Mon Sep 17 00:00:00 2001 From: Martin Lambers Date: Thu, 1 Aug 2024 11:53:45 +0200 Subject: [PATCH 1/2] Reduce GLSL version to 310 for OpenGL ES --- src/bino.cpp | 8 ++++---- src/qvrapp.cpp | 4 ++-- src/widget.cpp | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/bino.cpp b/src/bino.cpp index c5409c0..0f7f9e8 100644 --- a/src/bino.cpp +++ b/src/bino.cpp @@ -773,8 +773,8 @@ void Bino::rebuildColorPrgIfNecessary(int planeFormat, bool yuvValueRangeSmall, colorFS.replace("$VALUE_RANGE_SMALL", yuvValueRangeSmall ? "true" : "false"); colorFS.replace("$YUV_SPACE", QString::number(yuvSpace)); if (isGLES) { - colorVS.prepend("#version 320 es\n"); - colorFS.prepend("#version 320 es\n" + colorVS.prepend("#version 310 es\n"); + colorFS.prepend("#version 310 es\n" "precision mediump float;\n"); } else { colorVS.prepend("#version 330\n"); @@ -807,8 +807,8 @@ void Bino::rebuildViewPrgIfNecessary(SurroundMode surroundMode, bool nonLinearOu : "0"); viewFS.replace("$NONLINEAR_OUTPUT", nonLinearOutput ? "true" : "false"); if (isGLES) { - viewVS.prepend("#version 320 es\n"); - viewFS.prepend("#version 320 es\n" + viewVS.prepend("#version 310 es\n"); + viewFS.prepend("#version 310 es\n" "precision mediump float;\n"); } else { viewVS.prepend("#version 330\n"); diff --git a/src/qvrapp.cpp b/src/qvrapp.cpp index 2d5bfac..21f8d50 100644 --- a/src/qvrapp.cpp +++ b/src/qvrapp.cpp @@ -120,8 +120,8 @@ bool BinoQVRApp::initProcess(QVRProcess*) QString vrdeviceVS = readFile(":src/shader-vrdevice.vert.glsl"); QString vrdeviceFS = readFile(":src/shader-vrdevice.frag.glsl"); if (isGLES) { - vrdeviceVS.prepend("#version 320 es\n"); - vrdeviceFS.prepend("#version 320 es\n" + vrdeviceVS.prepend("#version 310 es\n"); + vrdeviceFS.prepend("#version 310 es\n" "precision mediump float;\n"); } else { vrdeviceVS.prepend("#version 330\n"); diff --git a/src/widget.cpp b/src/widget.cpp index abe3b48..56fefa1 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -186,8 +186,8 @@ void Widget::rebuildDisplayPrgIfNecessary(OutputMode outputMode) QString fragmentShaderSource = readFile(":src/shader-display.frag.glsl"); fragmentShaderSource.replace("$OUTPUT_MODE", QString::number(int(outputMode))); if (isGLES) { - vertexShaderSource.prepend("#version 320 es\n"); - fragmentShaderSource.prepend("#version 320 es\n" + vertexShaderSource.prepend("#version 310 es\n"); + fragmentShaderSource.prepend("#version 310 es\n" "precision mediump float;\n"); } else { vertexShaderSource.prepend("#version 330\n"); From 83e5999e0acec357d7965d997ceab866ea9c5ca7 Mon Sep 17 00:00:00 2001 From: Martin Lambers Date: Thu, 1 Aug 2024 22:44:30 +0200 Subject: [PATCH 2/2] Add support for --vr-screen=united|intersected, from upcoming QVR 4.1.0 The main purpose of this is to allow easy two-screen stereo setups with bino --vr --vr-screen=united --qvr-config=two-screen-stereo.qvr --- CMakeLists.txt | 2 +- doc/bino-manual.md | 14 +++++++++---- src/bino.cpp | 51 +++++++++++++++++++++++++++++++++++----------- src/bino.hpp | 14 +++++++++++-- src/main.cpp | 22 +++++++++++++------- src/qvrapp.cpp | 7 +++++-- src/widget.cpp | 4 +++- 7 files changed, 85 insertions(+), 29 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f7dc8f9..38967b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) # Required: Qt6 find_package(Qt6 6.6.0 REQUIRED COMPONENTS OpenGLWidgets Multimedia LinguistTools) # Optional: QVR for Virtual Reality support -find_package(QVR 4.0.0 QUIET) +find_package(QVR 4.1.0 QUIET) if(QVR_FOUND) add_definitions(-DWITH_QVR) include_directories(${QVR_INCLUDE_DIRS}) diff --git a/doc/bino-manual.md b/doc/bino-manual.md index fb969af..7fec9d5 100644 --- a/doc/bino-manual.md +++ b/doc/bino-manual.md @@ -58,10 +58,10 @@ Bino is a video player with a focus on 3D and Virtual Reality: - `--vr-screen` *screen* - Set VR screen geometry, either as a comma-separated list of nine values - representing three 3D coordinates that define a planar screen (bottom left, - bottom right, top left) or as a name of an OBJ file that contains the screen - geometry with texture coordinates. + Set VR screen geometry, either as the special values 'united' or 'intersected', or as a comma-separated list of nine + values representing three 3D coordinates that define a planar screen (bottom left, bottom right, top left), or as a an + aspect ratio followed by the name of an OBJ file that contains the screen geometry with texture coordinates (example: + '16:9,myscreen.obj'). - `--capture` @@ -329,6 +329,12 @@ their bottom left, bottom right and top left corners, or to load arbitrary screen geometry from an OBJ file. The latter case is useful e.g. if you want Bino's virtual screen to coincide with a curved physical screen. +The `--vr-screen` option also accepts the special values `united` and +`intersected`. This will unite (or intersect) the 2D geometries of all VR windows +at runtime. For example, use `--vr-screen=united --qvr-config=two-screen-stereo.qvr` +for a two-screen stereo setup, where the left view goes on the first screen and +the right view goes on the second screen. + Bino uses QVRs default navigation, which may be based on autodetected controllers such as the HTC Vive controllers, or on tracking and interaction hardware configured via QVR for your VR system, or on the mouse and WASDQE keys diff --git a/src/bino.cpp b/src/bino.cpp index 0f7f9e8..3b7079d 100644 --- a/src/bino.cpp +++ b/src/bino.cpp @@ -31,7 +31,7 @@ static Bino* binoSingleton = nullptr; -Bino::Bino(const Screen& screen, bool swapEyes) : +Bino::Bino(ScreenType screenType, const Screen& screen, bool swapEyes) : _wantExit(false), _videoSink(nullptr), _audioOutput(nullptr), @@ -43,6 +43,7 @@ Bino::Bino(const Screen& screen, bool swapEyes) : _captureSession(nullptr), _lastFrameInputMode(Input_Unknown), _lastFrameSurroundMode(Surround_Unknown), + _screenType(screenType), _screen(screen), _frameIsNew(false), _frameWasSerialized(true), @@ -499,12 +500,12 @@ SurroundMode Bino::assumeSurroundMode() const void Bino::serializeStaticData(QDataStream& ds) const { - ds << _screen; + ds << _screenType << _screen; } void Bino::deserializeStaticData(QDataStream& ds) { - ds >> _screen; + ds >> _screenType >> _screen; } void Bino::serializeDynamicData(QDataStream& ds) @@ -731,23 +732,20 @@ bool Bino::initProcess() // Screen geometry glGenVertexArrays(1, &_screenVao); glBindVertexArray(_screenVao); - GLuint positionBuf; - glGenBuffers(1, &positionBuf); - glBindBuffer(GL_ARRAY_BUFFER, positionBuf); + glGenBuffers(1, &_positionBuf); + glBindBuffer(GL_ARRAY_BUFFER, _positionBuf); glBufferData(GL_ARRAY_BUFFER, _screen.positions.size() * sizeof(float), _screen.positions.constData(), GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(0); - GLuint texcoordBuf; - glGenBuffers(1, &texcoordBuf); - glBindBuffer(GL_ARRAY_BUFFER, texcoordBuf); + glGenBuffers(1, &_texcoordBuf); + glBindBuffer(GL_ARRAY_BUFFER, _texcoordBuf); glBufferData(GL_ARRAY_BUFFER, _screen.texcoords.size() * sizeof(float), _screen.texcoords.constData(), GL_STATIC_DRAW); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(1); - GLuint indexBuf; - glGenBuffers(1, &indexBuf); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuf); + glGenBuffers(1, &_indexBuf); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuf); glBufferData(GL_ELEMENT_ARRAY_BUFFER, _screen.indices.length() * sizeof(unsigned int), _screen.indices.constData(), GL_STATIC_DRAW); @@ -1120,12 +1118,26 @@ void Bino::preRenderProcess(int screenWidth, int screenHeight, } void Bino::render( + const QVector3D& unitedScreenBottomLeft, const QVector3D& unitedScreenBottomRight, const QVector3D& unitedScreenTopLeft, + const QVector3D& intersectedScreenBottomLeft, const QVector3D& intersectedScreenBottomRight, const QVector3D& intersectedScreenTopLeft, const QMatrix4x4& projectionMatrix, const QMatrix4x4& orientationMatrix, const QMatrix4x4& viewMatrix, int view, // 0 = left, 1 = right int texWidth, int texHeight, unsigned int texture) { + // Update screen + switch (_screenType) { + case ScreenUnited: + _screen = Screen(unitedScreenBottomLeft, unitedScreenBottomRight, unitedScreenTopLeft); + break; + case ScreenIntersected: + _screen = Screen(intersectedScreenBottomLeft, intersectedScreenBottomRight, intersectedScreenTopLeft); + break; + case ScreenGeometry: + // do nothing + break; + } // Set up framebuffer object to render into glBindTexture(GL_TEXTURE_2D, _depthTex); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, texWidth, texHeight, @@ -1246,6 +1258,21 @@ void Bino::render( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); } else { glBindVertexArray(_screenVao); + if (_screenType == ScreenUnited || _screenType == ScreenIntersected) { + // the geometry might have changed; re-upload it + glBindBuffer(GL_ARRAY_BUFFER, _positionBuf); + glBufferData(GL_ARRAY_BUFFER, _screen.positions.size() * sizeof(float), + _screen.positions.constData(), GL_STATIC_DRAW); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); + glBindBuffer(GL_ARRAY_BUFFER, _texcoordBuf); + glBufferData(GL_ARRAY_BUFFER, _screen.texcoords.size() * sizeof(float), + _screen.texcoords.constData(), GL_STATIC_DRAW); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBuf); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, + _screen.indices.length() * sizeof(unsigned int), + _screen.indices.constData(), GL_STATIC_DRAW); + } glDrawElements(GL_TRIANGLES, _screen.indices.size(), GL_UNSIGNED_INT, 0); } } diff --git a/src/bino.hpp b/src/bino.hpp index 559e54c..d46a4d9 100644 --- a/src/bino.hpp +++ b/src/bino.hpp @@ -42,6 +42,13 @@ class Bino : public QObject, QOpenGLExtraFunctions { Q_OBJECT +public: + enum ScreenType { + ScreenUnited, // global 2D united screen given by QVR + ScreenIntersected, // global 2D intersected screen given by QVR + ScreenGeometry // explicit geometry stored in _screen + }; + private: /* Data not directly relevant for rendering */ bool _wantExit; @@ -63,6 +70,7 @@ Q_OBJECT SurroundMode _lastFrameSurroundMode; /* Static data for rendering, initialized on the main process */ + ScreenType _screenType; Screen _screen; /* Static data for rendering, initialized in initProcess() */ @@ -75,7 +83,7 @@ Q_OBJECT unsigned int _frameTex; unsigned int _extFrameTex; unsigned int _subtitleTex; - unsigned int _screenVao; + unsigned int _screenVao, _positionBuf, _texcoordBuf, _indexBuf; QOpenGLShaderProgram _colorPrg; int _colorPrgPlaneFormat; bool _colorPrgYuvValueRangeSmall; @@ -98,7 +106,7 @@ Q_OBJECT void convertFrameToTexture(const VideoFrame& frame, unsigned int frameTex); public: - Bino(const Screen& screen, bool swapEyes); + Bino(ScreenType screenType, const Screen& screen, bool swapEyes); virtual ~Bino(); static Bino* instance(); @@ -178,6 +186,8 @@ Q_OBJECT float* frameDisplayAspectRatio = nullptr, bool* surround = nullptr); void render( + const QVector3D& unitedScreenBottomLeft, const QVector3D& unitedScreenBottomRight, const QVector3D& unitedScreenTopLeft, + const QVector3D& intersectedScreenBottomLeft, const QVector3D& intersectedScreenBottomRight, const QVector3D& intersectedScreenTopLeft, const QMatrix4x4& projectionMatrix, const QMatrix4x4& orientationMatrix, const QMatrix4x4& viewMatrix, diff --git a/src/main.cpp b/src/main.cpp index 533dba7..c0c5661 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -133,9 +133,10 @@ int main(int argc, char* argv[]) parser.addOption({ "vr", QCommandLineParser::tr("Start in VR mode instead of GUI mode.")}); parser.addOption({ "vr-screen", - QCommandLineParser::tr("Set VR screen geometry, either as a comma-separated list of " - "nine values representing three 3D coordinates that define a planar screen (bottom left, bottom right, top left) " - "or as a name of an OBJ file that contains the screen geometry with texture coordinates."), + QCommandLineParser::tr("Set VR screen geometry, either as the special values 'united' or 'intersected', or " + "as a comma-separated list of nine values representing three 3D coordinates that define a planar screen (bottom left, bottom right, top left), " + "or as a an aspect ratio followed by the name of an OBJ file that contains the screen geometry with texture coordinates " + "(example: '16:9,myscreen.obj')."), "screen" }); parser.addOption({ "capture", QCommandLineParser::tr("Capture audio/video input from microphone and camera/screen/window.") }); @@ -546,30 +547,37 @@ int main(int argc, char* argv[]) } // Handle the VR Screen - Screen screen; + Bino::ScreenType screenType = Bino::ScreenGeometry; + Screen screen; // default screen for non-VR mode if (parser.isSet("vr")) { float screenCenterHeight = 1.76f - 0.15f; // does not matter, never used #ifdef WITH_QVR screenCenterHeight = QVRObserverConfig::defaultEyeHeight; #endif - screen = Screen( + screen = Screen( // default screen for VR mode QVector3D(-16.0f / 9.0f, -1.0f + screenCenterHeight, -8.0f), QVector3D(+16.0f / 9.0f, -1.0f + screenCenterHeight, -8.0f), QVector3D(-16.0f / 9.0f, +1.0f + screenCenterHeight, -8.0f)); if (parser.isSet("vr-screen")) { QStringList paramList = parser.value("vr-screen").split(','); float values[9]; - if (paramList.length() == 9 + if (parser.value("vr-screen") == "united") { + screenType = Bino::ScreenUnited; + } else if (parser.value("vr-screen") == "intersected") { + screenType = Bino::ScreenIntersected; + } else if (paramList.length() == 9 && 9 == std::sscanf(qPrintable(parser.value("vr-screen")), "%f,%f,%f,%f,%f,%f,%f,%f,%f", values + 0, values + 1, values + 2, values + 3, values + 4, values + 5, values + 6, values + 7, values + 8)) { + screenType = Bino::ScreenGeometry; screen = Screen( QVector3D(values[0], values[1], values[2]), QVector3D(values[3], values[4], values[5]), QVector3D(values[6], values[7], values[8])); } else if (paramList.length() == 2) { + screenType = Bino::ScreenGeometry; float ar; float ar2[2]; if (2 == std::sscanf(qPrintable(paramList[0]), "%f:%f", ar2 + 0, ar2 + 1)) { @@ -633,7 +641,7 @@ int main(int argc, char* argv[]) QSurfaceFormat::setDefaultFormat(format); // Initialize Bino (in VR mode: only from the main process!) - Bino bino(screen, parser.isSet("swap-eyes")); + Bino bino(screenType, screen, parser.isSet("swap-eyes")); if (guiMode || !vrChildProcess) { bino.initializeOutput(audioOutputDeviceIndex >= 0 ? audioOutputDevices[audioOutputDeviceIndex] diff --git a/src/qvrapp.cpp b/src/qvrapp.cpp index 21f8d50..072d8d8 100644 --- a/src/qvrapp.cpp +++ b/src/qvrapp.cpp @@ -4,7 +4,7 @@ * Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022 * Computer Graphics Group, University of Siegen * Written by Martin Lambers - * Copyright (C) 2022, 2023 + * Copyright (C) 2022, 2023, 2024 * Martin Lambers * * This program is free software; you can redistribute it and/or modify @@ -166,7 +166,10 @@ void BinoQVRApp::render(QVRWindow*, const QVRRenderContext& context, const unsig int v = (context.eye(view) == QVR_Eye_Right ? 1 : 0); int texWidth = context.textureSize(view).width(); int texHeight = context.textureSize(view).height(); - Bino::instance()->render(projectionMatrix, orientationMatrix, viewMatrix, v, texWidth, texHeight, textures[view]); + Bino::instance()->render( + context.unitedScreenWallBottomLeft(), context.unitedScreenWallBottomRight(), context.unitedScreenWallTopLeft(), + context.intersectedScreenWallBottomLeft(), context.intersectedScreenWallBottomRight(), context.intersectedScreenWallTopLeft(), + projectionMatrix, orientationMatrix, viewMatrix, v, texWidth, texHeight, textures[view]); // Render VR device models (optional) glUseProgram(_prg.programId()); for (int i = 0; i < QVRManager::deviceCount(); i++) { diff --git a/src/widget.cpp b/src/widget.cpp index 56fefa1..68469a3 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -297,7 +297,9 @@ void Widget::paintGL() (_surroundHorizontalAngleBase + _surroundHorizontalAngleCurrent), 0.0f); orientationMatrix.rotate(orientation.inverted()); } - Bino::instance()->render(projectionMatrix, orientationMatrix, viewMatrix, v, viewWidth, viewHeight, _viewTex[v]); + Bino::instance()->render( + QVector3D(), QVector3D(), QVector3D(), QVector3D(), QVector3D(), QVector3D(), + projectionMatrix, orientationMatrix, viewMatrix, v, viewWidth, viewHeight, _viewTex[v]); // generate mipmaps for the view texture glBindTexture(GL_TEXTURE_2D, _viewTex[v]); glGenerateMipmap(GL_TEXTURE_2D);