[SOLVED] Aliasing/shimmering of textured geometries

My WIP has many geometries like the wooden platform shown below. Its Mesh was created in Blender and imported into JME via glTF. The Material is Phong-shaded (lit) material with a 1024x1024 “DiffuseMap” texture. The texture has a Bilinear magnification filter and a Trilinear minification filter. I’ve cranked up the multi-sampling anti-aliasing to 8x, but I’m still not satisfied with the render quality.

When the camera is motionless, there’s obvious aliasing around the “cracks” in the texture. When the camera moves, the cracks appear to shimmer. The aliasing and shimmering subside when the camera gets close to the platform. I don’t understand why.

Probably I’m missing something obvious. Any suggestions?

PS: I’ve also tried texture.setAnisotropicFilter(16);
PPS: Here’s my graphics configuration:

LWJGL 2.9.3 context running on thread jME3 Main
 * Graphics Adapter: nvd3dumx,nvwgf2umx,nvwgf2umx
 * Driver Version: 23.21.13.8873
 * Scaling Factor: 1
OpenGL Renderer Information
 * Vendor: NVIDIA Corporation
 * Renderer: GeForce GT 545/PCIe/SSE2
 * OpenGL Version: 4.6.0 NVIDIA 388.73
 * GLSL Version: 4.60 NVIDIA
 * Profile: Compatibility

What’s the wire frame look like? From a static picture it’s hard to tell but when I see aliasing like that then I think “polygon edge” and not “texture”.

Other than that, I’d confirm that the loaded texture really does have a min filter, is mipmapped, etc… assuming you haven’t already confirmed that by dumping the values at runtime.

Edit: looking again, it’s definitely acting like there is no min filter.

1 Like

I dumped the texture properties at runtime, but will doublecheck. Here’s the wireframe:

I checked the texture parameters at runtime and confirmed that they have the expected values.

I’ve created a simple test that demonstrates the aliasing issue but not the shimmering issue.

Shimmering is a much bigger issue for me; perhaps my WIP does something strange that triggers the shimmering. I’ll keep pursuing that angle.

Curiously, increasing the degree of anisotropic filtering makes the aliasing issue more pronounced.

Here’s the test code:

package jme3test.texture;

import com.jme3.app.SimpleApplication;
import com.jme3.asset.AssetManager;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.material.Material;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Limits;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.jme3.texture.Texture;

public class TestAnisotropicFilter extends SimpleApplication implements ActionListener {

    private BitmapText text;
    private int globalAniso = 1;
    private int maxAniso;

    @Override
    public void simpleInitApp() {
        maxAniso = renderer.getLimits().get(Limits.TextureAnisotropy);
        renderer.setDefaultAnisotropicFilter(globalAniso);

        flyCam.setDragToRotate(true);
        flyCam.setMoveSpeed(5f);
        float fHeight = cam.getFrustumTop() - cam.getFrustumBottom();
        float fWidth = cam.getFrustumRight() - cam.getFrustumLeft();
        float fAspect = fWidth / fHeight;
        cam.setFrustumPerspective(30f, fAspect, 0.02f, 200f);
        cam.setLocation(new Vector3f(-0.141f, 0.52f, -0.27f));
        cam.setRotation(new Quaternion(0.06f, 0.87451f, -0.1129f, 0.46782f));

        Quad q = new Quad(3f, 3f);
        Geometry geom = new Geometry("quad", q);
        geom.rotate(-FastMath.HALF_PI, 0, 0);
        geom.setMaterial(createWoodMaterial(assetManager));
        geom.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
        rootNode.attachChild(geom);

        inputManager.addMapping("higher", new KeyTrigger(KeyInput.KEY_1));
        inputManager.addMapping("lower", new KeyTrigger(KeyInput.KEY_2));
        inputManager.addListener(this, "higher");
        inputManager.addListener(this, "lower");

        text = new BitmapText(guiFont, false);
        text.setLocalTranslation(0f, cam.getHeight(), 0f);
        guiNode.attachChild(text);
    }

    @Override
    public void simpleUpdate(float tpf) {
        String msg = String.format("Anisotropy degree=%d max=%d",
                globalAniso, maxAniso);
        text.setText(msg);
    }

    private static Material createWoodMaterial(AssetManager assetManager) {
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        Texture tex = assetManager.loadTexture("wood.jpg");
        tex.setMagFilter(Texture.MagFilter.Bilinear);
        tex.setMinFilter(Texture.MinFilter.Trilinear);
        tex.setWrap(Texture.WrapMode.Repeat);
        mat.setTexture("ColorMap", tex);
        return mat;
    }

    @Override
    public void onAction(String name, boolean isPressed, float tpf) {
        if (isPressed) {
            return;
        }
        switch (name) {
            case "higher":
                globalAniso++;
                if (globalAniso > maxAniso) {
                    globalAniso = maxAniso;
                }
                renderer.setDefaultAnisotropicFilter(globalAniso);
                break;
            case "lower":
                globalAniso--;
                if (globalAniso < 1) {
                    globalAniso = 1;
                }
                renderer.setDefaultAnisotropicFilter(globalAniso);
                break;
        }
    }

    public static void main(String[] args) {
        TestAnisotropicFilter app = new TestAnisotropicFilter();
        app.start();
    }
}

Here’s the “wood.jpg” texture:
https://i.imgur.com/gdPGXaR.jpg

And here’s how the test looks after pressing the “1” key 15 times:

Shot in the dark… but maybe try:

TextureKey key = new TextureKey(path);
key.setGenerateMips(true);
Texture t = assets.loadTexture(key);

I don’t know that it will make a difference but I for some reason got in the habit of always specifying.

Edit: also try leaving the min/mag filtering as default and see what happens. I guess I’ve never used Trilinear min filter so I have no basis to know if it will/won’t work… and/or maybe it requires MIP mapping.

1 Like

Tried:

        TextureKey key = new TextureKey("wood.jpg");
        key.setGenerateMips(true);
        Texture tex = assetManager.loadTexture(key);

and saw no perceptible change.

Also commented out the mag/min filter settings … with no perceptible effect.

I know this may sound strange but when all else fails, try a different import file type.

With GLTF there is always something just not quite right in everything I have imported. Theirs always just one more tweak to get things just right.

1 Like

FUD aside, his test app just makes a Quad… no gltf involved.

1 Like

There’s a comment in com.jme3.renderer.opengl.GLFbo that worries me:

 * Available by default in OpenGL ES 2, but on desktop GL 2 
 * an extension is required.

glGenerateMipmapEXT() seems to be used to generate mipmaps, and it’s part of that interface. Is it possible my graphics adapter lacks this capability?

wow, i think almost every graphic adapter should support it imo.

i cant help with this topic, because i had some problems with it myself(idk if my fault) so i stop trying and i was doing other things.

maybe i will later try your testcase to see myself.

1 Like

ok, before i go sleep.

i just copy paste your code and added image to assets to be able to run it.

and it got

It’s true that the test app doesn’t use glTF. But then, the test app doesn’t illustrate the shimmering issue, either. So there might be a connection.

Invoking setGenerateMips(true) didn’t help the test app, but when I tried it on my WIP, it solved the shimmering issue (!)

Feeling much happier now.

The aliasing issue is still present in both the WIP and the test app. I would love to solve it as well. However, it’s only noticeable in closeups when the camera is stationary. I can live with it.

Thanks to everyone who helped!