I have a blue line running down the center of my tarmac texture. I am using a 16x16 sized texture atlas and the blue is the color of the texture right next to the tarmac one. This seems to be a floating point error? I’m not sure. Any tips on how to track down this problem? I can post the shader code if that would be helpful in analyzing whats going on.
When I examined all the texture coordinates I’m sending to the shader they are all just multiples of 0.0625 because it is a 16x16 map. Which I checked because I thought maybe it was a floating point error in the texture coordinates I was sending.
I really need to figure this out and I’m at a loss at how to approach further debugging.
Yeah, that proves there must be some funky math in your code somewhere, I guess. Or maybe you hit the limits of floating point accuracy. (It’s quite likely, for example, that 0.0625 is actually 0.062499999999)
It should be pretty trivial to put together a simple test case that proves this one way or another.
Because all that being said, I’ve never hit this specific issue that it didn’t turn out to be an error somewhere on my part. I wasn’t ever dividing textures that small, though.
Can’t you just offset the texcoords inwards by the inverse of the error? Doesn’t really seem like a specific issue and something that’s bound to always happen with textures anyway.
Yes floating point errors exist, so it is and we have to live with it.
package mygame;
import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Quad;
import com.jme3.system.AppSettings;
import com.jme3.texture.Texture;
import com.jme3.util.BufferUtils;
import java.util.prefs.BackingStoreException;
/**
* This is the Main Class of your Game. You should only do initialization here.
* Move your Logic into AppStates or Controls
*
* @author wobblytrout
*/
public class Main extends SimpleApplication {
private static AppSettings settings = new AppSettings(true);
public static void main(String[] args) throws BackingStoreException {
Main app = new Main();
settings.setWidth(1024);
settings.setHeight(576);
settings.setVSync(true);
settings.load("Simple Test Case");
settings.setTitle("Simple Test Case");
settings.setMinResolution(1024, 576);
settings.setAudioRenderer(null);
app.setShowSettings(false);
app.setSettings(settings);
app.start();
}
@Override
public void simpleInitApp() {
// create a grid of quad objects and apply part of the texture from the texture atlas we are troubleshooting.
// set the camera to be controlled by the keyboard
getFlyByCamera().setEnabled(false);
getCamera().setLocation(new Vector3f(0f, 0f, 250f));
Node geomNode = new Node("geomNode");
for (int x = 0; x < 64; x++) {
for (int y = 0; y < 64; y++) {
Geometry geom = createTestQuad();
// quads are 16x16
geom.setLocalTranslation(x * 16f, y * 16f, 0f);
geomNode.attachChild(geom);
}
}
geomNode.setLocalTranslation(-512,-512,0);
rootNode.attachChild(geomNode);
}
private Geometry createTestQuad() {
// my game uses 16x16 sized tiles.
Quad quad = new Quad(16f, 16f);
Geometry geom = new Geometry("Quad", quad);
Mesh mesh = geom.getMesh();
Vector2f[] textureCoordinatesArray = new Vector2f[4];
// 1, 4 is the position in the texture we wish to test...
textureCoordinatesArray[0] = new Vector2f(0.1875f, 0.0625f);
textureCoordinatesArray[1] = new Vector2f(0.250f, 0.0625f);
textureCoordinatesArray[2] = new Vector2f(0.250f, 0.125f);
textureCoordinatesArray[3] = new Vector2f(0.1875f, 0.125f);
mesh.setBuffer(VertexBuffer.Type.TexCoord, 2, BufferUtils.createFloatBuffer(textureCoordinatesArray));
Material mat = setupQuadMaterial();
geom.setMaterial(mat);
return geom;
}
private Material setupQuadMaterial() {
Material meshMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
Texture texture = assetManager.loadTexture("Textures/terrain.png");
texture.setMagFilter(Texture.MagFilter.Nearest);
texture.setMinFilter(Texture.MinFilter.NearestNoMipMaps);
meshMaterial.setTexture("ColorMap", texture);
return meshMaterial;
}
@Override
public void simpleUpdate(float tpf) {
//TODO: add update code
}
@Override
public void simpleRender(RenderManager rm) {
//TODO: add render code
}
}
private Geometry createTestQuad() {
float unit = 0.1875f;
float x = 1*unit+0.001f;
float y = 0*unit+0.001f;
// my game uses 16x16 sized tiles.
Quad quad = new Quad(16f, 16f);
Geometry geom = new Geometry("Quad", quad);
Mesh mesh = geom.getMesh();
Vector2f[] textureCoordinatesArray = new Vector2f[4];
// 1, 4 is the position in the texture we wish to test...
textureCoordinatesArray[0] = new Vector2f(x,y);
textureCoordinatesArray[1] = new Vector2f(x,y + unit*0.99f);
textureCoordinatesArray[2] = new Vector2f(x + unit*0.99f,y + unit*0.99f);
textureCoordinatesArray[3] = new Vector2f(x + unit*0.99f,y);
mesh.setBuffer(VertexBuffer.Type.TexCoord, 2, BufferUtils.createFloatBuffer(textureCoordinatesArray));
Material mat = setupQuadMaterial();
geom.setMaterial(mat);
return geom;
}
Also your unit is kinda weird, 384x384? That doesn’t divide 2048 very well.
Ok, so I ended up discussing this at length on the discord with @MoffKalast and a few others.
Seems my options to fix this are this.
add padding between texture altas elements. In this case a 1 pixel repeat of the texture itself would work. So minimum 2 pixels of padding.
do some weird half pixel round stuff in the frag shader?
implement texture arrays and use that instead.
My question now is texture atlas vs texture arrays? Which is a better idea moving forward? Is there any performance reason to stick with a texture atlas instead of a texture array?
@pspeed thank you for your help man, I really appreciate it. Your libraries are awesome. I have learned so much from your examples and libraries I really appreciate what you’ve done.
Talking about texture array performance: When i changed my pbr terrain shader from a lot of diffuse samplers to texture array, i recognized a little drain of performance, i lost around 5-10fps in average (i was measuring in ms but that was not much).
I am glad that i changed to it - it makes some thing much easier( global snow /noise texture in the array). But here some things you will have to keep in mind:
You will duplicate the “data”, if you use the base textures in other places (They aren’t shared anymore, because you have to assign the data to the texture array). This will lead to the point where you could do the same as i’ve done, by having all textures in one huge texture array.
All elements in the texture array have the same format and dimensions (e.g. RGBA8 2048x2048). This can introduce the need of converting all textures.
Like was there somebody on the dev team like “Hey we should probably support arrays of all data types that we have, like every programming language in existence you know?” and some other guy with more authority was like “Nah, who’ll ever need arrays of textures, that’s just mad!”
Perhaps they just didn’t anticipate Apple being such dicks.