はじめに
WebGLで陰影を表現する際、法線ベクトルの情報が必要になる。形が固定されたオブジェクトの場合、Three.jsではcomputeFaceNormalsやcomputeVertexNormals等の関数を用いて法線ベクトルを自動計算させることが出来るが、GLSL内で頂点を移動させる場合はThree.jsの範疇にないのでこれらの関数を使って法線ベクトルを計算することができない。この記事は特定のルールに従って頂点シェーダ内で頂点位置を動かす際の法線ベクトルをGLSL内で計算する方法を実装した際のメモである。
前提条件
- Three.jsを使う(r121)
- ShaderMaterialを使う(Three.jsで自作シェーダを使用可能にする)
- Chrome、Firefox、Safariで動作する(Edgeでも動作すると思うが未検証)
ChromeとFirefoxはWebGL2対応、SafariはWebGL2非対応で、いずれでも動作させたい
スクリプトの実際
法線ベクトルを求めるにはGLSLのdFdx関数とdFdy関数を使用する。これらの関数はWebGL2からは標準で利用できるが、WebGLでは拡張機能を有効化しないと利用できない。ShaderMaterialには拡張機能を有効化する手段が提供されている。
const material = new THREE.ShaderMaterial({
'vertexShader' : vShader,
'fragmentShader' : fShader,
'uniforms' : uniforms,
'extensions' : {
'derivatives' : true //ココ
}
});
最初はこれを見つけられず(意味を理解していなかった)、手動でrendererに対してextensionを登録して、色々と工夫して有効化していたが、これを追加することで全てThree.jsが吸収してくれた(2020.11.01修正)。
varying vec4 vPosition;
//法線ベクトルを求める魔法の関数
vec3 getNormal ( vec3 position ) {
vec3 dx = dFdx( position );
vec3 dy = dFdy( position );
return normalize( cross(dx, dy) );
}
void main () {
//頂点座標から法線を求める
vec3 normal = getNormal( gl_FragCoord );
//ここから先の処理は光源の種類で異なる(と思う)
//平行光を使用した場合の光源の向きをlightとする
//元の出力色をoriginalColorとする
float diff = clamp( dot(light, normal), 0.1, 1.0 );
gl_FragColor = clamp( outColor * diff, 0.0, 1.0 );
}
getNormal関数でなぜ法線ベクトルが求まるのかはよくわからないが、陰影が出るようになったので取り敢えず良しとした。時間があるときにこの関数の意味を調べたい。