WebGL-动画精灵+动画坐标 [英] WebGL - animated sprites + animated coordinates

查看:90
本文介绍了WebGL-动画精灵+动画坐标的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要同时运行精灵动画和动画坐标。



即,为动画中的某些特定精灵指定了[0,1]中的纹理坐标,然后通过另一个坐标进行平移。



平移可能会导致[0,1]以外的坐标,这是重复所需的。



问题是-我将子画面作为纹理图集提供。
因此,选择一个精灵意味着在[0,1]中获得一个子矩形。
因为此Sprite介于其他Sprite之间,所以无法重复-毕竟,如果纹理坐标移动到Sprite的矩形之外,则将采样其他Sprite。



必须在纹理图集中提供精灵-我正在使用实例渲染,每个实例都可以使用动画中的任何精灵,据我所知,唯一的实现方法是



tl; dr-是否可以在WebGL中同时实现纹理重复和精灵动画?

解决方案

如果您知道子画面在地图集中的位置,那么您不能只计算范围为的纹理坐标模>在片段着色器中

  vec2 animationUV; //动画值
vec2 spriteStartUV; // Atlas
中sprite的转角uv坐标vec2 spriteEndVU; //在图集

中sprite的对角uv坐标vec2 spriteRange =(spriteEndUV-spriteStartUV);
vec2 uv = spriteStartUV + fract(texcoord + animationUV)* spriteRange;

vec4 color = texture2D(someTexture,uv);

是否适用于您的特定情况,我不知道,但也许它可以给您一些想法。 / p>

工作示例:



  const vs =`void main(){//使用点精灵,因为它很容易,但是概念//是相同的。 gl_Position = vec4(0,0,0,1); gl_PointSize = 40.0;}`;; const fs =`precision mediump float; //我将它们作为制服传递,但是如果可以的话,可以将它们作为变量从缓冲区传递//如果适合您的要求,可以更好地传递。 //动画值一致vec2 spriteStartUV; // atlasuniform vec2 spriteEndUV中子画面的转角uv坐标; //在atlasuniform sampler2D中,精灵的对角uv坐标为someTexture; void main(){//这通常来自变化但懒惰的,因此使用点精灵vec2 texcoord = gl_PointCoord.xy; vec2 spriteRange =(spriteEndUV-spriteStartUV); vec2 uv = spriteStartUV + fract(texcoord + animationUV)* spriteRange; vec4 color = texture2D(someTexture,uv); gl_FragColor = color;}`; //使用画布制作带有一个精灵的纹理图集ctx = document.querySelector(#atlas)。getContext( 2d); const w = ctx.canvas.width; const h = ctx.canvas.heightconst sx = 30; const sy = 40; const sw = 50; const sh = 60; ctx.fillStyle = red; ctx.fillRect(0,0,w,h); ctx.fillStyle =  blue; ctx.fillRect(sx,sy,sw,sh); ctx.fillStyle =黄色; ctx.font = 45px sans-serif; ctx.textAlign = center; ctx.textBaseline =中间; ctx.fillText( G,sx + sw / 2,sy + sh / 2); //计算spriteconst spriteStartUV = [sx / w,sy / h]; const spriteEndUV = [(sx + sw) / w,(sy + sh)/ h]; const gl = document.querySelector(#webgl)。getContext( webgl); const programInfo = twgl.createProgramInfo(gl,[vs,fs]); const tex = twgl.createTexture(gl,{src:ctx.canvas,});函数render(time){time * = 0.001; //秒gl.useProgram(programInfo.program); twgl.setUniforms(programInfo,{animationUV:[time,time * 1.1],spriteStartUV:spriteStartUV,spriteEndUV:spriteEndUV,someTexture:tex,}); gl.drawArrays(gl.POINTS,0,1); //绘制1点requestAnimationFrame(render);} requestAnimationFrame(render);  

  canvas {边框:1px纯黑色;边距:2px; }  

 < script src = https:// twgljs .org / dist / 4.x / twgl.min.js>< / script>< canvas id = atlas< / canvas>< canvas id = webgl< / canvas>  



