#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」様からダウンロードさせて戴きました。 無料グラフィックを提供されている各位に感謝いたします。
「完成した空中散歩デモの実行」
空中散歩デモの画像(例)は次の通りです。
#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:次のような周囲の環境を表示
周囲の環境として、「skybox」により晴れた海上を表示させました。 また、ライトとカメラも設定してありますので、マウスでカメラの視点を変更できます。 ソースコード全てを以下に示します。
<!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を使用しました。
追加した「color grading」の箇所を以下に示します。 なお、「color grading」については、Babylon.jsのサンプルを参照下さい。
// 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を参考に作成しました。
追加した雲のシェーダーの箇所は、以下の通りです。
// 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)については、次の通りです。
<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グラフィックを使用しています。
3Dグラフィックの表示用Javascript例を次に示します。 3Dグラフィックそれぞれのサイズや向きを合わせる他、それぞれに動作を設定しています。
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
- Babylon.js: Home page of Babylon.js
- RIG Models: 3D Graphics
- Babylon.jsで3Dアニメーションを含むグラフィックを描画してみる
- Babylon.jsで3Dゲームを作成してみた(その1:迷路編)
- Babylon.jsで3Dゲームを作成してみた(その2:脱出パズル編)
以上