使用Three.js的像素失真效果

883 阅读4分钟

创造性的编码者的梦想是统治他们屏幕上的像素。将它们排列成美丽的图案,并对它们做任何你想做的事。嗯,这正是我们在这个演示中要做的事情。让我们用鼠标光标的力量来扭曲和统治像素,就像了不起的无限坏蛋网站的开发者所做的那样!

设置

场景和往常一样,我们只是在屏幕上创建一个全屏图像,所以它保留了长宽比,并通过glsl着色器应用其 "背景尺寸:覆盖"。最后,我们有一个为整个视口拉伸的几何体,和一个像这样的小着色器。

vec2 newUV = (vUv - vec2(0.5))*aspect + vec2(0.5);
    gl_FragColor = texture2D(uTexture,newUV);
    

整个东西只是显示图像,还没有扭曲。

壮观的数据纹理

我希望这时你已经知道,WebGL中的任何纹理基本上都是与每个像素的颜色相对应的数字。

Three.js有一个特定的API来逐个像素地创建你自己的纹理。不出所料,它被称为DataTexture。因此,让我们为我们的演示创建另一个纹理,用随机数字。

    const size = rows * columns;
    const data = new Float32Array(3 * size);

    for(let i = 0; i < size; i++) {
          const stride = i * 3;
          let r = Math.random() * 255 ;
          let r1 = Math.random() * 255 ;

          data[stride] = r; // red, and also X
          data[stride + 1] = r1; // green, and also Y
          data[stride + 2] = 0; // blue
        }
    this.texture = new THREE.DataTexture(data, width, height, THREE.RGBFormat, THREE.FloatType);
    

这在很大程度上是基于文档中的默认例子。唯一的区别是,我们使用的是 FloatType 纹理,所以我们不受限于整数。其中一个有趣的事情是,数字应该在0到255之间,尽管在GLSL中它反正是0...1的范围。你应该记住这一点,所以你要使用正确的数字范围。

还有一个有趣的想法是,GLSL并不真正关心你的数据结构中的数字是什么意思。它既可以是color.rgb,也可以是color.xyz。而这正是我们将在这里使用的,我们并不关心这个纹理的确切颜色,我们将把它作为我们的演示的一个变形来使用只是作为GLSL的一个不错的数据结构。

但是,只是为了更好地理解,当你想预览纹理时,这就是纹理的样子。

你看到这些大的矩形是因为我选择了类似25×35的DataTexture尺寸,这是非常低的分辨率。
另外,它有颜色是因为我使用了两个不同的随机数作为XY(红-绿)变量,这导致了这个结果。

所以现在,我们已经可以在我们的片段着色器中使用这个纹理作为变形了。

    vec4 color = texture2D(uTexture,newUV);
    vec4 offset = texture2D(uDataTexture,vUv);
    // we are distorting UVs with new texture values
    gl_FragColor = texture2D(uTexture,newUV - 0.02*offset.rg);
    

鼠标和它的力量

那么现在,让我们把它变成动态的!我们需要一些东西。我们将需要一些东西。首先,我们需要鼠标的位置和速度。还有,鼠标的半径,也就是说,鼠标在什么距离上会扭曲我们的图像。

一个简短的解释。在动画的每一步,我将循环浏览我的网格单元,也就是DataTexture的像素。并根据鼠标的位置和速度分配一些值。第二,我将放宽扭曲的程度。这需要做的是,如果用户停止移动鼠标,失真就应该降到0。

所以,现在的代码看起来是这样的,为了更好地理解这个概念,简化了一点。

    let data = DataTexture.image.data;
    // loop through all the pixels of DataTexture
    for (let i = 0; i < rows; i++) {
    for (let j = 0; j < cols; j++) {
        // get distance between mouse, and current DataTexture pixel
      let distance = distanceBetween(mouse, [i,j])
      if (distance < maxDistance) {

        let index = 3 * (i + this.size * j); // get the pixel coordinate on screen
        data[index] = this.mouse.vX ; // mouse speed
        data[index + 1] =  this.mouse.vY ; // mouse speed
      }
    }
    // slowly move system towards 0 distortion
    for (let i = 0; i < data.length; i += 3) {
      data[i] *= 0.9
      data[i + 1] *= 0.9
    }
    DataTexture.needsUpdate = true;

为了使它看起来更好,我们添加了一些东西,但概念就在这里。如果你曾经使用过粒子系统,这正是这个概念,只是我们的粒子从不移动,我们只是改变粒子的一些值(每个大像素内的扭曲)。

结果

我在最后的演示中保留了设置,所以你可以玩玩参数,想出你自己独特的动画感觉。让我知道它给了你什么创作灵感