297 Codepen

Curl noise

Пример curl noise с элементами в виде коробок. Используются шейдеры и webGL библиотека Kgl


HTML

<script type="x-shader/x-fragment" id="reset-velocity">
  precision highp float;

  void main () {
    gl_FragColor = vec4(vec3(0.), 1.);
  }
</script>

<script type="x-shader/x-fragment" id="reset-position">
  precision highp float;

  uniform vec2 size;

  // https://github.com/mattdesl/glsl-random
  highp float random(vec2 co)
  {
      highp float a = 12.9898;
      highp float b = 78.233;
      highp float c = 43758.5453;
      highp float dt= dot(co.xy ,vec2(a,b));
      highp float sn= mod(dt,3.14);
      return fract(sin(sn) * c);
  }

  void main () {
    vec2 nPosition = gl_FragCoord.st / size * 2. - 1.;
    vec4 position = vec4(
      nPosition * size,
      0.,
      random(nPosition)
    );
    gl_FragColor = position;
  }
</script>

<script type="x-shader/x-fragment" id="velocity">
  precision highp float;

  uniform vec2 size;
  uniform sampler2D prevVelocityTexture;
  uniform sampler2D prevPositionTexture;

  //
  // Description : Array and textureless GLSL 2D/3D/4D simplex
  //               noise functions.
  //      Author : Ian McEwan, Ashima Arts.
  //  Maintainer : ijm
  //     Lastmod : 20110822 (ijm)
  //     License : Copyright (C) 2011 Ashima Arts. All rights reserved.
  //               Distributed under the MIT License. See LICENSE file.
  //               https://github.com/ashima/webgl-noise
  //

  vec3 mod289(vec3 x) {
    return x - floor(x * (1.0 / 289.0)) * 289.0;
  }

  vec4 mod289(vec4 x) {
    return x - floor(x * (1.0 / 289.0)) * 289.0;
  }

  vec4 permute(vec4 x) {
        return mod289(((x*34.0)+1.0)*x);
  }

  vec4 taylorInvSqrt(vec4 r)
  {
    return 1.79284291400159 - 0.85373472095314 * r;
  }

  float snoise(vec3 v)
    {
    const vec2  C = vec2(1.0/6.0, 1.0/3.0) ;
    const vec4  D = vec4(0.0, 0.5, 1.0, 2.0);

  // First corner
    vec3 i  = floor(v + dot(v, C.yyy) );
    vec3 x0 =   v - i + dot(i, C.xxx) ;

  // Other corners
    vec3 g = step(x0.yzx, x0.xyz);
    vec3 l = 1.0 - g;
    vec3 i1 = min( g.xyz, l.zxy );
    vec3 i2 = max( g.xyz, l.zxy );

    //   x0 = x0 - 0.0 + 0.0 * C.xxx;
    //   x1 = x0 - i1  + 1.0 * C.xxx;
    //   x2 = x0 - i2  + 2.0 * C.xxx;
    //   x3 = x0 - 1.0 + 3.0 * C.xxx;
    vec3 x1 = x0 - i1 + C.xxx;
    vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
    vec3 x3 = x0 - D.yyy;      // -1.0+3.0*C.x = -0.5 = -D.y

  // Permutations
    i = mod289(i);
    vec4 p = permute( permute( permute(
                i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
              + i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
              + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));

  // Gradients: 7x7 points over a square, mapped onto an octahedron.
  // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
    float n_ = 0.142857142857; // 1.0/7.0
    vec3  ns = n_ * D.wyz - D.xzx;

    vec4 j = p - 49.0 * floor(p * ns.z * ns.z);  //  mod(p,7*7)

    vec4 x_ = floor(j * ns.z);
    vec4 y_ = floor(j - 7.0 * x_ );    // mod(j,N)

    vec4 x = x_ *ns.x + ns.yyyy;
    vec4 y = y_ *ns.x + ns.yyyy;
    vec4 h = 1.0 - abs(x) - abs(y);

    vec4 b0 = vec4( x.xy, y.xy );
    vec4 b1 = vec4( x.zw, y.zw );

    //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
    //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
    vec4 s0 = floor(b0)*2.0 + 1.0;
    vec4 s1 = floor(b1)*2.0 + 1.0;
    vec4 sh = -step(h, vec4(0.0));

    vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
    vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;

    vec3 p0 = vec3(a0.xy,h.x);
    vec3 p1 = vec3(a0.zw,h.y);
    vec3 p2 = vec3(a1.xy,h.z);
    vec3 p3 = vec3(a1.zw,h.w);

  //Normalise gradients
    vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
    p0 *= norm.x;
    p1 *= norm.y;
    p2 *= norm.z;
    p3 *= norm.w;

  // Mix final noise value
    vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
    m = m * m;
    return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
                                  dot(p2,x2), dot(p3,x3) ) );
    }

  // https://www.npmjs.com/package/glsl-curl-noise
  vec3 snoiseVec3( vec3 x ){

    float s  = snoise(vec3( x ));
    float s1 = snoise(vec3( x.y - 19.1 , x.z + 33.4 , x.x + 47.2 ));
    float s2 = snoise(vec3( x.z + 74.2 , x.x - 124.5 , x.y + 99.4 ));
    vec3 c = vec3( s , s1 , s2 );
    return c;

  }


  vec3 curlNoise( vec3 p ){

    const float e = .1;
    vec3 dx = vec3( e   , 0.0 , 0.0 );
    vec3 dy = vec3( 0.0 , e   , 0.0 );
    vec3 dz = vec3( 0.0 , 0.0 , e   );

    vec3 p_x0 = snoiseVec3( p - dx );
    vec3 p_x1 = snoiseVec3( p + dx );
    vec3 p_y0 = snoiseVec3( p - dy );
    vec3 p_y1 = snoiseVec3( p + dy );
    vec3 p_z0 = snoiseVec3( p - dz );
    vec3 p_z1 = snoiseVec3( p + dz );

    float x = p_y1.z - p_y0.z - p_z1.y + p_z0.y;
    float y = p_z1.x - p_z0.x - p_x1.z + p_x0.z;
    float z = p_x1.y - p_x0.y - p_y1.x + p_y0.x;

    const float divisor = 1.0 / ( 2.0 * e );
    return normalize( vec3( x , y , z ) * divisor );

  }

  const float speed = 2.;
  const float density = 0.007;
  const float ease = 0.02;

  void main () {
    vec2 uv = gl_FragCoord.st / size;
    vec4 velocity = texture2D(prevVelocityTexture, uv);
    vec3 prevPosition = texture2D(prevPositionTexture, uv).xyz;
    velocity.xyz += curlNoise(prevPosition * density) * speed;
    velocity.xyz *= velocity.w;
    if (velocity.w > 0.01) {
      velocity.w -= velocity.w * ease;
    } else {
      velocity.w = 1.;
    }
    gl_FragColor = velocity;
  }