如果要重复更多,请增加texcoords或添加一个乘法器



  const vs =`void main( } {//使用点精灵,因为它很容易,但是概念//是相同的。 gl_Position = vec4(0,0,0,1); gl_PointSize = 40.0;}`;; const fs =`precision mediump float; //我将它们作为制服传递,但是如果可以的话,可以将它们作为变量从缓冲区传递//如果适合您的要求,可以更好地传递。 //动画值一致vec2 spriteStartUV; // atlasuniform vec2 spriteEndUV中子画面的转角uv坐标; // atlasuniform sampler2D中的子画面的对角uv坐标,someTexture; void main(){//这通常来自变化但懒惰的,所以使用点子vec2 texcoord = gl_PointCoord.xy * 3 .; //这个* 3可能已经//在您的texcoords vec2中spriteRange =(spriteEndUV-spriteStartUV); vec2 uv = spriteStartUV + fract(texcoord + animationUV)* spriteRange; vec4 color = texture2D(someTexture,uv); gl_FragColor = color;}`; ///使用一个精灵创建纹理地图集ctx = document.querySelector(#atlas)。getContext( 2d); const w = ctx.canvas.width; const h = ctx.canvas。 heightconst sx = 30; const sy = 40; const sw = 50; const sh = 60; ctx.fillStyle =红色; ctx.fillRect(0,0,w,h); ctx.fillStyle =蓝色; ctx .fillRect(sx,sy,sw,sh); ctx.fillStyle =黄色; ctx.font = 45px sans-serif; ctx.textAlign =中心; ctx.textBaseline =中间; ctx.fillText ( G,sx + sw / 2,sy + sh / 2); //计算Atlasconst中sprite的纹理坐标spriteStartUV = [sx / w,sy / h]; const spriteEndUV = [(sx + sw)/ w ,(sy + sh)/ h]; const gl = document.querySelector(#webgl)。getContext( webgl); const programInfo = twgl.createProgramInfo(gl,[vs,fs]); const tex = twgl .createTexture(gl,{src:ctx.canvas,});函数render(time){time * = 0.001; //秒gl.useProgram(programInfo.program); twgl.setUniforms(programInfo,{animationUV:[time,time * 1.1],spriteStartUV:spriteStartUV,spriteEndUV:spriteEndUV,someTexture:tex,}); gl.drawArrays(gl.POINTS,0,1); //绘制1点requestAnimationFrame(render);} requestAnimationFrame(render);  

  canvas {边框:1px纯黑色;边距:2px; }  

 < script src = https:// twgljs .org / dist / 4.x / twgl.min.js>< / script>< canvas id = atlas< / canvas>< canvas id = webgl< / canvas>  



请注意,上面的示例使用制服,但您可以可以轻松地使用每个顶点的spriteStartUV,spriteEndUV和其他使用属性的数据并将其添加到缓冲区中。



update



