フラグメントシェーダーの小技を集めてみました。
VanillaGLで書き換えているくらい(驚きの見通しのよさ)
	drawArraysはVanillaGLProgramのものではなく、WebGLRenderingContextの素のdrawArraysである点に注意
if文が増えている: if (...) discard
lengthすると、現在描画しているピクセルがポイントスプライトの中心からどれくらい離れているかが分かる
	gl.getParameter(ALIASED_POINT_SIZE_RANGE)で分かるが、GLの仕様ではサイズ1だけ書ければよいことになっているので、実はきちんと描画できないかもしれない
discardではなく、アルファを使っても見た目は同じにできる
VanillaGL.initを少し拡張する
init(opt) {
	...
	if (opt?.useDepth) {
		this.enable(this.DEPTH_TEST);
		this.clearMask |= this.DEPTH_BUFFER_BIT;
	}
},
opt引数を追加し、useDepthがtrueならば、深度テストを有効にして、initCanvas時に深度バッファもクリアするように設定
promise_allもprepareという別名を作ってある
promise_allはPromise.allに合わせて引数がプロミスの配列だが、prepareはプロミスを直接指定する
const ready = prepare(...);
...
ready.then((...) => {
	...
});
のように使う(prepare→ready→then)
discardによる円とアルファによる円を描いてから、キャンバス全体を覆う四角形を描く
gl.depthFuncで変えられる
discardするとフラグメントシェーダーの呼び出しがなかったが如く、カラーバッファだけではなく、深度バッファへも出力が行われない
discardした部分は、背後にあるものを後に描いても描画される
中を抜けば枠になる。
all(lessThan(abs(vTex - 0.5), vec2(0.45))))
vTex - 0.5: テクスチャ座標vTexの各成分から0.5を引く、結果は±0.5になる
abs(...): 絶対値をとる、結果はポリゴン中央の時が (0, 0) で一番外側はどちらに行っても0.5
vec2(0.45): x, yとも0.45のvec2を作る
lessThan(...): vec2の各成分を比較して、第1引数の方が小さければ対応する成分をtrueとしたboolのvec2、つまりbvec2を返す
all(...): 引数のbvec2の成分がすべてtrueならばtrue
VanillaGL.pers0を定義している
VanillaMat4.pers0を呼んでくれる
modeでも指定できる
VanillaMat4のtoRadianをデフォルトから変更するとVanillaGLTouchDragで問題が起きるので修正
	setupTouchDrag(canvas, ev => {
		Mat4.pushToRadian?.();
		...
		Mat4.popToRadian?.();
		this.draw();
	});
toRadianがない古いVanillaMat4でも動くようになっている
uBorderは.xyがボーダーの太さ(X方向・Y方向)で.zwが角の半径(X方向・Y方向)
p = 0.5 - abs(vTex.xy - 0.5)
p.xyのいずれかが半径より大きかった場合はボーダーの太さよりも大きい(内側にある)点をdiscard(else節)
p.xyの両方が角の半径より小さければ角の部分を書く
z、\(b\)をwとして、楕円より外側ならdiscard
z-x、\(b\)をw-yとして、楕円より内側ならdiscard
p = uBorder.zw - p
r.xy = max(vec2(0), r.zw - r.xy)
r.xyが内側、r.zwが外側の半径になっている
p *= pのように各成分を2乗しておけば、\(x^2b^2+y^2a^2\)はdot関数(内積を求める関数)で求められる
テクスチャの角を落とすこともできる。
VanillaGL.loadTextureは意外にも作ってなかったので作っておく(glUtil.loadTextureを呼び出す)
setTextureはsetUniformと並べた時に第1引数がUniformの型を示す文字列の方がコード全体の座りがいいため、第1引数が文字列だった場合は第2引数と順序を逆にしてglUtil.setTextureを呼び出す
gl-sample.cssに追い出した
.xyから0.5を引くと、テクスチャ座標の原点が (0.5, 0.5) に移り、座標の範囲は (-0.5, -0.5)〜(+0.5, +0.5)になる
absをとると、マイナス側にはみ出した部分がX軸・Y軸と対称な位置に「折り返す」
maxで0と座標値の大きい方を取るので、座標が0未満になると0を返す、つまり、最小値が0
discardすればよい
uRadiusの.a成分が最終結果のアルファ成分に設定される 31 Aug 2024
.zは使われていない
フラグメントシェーダーで円が描けるのだから、いわゆるラウンドキャップの直線(角が丸いやつ)が描けるのではないか?
uMatは投影変換行列
uThicknessは.xyにキャンパスサイズ(幅と高さ)を、.zに線の太さを入れる
p1とp2は直線の始点と終点
uvは直線および接合部のテクスチャ座標っぽいもので、どの位置の頂点かを示し、.zが0ならば直線、1ならば接合部の頂点であることを示す
uvで示し、p1とp2は12回すべて同じ値を指定し、uvだけを8通り(指定する頂点数は12個だが、そのうち4個は重複しているので全部で8通り)指定する
uv.xyzの指定の仕方は以下のようになる
| 直線部三角形1 | [ 0, 1, 0 ], [ 0, 0, 0 ], [ 1, 1, 0 ]
 | 
