[Solved] How to downsample the screen texture in a filter?

I haven’t been able to find much documentation on filters at all I can’t seem to figure out how this should work.

What I’ve pieced together from the BloomFilter which doesn’t seem to render anything when run:

@Override
protected void initFilter(AssetManager assetManager, RenderManager rm, ViewPort vp, final int w, final int h) {
    screenWidth = (int) Math.max(1, (w / downSamplingFactor));
    screenHeight = (int) Math.max(1, (h / downSamplingFactor));
	
    Material blank = new Material(assetManager, "assets/filters/lensflare/Blank.j3md");
    
    //I think I may need to make a new pass that renders at a lower res?
    downscalepass = new Pass();
    downscalepass.setPassMaterial(blank);
    downscalepass.init(rm.getRenderer(), screenWidth, screenHeight, Format.RGBA8, Format.Depth);
    
    //and then render it again?
    material = new Material(assetManager, "assets/filters/lensflare/Blank.j3md");
    Pass main = new Pass(){
	@Override
	public void beforeRender() {
	    material.setTexture("Texture", downscalepass.getRenderedTexture());
	}
    };
    main.setPassMaterial(material);
    main.init(rm.getRenderer(), w, h, Format.RGBA8, Format.Depth);
}

Blank.frag is pretty blank for testing:

#import "Common/ShaderLib/MultiSample.glsllib"

uniform sampler2D m_Texture;

in vec2 texCoord;
out vec4 fragColor;

void main() {
    fragColor = texture2D(m_Texture, texCoord);
}

The wiki only explains how to add filters to the game and not how they’re actually supposed to be set up when making custom ones so I’m kind of at a loss here.

Any help or tip would be appreciated.

You have to add the pass to a collection called postPasses or something like that (can’t look at the code right now).
Look for a collection like that in Filter.java

You’re right, I somehow missed that (postRenderPasses). Thanks!

It didn’t start to work right away, since I forgot a few other things but starting from the BloomFilter and removing all blur related stuff seems to have solved it.

Also requiresSceneAsTexture looks important.

For future use, a filter that does only downsampling (sorry bout the indentation mess):

PixelationFilter.java

public class PixelationFilter extends Filter {
	
    private float downSamplingFactor = 4;
    private Pass downScalePass;
    private Material extractMat;
    private int screenWidth;
    private int screenHeight;    
    private RenderManager renderManager;
    private ViewPort viewPort;

    private AssetManager assetManager;
    private int initalWidth;
    private int initalHeight;
    
    public PixelationFilter() {
        super("PixelationFilter");
    }


    @Override
    protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
        this.renderManager = renderManager;
        this.viewPort = vp;

        this.assetManager = manager;
                
        screenWidth = (int) Math.max(1, (w / downSamplingFactor));
        screenHeight = (int) Math.max(1, (h / downSamplingFactor));
        
        postRenderPasses = new ArrayList<Pass>();
        
        extractMat = new Material(manager, "assets/filters/Blank.j3md");
        downScalePass = new Pass() {
            @Override
            public boolean requiresSceneAsTexture() {
                return true;
            }
        };

        downScalePass.init(renderManager.getRenderer(), screenWidth, screenHeight, Format.RGBA8, Format.Depth, 1, extractMat);
        postRenderPasses.add(downScalePass);     
        
        material = new Material(manager, "assets/filters/Blank.j3md");
        material.setTexture("Texture2", downScalePass.getRenderedTexture());
    }


    protected void reInitFilter() {
        initFilter(assetManager, renderManager, viewPort, initalWidth, initalHeight);
    }

    @Override
    protected void cleanUpFilter(Renderer r) {
    	downScalePass.cleanup(r);
    }

    
    public float getFactor() {
        return downSamplingFactor;
    }

    public void setFactor(float downSamplingFactor) {
        this.downSamplingFactor = downSamplingFactor;
        if (assetManager != null)
            reInitFilter();
    }


	@Override
	protected Material getMaterial() {
		return material;
	}
}

Pixelate.frag

uniform sampler2D m_Texture;

#ifdef TEX2
	uniform sampler2D m_Texture2;
#endif

varying vec2 texCoord;

void main() {
	#ifdef TEX2
		gl_FragColor = texture2D(m_Texture2, texCoord);
	#else
		gl_FragColor = texture2D(m_Texture, texCoord);
	#endif
}

Pixelate.j3md

MaterialDef Fade {

    MaterialParameters {
        Int NumSamples
        Texture2D Texture
        Texture2D Texture2
    }

    Technique {
        VertexShader GLSL100:   Common/MatDefs/Post/Post.vert
        FragmentShader GLSL100: Common/MatDefs/Pixelate/Pixelate.frag

        WorldParameters {
            WorldViewProjectionMatrix
        }
        
        Defines {
        	TEX2: Texture2
        }
    }
}

Probably not the best idea but the default pass always gets the scene as the “Texture” param and overwrites the passed one.

2 Likes