I am trying to achieve an inverse lens ditortion for the Oculus Rift during Side-By-Side stereo in the game engine. I am using a custom Filter2D with this GLSL shader.

The distortion can be achieved as described here. Another fragment shader for that cause can be found in the OpenHMD examples.

The distortion looks good without stereo, but is asymmetric when I render it with Side-By-Side. For other stereo options, the output is not correct, but symmetric. Where is the problem here?

uniform sampler2D bgl_RenderedTexture;

const vec4 kappa = vec4(1.0,1.7,0.7,15.0);

uniform float screen_width;
uniform float screen_height;

const float scaleFactor = 0.9;

const vec2 leftCenter = vec2(0.25, 0.5);
const vec2 rightCenter = vec2(0.75, 0.5);

// Scales input texture coordinates for distortion.
vec2 hmdWarp(vec2 LensCenter, vec2 texCoord, vec2 Scale, vec2 ScaleIn) {
    vec2 theta = (texCoord - LensCenter) * ScaleIn; 
    float rSq = theta.x * theta.x + theta.y * theta.y;
    vec2 rvector = theta * (kappa.x + kappa.y * rSq + kappa.z * rSq * rSq + kappa.w * rSq * rSq * rSq);
    vec2 tc = LensCenter + Scale * rvector;
    return tc;

bool validate(vec2 tc, int left_eye) {
    //keep within bounds of texture 
    if ((left_eye == 1 && (tc.x < 0.0 || tc.x > 0.5)) ||   
        (left_eye == 0 && (tc.x < 0.5 || tc.x > 1.0)) ||
        tc.y < 0.0 || tc.y > 1.0) {
        return false;
    return true;

void main() {
    vec2 screen = vec2(screen_width, screen_height);

    float as = float(screen.x / 2.0) / float(screen.y);
    vec2 Scale = vec2(0.5, as);
    vec2 ScaleIn = vec2(2.0 * scaleFactor, 1.0 / as * scaleFactor);

    vec2 texCoord = gl_TexCoord[0].st;

    vec2 tc = vec2(0);
    vec4 color = vec4(0);

    if (texCoord.x < 0.5) {
        tc = hmdWarp(leftCenter, texCoord, Scale, ScaleIn );
        color = texture2D(bgl_RenderedTexture, tc);
        if (!validate(tc, 1))
            color = vec4(0);
    } else {
        tc = hmdWarp(rightCenter, texCoord, Scale, ScaleIn);
        color = texture2D(bgl_RenderedTexture, tc);
        if (!validate(tc, 0))
            color = vec4(0);
    gl_FragColor = color;

These are my render results.

First Image: Rendered with Above-Below, the result is at least symmetric.

Second Image: Rendered with no stereo, looking good, but unusable for the glasses.

Third Image: Asymmetric Side-By-Side rendering cropped.

Fourth Image: Aysmmetric Side-By-Side rendering without crop (validate function commented out)

The relevant stereo code is in RAS_OpenGLRasterizer.cpp, but I couldn't find the cause of the asymmetry.

Edit: A blend file with this issue can be downloaded here


closed as off-topic by iKlsR Jul 9 '13 at 5:26

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "This question appears to be off-topic because it is a bug report and not an actual question. These should be reported to Blender's tracker instead." – iKlsR
If this question can be reworded to fit the rules in the help center, please edit the question.

  • 1
    $\begingroup$ Can you provide a .blend file? I'm not sure how to hook up a GLSL post-processing filter to the game engine's final output. Without looking at it, it's hard to say if your input coordinates are off, or what. $\endgroup$ – Wray Bowling Jun 25 '13 at 7:43

Moguri answered my question on the forums by posting this patch that fixes the Filter 2D issue in Blender.

The patch:

diff --git blender/source/gameengine/Rasterizer/RAS_2DFilterManager.cpp blender/source/gameengine/Rasterizer/RAS_2DFilterManager.cpp index 79ce853..1781852 100644
--- blender/source/gameengine/Rasterizer/RAS_2DFilterManager.cpp
+++ blender/source/gameengine/Rasterizer/RAS_2DFilterManager.cpp @@ -429,6 +429,9 @@ void RAS_2DFilterManager::RenderFilters(RAS_ICanvas* canvas)
        canvas->SetViewPort(0, 0, rect_width-1, rect_height-1);  
+       RAS_Rect scissor_rect = canvas->GetDisplayArea();
+       glScissor(scissor_rect.GetLeft()+viewport[0], scissor_rect.GetBottom()+viewport[1], scissor_rect.GetWidth()+1, scissor_rect.GetHeight()+1);
        // in case the previous material was wire
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

Note: This patch is already included in the trunk, verified at 08/25/2013

  • 1
    $\begingroup$ Please consider putting the solution here since link-only questions are not considered good. $\endgroup$ – Alvin Wong Jun 27 '13 at 7:32
  • $\begingroup$ @lubosz Thanks for your wrapper for the oculus rift. How should the wrapper installed in blender without distutils? $\endgroup$ – stacker Aug 25 '13 at 5:51

Not the answer you're looking for? Browse other questions tagged or ask your own question.