| 直線部三角形2 | [ 1, 1, 0 ], [ 0, 0, 0 ], [ 1, 0, 0 ]
 | 
| 接合部三角形1 | [ 0, 1, 1 ], [ 0, 0, 1 ], [ 1, 1, 1 ]
 | 
| 接合部三角形2 | [ 1, 1, 1 ], [ 0, 0, 1 ], [ 1, 0, 1 ]
 | 
p1・p2を投影変換行列で斉次座標に変換、さらに.wで割ってスクリーン座標を求める
offsetはuv.xyが (0, 0) - (1, 1) なのに対し、(-1, -1) - (1, 1) になる
kは直線太さをキャンバスサイズで割ったもので、直線の太さをスクリーン座標系で表していることになる
kが1になるが、スクリーン座標系は-1〜+1なので、実際には画面の半分にしかならず、直線の太さの半分に相当する
uv.zが正の場合(通常は1を指定)は接合部なのでuv.xyをテクスチャ座標としてそのまま流す
p1とp2のスクリーン座標が一致する場合は法線ベクトルの長さが0になるが、ゼロ除算を避けるため、長さの最小を1/1,000,000に押さえ込む
uv.xによって始点と終点のどちらかを選び、uv.yで直線の上側と下側のどちらの頂点なのかを決定し、kをかけて始点もしくは終点に足すことで頂点座標を作る
kは直線の太さの半分なので、上下に半分ずつ描くことで、できあがりの直線の幅は指定どおりになる
prepareRoundCapLineが線を用意してくれる
pointlistには点列を渡すが、次元は問わない
closedはfalseならば点列は閉じていない、trueならば点列は閉じている
drawRoundCapLineで線を描く
attrはprepareRoundCapLineに渡した点列の型を頂点属性の形で示す
f3の部分だけ指定する
p1:f3, p2:f3のように展開される
f1で、prepareRoundCapLineで生成した直線を描画できる
prepareEquatorRoundCapはprepareRoundCapLineに2次元の点列を渡し、drawEquatorRoundCapはdrawRoundCapLineにf2を渡している
ポリゴンの表と裏で表示を変えることができる。
VanillaMat4.pers(クラスメソッド版)を呼び出す
wとhはthis.canvasから自動で埋めてくれる
gl.TRIANGLESの場合、の絵のように三角形の頂点を結び、それを視点から見た場合に反時計回りに見えるなら、ポリゴンの表が見えている
gl.TRIANGLE_STRIPの場合、最初の三角形はgl.TRIANGLESと同じになるが、ふたつ目の三角形は逆になる
gl.TRIANGLE_STRIPの場合、最初の三角形は反時計回りに見える場合が表、次の三角形は時計回りに見える場合が表、その次の三角形は反時計回りに見える場合が表、のように、表に見える回転方向が交互に入れ替わる
gl_FrontFacingで分かる
trueならば表が見えている、falseならば裏が見えている
ifは避けるべきと言われている
gl_FrontFacingをfloatにキャストすると0か1になるため、mix関数の第3引数に渡せば、表が見えている時に第2引数の値が、裏が見えている時に第1引数の値が得られる
trueだが、隣のユニットではfalseといった場合を考えると、実際にはジャンプできないことが分かる
gl_FrontFacingで分岐して1回実行するよりも、これまた説明していないが、カリングを設定して裏と表を別々のシェーダーで描画した方が性能が出るんじゃなかろうか(やってないからじっさいのところはわかんない)
09 Sep 2024: applyをaffectに変更
20 Aug 2024: 本編
15 Aug 2024: 予告編
Copyright (C) 2024 akamoz.jp