From 49dc37af45fe2d1ea33050275a708b295bf3276e Mon Sep 17 00:00:00 2001 From: skyjake Date: Wed, 30 Oct 2013 10:35:15 +0200 Subject: [PATCH] Stereo 3D|Client|Refactor: Use libgui to draw Oculus Rift frames The Oculus Rift barrel distortion was moved to shaders.dei and adapted to use GLBuffer. Created a static vertex buffer for the simple quad used for drawing the distorted screen. --- doomsday/client/data/shaders.dei | 17 ++ .../client/data/shaders/oculusrift-barrel.fsh | 53 ++++++ doomsday/client/src/ui/clientwindow.cpp | 151 +++++------------- 3 files changed, 111 insertions(+), 110 deletions(-) create mode 100644 doomsday/client/data/shaders/oculusrift-barrel.fsh diff --git a/doomsday/client/data/shaders.dei b/doomsday/client/data/shaders.dei index 4d8de08f23..822e6ec380 100644 --- a/doomsday/client/data/shaders.dei +++ b/doomsday/client/data/shaders.dei @@ -135,4 +135,21 @@ group fx { }" } } +} + +group vr { + group oculusrift { + shader barrel { + vertex = " + #version 120 + attribute highp vec4 aVertex; + attribute highp vec2 aUV; + varying highp vec2 vTexCoord; + void main() { + gl_Position = aVertex; + vTexCoord = aUV; + }" + path.fragment = "shaders/oculusrift-barrel.fsh" + } + } } \ No newline at end of file diff --git a/doomsday/client/data/shaders/oculusrift-barrel.fsh b/doomsday/client/data/shaders/oculusrift-barrel.fsh new file mode 100644 index 0000000000..59f8774d83 --- /dev/null +++ b/doomsday/client/data/shaders/oculusrift-barrel.fsh @@ -0,0 +1,53 @@ +#version 120 + +uniform sampler2D texture; + +varying highp vec2 vTexCoord; + +const float aspectRatio = 1.0; +const float distortionScale = 1.714; // TODO check this +const vec2 screenSize = vec2(0.14976, 0.0936); +const vec2 screenCenter = 0.5 * screenSize; +const vec2 lensCenter = vec2(0.57265, 0.5); // left eye +const vec2 inputCenter = vec2(0.5, 0.5); // I rendered center at center of unwarped image +const vec2 scale = vec2(0.5/distortionScale, 0.5*aspectRatio/distortionScale); +const vec2 scaleIn = vec2(2.0, 2.0/aspectRatio); +const vec4 hmdWarpParam = vec4(1.0, 0.220, 0.240, 0.000); +const vec4 chromAbParam = vec4(0.996, -0.004, 1.014, 0.0); + +void main() { + vec2 tcIn = vTexCoord; + vec2 uv = vec2(tcIn.x*2, tcIn.y); // unwarped image coordinates (left eye) + if (tcIn.x > 0.5) // right eye + uv.x = 2 - 2*tcIn.x; + vec2 theta = (uv - lensCenter) * scaleIn; + float rSq = theta.x * theta.x + theta.y * theta.y; + vec2 rvector = theta * ( hmdWarpParam.x + + hmdWarpParam.y * rSq + + hmdWarpParam.z * rSq * rSq + + hmdWarpParam.w * rSq * rSq * rSq); + // Chromatic aberration correction + vec2 thetaBlue = rvector * (chromAbParam.z + chromAbParam.w * rSq); + vec2 tcBlue = inputCenter + scale * thetaBlue; + // Blue is farthest out + if ( (abs(tcBlue.x - 0.5) > 0.5) || (abs(tcBlue.y - 0.5) > 0.5) ) { + gl_FragColor = vec4(0, 0, 0, 1); + return; + } + vec2 thetaRed = rvector * (chromAbParam.x + chromAbParam.y * rSq); + vec2 tcRed = inputCenter + scale * thetaRed; + vec2 tcGreen = inputCenter + scale * rvector; // green + tcRed.x *= 0.5; // because output only goes to 0-0.5 (left eye) + tcGreen.x *= 0.5; // because output only goes to 0-0.5 (left eye) + tcBlue.x *= 0.5; // because output only goes to 0-0.5 (left eye) + if (tcIn.x > 0.5) { // right eye 0.5-1.0 + tcRed.x = 1 - tcRed.x; + tcGreen.x = 1 - tcGreen.x; + tcBlue.x = 1 - tcBlue.x; + } + float red = texture2D(texture, tcRed).r; + vec2 green = texture2D(texture, tcGreen).ga; + float blue = texture2D(texture, tcBlue).b; + + gl_FragColor = vec4(red, green.x, blue, green.y); +} \ No newline at end of file diff --git a/doomsday/client/src/ui/clientwindow.cpp b/doomsday/client/src/ui/clientwindow.cpp index 1a9bd8fde7..5f0c168f91 100644 --- a/doomsday/client/src/ui/clientwindow.cpp +++ b/doomsday/client/src/ui/clientwindow.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "gl/sys_opengl.h" @@ -83,6 +84,10 @@ DENG2_OBSERVES(App, GameChange) LabelWidget *fpsCounter; float oldFps; + // GL objects. + Drawable oculusRift; + GLUniform uOculusRiftFB; + Instance(Public *i) : Base(i), needMainInit(true), @@ -99,7 +104,8 @@ DENG2_OBSERVES(App, GameChange) sidebar(0), busyRoot(thisPublic), fpsCounter(0), - oldFps(0) + oldFps(0), + uOculusRiftFB("texture", GLUniform::Sampler2D) { /// @todo The decision whether to receive input notifications from the /// canvas is really a concern for the input drivers. @@ -260,6 +266,9 @@ DENG2_OBSERVES(App, GameChange) self.canvas().makeCurrent(); DD_FinishInitializationAfterWindowReady(); + + /// @todo This should be called when a VR mode is actually used. + vrInit(); } void keyEvent(KeyEvent const &ev) @@ -449,6 +458,31 @@ DENG2_OBSERVES(App, GameChange) * Special Viewing Modes: Oculus Rift and other VR */ + typedef GLBufferT OculusRiftVBuf; + QScopedPointer unwarpedTarget; + GLTexture unwarpedTexture; + + void vrInit() + { + /// @todo Only do this when Oculus Rift mode is enabled. + /// Free the allocated resources when non-Oculus mode in use. + + OculusRiftVBuf *buf = new OculusRiftVBuf; + oculusRift.addBuffer(buf); + + // Set up a simple static quad. + OculusRiftVBuf::Type const verts[4] = { + { Vector3f(-1, 1, 0.5f), Vector2f(0, 1), }, + { Vector3f( 1, 1, 0.5f), Vector2f(1, 1), }, + { Vector3f(-1, -1, 0.5f), Vector2f(0, 0), }, + { Vector3f( 1, -1, 0.5f), Vector2f(1, 0), } + }; + buf->setVertices(gl::TriangleStrip, verts, 4, gl::Static); + + root.shaders().build(oculusRift.program(), "vr.oculusrift.barrel") + << uOculusRiftFB; + } + void updateRootSize() { needRootSizeUpdate = false; @@ -486,9 +520,6 @@ DENG2_OBSERVES(App, GameChange) busyRoot.setViewSize(size); } - QScopedPointer unwarpedTarget; - GLTexture unwarpedTexture; - /** * Draws the entire UI in two halves, one for the left eye and one for the * right. The Oculus Rift optical distortion effect is applied using a @@ -510,85 +541,8 @@ DENG2_OBSERVES(App, GameChange) unwarpedTexture.setWrap(gl::ClampToEdge, gl::ClampToEdge); unwarpedTexture.setFilter(gl::Linear, gl::Linear, gl::MipNone); unwarpedTarget.reset(new GLTarget(unwarpedTexture, GLTarget::DepthStencil)); - } - // TODO somehow get a shader program in here. - static QGLShaderProgram * shaderProgram = NULL; - if (shaderProgram == NULL) { - shaderProgram = new QGLShaderProgram(); - bool success; - success = shaderProgram->addShaderFromSourceCode(QGLShader::Vertex, - "#version 120 \n" - " \n" - "void main() { \n" - " gl_Position = gl_Vertex; \n" - " gl_TexCoord[0] = gl_MultiTexCoord0; \n" - "}; \n"); - if (! success) - { - qDebug() << shaderProgram->log(); - } - success = shaderProgram->addShaderFromSourceCode(QGLShader::Fragment, - "#version 120 \n" - " \n" - "uniform sampler2D texture; \n" - " \n" - "const float aspectRatio = 1.0; \n" - "const float distortionScale = 1.714; // TODO check this \n" - "const vec2 screenSize = vec2(0.14976, 0.0936);" - "const vec2 screenCenter = 0.5 * screenSize; \n" - "const vec2 lensCenter = vec2(0.57265, 0.5); // left eye \n" - "const vec2 inputCenter = vec2(0.5, 0.5); // I rendered center at center of unwarped image \n" - "const vec2 scale = vec2(0.5/distortionScale, 0.5*aspectRatio/distortionScale); \n" - "const vec2 scaleIn = vec2(2.0, 2.0/aspectRatio); \n" - "const vec4 hmdWarpParam = vec4(1.0, 0.220, 0.240, 0.000); \n" - "const vec4 chromAbParam = vec4(0.996, -0.004, 1.014, 0.0); \n" - " \n" - "void main() { \n" - " vec2 tcIn = gl_TexCoord[0].st; \n" - " vec2 uv = vec2(tcIn.x*2, tcIn.y); // unwarped image coordinates (left eye) \n" - " if (tcIn.x > 0.5) // right eye \n" - " uv.x = 2 - 2*tcIn.x; \n" - " vec2 theta = (uv - lensCenter) * scaleIn; \n" - " float rSq = theta.x * theta.x + theta.y * theta.y; \n" - " vec2 rvector = theta * ( hmdWarpParam.x + \n" - " hmdWarpParam.y * rSq + \n" - " hmdWarpParam.z * rSq * rSq + \n" - " hmdWarpParam.w * rSq * rSq * rSq); \n" - " // Chromatic aberration correction \n" - " vec2 thetaBlue = rvector * (chromAbParam.z + chromAbParam.w * rSq); \n" - " vec2 tcBlue = inputCenter + scale * thetaBlue; \n" - " // Blue is farthest out \n" - " if ( (abs(tcBlue.x - 0.5) > 0.5) || (abs(tcBlue.y - 0.5) > 0.5) ) { \n" - " gl_FragColor = vec4(0, 0, 0, 1); \n" - " return; \n" - " } \n" - " vec2 thetaRed = rvector * (chromAbParam.x + chromAbParam.y * rSq); \n" - " vec2 tcRed = inputCenter + scale * thetaRed; \n" - " vec2 tcGreen = inputCenter + scale * rvector; // green \n" - " tcRed.x *= 0.5; // because output only goes to 0-0.5 (left eye) \n" - " tcGreen.x *= 0.5; // because output only goes to 0-0.5 (left eye) \n" - " tcBlue.x *= 0.5; // because output only goes to 0-0.5 (left eye) \n" - " if (tcIn.x > 0.5) { // right eye 0.5-1.0 \n" - " tcRed.x = 1 - tcRed.x; \n" - " tcGreen.x = 1 - tcGreen.x; \n" - " tcBlue.x = 1 - tcBlue.x; \n" - " } \n" - " float red = texture2D(texture, tcRed).r; \n" - " vec2 green = texture2D(texture, tcGreen).ga; \n" - " float blue = texture2D(texture, tcBlue).b; \n" - " \n" - - " gl_FragColor = vec4(red, green.x, blue, green.y); \n" - "} \n"); - if (! success) - { - qDebug() << shaderProgram->log(); - } - success = shaderProgram->link(); - if (! success) - { - qDebug() << shaderProgram->log(); - } + + uOculusRiftFB = unwarpedTexture; } // Set render target to offscreen temporarily. @@ -617,34 +571,11 @@ DENG2_OBSERVES(App, GameChange) self.canvas().renderTarget().clear(GLTarget::Color); GLState::push() .setBlend(false) - .setDepthTest(false) - .apply(); + .setDepthTest(false); + + // Copy contents of offscreen buffer to normal screen. + oculusRift.draw(); - // Copy contents of offscreen buffer to normal screen - unwarpedTexture.glBindToUnit(0); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - // glRotatef(5, 0, 0, 1); // to make it evident (remove this) - // if (useShader) - // glUseProgram(shader); - shaderProgram->bind(); - glBegin(GL_TRIANGLE_STRIP); - glTexCoord2d(0, 1); glVertex3f(-1, 1, 0.5); - glTexCoord2d(1, 1); glVertex3f( 1, 1, 0.5); - glTexCoord2d(0, 0); glVertex3f(-1, -1, 0.5); - glTexCoord2d(1, 0); glVertex3f( 1, -1, 0.5); - glEnd(); - shaderProgram->release(); - // if (useShader) - // glUseProgram(0); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); glBindTexture(GL_TEXTURE_2D, 0); GLState::pop().apply();