LoginSignup
61
47

More than 3 years have passed since last update.

Babylon.jsで空中散歩はいかが?

Last updated at Posted at 2020-06-20

1.概要

3Dグラフィック・エンジンの一つであるBabylon.jsを使用して、空中散歩のデモを作成してみました。 なお、Windows10の環境において Microsoft Edge (Ver. 11.0.17763.379)、 Firefox (Ver. 65.0.2/64 bit) 及び Google Chrome (Ver. 73.0.3683.86/64 bit) で動作を確認しました。 また、Androidの一部及びiOSの一部での動作も確認しました。 ここで使用している3Dグラフィックの多くは「RIG Models」様からダウンロードさせて戴きました。 無料グラフィックを提供されている各位に感謝いたします。

完成した空中散歩デモの実行」 

空中散歩デモの画像(例)は次の通りです。 
Cruising_in_the_Sky.png

2.ファイル構成

ここで説明する内容に関するファイルは全て「GitHub Babylon.js_3D_Graphics」からダウンロード可能です。 また、そのファイル構成は次の通りです。
- scenes: 3Dグラフィック・データを保存したフォルダーです。
- texture: 「Babylon.js on GITHUB」からダウンロードしたskybox等のテクスチュア・データ等を保存したフォルダーです。
- index_Cruising.html: 今回作成した空中散歩デモ用HTMLファイルの選択・実行用メニューです。(注:本メニューは「safari」では動作しませんので悪しからず!)
- Cruising_in_the_sky_001.html - Cruising_in_the_sky_004.html: 今回作成した空中散歩デモ用JavaScriptを含むHTMLファイル本体です。

3.空中散歩デモの作成

ここでは、周囲の環境と夕暮れ時の空のグラデーション変化、雲の追加表示と途中に配置した3Dグラフィックについて、ステップバイステップで説明します。 なお、Babylon.jsの使い方については、「Babylon.jsで3Dアニメーションを含むグラフィックを描画してみる」で説明していますのでそちらを参照ください。
index Cruising in the sky」: ここで説明するプログラム全てをメニューから選択して表示できます。 但し、このメニューは「Safari」では動作しませんので悪しからず。

Step-1: 周囲の環境を表示する

Cruising_in_the_sky_001.html:次のような周囲の環境を表示
Cruising_in_the_Sky_001.png
周囲の環境として、「skybox」により晴れた海上を表示させました。 また、ライトとカメラも設定してありますので、マウスでカメラの視点を変更できます。 ソースコード全てを以下に示します。

Cruising_in_the_sky_001.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width">
<title>Babylon.js - Cruising in the sky with Vertex Shaders #001</title>
<style>
     html,body,canvas { margin: 0; padding: 0; width: 100%; height: 100%; font-size: 0; }
</style>
<script src="https://code.jquery.com/pep/0.4.1/pep.min.js"></script>
<script src="https://preview.babylonjs.com/babylon.js"></script>
<script>

var Camera_Control = "ON";
var CreateVertexDataScene = function(engine) {
    var scene = new BABYLON.Scene(engine);
    var start_time = Date.now();
    var skybox_data = "./textures/TropicalSunnyDay";
    var pos_x = 0;
    var pos_y = -128;
    var pos_z = 0;
    var Limit_z = 5000;

// Set the skybox
    var skybox0 = BABYLON.MeshBuilder.CreateBox("skyBox", {size: Limit_z * 2.0}, scene);
    skybox0.rotation.x = Math.PI * 1.0;
    skybox0.rotation.y = Math.PI * 0.14;
    var skybox0_material = new BABYLON.StandardMaterial('skybox', scene);
    skybox0_material.backFaceCulling = false;
    skybox0_material.reflectionTexture = new BABYLON.CubeTexture(skybox_data, scene);
    skybox0_material.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
    skybox0_material.diffuseColor = new BABYLON.Color3(0, 0, 0);
    skybox0_material.specularColor = new BABYLON.Color3(0, 0, 0);
    skybox0_material.disableLighting = true;
    skybox0.material = skybox0_material;

// Set the light
    var light_1 = new BABYLON.DirectionalLight('light', new BABYLON.Vector3(100, -1000, 100), scene)
    light_1.intensity = 1.0;

// Set the camera
    var camera = new BABYLON.FreeCamera("camera", new BABYLON.Vector3(0, -128, 0), scene);
    camera.fov = 30;
    camera.minZ = 1;
    camera.maxZ = Limit_z * 2.0;

    return scene;
}

