Blitting in VR display happens on left eye only

Started by Fixta, November 16, 2016, 08:16:29 PM

Previous topic - Next topic

Fixta

Howdy!
In the frame of a VR app, I split the screen in two halves. Each half renders the scene with a different camera position. I set the Config.viewportOffsetX to 0.0f for the left eye and 1.0f for the right eye.
Everything is working like a charm.
I tried to add a reticle in the center of each half screen by blitting a texture but it appears only on the left part of the screen. I understand that it is the same framebuffer that is rendered twice, but I can't figure out a way to do this (I assume I would have the same issue using an Overlay).
My code in onDrawFrame is:
[...]

        //LEFT EYE
        scn.update();
        fb.clear(back);
        //The 2 following rotations of the camera
        //are needed to set the JPCT camera as it is in Blender.
        cam.rotateAxis(cam.getXAxis(), (float) Math.PI/2);
        cam.rotateAxis(cam.getYAxis(), (float) Math.PI/2);

        //Position left eye
        cam.setPosition(new SimpleVector(0.0f+eyeDistance, 0.0f, 1.3f));
        //Rendering in the left viewport
        com.threed.jpct.Config.viewportOffsetX = 0.0f;
        world.renderScene(fb);
        world.draw(fb);
        //Left reticle
        fb.blit(reticleTex, 0, 0, fb.getWidth()/2, fb.getHeight()/2, 256, 256 ,128, 128, 2, false);

        //RIGHT EYE
        cam.setPosition(new SimpleVector(0.0f-eyeDistance, 0.0f, 1.3f));
        com.threed.jpct.Config.viewportOffsetX = 1.0f;
        world.renderScene(fb);
        world.draw(fb);
        // /!\
        // Right reticle
        // This one appears cut in half by the limit of the (left drawn) framebuffer
        fb.blit(reticleTex, 0, 0, fb.getWidth()/2+290, fb.getHeight()/2, 256, 256 ,64, 64, 2, false);
        fb.display();

Does someone could give me any advice on how to do this ? This would make my night :)

And also, I noticed that the reticle is not well centered. It's a bit off-centered and it surprised me since I used the width and height of the framebuffer divided by two. I saw a deprecated method, getOutputWidth/Height, so maybe for some reasons getWidth/Height might not return the right values. Someone knows where I could have some details about this please ? Thank you!

Fixta.

EgonOlsen

I'm not 100% sure if I understand the problem correctly. Do you have a screenshot that shows the issue?

Fixta

#2
Here is a picture.
My question is: how can I have a second reticle on the right half of the screen. Same buffer, with a blit of a texture, rendered twice. But It only appears on the left half of the screen. The little reticle cut in half by the limit of the framebuffer is a test to check that the second blitting happend on the first (left) framebuffer and correspond to the line of code:
fb.blit(reticleTex, 0, 0, fb.getWidth()/2+290, fb.getHeight()/2, 256, 256 ,64, 64, 2, false);

I hope it's clearer now...
I know I'm missing something with the use of the framebuffer in this particular case (two renderings with a viewport offset).
Thank you!


AeroShark333

About the not centered issue, you probably need to correct your placement with (the destination height)/2 and the same for the width I'd say.
So: fb.getWidth()/2-destinationWidth/2
(not sure whether you need to subtract or add, I guess you'll find out if you try it)
destinationWidth being the amount of pixels your blit will take in width.

fb.blit(reticleTex, 0, 0, fb.getWidth()/2+290, fb.getHeight()/2, 256, 256 ,64, 64, 2, false);
Why are your arguments "64, 64" here? And "128, 128" in the other?:
And why "+290", it might look okay on your phone but I doubt placement would look good on phones with other screen resolutions! :p

Fixta

Hi AeroShark!
Thank you for your reply.

About the centered question, you might be right. I presumed the default coordinate was at the center of the texture but it sounds stupid now when I write this :-) I will try this and let you know.

