如何在WebGL上使用图层? [英] How can I use layers on WebGL?

查看:114
本文介绍了如何在WebGL上使用图层?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用光贴图绘制平铺图,我想知道如何将平铺图(分为两个三角形)栅格化为第1层,上面的其他平铺图在某些部分具有透明性,以了解第一个层?

I´m doing a tile map with light mapping, and I want to know how can I declare a tile map (rasterized in two triangules) as layer 1, and other tile map above with transparency in some parts to see the first layer?

推荐答案

WebGL是栅格化API.它只是画.它没有层"的概念.

WebGL is a rasterization API. It just draws. It has no concept of "layers".

您可以按每一帧绘制图层,绘制第一个图块,然后在第一个图块的顶部绘制第二个图块.这与canvas 2D API没什么不同.

You can achieve layers by every frame, drawing your first tilemap, then drawing your second tilemap on top of the first one. This is no different than the canvas 2D API.

关于如何仅绘制2个三角形(或一个偶数)的图块地图查看本文

As for how to render a tilemap with just 2 triangles (or one even) see this article

此项目中也使用了相同的技术,但它还支持翻转和旋转瓷砖(旋转90度),并提供了从 Tiled 加载地图的代码.抱歉,没有文档.参见 tilemap.js 用于绘制图层的着色器和代码,以及

There is also in this project that uses the same technique but it also supports flipped and rotated tiles (by 90 degrees) and there's code to load maps from Tiled. Sorry there's no docs though. See tilemap.js for the shader and code that draws a layer and tiledloader.js for code that loads maps and tiles from Tiled.

让我们从基础开始.首先,如果我们仅绘制2个矩形,则第二个(蓝色)是第一个(红色)上方的层"

Let's start from basics. First if we just draw 2 rectangles the 2nd one (blue) is a "layer" over the first (red)

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中没有什么不同.

This would be no different in WebGL.

如果我们在每个图像中放置静态tilemap之类的图像,则矩形的内容除外.

If we put static tilemap like images in each nothing changes except the content of the rectangles.

这是第一张图片

这是第二个

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中也没有什么不同.

Again, no different in WebGL.

现在,您需要从tilemap生成这些图像,而不是静态加载它们,这是链接的代码和下面的代码的作用.

Now you need to generate those image from a tilemap instead of statically loading them which is what the code linked does and the code below.

基于此图块

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>

这篇关于如何在WebGL上使用图层?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