我需要运行精灵动画和动画坐标。
也就是说,对于动画中的特定精灵,给出了0,1的纹理坐标,然后再用另一个坐标进行转换。
转换可能导致0,1之外的坐标,这是重复所需的。
问题是-我提供精灵作为纹理地图集。因此,选择一个精灵意味着在0,1中得到一个子矩形。因为这个精灵介于其他人之间,所以没有办法重复--毕竟,如果纹理坐标在精灵矩形之外移动,其他精灵就会被采样。
在纹理地图集中提供精灵是必要的--我使用的是实例渲染,每个实例都可以在动画中使用任何sprite,据我所知,实现这一点的唯一方法是使用纹理地图集(或OpenGL中的纹理数组等)。
在WebGL中有什么方法可以同时实现纹理重复和精灵动画吗?
发布于 2017-11-30 00:58:39
如果您知道雪碧在地图集中的位置,那么您就不能计算一个纹理坐标模块,即片段着色器中的纹理坐标模块
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);
这是否适用于你的特殊情况,我不知道,但也许它给你一些想法。
工作实例:
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>
如果你想要它重复更多,那么增加你的纹理和弦,或者增加一个复调器。
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>
注意,上面的示例使用制服,但您也可以使用每个顶点spriteStartUV、spriteEndUV和任何其他使用属性并将数据添加到缓冲区的数据。
更新
用更多的精灵来使它更清晰,它使用的是纹理地图集
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>
https://stackoverflow.com/questions/47565382
复制相似问题