3/14

日記

2026/03/14

GLSL

fractの中身が負

fract((_index - 0.5) * 2.0);
という式に直面した。

_indexは0~1なので、fractの中身は -1 ~ 1 になる。

fract(x)は x - floor(x) で定義される。

fract(-0.2)の場合、 -0.2 - (-1) = 0.8 となる。

コード解説

float random(vec2 st) {
  return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
}

vec2 truchetPattern(vec2 _st, float _index) {
  _index = fract((_index - 0.5) * 2.0);

  if(_index > 0.75) {
    _st = vec2(1.0) - _st;
  } else if(_index > 0.5) {
    _st = vec2(1.0 - _st.x, _st.y);
  } else if(_index > 0.25) {
    _st = 1.0 - vec2(_st.x, 1.0 - _st.y);
  }
  return _st;
}

void main() {
  vec2 st = gl_FragCoord.xy / uResolution;

  st *= 10.0;
  vec2 ipos = floor(st);
  vec2 fpos = fract(st);

  vec2 tile = truchetPattern(fpos, random(ipos));

  float color = 0.0;

  color = smoothstep(tile.x - 0.1, tile.x, tile.y) - 
           smoothstep(tile.x, tile.x + 0.1, tile.y);

  gl_FragColor = vec4(vec3(color), 1.0);
}

st = 10.0 で0~1を0~10に 10 * 10 等分

ipos は 10*10をセル番号(0,0) (1.0) ...

fpos は セル内でuvのような

random(ipos)はセル内では同じ値が返るので、10*10タイル内でランダムに

truchetPattern内
最初のfractは、0~1を0~1に分布し直しているだけなので、消しても問題なかった。
その後_index(ランダムな値)によって、タイル内座標を変更している。

smooshstepで線を書いている。 変換なし(x,y)では、タイル内座標はuvのような感じのままなので、右上に伸びるような見た目になる。(y = x)

Three.js

Fluid x 3Dオブジェクト実装

やりたいこと

流体シミュレーションがある。3dオブジェクトとして、planeが画面中央付近にあるとする。
流体のかかっている箇所だけplaneを別テクスチャに切り替えたい。

問題点

流体の速度をそのままplaneにわたすと、例えば画面右上にカーソルがある時、planeの右上が対応してしまう。
fluid = texture2D(uFluidVelocity, vUv).xy;
vUvはplaneの座標だけど、uFluidVelocityはスクリーン座標を参照しており座標系が違うから。

流体テクスチャ - スクリーン空間(0~1 UV)
Faceメッシュ - オブジェクト空間

対処

カメラの視点からみて、このplaneの頂点が画面上のどこにあるかを0~1UVに変換する

plane側を流体テクスチャの座標系に合わせる感じ?

  1. 頂点の3D位置を Model-view-projectionでクリップ空間へ変換
  2. Perspective Divide (xy / w)で NDC(-1~1)を得る
  3. NDCを *0.5+0.5して、0~1のスクリーンUVに変換
  4. そのUVで流体テクスチャをサンプリング

NDCとは

Normalized Device Coordinates(正規化デバイス座標)

  • 画面全体を -1~1 で表した座標
  • 解像度に依存しない共通の「画面位置」
  • clipPos.xy / clipPos.wで得られる(遠近法を考慮した割り算)
  • 左端 x=-1、右端 x=1、中央 (0,0)

particle

やりたいことをuProgress, mix, smoothstepを用いて実装。
かなりこれらに慣れたのではないだろうか。

カメラからパーティクルの距離

vec4 modelPosition = modelMatrix * vec4(newPosition, 1.0);
vec4 viewPosition = viewMatrix * modelPosition;
vec4 projectedPosition = projectionMatrix * viewPosition;

float cameraZ = length(viewPosition.xyz);

viewPositionはカメラの視点基準の空間での位置。
ビュー空間では、カメラが原点 (0, 0, 0) にあり、視線方向が -Z になっている。

ビュー空間でカメラが原点なので、viewPosition.xyz は「カメラからその頂点へのベクトル」と同じ。したがって:
length(viewPosition.xyz)から求まる。

ちなみにfogに使う。

fog

const float fogNear = 10.0;
const float fogFar = 1000.0;
{
 float fog = clamp((fogFar - cameraZ) / (fogFar - fogNear), 0.0, 1.0);
}

別の場所のGLSLで同じ関数を使いたい

関数をglslファイルとして分離する。そして

import noiseGlsl from "./shaders/noise.glsl";
import particlesVert from "./shaders/particles.vert";

new THREE.ShaderMaterial() {
 vertexShader: noiseGlsl + particlesVert
}

+でつなぐと特に宣言無しでparticlesVert内でnoiseGlslの関数を使える。