</script>

<script type="x-shader/x-fragment" id="position">
  precision highp float;

  uniform vec2 size;
  uniform sampler2D prevPositionTexture;
  uniform sampler2D velocityTexture;

  void main () {
    vec2 uv = gl_FragCoord.st / size;
    vec4 prevPosition = texture2D(prevPositionTexture, uv);
    vec4 velocity = texture2D(velocityTexture, uv);

    vec3 position;
    if (velocity.w == 1.) {
      vec2 nPosition = uv * 2. - 1.;
      position = vec3(nPosition * size, 0.);
    } else {
      position = prevPosition.xyz + velocity.xyz;
    }

    gl_FragColor = vec4(position, prevPosition.w);
  }
</script>

<script type="x-shader/x-vertex" id="main-vs">
  attribute vec3 position;
  attribute vec3 normal;
  attribute vec2 instancedUv;

  uniform sampler2D positionTexture;
  uniform sampler2D velocityTexture;
  uniform mat4 mvpMatrix;
  uniform mat4 invMatrix;
  uniform vec3 lightDirection;
  uniform vec3 ambientColor;
  uniform vec3 eyeDirection;
  uniform float time;

  varying vec4 vColor;

  // https://github.com/yuichiroharai/glsl-y-rotate#readme
  mat3 rotateQ(vec3 axis, float rad) {
    float hr = rad / 2.0;
    float s = sin( hr );
    vec4 q = vec4(axis * s, cos( hr ));
    vec3 q2 = q.xyz + q.xyz;
    vec3 qq2 = q.xyz * q2;
    vec2 qx = q.xx * q2.yz;
    float qy = q.y * q2.z;
    vec3 qw = q.w * q2.xyz;

    return mat3(
        1.0 - (qq2.y + qq2.z),  qx.x - qw.z,            qx.y + qw.y,
        qx.x + qw.z,            1.0 - (qq2.x + qq2.z),  qy - qw.x,
        qx.y - qw.y,            qy + qw.x,              1.0 - (qq2.x + qq2.y)
    );
  }

  // https://github.com/mattdesl/glsl-random
  highp float random(vec2 co)
  {
      highp float a = 12.9898;
      highp float b = 78.233;
      highp float c = 43758.5453;
      highp float dt= dot(co.xy ,vec2(a,b));
      highp float sn= mod(dt,3.14);
      return fract(sin(sn) * c);
  }

  vec3 hsv(float h, float s, float v) {
    vec4 t = vec4(1., 2. / 3., 1. / 3., 3.);
    vec3 p = abs(fract(vec3(h) + t.xyz) * 6. - vec3(t.w));
    return v * mix(vec3(t.x), clamp(p - vec3(t.x), 0., 1.), s);
  }

  const float PI = 3.1415926;
  const float PI2 = PI * 2.;

  const float colorInterval = PI2 * 10.;
  const float scale = 2.;
  const float maxScaleRate = 1.4;
  const float rotationSpeed = 100.;
  const float minRotationSpeed = 0.1;

  void main () {
    vec3 modelPosition = position;
    vec4 instancedPosition = texture2D(positionTexture, instancedUv);
    float randomValue = instancedPosition.w;

    float velocity = texture2D(velocityTexture, instancedUv).w;
    float life = smoothstep(0.01, 0.04, velocity);

    float cScale = scale;
    cScale *= life;
    cScale *= mix(1., maxScaleRate, (instancedPosition.z - 1.) * 0.01);
    modelPosition *= 0.5 * cScale;

    vec3 axis = normalize(vec3(
      random(vec2(randomValue, 0.)),
      random(vec2(0., randomValue)),
      random(vec2(randomValue, 1.))
    ));
    float radian = PI2 * random(vec2(randomValue));
    radian += velocity * mix(minRotationSpeed, rotationSpeed, randomValue);
    mat3 rotate = rotateQ(axis, radian);
    modelPosition *= rotate;

    vec3 cNormal = normalize(normal);
    cNormal *= rotate;
    vec3 invLight = normalize(invMatrix * vec4(lightDirection, 0.)).rgb;
    vec3 invEye = normalize(invMatrix * vec4(eyeDirection, 0.)).rgb;
    vec3 halfLE = normalize(invLight + invEye);
    float diffuse = clamp(dot(cNormal, invLight), 0.1, 1.);
    float specular = pow(clamp(dot(cNormal, halfLE), 0., 1.), 50.);

    float colorNTime = mod(time * 0.001, colorInterval) / colorInterval;
    float alpha = 1.;
    vColor = vec4(hsv(colorNTime * PI2, 0.25 + 0.7 * colorNTime, 0.85 + 0.1 * colorNTime), alpha);
    vColor.rgb *= vec3(diffuse + specular);
    vColor.rgb += ambientColor;
    vColor.rgb *= mix(0.2, 1., clamp(((instancedPosition + 100.) / 200.).z, 0., 1.));
    vColor.rgb *= life;

    gl_Position = mvpMatrix * (vec4(modelPosition + instancedPosition.xyz, 1.));
  }
