Skip to content

Commit

Permalink
Stereo 3D|Client|Refactor: Use libgui to draw Oculus Rift frames
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
skyjake committed Oct 30, 2013
1 parent f4809ac commit 49dc37a
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 110 deletions.
17 changes: 17 additions & 0 deletions doomsday/client/data/shaders.dei
Expand Up @@ -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"
}
}
}
53 changes: 53 additions & 0 deletions 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);
}
151 changes: 41 additions & 110 deletions doomsday/client/src/ui/clientwindow.cpp
Expand Up @@ -30,6 +30,7 @@
#include <de/NumberValue>
#include <QGLFormat>
#include <de/GLState>
#include <de/Drawable>
#include <QCloseEvent>

#include "gl/sys_opengl.h"
Expand Down Expand Up @@ -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),
Expand All @@ -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.
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -449,6 +458,31 @@ DENG2_OBSERVES(App, GameChange)
* Special Viewing Modes: Oculus Rift and other VR
*/

typedef GLBufferT<Vertex3Tex> OculusRiftVBuf;
QScopedPointer<GLTarget> 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;
Expand Down Expand Up @@ -486,9 +520,6 @@ DENG2_OBSERVES(App, GameChange)
busyRoot.setViewSize(size);
}

QScopedPointer<GLTarget> 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
Expand All @@ -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.
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 49dc37a

Please sign in to comment.