For my tilemap implementation using Threejs and the custom shaders by Brandon Jones found here, I am utilizing a THREE.Plane
geometry for each layer. The face of the plane is painted with the following vertex and fragment shaders:
Vertex Shader:
var tilemapVS = [
"varying vec2 pixelCoord;",
"varying vec2 texCoord;",
"uniform vec2 mapSize;",
"uniform vec2 inverseTileTextureSize;",
"uniform float inverseTileSize;",
"void main(void) {",
" pixelCoord = (uv * mapSize);",
" texCoord = pixelCoord * inverseTileTextureSize * inverseTileSize;",
" gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);",
"}"
].join("\n");
Fragment Shader:
var tilemapFS = [
"varying vec2 pixelCoord;",
"varying vec2 texCoord;",
"uniform sampler2D tiles;",
"uniform sampler2D sprites;",
"uniform vec2 inverseTileTextureSize;",
"uniform vec2 inverseSpriteTextureSize;",
"uniform float tileSize;",
"uniform int repeatTiles;",
"void main(void) {",
" vec4 tile = texture2D(tiles, texCoord);", //load this pixel of the tilemap
" if(tile.x == 1.0 && tile.y == 1.0) { discard; }", //discard if R is 255 and G is 255
" vec2 spriteOffset = floor(tile.xy * 256.0) * tileSize;", //generate the offset in the tileset this pixel represents
" vec2 spriteCoord = mod(pixelCoord, tileSize);",
" vec4 texture = texture2D(sprites, (spriteOffset + spriteCoord) * inverseSpriteTextureSize);",
" gl_FragColor = texture;",
"}"
].join("\n");
The texturing setup for each texture is as follows:
//Setup Tilemap
this.tilemap.magFilter = THREE.NearestFilter;
this.tilemap.minFilter = THREE.NearestMipMapNearestFilter;
//tilemap.flipY = false;
if(this.repeat) {
this.tilemap.wrapS = this.tilemap.wrapT = THREE.RepeatWrapping;
} else {
this.tilemap.wrapS = this.tilemap.wrapT = THREE.ClampToEdgeWrapping;
}
//Setup Tileset
this.tileset.wrapS = this.tileset.wrapT = THREE.ClampToEdgeWrapping;
this.tileset.flipY = false;
if(this.filtered) {
this.tileset.magFilter = THREE.LinearFilter;
this.tileset.minFilter = THREE.LinearMipMapLinearFilter;
} else {
this.tileset.magFilter = THREE.NearestFilter;
this.tileset.minFilter = THREE.NearestMipMapNearestFilter;
}
The uniforms used in the shaders are set up like this:
//setup shader uniforms
this._uniforms = {
mapSize: { type: 'v2', value: new THREE.Vector2(this.tilemap.image.width * this.tileSize, this.tilemap.image.height * this.tileSize) },
inverseSpriteTextureSize: { type: 'v2', value: new THREE.Vector2(1/this.tileset.image.width, 1/this.tileset.image.height) },
tileSize: { type: 'f', value: this.tileSize },
inverseTileSize: { type: 'f', value: 1/this.tileSize },
tiles: { type: 't', value: this.tilemap },
sprites: { type: 't', value: this.tileset },
inverseTileTextureSize: { type: 'v2', value: new THREE.Vector2(1/this.tilemap.image.width, 1/this.tilemap.image.height) },
repeatTiles: { type: 'i', value: this.repeat ? 1 : 0 }
};
The geometry and mesh creation takes place as follows:
//create the shader material
this._material = new THREE.ShaderMaterial({
uniforms: this._uniforms,
vertexShader: tilemapVS,
fragmentShader: tilemapFS,
transparent: false
});
this._plane = new THREE.PlaneGeometry(
this.tilemap.image.width * this.tileSize * this.tileScale,
this.tilemap.image.height * this.tileSize * this.tileScale
);
this._mesh = new THREE.Mesh(this._plane, this._material);
this._mesh.z = this.zIndex;
The variables this.tileSize
and this.tileScale
have values of 16 and 4 respectively. However, I am encountering some tearing around the edges of the 16x16 tiles:
Interestingly, this tearing only occurs sporadically when moving along the y-axis. On my Linux machine, the issue is more pronounced and affects the x-axis as well.
It seems like there might be a slight misalignment of the 16x16 tiles caused by my vertex shader, but I'm unsure of the root cause. Any assistance on resolving this issue would be greatly appreciated. Thank you!
Edit
Here's an enhanced image showing the tearing, particularly noticeable on grassy areas:
As evident from the image, the tearing occurs along the edges of the scaled 16x16 tiles (scaled by 4).