在鼠标移动中创建涂抹/液化效果,使用webgl连续动画回原始状态
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在鼠标移动中创建涂抹/液化效果,使用webgl连续动画回原始状态相关的知识,希望对你有一定的参考价值。
我试图找到可用于创建涂抹/液化效果的信息或示例,这些效果会持续动画回原始状态。
最初我正在考虑使用three.js或pixi.js渲染一些文本,然后使用鼠标事件和光线投射将网格拖出位置,我发现最接近的是这个。
https://codepen.io/shshaw/pen/qqVgbg
let renderer = PIXI.autoDetectRenderer(window.innerWidth,
window.innerHeight, { transparent: true });
我认为理想情况下我会将文本渲染为图像,然后涂抹效果将应用于像素,并且它们会慢慢地动画回原始状态。与此类似。
http://www.duhaihang.com/#/work/
我想我可能需要使用自定义的GLSL着色器和某种缓冲区来保持构成图像的像素的原始状态和当前状态。
任何帮助或方向将不胜感激。
答案
两者似乎相对简单。
第一个,就像你提到的那样,你制作一个绘制平面的顶点网格(网格)。您将纹理贴图到平面,当您拖动鼠标时,向鼠标触摸的每个顶点添加一个位移。随着时间的推移将位移重置为0(如0位移量)
这是一个例子:它只是将一个顶点移位一个随机数量而不是更可预测的东西。最后我只是节省了位移应该消失的时间,然后在着色器中我做了一个简单的线性lerp(可以使用更高级的lerp来反弹或者其他东西)。这几乎是着色器中发生的一切。
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
const vs = `
attribute vec4 position;
attribute vec3 displacement;
uniform mat4 u_matrix;
uniform float u_time;
uniform float u_timeToGoBack;
varying vec2 v_texcoord;
void main() {
// because position goes -1 <-> 1 we can just use
// it for texture coords
v_texcoord = position.xy * .5 + .5;
// displacement.z is the time at which it should be undisplaced
float displaceTime = displacement.z - u_time;
float lerp = clamp(displaceTime / u_timeToGoBack, 0., 1.);
vec2 displace = displacement.xy * lerp;
gl_Position = u_matrix * (position + vec4(displace, 0, 0));
}
`;
const fs = `
precision mediump float;
uniform sampler2D texture;
varying vec2 v_texcoord;
void main() {
gl_FragColor = texture2D(texture, v_texcoord);
}
`;
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
// create a grid of points in a -1 to +1 quad
const positions = [];
const displacements = [];
const indices = [];
const res = 100;
for (var y = 0; y < res; ++y) {
var v = (y / (res - 1)) * 2 - 1;
for (var x = 0; x < res; ++x) {
var u = (x / (res - 1)) * 2 - 1;
positions.push(u, v);
displacements.push(0, 0, 0);
}
}
for (var y = 0; y < res - 1; ++y) {
var off0 = (y + 0) * res;
var off1 = (y + 1) * res;
for (var x = 0; x < res - 1; ++x) {
indices.push(
off0 + x + 0, off0 + x + 1, off1 + x + 0,
off1 + x + 0, off0 + x + 1, off1 + x + 1
);
}
}
// create buffers and fills them in.
// (calls gl.createBuffer and gl.bufferData for each array)
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
position: { numComponents: 2, data: positions, },
displacement: { numComponents: 3, data: displacements, },
indices: indices,
});
// this will be replaced when the image has loaded;
var img = { width: 1, height: 1 };
const tex = twgl.createTexture(gl, {
src: 'https://farm6.staticflickr.com/5078/14032935559_8c13e9b181_z_d.jpg',
crossOrigin: '',
}, function(err, texture, source) {
img = source;
});
var currentTime = 0;
var currentMatrix;
const timeToGoBack = 2; // in seconds;
function render(time) {
time *= 0.001; // convert to seconds
currentTime = time;
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.useProgram(programInfo.program);
var aspect = img.width / img.height;
var mat = m4.ortho(0, gl.canvas.clientWidth, gl.canvas.clientHeight, 0, -1, 1);
mat = m4.translate(mat, [gl.canvas.clientWidth / 2, gl.canvas.clientHeight / 2, 0]);
mat = m4.scale(mat, [img.width * .25, img.height * .25, 1]);
currentMatrix = mat;
// calls gl.bindBuffer, gl.vertexAttribPointer to setup
// attributes
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, {
u_matrix: mat,
u_texture: tex,
u_time: currentTime,
u_timeToGoBack: timeToGoBack,
});
gl.drawElements(gl.TRIANGLES, bufferInfo.numElements, gl.UNSIGNED_SHORT, 0);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
const displace = new Float32Array(3);
gl.canvas.addEventListener('mousemove', function(event, target) {
target = target || event.target;
const rect = target.getBoundingClientRect();
const rx = event.clientX - rect.left;
const ry = event.clientY - rect.top;
const x = rx * target.width / target.clientWidth;
const y = ry * target.height / target.clientHeight;
// reverse project the mouse onto the image
var rmat = m4.inverse(currentMatrix);
var s = m4.transformPoint(
rmat, [x / target.width * 2 - 1, y / target.height * 2 - 1, 0]);
// s is now a point in the space of `position`
// lets just move closest point?
var gx = Math.round((s[0] * .5 + .5) * res);
var gy = Math.round((s[1] * .5 + .5) * res);
gx = clamp(gx, 0, res - 1);
gy = clamp(gy, 0, res - 1);
const offset = ((res - gy - 1) * res + gx) * 3 * 4;
displace[0] = rand(-.1, .1);
displace[1] = rand(-.1, .1);
displace[2] = currentTime + timeToGoBack;
gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.displacement.buffer);
gl.bufferSubData(gl.ARRAY_BUFFER, offset, displace);
});
function rand(min, max) {
return Math.random() * (max - min) + min;
}
function clamp(v, min, max) {
return Math.max(min, Math.min(max, v));
}
body {
margin: 0;
}
canvas {
width: 100vw;
height: 100vh;
display: block;
}
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
以上是关于在鼠标移动中创建涂抹/液化效果,使用webgl连续动画回原始状态的主要内容,如果未能解决你的问题,请参考以下文章