三角形▲ABCが与えられた場合、角度∠BACを直線ADで二等分し、角度二等分定理で導出されます。
BA / BD = CA / CD
ポイントEは、結果として得られる希望のはめ込み三角形上での目的の精密な位置を表します。角度二等分線ADにあるため、BAとCAの側面から等距離にあり、同一の直角三角形▲AFEと▲AGEを形成します。Sine for Right Trianglesを使用して、AEの長さを見つけることができます。
AE = EG / Sin(∠EAG)
これですべての計算が完了するので、GLSLを作ってみましょう。
まず、位置、法線、変換行列などのすべての一般的な属性から始めますが、頂点シェーダーは単一の頂点でのみ機能するため、隣接する頂点を追加の属性として追加する必要があります。このようにして、各頂点は独自の「点E」を見つけ、結果のはめ込み三角形を作成します。(注:それらはまだ画面スペースにないため、ここでは「B」および「C」と呼ばないでください。)
attribute vec3 left; //vertex to the left of this vertex
attribute vec3 right; //vertex to the right of this vertex
画面スペースと言えば、ディスプレイの縦横比も含めています(ウィンドウのサイズが変更された場合に備えて、画面のアスペクト比を均一にします)。
フラグメントシェーダーのさまざまな法線を準備し、顔をクリッピングスペースに変換したら、上記の計算を適用することに取り掛かることができます。
attribute vec3 left; //vertex to the left of this vertex
attribute vec3 right; //vertex to the right of this vertex
uniform float aspect;
varying vec3 vNormal;
varying vec2 vUv;
void main() {
vNormal = normal;
vUv = uv;
mat4 xform= projectionMatrix * modelViewMatrix;
vec4 A = xform * vec4( position, 1.0 );
vec4 B = xform * vec4( left, 1.0 );
vec4 C = xform * vec4( right, 1.0 );
vec3 CB = C.xyz - B.xyz;
vec2 BA = B.xy - A.xy;
vec2 CA = C.xy - A.xy;
float lengthBA = length(BA);
float lengthCA = length(CA);
float ratio = lengthBA / ( lengthBA + lengthCA );
vec3 D = B.xyz + ratio * CB.xyz;
vec3 AD = D - A.xyz;
vec3 bisect = normalize(AD);
float theta = acos( dot(BA, CA) / (lengthBA * lengthCA) ) / 2.0;
float AE = 1.0/(sin(theta)*aspect);
newPos.z += AE/length(AD) * (D.z - A.z);
newPos.x += bisect.x*AE;
newPos.y += bisect.y*AE;
gl_Position = newPos;
}
このコードにより、以下の結果が得られます。
このプロセスによって裏切りされた三角形のほとんどに関係するエッジケースがいくつかあることに注意してください。コードでこれに対処し始めましたが、今のところこれらのケースを単に回避することにしました。たぶん、このプロジェクトが完了したら、もう一度見直すでしょう。