Визуализация карты TMX на плоскости Threejs
Вопрос обновлен новым кодом
Я пытаюсь написать шейдер WebGL, который будет рисоватьTMX Layer (экспортировано из Tiled-редактора). Я использую THREE.js для созданияPlane
сетка и материал должен бытьShaderMaterial
это нарисует карту на нем.
Для тех, кто не знает карту тайла, экспортируемуюПлиточный редактор как JSON дастdata
атрибут для каждого слоя; он содержит массив числовых значений, каждое из которых является индексом плитки в наборе плиток, например:
"data": [5438, 5436, 5437, 5438, 5436, 5437, 5438, 5436, 5437, 5438, 845, ...]
Поскольку моя мозаичная карта имеет размер 256 на 256 плиток, длина массива составляет 65536 элементов. Значение каждого элемента относится к листу в карте листов, где индексы определены следующим образом:
набор плиток http://www.melonjs.org/docs/symbols/spritesheet_grid.png
Таким образом, индекс 0 карты тайлов относится к тайлу 5438, где они подсчитываются, как указано выше. Индексы представляют, какая плитка в карте тайлов имеет эту плитку из набора тайлов, где они подсчитываются одинаковым образом.
Вот как я создаю материал, плоскость и сетку:
this.size = new THREE.Vector2(256, 256);
this.tileSize = new THREE.Vector2(16, 16);
this._material = new THREE.ShaderMaterial({
uniforms: this._uniforms,
vertexShader: this.vShader,
fragmentShader: this.fShader,
transparent: (this.opacity === 0)
});
this._plane = new THREE.PlaneGeometry(
this.size.x * this.tileSize.x,
this.size.y * this.tileSize.y
);
this._mesh = new THREE.Mesh(this._plane, this._material);
И, наконец, униформа и шейдеры. По сути, мне нужно отобразить элемент данных на фактическую плитку в наборе плиток и нарисовать его. Для того, чтобы получитьdata
массив в шейдер я загружаю его какTHREE.DataTexture
и рассматривая это как текстуру.
Вот моя вторая попытка:
//Shaders
var vShader = [
'varying vec2 pixelCoord;',
'varying vec2 texCoord;',
'uniform vec2 layerSize;',
'uniform vec2 tilesetSize;',
'uniform vec2 inverseTilesetSize;',
'uniform vec2 tileSize;',
'uniform vec2 inverseTileSize;',
'uniform float scale;',
'void main(void) {',
' pixelCoord = (uv * layerSize) * tileSize * scale;', //pixel we are at
' texCoord = pixelCoord * inverseTilesetSize * inverseTileSize;', //calculate the coord on this map
' gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);',
'}'
].join('\n');
var fShader = [
//"precision highp float;",
'varying vec2 pixelCoord;',
'varying vec2 texCoord;',
'uniform vec2 tilesetSize;',
'uniform vec2 inverseTilesetSize;',
'uniform vec2 tileSize;',
'uniform vec2 inverseTileSize;',
'uniform vec2 numTiles;',
'uniform float scale;',
'uniform sampler2D tileset;',
'uniform sampler2D tileIds;',
//I store the tile IDs as a texture (1 float value = rgba)
//this will decode the rgba values back into a float ID
'highp float decode32(highp vec4 rgba) {',
' const vec4 bit_shift = vec4(1.0/(256.0*256.0*256.0), 1.0/(256.0*256.0), 1.0/256.0, 1.0);',
' float depth = dot(rgba, bit_shift);',
' return depth;',
'}',
'void main(void) {',
' vec4 tileId = texture2D(tileIds, texCoord);', //grab this tileId from the layer data
' tileId.rgba = tileId.abgr;', //flip flop due to endianess
//I find that this value is always `0 < tileValue < 1`, I think my `decode32` sucks...
' float tileValue = decode32(tileId);', //decode the vec4 into the float ID
' vec2 tileLoc = vec2(mod(tileValue, numTiles.y), floor(tileValue / numTiles.y));', //convert the ID into x, y coords
' vec2 coord = floor(tileLoc * 256.0) * tileSize;', //coord in the tileset
' vec2 offset = mod(pixelCoord, tileSize);', //how much to draw
' gl_FragColor = texture2D(tileset, (coord + offset) * inverseTilesetSize);', //grab tile from tilset
'}'
].join('\n');
И униформа, и текстура данных:
//tried making this 256 x 256 like it is conceptually,
//and also tried 65536 x 1 like the data structure
this.dataTex = new THREE.DataTexture(
this.data,
this.data.length, //width (65536)
1, //height (1)
THREE.RGBAFormat, //format
THREE.UnsignedByteType, //type
THREE.UVMapping, //mapping
THREE.ClampToEdgeWrapping, //wrapS
THREE.ClampToEdgeWrapping, //wrapT
THREE.NearestFilter, //magFilter
THREE.NearestMipMapNearestFilter //minFilter
);
this.dataTex.needsUpdate = true;
this._uniforms = window._uniforms = {
layerSize: { type: 'v2', value: this.size },
tilesetSize: { type: 'v2', value: new THREE.Vector2(this.tileset.image.width, this.tileset.image.height) },
inverseTilesetSize: { type: 'v2', value: new THREE.Vector2(1 / this.tileset.image.width, 1 / this.tileset.image.height) },
tileSize: { type: 'v2', value: this.tileSize },
inverseTileSize: { type: 'v2', value: new THREE.Vector2(1 / this.tileSize.x, 1 / this.tileSize.y) },
numTiles: { type: 'v2', value: new THREE.Vector2(this.tileset.image.width / this.tileSize.x, this.tileset.image.height / this.tileSize.y) },
scale: { type: 'f', value: 1 / this.scale },
tileset: { type: 't', value: this.tileset },
tileIds: { type: 't', value: this.dataTex },
repeatTiles: { type: 'i', value: this.repeat ? 1 : 0 }
};
Поэтому, когда это рендерится, я просто получаю первый тайл набора тайлов снова и снова:
Не уверен, что это вызывает, но так как он находится в положении0, 0
Я думаю, что у меня где-то есть ноль.