带有更多精灵的示例,使其更清晰,它使用的是纹理图集



  const vs =`uniform vec4 u_position; void main(){//使用点精灵,因为它很容易,但概念//相同。 gl_Position = u_position; gl_PointSize = 40.0;}`;; const fs =`precision mediump float; //我将它们作为制服传递,但是如果可以的话,可以将它们作为变量从缓冲区传递//如果适合您的要求,可以更好地传递。 //动画值一致vec2 spriteStartUV; // atlasuniform vec2 spriteEndUV中子画面的转角uv坐标; // atlasuniform sampler2D中的子画面的对角uv坐标,someTexture; void main(){//这通常来自变化但懒惰的,所以使用点子vec2 texcoord = gl_PointCoord.xy * 3 .; //这个* 3可能已经//在您的texcoords vec2中spriteRange =(spriteEndUV-spriteStartUV); vec2 uv = spriteStartUV + fract(texcoord + animationUV)* spriteRange; vec4 color = texture2D(someTexture,uv); gl_FragColor = color;}`; ///使用36个精灵创建纹理图集。const ctx = document.querySelector(#atlas)。getContext( 2d); const w = ctx.canvas.width; const h = ctx.canvas。 height; ctx.fillStyle = red; ctx.fillRect(0,0,w,h); const sw = 16; const sh = 16; const spritesAcross = w / sw | 0; const spriteData = []; const backgroundColors = [#884,#848,#488,#448,#484,#488,#222,]; ABCDEFGHIIJKLMNOPQRSTUVWXYZ0123456789。 backgroundColors.length]; ctx.fillRect(sx,sy,sw,sh); ctx.fillStyle =黄色; ctx.font = 16px sans-serif; ctx.textAlign =中心; ctx.textBaseline =中间; ctx.fillText(letter,sx + sw / 2,sy + sh / 2); spriteData.push({spriteStartUV:[sx / w,sy / h],spriteEndUV:[(sx + sw)/ w, (sy + sh)/ h],});});)///计算atlasconst中sprite的纹理坐标gl = document.querySelector(#webgl)。getContext( webgl); const programInfo = twgl.createProgramInfo( gl,[vs,fs]); const tex = twgl.createTexture(gl,{src:ctx.canvas,});函数render(time){time * = 0.001; //秒gl.useProgram(programInfo.program); for(let i = 0; i< 100; ++ i){const spriteInfo = spriteData [i%spriteData.length]; const t =时间+ i; twgl.setUniforms(programInfo,{u_position:[Math.sin(t * 1.2),Math.sin(t * 1.3),0,1],animationUV:[t,t * 1.1],spriteStartUV:spriteInfo.spriteStartUV,spriteEndUV :spriteInfo.spriteEndUV,someTexture:tex,}); gl.drawArrays(gl.POINTS,0,1); //绘制1点} requestAnimationFrame(render);} requestAnimationFrame(render);  

  canvas {边框:1px纯黑色;边距:2px; }  

 < script src = https:// twgljs .org / dist / 4.x / twgl.min.js>< / script>< canvas id = atlas< / canvas>< canvas id = webgl< / canvas>  


I need to run both sprite animations and animated coordinates.

That is, a texture coordinate in [0, 1] is given for some specific sprite in an animation, and then it gets translated by another coordinate.

The translation can result in coordinates outside of [0, 1], and this is needed for repeating.

The problem is this - I supply the sprites as a texture atlas. Therefore, selecting a sprite means getting a sub-rectangle in [0, 1]. Because this sprite is in between others, there is no way to get repeating - after all, if the texture coordinate moves outside of the sprite's rectangle, other sprites will be sampled.

The sprites are given in a texture atlas as a necessity - I am using instanced rendering, where each instance can use any sprite in the animation, and the only way to implement that, as far as I know, is with a texture atlas (or a texture array etc. in OpenGL).

tl;dr - is there a way to achieve both texture repeating, and sprite animations, in WebGL?

解决方案

If you know where the sprite is in the atlas then can't you just compute a texture coordinate modulo that range in the fragment shader?

vec2 animatedUV;      // animation value
vec2 spriteStartUV;   // corner uv coord for sprite in atlas
vec2 spriteEndVU;     // opposite corner uv coord for sprite in atlas

vec2 spriteRange = (spriteEndUV - spriteStartUV);
vec2 uv = spriteStartUV + fract(texcoord + animatedUV) * spriteRange;

vec4 color = texture2D(someTexture, uv);

Whether that works for your particular case I don't know but maybe it gives you some ideas.

Working example:

const vs = `
void main() {
  // using a point sprite because it's easy but the concept 
  // is the same.
  gl_Position = vec4(0, 0, 0, 1);
  gl_PointSize = 40.0;
}
`;

const fs = `
precision mediump float;

// I'm passing these in as uniforms but you can pass them in as varyings
// from buffers if that fits your needs better

uniform vec2 animatedUV;      // animation value
uniform vec2 spriteStartUV;   // corner uv coord for sprite in atlas
uniform vec2 spriteEndUV;     // opposite corner uv coord for sprite in atlas

uniform sampler2D someTexture;

void main() {
  // this would normally come from a varying but lazy so using point sprite
  vec2 texcoord = gl_PointCoord.xy;  
  
  vec2 spriteRange = (spriteEndUV - spriteStartUV);
  vec2 uv = spriteStartUV + fract(texcoord + animatedUV) * spriteRange;

  vec4 color = texture2D(someTexture, uv);
  
  gl_FragColor = color;
}
`;

