So to implement a tilemap using Threejs and Brandon Jone's tilemap method (found here) I am using a THREE.Plane
geometry for each layer, and painting the face with the following custom 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");
Each texture is setup like:
//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; }
And the uniforms are:
//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 } };
And the actual geometry and mesh:
//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;
this.tileSize
is 16
and this.tileScale
is 4
. The problem I am having is that around the edges of the 16x16 tiles I get some tearing:
The strange part is that it doesn't happen all the time, only sparatically when moving along the y axis (however on my linux box the issue is much worse and effects the x axis as well).
It is almost like the 16x16 tiles are off by a small amount when placed with my vertex shader; but I am not sure what is causing it. Any help is appreciated, thanks!
Edit
Here is a better image of the tearing, it is more visible on grassy areas:
As you can see it is along the 16x16 tile edges (since they are scaled by 4
).