three.jsのShaderMaterialでシーンのフォッグを使用する

[以前の記事](https://qiita.com/aa_debdeb/items/870e52499dc94a2942c3)で、three.jsのShaderMaterialでシーン内のライトを使用してライティングする方法について書きました。今回はシーンに設定されたフォッグをShaderMaterialでも使用してみたいと思います。

three.jsのバージョンはr109です。

---

まず、オブジェクトを単色で塗りつぶすだけの簡単なShaderMaterialは以下のようになります。

```js
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) },
  },
});

```

これを以下のようにフォッグが適用されているシーンでレンダリングしてみます。

```js
const scene = new THREE.Scene();
scene.fog = new THREE.Fog(0x000000, 0, 10);
```

![shadermaterial_nofog.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61569/ddd33471-d03d-8046-e309-ed89485e3210.png)
左側が先ほどのShaderMaterialで描画したオブジェクトで、右側が参考のためにMeshBasicMaterialで描画したオブジェクトになります。MeshBasicMaterialにはフォッグが適用されていますが、ShaderMaterialにはフォッグが適用されていません。

MeshBasicMaterialの頂点シェーダーのソースコードは[meshbasic_vert.glsl.js](https://github.com/mrdoob/three.js/blob/dev/src/renderers/shaders/ShaderLib/meshbasic_vert.glsl.js)に、フラグメントシェーダーのソースコードは[meshbasic_frag.glsl.js](https://github.com/mrdoob/three.js/blob/dev/src/renderers/shaders/ShaderLib/meshbasic_frag.glsl.js)にあります。これを見てみると頂点シェーダーでは`#include <fog_pars_vertex>`と`#include <fog_vertex>`が、フラグメントシェーダーでは`#include <fog_pars_fragment>`と`#include <fog_fragment>`がフォッグに関係していそうです。

このインクルードされるファイルの中身を見てみると次のようになっており、頂点シェーダーからカメラ座標系のz値をvaryingsでフラグメントシェーダーに渡し、フラグメントシェーダーでuniformsの値とvaryingsの値をもとにフォッグを計算していることがわかります。

```glsl:/src/renderers/shaders/ShaderChunk/fog_pars_vertex.glsl.js
#ifdef USE_FOG
	varying float fogDepth;
#endif
```

```glsl:/src/renderers/shaders/ShaderChunk/fog_vertex.glsl.js
#ifdef USE_FOG
	fogDepth = -mvPosition.z;
#endif
```

```glsl:/src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl.js
#ifdef USE_FOG
	uniform vec3 fogColor;
	varying float fogDepth;
	#ifdef FOG_EXP2
		uniform float fogDensity;
	#else
		uniform float fogNear;
		uniform float fogFar;
	#endif
#endif
```

```glsl:/src/renderers/shaders/ShaderChunk/fog_fragment.glsl.js
#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`を追加しています。

```js
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にもフォッグが適用されています。
![shadermaterial_fog.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61569/c6b65ac1-8522-cf01-b5b0-1fc7f4089ecf.png)
デモとソースコードはここに置いてあります。
デモ: https://aadebdeb.github.io/Sample_Three_ShaderMaterial_Fog/index.html
ソースコード: https://github.com/aadebdeb/Sample_Three_ShaderMaterial_Fog