Note that much of this code as changed as of edit 3 below.Three.js Custom Shader
所以我非常喜歡Brandon Jones的博客文章(found here)。我想將他的代碼轉換爲Three.js,但我遇到了一些問題。你可以找到他的完整代碼here。這裏是我的嘗試,到目前爲止,與問題一對夫婦的意見,我有:
// 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自定義着色器的文檔。
編輯:這裏是博客文章來填充頂點着色器的2個屬性的代碼(pos
和texture
):
//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);
我真的不完全瞭解到底是什麼發生在這裏,但如果我是正確的,我認爲它是填充2 Float32Array
s與每個的數據的一半。我不知道爲什麼,我不確定我是否正確,也不知道如何將其轉換爲Three.js方法。
EDIT2:現在我使用的是平面顯示(2D)的背景下,我應該使用一個精靈呢?
EDIT3:
所以我有點遠,當我意識到three.js所將設置爲我的位置和紫外線向量(這似乎是相似的,如果不一樣的位置/紋理在上面的例子中)。我也注意到我可能有一些類型錯誤,因爲我有很多'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是實際紋理貼圖,而不是紅色貼圖。我把所有正確的瓷磚着陸在正確的地方;除了他們都倒過來!
下面是它看起來像這樣改變之後:
是否有某種方式來翻轉Y軸每一個人的質地(沒有編輯地形設置圖像)?我覺得有一些簡單的矢量數學可以添加到我的着色器中,以翻轉它繪製的每個紋理並最終確定它。
我注意到,如果我不翻轉兩個(tilemap.flipY = false;
和tileset.flipY = false;
),我會得到正確的紋理,在正確的位置,正確地裝配在一起。但整個地圖是顛倒的!如此接近......
任何機會,你可以分享鏈接* *你的*完整的代碼?如果你有一個工作鏈接,我們可以調試錯誤,那更好。 – mrdoob
@mrdoob當然,https://github.com/englercj/lttp-webgl是代碼。您可以將其克隆並打開index.html。選擇「加載資源」,然後選擇「啓動遊戲」。相關代碼主要可以在'js/game/lib/core/TileMap.js'中找到,它被實例化並添加到'js/game/lib/core/Engine.js'中的場景中。 'resources'對象中的所有屬性都是'THREE.Texture',加載了'THREE.TextureLoader'。謝謝! – Chad
我也有brandon的代碼,以供參考,儘管它們沒有被使用(它們的前綴是'brandons_')。 – Chad