About the +290, as I wrote in my previous post: "The little reticle cut in half by the limit of the framebuffer is a test [...]"
The values 256, 256, 64, 64 are a guess work: my texture is 128x128 but it seems that Android rescaled it by a factor of 2 so I played with the values to get something right. I'll definitely change this when I will have solved the real issue of having the reticle correctly displayed on the right half of the screen.

Thanks again!

EgonOlsen

I have to admit that I'm still confused...you are offsetting the framebuffer by 1, which actually means that you are shifting it out of view!? Also, blitting shouldn't be affected by the offset anyway, so your blitting coordinates should be the same ones as if there were no offset at all.

Anyway, I think that it might not be the best to do this. I would rather draw the scene twice into different textures and blit the results. Like so:


package com.threed.jpct.rendertargets;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.opengl.GLSurfaceView;
import android.os.Bundle;

import com.threed.jpct.Camera;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.Light;
import com.threed.jpct.Logger;
import com.threed.jpct.NPOTTexture;
import com.threed.jpct.Object3D;
import com.threed.jpct.Primitives;
import com.threed.jpct.RGBColor;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.Texture;
import com.threed.jpct.TextureManager;
import com.threed.jpct.World;
import com.threed.jpct.util.MemoryHelper;

public class RenderTargets extends Activity {

private GLSurfaceView mGLView;
private MyRenderer renderer = null;
private FrameBuffer fb = null;
private World world = null;
private World dummyWorld = null;

private NPOTTexture target1 = null;
private NPOTTexture target2 = null;

private Object3D cube = null;
private int fps = 0;

private Light sun = null;

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLView = new GLSurfaceView(getApplication());

mGLView.setEGLContextClientVersion(2);

mGLView.setPreserveEGLContextOnPause(true);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

renderer = new MyRenderer();
mGLView.setRenderer(renderer);
setContentView(mGLView);
}

@Override
protected void onPause() {
super.onPause();
mGLView.onPause();
}

@Override
protected void onResume() {
super.onResume();
mGLView.onResume();
}

@Override
protected void onStop() {
super.onStop();
}

protected boolean isFullscreenOpaque() {
return true;
}

class MyRenderer implements GLSurfaceView.Renderer {

private long time = System.currentTimeMillis();

public MyRenderer() {
//
}

public void onSurfaceChanged(GL10 gl, int w, int h) {

fb = new FrameBuffer(w, h); // OpenGL ES 2.0 constructor

world = new World();
world.setAmbientLight(20, 20, 20);

sun = new Light(world);
sun.setIntensity(250, 250, 250);

TextureManager.getInstance().addTexture("texture", new Texture(16, 16, RGBColor.GREEN));
TextureManager.getInstance().addTexture("texture2", new Texture(16, 16, RGBColor.RED));
TextureManager.getInstance().addTexture("texture3", new Texture(16, 16, RGBColor.WHITE));

cube = Primitives.getCube(10);
cube.calcTextureWrapSpherical();
cube.setTexture("texture");
cube.strip();
cube.build();

world.addObject(cube);

Object3D cube2 = Primitives.getSphere(5);
cube2.calcTextureWrapSpherical();
cube2.setTexture("texture2");
cube2.strip();
cube2.build();
cube2.translate(10, -10, -5);

world.addObject(cube2);

Object3D cube3 = Primitives.getSphere(10);
cube3.calcTextureWrapSpherical();
cube3.setTexture("texture3");
cube3.strip();
cube3.build();
cube3.translate(10, 20, -15);

world.addObject(cube3);

Camera cam = world.getCamera();
cam.moveCamera(Camera.CAMERA_MOVEOUT, 50);
cam.lookAt(cube.getTransformedCenter());

SimpleVector sv = new SimpleVector();
sv.set(cube.getTransformedCenter());
sv.y -= 100;
sv.z -= 100;
sun.setPosition(sv);
MemoryHelper.compact();

target1 = new NPOTTexture(w / 2, h, RGBColor.BLUE);
target2 = new NPOTTexture(w / 2, h, RGBColor.RED);

dummyWorld = new World();
}

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
}

