2025.11.10

Github:

https://github.com/sakkom/portfolio-kuhu/tree/main/app/kuhu/ripple

uv座標のリップル効果


    precision mediump float;
    uniform sampler2D tDiffuse;
    uniform vec2 uResolution;
    uniform float uTime;
    uniform float uFreq;
    uniform float uAmp;
    in vec2 vUv;

    void main() {
      vec2 uv = vUv;
      float min_res = min(uResolution.x, uResolution.y);
      vec2 normalizedCoord = (gl_FragCoord.xy * 2.0 - uResolution) / min_res;

      float dist = length(normalizedCoord);
      float wave = sin(dist * uFreq -uTime * 5.0) * uAmp;
      vec2 dir = normalize(normalizedCoord);
      // vec2 dir = vec2(normalizedCoord.x / dist, normalizedCoord.y / dist);
      vec2 distortedUV = (uv + dir * wave);

      vec4 color = texture2D(tDiffuse, distortedUV);
      gl_FragColor = color;
    }
  

  float sinValue = sin(dist * uFreq - uTime);
  float brightness = 1.0 - abs(sinValue);
  gl_FragColor = vec4(vec3(brightness), 1.0);
  

色のみの波紋を考えてみます。 length()で取得した距離をsin()の入力します。これは[-1, 1]のuv座標で考えた場合に [0, 1.414]の範囲をとります。このままでは十分なsin波の周期を持たないので10倍して[0, 14.14]で考えてみます。 sin波は[-1, 1]で周期を繰り返します。sin波の最小値最大値はそれぞれ0を経由します。

uv座標に対してリップルを考えてみます。uAmp=0.01の場合に各ピクセルは[-1%, 1%]のオフセットを持ちます。 それがuvの増加とtの増加によって行ったり来たりすることでリップル効果が生まれます。 一次元で考えた場合sin(u - time) = 1、(u-time)=1.57での最大値は時間が増えるにしたがいuが増加し最大値の山が外側に移動していくことがわかります。

なぜnormalize()するのか考えます。45度での各ピクセルのnormalizeはvec2(0.707, 0.707)の値になります。これは0度の各ピクセルではvec2(1, 0)が成り立ちます。 normalizeされた長さ1を0.01にすることで[-1%, 1%]になります。各ピクセルは0 -> 0.01 -> 0の+のオフセットと0 -> -0.01 -> 0のマイナスのオフセットを 時間経過にしたがい繰り返しリップル効果が生まれます。