以前の記事で、three.jsのShaderMaterialでシーン内のライトを使用してライティングする方法について書きました。今回はシーンに設定されたフォッグをShaderMaterialでも使用してみたいと思います。
three.jsのバージョンはr109です。
まず、オブジェクトを単色で塗りつぶすだけの簡単なShaderMaterialは以下のようになります。
const material = new THREE.ShaderMaterial({
vertexShader: `
void main(void) {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform vec3 color;
void main(void) {
gl_FragColor = vec4(color, 1.0);
}
`,
uniforms: {
color: { value: new THREE.Color(0xff0000) },
},
});
これを以下のようにフォッグが適用されているシーンでレンダリングしてみます。
const scene = new THREE.Scene();
scene.fog = new THREE.Fog(0x000000, 0, 10);
左側が先ほどのShaderMaterialで描画したオブジェクトで、右側が参考のためにMeshBasicMaterialで描画したオブジェクトになります。MeshBasicMaterialにはフォッグが適用されていますが、ShaderMaterialにはフォッグが適用されていません。
MeshBasicMaterialの頂点シェーダーのソースコードはmeshbasic_vert.glsl.jsに、フラグメントシェーダーのソースコードはmeshbasic_frag.glsl.jsにあります。これを見てみると頂点シェーダーでは#include <fog_pars_vertex>
と#include <fog_vertex>
が、フラグメントシェーダーでは#include <fog_pars_fragment>
と#include <fog_fragment>
がフォッグに関係していそうです。
このインクルードされるファイルの中身を見てみると次のようになっており、頂点シェーダーからカメラ座標系のz値をvaryingsでフラグメントシェーダーに渡し、フラグメントシェーダーでuniformsの値とvaryingsの値をもとにフォッグを計算していることがわかります。
#ifdef USE_FOG
varying float fogDepth;
#endif
#ifdef USE_FOG
fogDepth = -mvPosition.z;
#endif
#ifdef USE_FOG
uniform vec3 fogColor;
varying float fogDepth;
#ifdef FOG_EXP2
uniform float fogDensity;
#else
uniform float fogNear;
uniform float fogFar;
#endif
#endif
#ifdef USE_FOG
#ifdef FOG_EXP2
float fogFactor = 1.0 - exp( - fogDensity * fogDensity * fogDepth * fogDepth );
#else
float fogFactor = smoothstep( fogNear, fogFar, fogDepth );
#endif
gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );
#endif
このことから、ShaderMaterialのシェーダーでもこれらをインクルードするようにして、必要なuniformsがシェーダーに渡るようにすればShaderMaterialでもシーンに設定されたフォッグが使えそうです。
というわけで最初に紹介したシンプルなShaderMaterialにシーンのフォッグを適用するコードは次のようになります。シェーダーで必要なファイルをインクルードするとともに、fog
プロパティをtrueにしてuniforms
プロパティにTHREE.UniformsLib.fog
を追加しています。
const material = new THREE.ShaderMaterial({
vertexShader: `
#include <fog_pars_vertex>
void main(void) {
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
gl_Position = projectionMatrix * mvPosition;
#include <fog_vertex>
}
`,
fragmentShader: `
uniform vec3 color;
#include <fog_pars_fragment>
void main(void) {
gl_FragColor = vec4(color, 1.0);
#include <fog_fragment>
}
`,
uniforms: THREE.UniformsUtils.merge([
THREE.UniformsLib.fog,
{
color: { value: new THREE.Color(0xff0000) },
}
]),
fog: true,
});
これでレンダリングすると以下のようになります。先ほどと同じように左側がShaderMaterial、右側がMeshBasicMaterialです。ShaderMaterialにもフォッグが適用されています。
デモとソースコードはここに置いてあります。
デモ: https://aadebdeb.github.io/Sample_Three_ShaderMaterial_Fog/index.html
ソースコード: https://github.com/aadebdeb/Sample_Three_ShaderMaterial_Fog