var demo = {
    constructor: CreateVertexDataScene,
    onload: function (scene) {
    if (Camera_Control == "ON") {
        scene.activeCamera.attachControl(canvas);
    }
    else {
        scene.activeCamera.detachControl(scene.getEngine().getRenderingCanvas());
    }
    }
};

</script>
</head>
<body>
    <canvas id="renderCanvas" touch-action="none"></canvas>
    <div id="notSupported" class="hidden">Sorry but your browser does not support WebGL...</div>
    <script src="loaderCustoms.js"></script>
</body>
</html>

Step-2: 時間経過を表現する

Cruising_in_the_sky_002.html:次のような朝方/夕暮れ時に変化するグラデーションを表示
このデモでは、時間と共に朝方から昼、夕暮れ時に徐々に変化するようにcolor gradingを使用しました。
Cruising_in_the_Sky_002.png
追加した「color grading」の箇所を以下に示します。 なお、「color grading」については、Babylon.jsのサンプルを参照下さい。

Cruising_in_the_sky_002.htmlの一部
// Added the color grading
    var colorGrading = new BABYLON.ColorGradingTexture("./textures/LateSunset.3dl", scene);
    skybox0_material.cameraColorGradingTexture = colorGrading;
    skybox0_material.cameraColorGradingEnabled = true;


// Control the color grading
    var j = 0;
    scene.registerBeforeRender(function () {
        colorGrading.level = Math.sin(j++ / 240) * 0.5 + 0.5; 
    });

Step-3: 追加で雲を表示させる

Cruising_in_the_sky_003.html:次のように下部に雲を表示
Shaderを使用して雲をたくさん表示しています。 この雲のシェーダーについてはBabylon.jsのShader Materialを参考に作成しました。
Cruising_in_the_Sky_003.png
追加した雲のシェーダーの箇所は、以下の通りです。

Cruising_in_the_sky_003.htmlの一部
// Instrumentation
    var instrumentation = new BABYLON.EngineInstrumentation(engine);
    instrumentation.captureGPUFrameTime = true;
    instrumentation.captureShaderCompilationTime = true;

// Create clouds
    var cloudMaterial = new BABYLON.ShaderMaterial("cloud", scene, {
        vertexElement: "vertexShaderCode",
        fragmentElement: "fragmentShaderCode"
    },
    {
        needAlphaBlending: true,
        attributes: ["position", "uv"],
        uniforms: ["worldViewProjection"],
        samplers: ["textureSampler"]
    });
    var temp = Math.random() * 3.0;
    var url = "./textures/smoke_15.png";
    if(temp >= 1) {
    url = "./textures/cloud.png";
    } 
    if(temp >= 2) {
    url = "./textures/cloud_01.png";
    }

    cloudMaterial.setTexture("textureSampler", new BABYLON.Texture(url, scene));
    cloudMaterial.setFloat("fogNear", -100);
    cloudMaterial.setFloat("fogFar", Limit_z);
    cloudMaterial.setColor3("fogColor", BABYLON.Color3.FromInts(69, 132, 180));

