[SOLVED] Multisampling Framebuffers

I’m developing an application at my day job that offscreen renders maps using a JME offscreen viewport. I have the offscreen camera/viewport/framebuffer set up and working (that’s no problem), but when I tried to enable multiple samples on the framebuffer then I get the following:

It should look like this, but hopefully with the mostly horizontal-ish green line looking nicely antialiased:

Here’s the class that handles the offscreen rendering:

public class TileRenderer extends BaseAppState {
	
	private Application app;
	
	private Node mapRoot;
	
	private ViewPort viewport;
	private Camera cam;
	private FrameBuffer buff;
	private Texture2D tex;
	
	public TileRenderer(){
		mapRoot = new Node("Map Root");
		
		tex = new Texture2D(512, 512, 1, Image.Format.RGBA8);
		tex.setMinFilter(Texture.MinFilter.Trilinear);
		tex.setMagFilter(Texture.MagFilter.Bilinear);
	}

	@Override
	public void initialize(Application app) {
		this.app = app;
		
		cam = new Camera(512, 512);
		
		// map textures should be parallel projected
		cam.setParallelProjection(true);
		
		// TODO - adjust frustum with zoom level
		cam.setFrustum(-1000, 1000, -512, 512, 512, -512);
		
		// on X,Y axes, small Z distance
		cam.setLocation(new Vector3f(0, 0, 50f));
		cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);
		
		viewport = app.getRenderManager().createPreView("mapViewport", cam);
		viewport.setClearColor(true);
		viewport.setBackgroundColor(ColorRGBA.White);
		
		buff = new FrameBuffer(512, 512, 1);
		
		// Enable color texture only. We don't need depth
		// to render map tiles
		buff.setColorTexture(tex);
		
		viewport.setOutputFrameBuffer(buff);
		
	}
	
	public Texture2D getTile(){
		return tex;
	}

	@Override
	protected void cleanup(Application app) {
		app.getRenderer().deleteFrameBuffer(buff);
	}

	@Override
	protected void onEnable() {
		
		Line line = new Line(Vector3f.ZERO, new Vector3f(0f, 256f, 0f));
		Geometry lineGeom = new Geometry();
		lineGeom.setMesh(line);
		Material lineMat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
		lineMat.setColor("Color", ColorRGBA.Green);
		lineMat.getAdditionalRenderState().setLineWidth(1.0f);
		lineGeom.setMaterial(lineMat);
		mapRoot.attachChild(lineGeom);
		
		line = new Line(Vector3f.ZERO, new Vector3f(474f, 6f, 0f));
		lineGeom = new Geometry();
		lineGeom.setMesh(line);
		lineGeom.setMaterial(lineMat);
		mapRoot.attachChild(lineGeom);
		
        viewport.attachScene(mapRoot);
	}
	
	@Override
	public void update(float tpf){
		mapRoot.updateLogicalState(tpf);
		mapRoot.updateGeometricState();
	}

	@Override
	protected void onDisable() {
		mapRoot.detachAllChildren();
		viewport.detachScene(mapRoot);
	}

}

This class, as posted, correctly renders the (non-antialiased) result in the second screenshot. If you change the texture and framebuffer creation to:

tex = new Texture2D(512, 512, 2, Image.Format.RGBA8);

and

buff = new FrameBuffer(512, 512, 2);

respectively, it produces the result in the first screenshot.

Here’s my main class:


import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.jme3.system.AppSettings;

public class TestTileRenderer extends SimpleApplication {
	
	private TileRenderer mapper;
	
	public static void main(String[] args){
		
		TestTileRenderer mapper = new TestTileRenderer();
		
		// enable antialiasing by default, and default
		// to a reasonably large resolution
		AppSettings settings = new AppSettings(true);
		settings.setSamples(4);
		settings.setResolution(1280, 960);
		mapper.setSettings(settings);
		
		mapper.start();
	}

	@Override
	public void simpleInitApp() {
		
		// set up the mapper
		mapper = new TileRenderer();
		stateManager.attach(mapper);
		
		Quad testQuad = new Quad(10, 10);
		Geometry testGeo = new Geometry("Test Quad");
		testGeo.setMesh(testQuad);
		Material testMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
		testMat.setTexture("ColorMap", mapper.getTile());
		testGeo.setMaterial(testMat);
		
		rootNode.attachChild(testGeo);
		testGeo.setLocalTranslation(-5f, -5f, -5f);
		
	}

}

Have I committed a no-no in my offscreen rendering setup, or is this a bug?

you can’t read a multisampled framebuffer if you are bellow opengl3.2, you have to copy it in a single sample framebuffer, then read this framebuffer.
Look at how it’s done in the FilterPostProcessor.
the init

the copy

That resolved it for me. Thanks much!