</script>

<script type="x-shader/x-fragment" id="main-fs">
  precision highp float;

  varying vec4 vColor;

  void main () {
    gl_FragColor = vColor;
  }
</script>

<script type="x-shader/x-fragment" id="texture">
  precision highp float;

  uniform vec2 resolution;
  uniform sampler2D texture;

  void main() {
    gl_FragColor = texture2D(texture, gl_FragCoord.st / resolution);
  }
</script>

CSS

html,
body {
  height: 100%;
}
body {
  overflow: hidden;
  margin: 0;
}
canvas {
  width: 100%;
  height: 100%;
}

JS

webGL библиотека
https://unpkg.com/@ko-yelie/kgl@0.2.1/dist/index.iife.min.js
и скрипт
// using original library https://github.com/ko-yelie/kgl

const width = 100
const height = 100

const sizeUniform = [width, height]
const particleUv = []

for (let j = 0; j < height; j++) {
  for (let i = 0; i < width; i++) {
    particleUv.push(i / (width - 1), 1 - j / (height - 1))
  }
}

const webgl = new Kgl({
  cameraPosition: [0, 0, 480.],
  fov: 50,
  programs: {
    resetVelocity: {
      fragmentShaderId: 'reset-velocity',
      isFloats: true,
    },
    resetPosition: {
      fragmentShaderId: 'reset-position',
      uniforms: {
        size: sizeUniform,
      },
      isFloats: true,
    },
    velocity: {
      fragmentShaderId: 'velocity',
      uniforms: {
        size: sizeUniform,
        prevVelocityTexture: 'framebuffer',
        prevPositionTexture: 'framebuffer',
      },
      isFloats: true,
    },
    position: {
      fragmentShaderId: 'position',
      uniforms: {
        size: sizeUniform,
        prevPositionTexture: 'framebuffer',
        velocityTexture: 'framebuffer',
      },
      isFloats: true,
    },
    main: {
      vertexShaderId: 'main-vs',
      fragmentShaderId: 'main-fs',
      attributes: {
        position: {
          value: [1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1],
          size: 3,
        },
        normal: {
          value: [1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1],
          size: 3
        },
        indices: {
          value: [0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23],
          isIndices: true
        },
      },
      instancedAttributes: {
        instancedUv: {
          value: particleUv,
          size: 2
        }
      },
      uniforms: {
        positionTexture: 'framebuffer',
        velocityTexture: 'framebuffer',
        time: 0,
      },
      isDepth: true,
      isTransparent: true,
    },
    texture: {
      fragmentShaderId: 'texture',
      uniforms: {
        texture: 'framebuffer'
      },
      clearedColor: [0, 0, 0, 1],
      isTransparent: true
    }
  },
  framebuffers: [
    'scene',
  ],
  framebufferFloats: {
    velocity0: {
      width,
      height
    },
    velocity1: {
      width,
      height
    },
    position0: {
      width,
      height
    },
    position1: {
      width,
      height
    }
  },
  isAutoStart: false
})

