我在做一个带有光照贴图的瓷砖地图,我想知道如何将瓷砖贴图(在两个三角形中光栅化)声明为第1层,而上面的其他瓷砖贴图在某些部分使用透明度来查看第一个层
答案 0 :(得分:0)
WebGL是一种光栅化API。它只是画画。它没有“层”的概念。
您可以按帧实现图层,绘制第一个图块,然后在第一个图块之上绘制第二个图块。这与canvas 2D API没有什么不同。
至于如何使用2个三角形(或偶数)see this article
渲染瓷砖地图在this project中也有使用相同的技术,但它也支持翻转和旋转的图块(90度),还有从Tiled加载地图的代码。对不起,虽然没有文档。有关用于绘制图层的着色器和代码,请参阅tilemap.js,有关从Tiled加载地图和图块的代码,请参阅tiledloader.js。
让我们从基础开始。首先,如果我们只绘制2个矩形,则第二个(蓝色)是第一个(红色)
上的“层”
const ctx = document.querySelector("canvas").getContext("2d");
function render(time) {
time *= 0.001; // seconds
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
var t1 = time * -1.1;
ctx.fillStyle = "red";
ctx.fillRect(50 + Math.sin(t1) * 20, 50 + Math.cos(t1) * 20, 128, 64);
var t2 = time * 1.3;
ctx.fillStyle = "blue";
ctx.fillRect(75 + Math.sin(t2) * 20, 30 + Math.cos(t2) * 20, 64, 128);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
canvas { border: 1px solid black; }
<canvas />
这在WebGL中没有什么不同。
如果我们将静态tilemap放在每个图像中,除了矩形的内容之外什么都没有变化。
这是第一张图片
这是第二个
const ctx = document.querySelector("canvas").getContext("2d");
const layer1 = new Image();
layer1.src = "http://i.imgur.com/KTXDmsa.png";
const layer2 = new Image();
layer2.src = "http://i.imgur.com/3qVLkO5.png";
function render(time) {
time *= 0.001; // seconds
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
var t1 = time * -1.1;
ctx.drawImage(layer1, 50 + Math.sin(t1) * 20, 50 + Math.cos(t1) * 20);
var t2 = time * 1.3;
ctx.drawImage(layer2, 75 + Math.sin(t2) * 20, 30 + Math.cos(t2) * 20);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
canvas { border: 1px solid black; }
<canvas />
同样,WebGL也没有什么不同。
现在您需要从tilemap生成这些图像,而不是静态加载它们,这是代码链接所做的和下面的代码。
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
// compile & link shaders and lookup locations
const progInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
// make a unit quad
const quadBufferInfo = twgl.primitives.createXYQuadBufferInfo(gl, 1, .5, .5);
// load tiles into texture
const tilesAcross = 16;
const tilesDown = 16;
const tileWidth = 32;
const tileHeight = 32;
const tiles = twgl.createTexture(gl, {
src: "http://i.imgur.com/sz79FPd.png",
crossOrigin: "",
minMag: gl.NEAREST,
});
// layer 0
const tilemap0 = createTilemap({
width: 8,
height: 5,
map: new Uint32Array([
t(1, 2), t(1, 2), t(1, 2), t(1, 2), t(1, 2), t(1, 2), t(1, 2), t(1, 2),
t(1, 2), t(9, 6), t(9, 6), t(9, 6), t(9, 6), t(9, 6), t(9, 6), t(1, 2),
t(1, 2), t(9, 6), t(9, 6), t(9, 6), t(9, 6), t(9, 6), t(9, 6), t(1, 2),
t(1, 2), t(9, 6), t(9, 6), t(9, 6), t(9, 6), t(9, 6), t(9, 6), t(1, 2),
t(1, 2), t(1, 2), t(1, 2), t(1, 2), t(1, 2), t(1, 2), t(1, 2), t(1, 2),
]),
});
// layer 1
const tilemap1 = createTilemap({
width: 8,
height: 5,
map: new Uint32Array([
t(0, 0), t(0, 0), t(0, 0), t(0, 0), t(0, 0), t(0, 0), t(0, 0), t(0, 0),
t(0, 0), t(4, 5), t(5, 5), t(6, 5), t(0, 0), t(0, 0), t(0, 0), t(0, 0),
t(0, 0), t(0, 0), t(0, 0), t(0, 0), t(0, 0), t(4, 5), t(5, 5), t(6, 5),
t(4, 5), t(5, 5), t(6, 5), t(0, 0), t(0, 0), t(0, 0), t(0, 0), t(0, 0),
t(0, 0), t(0, 0), t(0, 0), t(0, 0), t(0, 0), t(0, 0), t(0, 0), t(0, 0),
]),
});
function t(x, y, xflip, yflip, xyswap) {
return x | (y << 8) |
(((xflip ? 0x80 : 0) | (yflip ? 0x40 : 0) | (xyswap ? 0x20 : 0)) << 24);
}
// copy the tilemap into a texture
function createTilemap(tilemap) {
tilemap.texture = twgl.createTexture(gl, {
src: new Uint8Array(tilemap.map.buffer),
width: tilemap.width,
minMag: gl.NEAREST,
});
return tilemap;
};
function drawTilemap(options) {
const tilemap = options.tilemap;
const scaleX = options.scaleX || 1;
const scaleY = options.scaleY || 1;
const dispScaleX = options.width / gl.canvas.width;
const dispScaleY = options.height / gl.canvas.height;
let texMat = m4.translation([options.scrollX, options.scrollY, 0]);
texMat = m4.rotateZ(texMat, options.rotation);
texMat = m4.scale(texMat, [
gl.canvas.width / tileWidth / scaleX * (dispScaleX),
gl.canvas.height / tileHeight / scaleY * (dispScaleY),
1,
]);
texMat = m4.translate(texMat, [
-options.originX / gl.canvas.width,
-options.originY / gl.canvas.height,
0,
]);
const matrix = [
2 * dispScaleX,0,0,0,
0,-2 * dispScaleY,0,0,
0,0,1,0,
-1 + 2 * (options.x | 0) / gl.canvas.width, 1 - 2 * (options.y | 0) / gl.canvas.height,0,1,
];
gl.useProgram(progInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, progInfo, quadBufferInfo);
// calls gl.uniformXXX and gl.activeTexture, gl.bindTexture
twgl.setUniforms(progInfo, {
u_matrix: matrix,
u_texMatrix: texMat,
u_tilemap: tilemap.texture,
u_tiles: tiles,
u_tilemapSize: [tilemap.width, tilemap.height],
u_tilesetSize: [tilesAcross, tilesDown],
});
// calls gl.drawElements
twgl.drawBufferInfo(gl, quadBufferInfo);
}
function render(time) {
time *= 0.001;
// draw layer 0
drawTilemap({
tilemap: tilemap0,
tiles: tiles,
// position and width, height on canvas
x: Math.cos(time * .9) * 20,
y: Math.sin(time * .9) * 20,
width: 256,
height: 160,
// offset into tilemap (repeats at edges)
scrollX: 0,
scrollY: 0,
// rotation/scale point
originX: 0,
originY: 0,
// rotation in radians
rotation: 0,
// scale
scaleX: 1,
scaleY: 1,
});
// draw layer 1
drawTilemap({
tilemap: tilemap1,
tiles: tiles,
x: Math.sin(time) * 20,
y: Math.cos(time) * 20,
width: 256,
height: 160,
scrollX: 0,
scrollY: 0,
originX: 0,
originY: 0,
rotation: 0,
});
requestAnimationFrame(render);
}
requestAnimationFrame(render);
canvas { border: 1px solid black; }
<canvas />
<script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>
<script id="vs" type="foo">
attribute vec4 position;
attribute vec4 texcoord;
uniform mat4 u_matrix;
uniform mat4 u_texMatrix;
varying vec2 v_texcoord;
void main() {
gl_Position = u_matrix * position;
v_texcoord = (u_texMatrix * texcoord).xy;
}
</script>
<script id="fs" type="foo">
precision mediump float;
uniform sampler2D u_tilemap;
uniform sampler2D u_tiles;
uniform vec2 u_tilemapSize; // tiles across/down map
uniform vec2 u_tilesetSize; // pixels across a single tile
varying vec2 v_texcoord;
void main() {
// v_texcoord is in tile units which is based on u_texMatrix from the
// vertex shader
// this is the tile to start at
vec2 tilemapCoord = floor(v_texcoord);
// this is a fractional amount into a tile
vec2 texcoord = fract(v_texcoord);
// computes the UV coord pull the correct value out of tilemap
vec2 tileFoo = fract((tilemapCoord + vec2(0.5, 0.5)) / u_tilemapSize);
// get a single tile out of the tilemap and convert from 0 -> 1 to 0 -> 255
vec4 tile = floor(texture2D(u_tilemap, tileFoo) * 256.0);
// flags for the tile are in w (xflip, yflip, xyswap)
float flags = tile.w;
float xflip = step(128.0, flags);
flags = flags - xflip * 128.0;
float yflip = step(64.0, flags);
flags = flags - yflip * 64.0;
float xySwap = step(32.0, flags);
// based on the flags swap the texcoord inside the tile
if (xflip > 0.0) {
texcoord = vec2(1.0 - texcoord.x, texcoord.y);
}
if (yflip > 0.0) {
texcoord = vec2(texcoord.x, 1.0 - texcoord.y);
}
if (xySwap > 0.0) {
texcoord = texcoord.yx;
}
// scale the tex coords for a single tile
vec2 tileCoord = (tile.xy + texcoord) / u_tilesetSize;
// get the color from the tile
vec4 color = texture2D(u_tiles, tileCoord);
// if alpha is below some threshold don't draw at all
if (color.a <= 0.1) {
discard;
}
gl_FragColor = color;
}
</script>