// Create merged planes
    size = 128;
    var globalVertexData;

    for (var i = 0; i < Limit_z; i++) {
        var planeVertexData = BABYLON.VertexData.CreatePlane({ size: 128 });
        delete planeVertexData.normals;     // We do not need normals

// Transform
    var randomScaling = Math.random() * Math.random() * 1.5 + 0.5;
    var transformMatrix = BABYLON.Matrix.Scaling(randomScaling, randomScaling, 1.0);
        transformMatrix = transformMatrix.multiply(BABYLON.Matrix.RotationZ(Math.random() * Math.PI));
        transformMatrix = transformMatrix.multiply(BABYLON.Matrix.Translation(Math.random() * 1000 - 500, -Math.random() * Math.random() * 100, Limit_z - i));

        planeVertexData.transform(transformMatrix);

// Merge
    if (!globalVertexData) {
        globalVertexData = planeVertexData;
    } else {
        globalVertexData.merge(planeVertexData);
    }
    }

    var clouds = new BABYLON.Mesh("Clouds", scene);
    globalVertexData.applyToMesh(clouds);

    clouds.material = cloudMaterial;

    var clouds2 = clouds.clone();
    clouds2.position.z = -500;

シェーダー本体(vertexShader及びfragmentShader)については、次の通りです。

Cruising_in_the_sky_003.htmlの一部
<script type="application/vertexShader" id="vertexShaderCode">
    #ifdef GL_ES
    precision highp float;
    #endif

    // Attributes
    attribute vec3 position;
    attribute vec2 uv;

    // Uniforms
    uniform mat4 worldViewProjection;

    // Normal
    varying vec2 vUV;

    void main(void) {
        gl_Position = worldViewProjection * vec4(position, 1.0);

        vUV = uv;
    }
</script>

<script type="application/fragmentShader" id="fragmentShaderCode">
    #ifdef GL_ES
    precision highp float;
    #endif

    varying vec2 vUV;

    uniform vec3 fogColor;
    uniform float fogNear;
    uniform float fogFar;

    // Refs
    uniform sampler2D textureSampler;

    void main(void) {
        float depth = gl_FragCoord.z / gl_FragCoord.w;
        float fogFactor = smoothstep(fogNear, fogFar, depth);

        gl_FragColor = texture2D(textureSampler, vUV);
        gl_FragColor.w *= pow(abs(gl_FragCoord.z), 20.0);
        gl_FragColor = mix(gl_FragColor, vec4(fogColor, gl_FragColor.w), fogFactor);
    }
</script>

Step-4: 船団と行き交う船等を表示させる

Cruising_in_the_sky_004.html:次のように船団と途中の行き交う等を表示
空と雲だけでは寂しいので、次のように船団と行き交う船等を表示させました。 今回は、全てGLTFフォーマットの3Dグラフィックを使用しています。
Cruising_in_the_Sky_004.png
3Dグラフィックの表示用Javascript例を次に示します。 3Dグラフィックそれぞれのサイズや向きを合わせる他、それぞれに動作を設定しています。

Cruising_in_the_sky_004.htmlの一部
    var ship = new Array();
    var ship_dir = "./scenes/crusing/";
    var ship_data = new Array();
    var ship_direction = new Array();
    var ship_scale = new Array();

    ship_data[5] = "Sailing_Ship_004_Whale.glb";
    ship_scale[5] = 100;

    BABYLON.SceneLoader.ImportMesh("", ship_dir, ship_data[5], scene, function (newMeshes, particleSystems, skeletons) {
        ship[5] = newMeshes[0];
        ship[5].rotationQuaternion = undefined;
        ship[5].rotation.z = 180/180 * Math.PI;
        ship[5].scaling = new BABYLON.Vector3(ship_scale[5], ship_scale[5], ship_scale[5]);
        scene.registerBeforeRender(function() {
        ship[5].position = new BABYLON.Vector3( pos_x + 350, pos_y - 0, pos_z * 0 + 3000);
        ship[5].rotation.x = (Math.sin(ship_direction[5].x) * 20 + 0) / 180 * Math.PI;
        ship[5].rotation.y = (Math.sin(ship_direction[5].y) * 20 + 180) / 180 * Math.PI;
        ship[5].rotation.z = (Math.sin(ship_direction[5].z) * 10 + 180) / 180 * Math.PI;
        ship_direction[5].x = ship_direction[5].x + 0.01;
        ship_direction[5].y = ship_direction[5].y + 0.005;
        ship_direction[5].z = ship_direction[5].z + 0.01;
        });
    });

4、Reference

以上

61
47
0

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
61
47