// use the canvas to make a texture atlas with one sprite
const ctx = document.querySelector("#atlas").getContext("2d");
const w = ctx.canvas.width;
const h = ctx.canvas.height
const sx = 30;
const sy = 40;
const sw = 50;
const sh = 60;
ctx.fillStyle = "red";
ctx.fillRect(0, 0, w, h);
ctx.fillStyle = "blue";
ctx.fillRect(sx, sy, sw, sh);
ctx.fillStyle = "yellow";
ctx.font = "45px sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("G", sx + sw / 2, sy + sh / 2);

// compute texcoods for sprite
const spriteStartUV = [ sx / w, sy / h ];
const spriteEndUV = [ (sx + sw) / w, (sy + sh) / h ];

const gl = document.querySelector("#webgl").getContext("webgl");
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

const tex = twgl.createTexture(gl, {
  src: ctx.canvas,
});

function render(time) {
  time *= 0.001;  // seconds
  gl.useProgram(programInfo.program);
  twgl.setUniforms(programInfo, {
    animatedUV: [time, time * 1.1],
    spriteStartUV: spriteStartUV,
    spriteEndUV: spriteEndUV,
    someTexture: tex,
  });
  gl.drawArrays(gl.POINTS, 0, 1);  // draw 1 point
  requestAnimationFrame(render);
}
requestAnimationFrame(render);

canvas { border: 1px solid black; margin: 2px; }

<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas id="atlas"></canvas>
<canvas id="webgl"></canvas>

If you want it repeat more then increase your texcoords or add a multplier

const vs = `
void main() {
  // using a point sprite because it's easy but the concept 
  // is the same.
  gl_Position = vec4(0, 0, 0, 1);
  gl_PointSize = 40.0;
}
`;

const fs = `
precision mediump float;

// I'm passing these in as uniforms but you can pass them in as varyings
// from buffers if that fits your needs better

uniform vec2 animatedUV;      // animation value
uniform vec2 spriteStartUV;   // corner uv coord for sprite in atlas
uniform vec2 spriteEndUV;     // opposite corner uv coord for sprite in atlas

uniform sampler2D someTexture;

void main() {
  // this would normally come from a varying but lazy so using point sprite
  vec2 texcoord = gl_PointCoord.xy * 3.;  // this * 3 could already be
                                          // in your texcoords
  
  vec2 spriteRange = (spriteEndUV - spriteStartUV);
  vec2 uv = spriteStartUV + fract(texcoord + animatedUV) * spriteRange;

  vec4 color = texture2D(someTexture, uv);
  
  gl_FragColor = color;
}
`;

// create texture atlas with one sprite
const ctx = document.querySelector("#atlas").getContext("2d");
const w = ctx.canvas.width;
const h = ctx.canvas.height
const sx = 30;
const sy = 40;
const sw = 50;
const sh = 60;
ctx.fillStyle = "red";
ctx.fillRect(0, 0, w, h);
ctx.fillStyle = "blue";
ctx.fillRect(sx, sy, sw, sh);
ctx.fillStyle = "yellow";
ctx.font = "45px sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("G", sx + sw / 2, sy + sh / 2);

// compute texture coords for sprite in atlas
const spriteStartUV = [ sx / w, sy / h ];
const spriteEndUV = [ (sx + sw) / w, (sy + sh) / h ];

const gl = document.querySelector("#webgl").getContext("webgl");
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

const tex = twgl.createTexture(gl, {
  src: ctx.canvas,
});

