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側を流体テクスチャの座標系に合わせる感じ?
- 頂点の3D位置を Model-view-projectionでクリップ空間へ変換
- Perspective Divide (xy / w)で NDC(-1~1)を得る
- NDCを *0.5+0.5して、0~1のスクリーンUVに変換
- その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の関数を使える。