我非常喜欢Brandon Jones的一篇博客文章(在这里找到)。我想将他的代码转换为Three.js,但是我遇到了一些问题。你可以在这里找到他的完整代码。以下是我迄今为止的尝试,并附有一些我提出的问题的注释:
// Shader
var tilemapVS = [
"attribute vec2 pos;",
"attribute vec2 texture;",
"varying vec2 pixelCoord;",
"varying vec2 texCoord;",
"uniform vec2 viewOffset;",
"uniform vec2 viewportSize;",
"uniform vec2 inverseTileTextureSize;",
"uniform float inverseTileSize;",
"void main(void) {",
" pixelCoord = (texture * viewportSize) + viewOffset;",
" texCoord = pixelCoord * inverseTileTextureSize * inverseTileSize;",
" gl_Position = vec4(pos, 0.0, 1.0);",
"}"
].join("\n");
var tilemapFS = [
"precision highp float;",
"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) {",
" if(repeatTiles == 0 && (texCoord.x < 0.0 || texCoord.x > 1.0 || texCoord.y < 0.0 || texCoord.y > 1.0)) { discard; }",
" vec4 tile = texture2D(tiles, texCoord);",
" if(tile.x == 1.0 && tile.y == 1.0) { discard; }",
" vec2 spriteOffset = floor(tile.xy * 256.0) * tileSize;",
" vec2 spriteCoord = mod(pixelCoord, tileSize);",
" gl_FragColor = texture2D(sprites, (spriteOffset + spriteCoord) * inverseSpriteTextureSize);",
//" gl_FragColor = tile;",
"}"
].join("\n");
this.material = new THREE.ShaderMaterial({
attributes: {
//not really sure what to use here, he uses some quadVertBuffer
//for these values, but not sure how to translate.
pos: { type: 'v2', value: new THREE.Vector2(0, 0) },
texture: { type: 'v2', value: new THREE.Vector2(0, 0) }
},
uniforms: {
viewportSize: { type: 'v2', value: new THREE.Vector2(viewport.width() / this.tileScale, viewport.height() / this.tileScale) },
inverseSpriteTextureSize: { type: 'v2', value: new THREE.Vector2(1/tileset.image.width, 1/tileset.image.height) },
tileSize: { type: 'f', value: this.tileSize },
inverseTileSize: { type: 'f', value: 1/this.tileSize },
tiles: { type: 't', value: tilemap },
sprites: { type: 't', value: tileset },
viewOffset: { type: 'v2', value: new THREE.Vector2(Math.floor(0), Math.floor(0)) },
inverseTileTextureSize: { type: 'v2', value: new THREE.Vector2(1/tilemap.image.width, 1/tilemap.image.height) },
//is 'i' the correct type for an int?
repeatTiles: { type: 'i', value: 1 }
},
vertexShader: tilemapVS,
fragmentShader: tilemapFS,
transparent: false
});
/*this.material = new THREE.MeshBasicMaterial({
color: 0xCC0000
})*/
this.plane = new THREE.PlaneGeometry(
tilemap.image.width * this.tileSize * this.tileScale, //width
tilemap.image.height * this.tileSize * this.tileScale//, //height
//tilemap.image.width * this.tileScale, //width-segments
//tilemap.image.height * this.tileScale //height-segments
);
this.plane.dynamic = true;
this.mesh = new THREE.Mesh(this.plane, this.material);
一旦我加载页面,就会出现以下错误:
TypeError: v1 is undefined
customAttribute.array[ offset_custom ] = v1.x;
我确定这与我的属性设置有关,但我不确定应该是什么。由于Three.js中的自定义着色器文档很少或根本没有,因此任何帮助都将不胜感激。
编辑:以下是博客文章中用于填充顶点着色器(pos和texture)的2个属性的代码:
//in ctor
var quadVerts = [
//x y u v
-1, -1, 0, 1,
1, -1, 1, 1,
1, 1, 1, 0,
-1, -1, 0, 1,
1, 1, 1, 0,
-1, 1, 0, 0
];
this.quadVertBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVertBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(quadVerts), gl.STATIC_DRAW);
this.tilemapShader = GLUtil.createProgram(gl, tilemapVS, tilemapFS);
//...
//then on the draw method
gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVertBuffer);
gl.enableVertexAttribArray(shader.attribute.position);
gl.enableVertexAttribArray(shader.attribute.texture);
gl.vertexAttribPointer(shader.attribute.position, 2, gl.FLOAT, false, 16, 0);
gl.vertexAttribPointer(shader.attribute.texture, 2, gl.FLOAT, false, 16, 8);
我真的不完全理解这里到底发生了什么,但如果我没错的话,我认为它正在将两个
Float32Array
填充到每个 quadVertBuffer
的一半数据中。我不仅不确定为什么,也不确定我是否正确,也不知道如何将其转换为 Three.js 的方法。
编辑2:目前我正在使用平面来显示(2D)背景,我是否应该改用精灵?
编辑3::
当我意识到Three.js会为我设置位置和uv向量时,我又前进了一步(这似乎与上面的例子中的位置/纹理相似甚至相同)。我还注意到,由于许多我的'v2'
类型(调用uniform2f
)实际上是通过uniform2fv
加载的,所以我把它们改成了'v2v'
并更新了值。现在我不再收到错误信息了,并且它确实绘制了某些东西,只是不完全是瓦片地图。
以下是更新后的顶点着色器:
var tilemapVS = [
"varying vec2 pixelCoord;",
"varying vec2 texCoord;",
"uniform vec2 viewOffset;",
"uniform vec2 viewportSize;",
"uniform vec2 inverseTileTextureSize;",
"uniform float inverseTileSize;",
"void main(void) {",
" pixelCoord = (uv * viewportSize) + viewOffset;",
" texCoord = pixelCoord * inverseTileTextureSize * inverseTileSize;",
" gl_Position = vec4(position.x, position.y, 0.0, 1.0);",
"}"
].join("\n");
和更新后的着色器材质:
this._material = new THREE.ShaderMaterial({
uniforms: {
viewportSize: { type: 'v2v', value: [new THREE.Vector2(viewport.width() / this.tileScale, viewport.height() / this.tileScale)] },
inverseSpriteTextureSize: { type: 'v2v', value: [new THREE.Vector2(1/tileset.image.width, 1/tileset.image.height)] },
tileSize: { type: 'f', value: this.tileSize },
inverseTileSize: { type: 'f', value: 1/this.tileSize },
tiles: { type: 't', value: tilemap },
sprites: { type: 't', value: tileset },
viewOffset: { type: 'v2', value: new THREE.Vector2(0, 0) },
inverseTileTextureSize: { type: 'v2v', value: [new THREE.Vector2(1/tilemap.image.width, 1/tilemap.image.height)] },
repeatTiles: { type: 'i', value: 1 }
},
vertexShader: tilemapVS,
fragmentShader: tilemapFS,
transparent: false
});
这是我得到的结果:
编辑4:
如果我将顶点着色器更改为使用我发现的“Three.js方法”来设置gl_Position
,我可以更接近目标,但是精灵表中的偏移量不正确。我认为pixelCoord
变量设置错误了(因为uv
比texture
略有不同)。
我将顶点着色器的主函数更改为:
void main(void) {
pixelCoord = (uv * viewportSize) + viewOffset;
texCoord = pixelCoord * inverseTileTextureSize * inverseTileSize;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
现在我从纹理图中获取了实际的瓷砖,但它选择的实际瓷砖是错误的。
编辑5:
我猜这将是我的最后一次更新,因为我即将得到答案。在设置tileset.flipY = false;
之后,其中tileset是实际的纹理图块,而不是红色地图。我得到了所有正确的图块落在正确的位置;除了它们都是上下颠倒的!
在此更改后,它看起来像这样:
我注意到,如果我不翻转两个(tilemap.flipY = false;和tileset.flipY = false;),我会得到正确的纹理,在正确的位置上,正确地拼合在一起。但是整个地图是颠倒的!就差一步了...
js/game/lib/core/TileMap.js
中找到,并在js/game/lib/core/Engine.js
中实例化并添加到场景中。resources
对象中的所有属性都是使用THREE.TextureLoader
加载的THREE.Texture
。谢谢! - Chad.flipY
属性的作用。它来自于_gl.pixelStorei(_gl.UNPACK_FLIP_Y_WEBGL, texture.flipY)
,但是生成的纹理仍然是倒置的。如果我不在tilemap
和tileset
纹理上都设置这个标志,那么地图就无法对齐,会使用错误的纹理。 - Chad