function render(time) {
  time *= 0.001;  // seconds
  gl.useProgram(programInfo.program);
  twgl.setUniforms(programInfo, {
    animatedUV: [time, time * 1.1],
    spriteStartUV: spriteStartUV,
    spriteEndUV: spriteEndUV,
    someTexture: tex,
  });
  gl.drawArrays(gl.POINTS, 0, 1);  // draw 1 point
  requestAnimationFrame(render);
}
requestAnimationFrame(render);

canvas { border: 1px solid black; margin: 2px; }

<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas id="atlas"></canvas>
<canvas id="webgl"></canvas>

note the sample above uses uniforms but you could just as easily use per vertex spriteStartUV, spriteEndUV and any other data using attributes and adding that data to your buffers.

update

Example with more sprites to make it clearer it's using a texture atlas

const vs = `
uniform vec4 u_position;
void main() {
  // using a point sprite because it's easy but the concept 
  // is the same.
  gl_Position = u_position;
  gl_PointSize = 40.0;
}
`;

const fs = `
precision mediump float;

// I'm passing these in as uniforms but you can pass them in as varyings
// from buffers if that fits your needs better

uniform vec2 animatedUV;      // animation value
uniform vec2 spriteStartUV;   // corner uv coord for sprite in atlas
uniform vec2 spriteEndUV;     // opposite corner uv coord for sprite in atlas

uniform sampler2D someTexture;

void main() {
  // this would normally come from a varying but lazy so using point sprite
  vec2 texcoord = gl_PointCoord.xy * 3.;  // this * 3 could already be
                                          // in your texcoords
  
  vec2 spriteRange = (spriteEndUV - spriteStartUV);
  vec2 uv = spriteStartUV + fract(texcoord + animatedUV) * spriteRange;

  vec4 color = texture2D(someTexture, uv);
  
  gl_FragColor = color;
}
`;

// create texture atlas with 36 sprites
const ctx = document.querySelector("#atlas").getContext("2d");
const w = ctx.canvas.width;
const h = ctx.canvas.height;
ctx.fillStyle = "red";
ctx.fillRect(0, 0, w, h);

const sw = 16;
const sh = 16;
const spritesAcross = w / sw | 0;
const spriteData = [];
const backgroundColors = [
  "#884", "#848", "#488", "#448", "#484", "#488", "#222",
];
"ABCDEFGHIIJKLMNOPQRSTUVWXYZ0123456789".split('').forEach((letter, ndx) => {
  const sx = ndx % spritesAcross * sw;   
  const sy = (ndx / spritesAcross | 0) * sh;
  ctx.fillStyle = backgroundColors[ndx % backgroundColors.length];
  ctx.fillRect(sx, sy, sw, sh);
  ctx.fillStyle = "yellow";
  ctx.font = "16px sans-serif";
  ctx.textAlign = "center";
  ctx.textBaseline = "middle";
  ctx.fillText(letter, sx + sw / 2, sy + sh / 2);
  spriteData.push({
    spriteStartUV: [ sx / w, sy / h ],
    spriteEndUV: [ (sx + sw) / w, (sy + sh) / h ],
  });
});

// compute texture coords for sprite in atlas
const gl = document.querySelector("#webgl").getContext("webgl");
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

const tex = twgl.createTexture(gl, {
  src: ctx.canvas,
});

function render(time) {
  time *= 0.001;  // seconds
  gl.useProgram(programInfo.program);
  for (let i = 0; i < 100; ++i) {
    const spriteInfo = spriteData[i % spriteData.length];
    const t = time + i;
    twgl.setUniforms(programInfo, {
      u_position: [Math.sin(t * 1.2), Math.sin(t * 1.3), 0, 1],
      animatedUV: [t, t * 1.1],
      spriteStartUV: spriteInfo.spriteStartUV,
      spriteEndUV: spriteInfo.spriteEndUV,
      someTexture: tex,
    });
    gl.drawArrays(gl.POINTS, 0, 1);  // draw 1 point
  }
  requestAnimationFrame(render);
}
requestAnimationFrame(render);

canvas { border: 1px solid black; margin: 2px; }

<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas id="atlas"></canvas>
<canvas id="webgl"></canvas>

这篇关于WebGL-动画精灵+动画坐标的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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