LoginSignup
4

More than 3 years have passed since last update.

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

Posted at

以前の記事で、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_nofog.PNG
左側が先ほどの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の値をもとにフォッグを計算していることがわかります。

/src/renderers/shaders/ShaderChunk/fog_pars_vertex.glsl.js
#ifdef USE_FOG
    varying float fogDepth;
#endif
/src/renderers/shaders/ShaderChunk/fog_vertex.glsl.js
#ifdef USE_FOG
    fogDepth = -mvPosition.z;
#endif
/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
/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を追加しています。

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://aadebdeb.github.io/Sample_Three_ShaderMaterial_Fog/index.html
ソースコード: https://github.com/aadebdeb/Sample_Three_ShaderMaterial_Fog

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4