let loopCount = 0
let targetbufferIndex
let prevbufferIndex
let i

targetbufferIndex = loopCount++ % 2

{
  webgl.bindFramebuffer('velocity' + targetbufferIndex)
  webgl.programs['resetVelocity'].draw()
}

{
  webgl.bindFramebuffer('position' + targetbufferIndex)
  webgl.programs['resetPosition'].draw()
}

const draw = time => {
  targetbufferIndex = loopCount++ % 2
  prevbufferIndex = 1 - targetbufferIndex

  {
    webgl.bindFramebuffer('velocity' + targetbufferIndex)

    webgl.programs['velocity'].draw({
      prevVelocityTexture: 'velocity' + prevbufferIndex,
      prevPositionTexture: 'position' + prevbufferIndex,
    })
  }

  {
    webgl.bindFramebuffer('position' + targetbufferIndex)

    webgl.programs['position'].draw({
      prevPositionTexture: 'position' + prevbufferIndex,
      velocityTexture: 'velocity' + targetbufferIndex,
    })
  }

  {
    webgl.bindFramebuffer('scene')

    webgl.programs['main'].draw({
      positionTexture: 'position' + targetbufferIndex,
      velocityTexture: 'velocity' + targetbufferIndex,
      time,
    })
  }

  {
    webgl.unbindFramebuffer()

    webgl.programs['texture'].draw({
      texture: 'scene',
    })
  }

  requestAnimationFrame(draw)
}
requestAnimationFrame(draw)

Комментарии

  • Facebook
  • Вконтакте

Похожие статьи