Context
I’m trying to get multisampling anti aliasing to work in VR. My understanding is (and correct me if I’m wrong) that I need to render to a framebuffer that has multiple samples but then blit to one that hasn’t (because the swapchain images I obtain from OpenXR inherently do not support multiple samples).
JME provides this blitting functionality via renderer#copyFrameBuffer. I am doing all of this within a postRender of an AppState because this is all really pushing things towards the VR headset after JME has finished rendering
Problem
I’m finding that the framebuffer that is blitted to is mostly blank, with just a corner filled in correctly. After a while of experimenting I found that the corner that the size of the corner that is filled in is controlled by the settings for the main window (Not actually the same, they just seem related; if I change the main window size it changes how much of the cameras view copied). Both frame buffers have the same width and height.
Minimal reproducible example
In this I have a second camera in the scene, in takes a photo and in the post render stage adds a cube to the scene to show what it saw (I recognise that this is a bit odd, but its the closest I could get to what’s happening in the VR flow). The picture rendered on the cube is what would get sent to the VR headset
public class AntialiasTest extends SimpleApplication {
int width = 1500;
int height = 1500;
FrameBuffer renderedIntoframeBuffer;
FrameBuffer blittedToFrameBuffer;
boolean addedBox = false;
public static void main(String[] args) {
AntialiasTest app = new AntialiasTest();
AppSettings settings = new AppSettings(true);
settings.setSamples(1);
app.setSettings(settings);
app.setShowSettings(false);
app.start();
}
@Override
public void simpleInitApp() {
getViewPort().setBackgroundColor(ColorRGBA.Brown);
Camera additionalCamera = new Camera(width, height);
additionalCamera.setLocation(cam.getLocation());
additionalCamera.setParallelProjection(false);
additionalCamera.setFrustumPerspective(90f, (float) width / height, 0.1f, 100f);
additionalCamera.lookAt(new Vector3f(3,4,0), Vector3f.UNIT_Y);
ViewPort newViewport = getRenderManager().createMainView( "Additional Eye", additionalCamera);
newViewport.setClearFlags(true, true, true);
newViewport.attachScene(rootNode);
newViewport.setBackgroundColor(ColorRGBA.Brown);
renderedIntoframeBuffer = frameBufferRenderedInto();
blittedToFrameBuffer = frameBufferBlittedTo();
newViewport.setOutputFrameBuffer(renderedIntoframeBuffer);
Box b = new Box(1, 1, 1);
Geometry geom = new Geometry("Box", b);
Quaternion q = new Quaternion();
q.fromAngleAxis(FastMath.PI/3, new Vector3f(1,1,1));
geom.setLocalRotation(q);
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
mat.setColor("Color", ColorRGBA.Blue);
geom.setMaterial(mat);
rootNode.attachChild(geom);
getStateManager().attach(new BaseAppState(){
@Override protected void initialize(Application app){}
@Override protected void cleanup(Application app){}
@Override protected void onEnable(){}
@Override protected void onDisable(){}
@Override
public void postRender(){
renderer.copyFrameBuffer(renderedIntoframeBuffer, blittedToFrameBuffer, true, false);
if(!addedBox){
updateTextureFromFrameBuffer(blittedToFrameBuffer); //<-- if I change this to renderedIntoframeBuffer it looks fine
}
}
});
}
private FrameBuffer frameBufferRenderedInto(){
int samples = 1;
FrameBuffer frameBuffer = new FrameBuffer(width, height,samples);
frameBuffer.setName("OpenXR eye buffer copied mode");
Texture2D texture = new Texture2D(width, height, samples, Image.Format.RGBA8);
Texture2D msDepth = new Texture2D(width, height, samples, Image.Format.Depth);
frameBuffer.addColorTarget(FrameBuffer.FrameBufferTarget.newTarget(texture));
frameBuffer.setDepthTarget(FrameBuffer.FrameBufferTarget.newTarget(msDepth));
return frameBuffer;
}
private FrameBuffer frameBufferBlittedTo(){
FrameBuffer frameBuffer = new FrameBuffer(width, height, 1);
Texture2D texture = new Texture2D(width, height, 1, Image.Format.RGBA8);
frameBuffer.addColorTarget(FrameBuffer.FrameBufferTarget.newTarget(texture));
return frameBuffer;
}
/**
* Render the FrameBuffer to a cube to see what it sees
*/
private void updateTextureFromFrameBuffer(FrameBuffer frameBuffer) {
Texture2D tex = (Texture2D) frameBuffer.getColorTarget().getTexture();
Material texturedMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
texturedMat.setTexture("ColorMap", tex);
Box boxWithTexture = new Box(1, 1, 1);
Geometry texturedGeom = new Geometry("TexturedBox", boxWithTexture);
texturedGeom.setMaterial(texturedMat);
texturedGeom.setLocalTranslation(2, 0, -5);
rootNode.attachChild(texturedGeom);
}
}
If instead I render the renderedIntoframeBuffer on the cube it looks correct, so whatever is going wrong it’s in the copy
updateTextureFromFrameBuffer(renderedIntoframeBuffer);
Any idea what is going on? And how I can copy the whole of the secondary scene from one frame buffer to another?
This occurs both in JME 3.6.1 and 3.7.0-alpha3