public void onDrawFrame(GL10 gl) {
cube.rotateY(0.01f);
cube.rotateX(0.01f);

// Render into first target
fb.setRenderTarget(target1);
fb.clear(RGBColor.BLUE);
world.getCamera().moveCamera(Camera.CAMERA_MOVERIGHT, -5);
world.renderScene(fb);
world.draw(fb);
fb.display();
world.getCamera().moveCamera(Camera.CAMERA_MOVERIGHT, 5);
fb.removeRenderTarget();

// Render into second target
fb.setRenderTarget(target2);
fb.clear(RGBColor.BLUE);
world.getCamera().moveCamera(Camera.CAMERA_MOVERIGHT, 5);
world.renderScene(fb);
world.draw(fb);
fb.display();
world.getCamera().moveCamera(Camera.CAMERA_MOVERIGHT, -5);
fb.removeRenderTarget();

// jPCT-AE needs this to setup some stuff for correct blitting
dummyWorld.renderScene(fb);
dummyWorld.draw(fb);

// Blit both textures. Please note that the blitting is upside down
// (starting at the bottom with negative destination height, because
// OpenGL's coordinate system starts at the bottom. This compensates
// for that.
fb.blit(target1, 0, 0, 0, fb.getHeight(), target1.getWidth(), target1.getHeight(), fb.getWidth() / 2, -fb.getHeight(), -1, false);
fb.blit(target2, 0, 0, fb.getWidth() / 2, fb.getHeight(), target1.getWidth(), target1.getHeight(), fb.getWidth() / 2, -fb.getHeight(), -1, false);

fb.display();

if (System.currentTimeMillis() - time >= 1000) {
Logger.log(fps + "fps");
fps = 0;
time = System.currentTimeMillis();
}
fps++;
}
}

}


Fixta

Hi!
First of all, thank you so much for your time replying the post.

You surely don't remember this because it's dug out from a 2011 post, but what confuses you is a hint that you gave to monkokoi :-) :
___
[...] You might want to play around with http://www.jpct.net/jpct-ae/doc/com/threed/jpct/Config.html#viewportOffsetX, but i'm not sure if this will work properly on all devices. « Last Edit: August 23, 2011, 08:31:09 am by EgonOlsen »
___

Then I followed what monkokoi did:
// ... when it comes time to render ...

cam.setPosition( LEFT_EYE_POSITION );
cam.lookAt( TARGET_POSITION );
com.threed.jpct.Config.viewportOffsetX = 0.0f;
world.renderScene(fb);
world.draw(fb);

cam.setPosition(RIGHT_EYE_POSITION );
cam.lookAt( TARGET_POSITION );
com.threed.jpct.Config.viewportOffsetX = 1.0f;
world.renderScene(fb);
world.draw(fb);

fb.display();
« Reply #9 on: August 23, 2011, 07:50:37 am »
___

But, rendering in two textures as you suggest is definitely a straightforward, very clean solution. Your code is by the way cristal clear. I'll go on with that solution. I might also put this in a Helper class as you suggested to Monokoi if you still find this useful for the wiki.

Again, thank you for your support.
The more I read about your engine, the more I like it because it doesn't constraint you in a specific way of using it and has all the necessary to build quite complex apps. Plus the fact that you keep full control on GLSL shading...
Congrats!

Fixta

@AeroShark

Reticles are perfectly aligned now that I take the half of the texture size as parameter for positioning.
How surprising ! :-)
Thanks for pointing that out in your reply!

Quote from: AeroShark333 on November 17, 2016, 02:55:54 PM
About the not centered issue, you probably need to correct your placement with (the destination height)/2 and the same for the width I'd say.
So: fb.getWidth()/2-destinationWidth/2
(not sure whether you need to subtract or add, I guess you'll find out if you try it)
destinationWidth being the amount of pixels your blit will take in width.

fb.blit(reticleTex, 0, 0, fb.getWidth()/2+290, fb.getHeight()/2, 256, 256 ,64, 64, 2, false);
Why are your arguments "64, 64" here? And "128, 128" in the other?:
And why "+290", it might look okay on your phone but I doubt placement would look good on phones with other screen resolutions! :p