電話で博物館の冒険

親愛なる読者への挨拶。 私の名前はアンドレイです。最近、大祖国戦争の中央博物館で働いています。 通常の博物館を作成することに加えて、仮想展示も開発しています。 したがって、Nokia Lumia 1520のレビューでは、この電話を使用して、20メガピクセルのカメラとThree.JS JavaScriptライブラリを使用して小さな仮想ツアーを作成する方法を示します。



しかし、最初に、数日間の短いテストで何が私の手に落ちたかを考えましょう。 これは、小さな青い箱から見たデバイスの外観です。



画像



Lumia 1520の2つの主な機能は、20メガピクセルのカメラとWindows Phone 8オペレーティングシステムであり、さらに、わずか数日で20〜30%しか放電できなかったバッテリーに注目したいと思います。 奇妙なものから-あなたのMicrosoftアカウントを示すことはできませんでした、接続の欠如やサーバーエラーなどのいくつかのあいまいな理由のために、新しいものを作成することさえできませんでした。 これがなければ、単一のアプリケーションをインストールしてシステムの価値を確認することは不可能だったため、WP8への関心は急速に薄れました。 ただし、テストの目的はやや異なります。この電話を使用して、実際のしゃれた環境で仮想展示を作成する方法を示します。 さらに興味深いことに、Oculus Riftヘルメットを使用する機能を追加します。



この行動のために、博物館に隣接する領土に軍事装備の場所を選びました。



画像

写真はNokia Lumia 1520で撮影され、GIMPでわずかにトリミングされました



それでは、何をする必要がありますか?



1.各視点の写真を撮ります。

2.各キットからパノラマ写真を取得します。

3. ThreeJSを使用して、前の段階で得たものを示します。



結果はサイトに投稿できます。必要に応じて、悪名高いQt5またはAwesomiumのHTML5アプリケーションテンプレートを使用して、独立したプログラムに変換できます。



プラットフォームを機器で歩いた後、PureViewと呼ばれ、その名前に完全に対応するLumia 1520カメラで(私の視点から)興味深い場所をキャプチャしました。



撮影の方法は非常に簡単です-1か所でねじれているので、一連の写真を前の写真と30〜50%で重複して撮影する必要があります。



ある調査地点から別の調査地点に移動する過程で、電話がどのように手にあるかを評価することができました。 音を制御し、電話とカメラをロックするためのキーは、左手の指のすぐ下にありました。



画像



最初の段階で取得する必要がある写真は何ですか? この携帯電話のカメラで撮影したサンプル写真を次に示します。 垂直軸を中心とした回転角度の変化に注意してください。







したがって、ポクロンナヤの丘にある軍事装備のプラットフォームの5つの視点の一連の画像が取得されました。



受け取った写真の処理を開始します。つまり、私の小さな計画の2番目の段落に進みます。 保存された写真を電話機の大容量メモリからコンピュータに転送するために、標準パッケージの青いボックスのUSBケーブルを使用しました。 写真の転送中に、このユニットでサポートされているメモリカードの音楽を少し聞きました。 ケーブルに加えて、キットには快適なタッチワイヤと優れたサウンドを備えたヘッドフォンがあります。







写真の入ったフォルダーを開くと、それらを保存するための異常な方法が見つかりました。 各写真には、解像度の異なる2つのグラフィックファイルがあります。1つ目は小さい(3072 x 1728)、2つ目は5376(3024)です。高解像度のファイルを使用することをお勧めします。



次に、パノラマビューで一連の写真を接着する必要があります。 これを可能にするいくつかのプログラムがありますが、それらの中には無料のものがあります。 パノラマ写真を準備するための2つの無料プログラム、 HuginMicrosoft ICEに注目してください。



最初のものは、2番目のものよりもマスターするのが少し難しいので、Microsoft Image Composite Editorを使用して結果をすばやく取得します。 高解像度の写真を選択し、ICEワークスペースにドラッグして、結果のファイルに必要な設定を示しました-完了しました! エクスポートパラメーターで幅4096を指定し、キューブ(赤で囲まれた)をクリックします-球面投影が必要であることを示し、[適用]ボタン、[ディスクにエクスポート...]ボタンの順にクリックします。







作成されたパノラマ写真はほぼ使用できます。4096x2048のサイズにするだけで、GIMPやPaint.NETなどのグラフィックエディターで簡単に作成できます。 これを行うには、単に背景が黒の4096x2048サイズの新しいファイルを作成し、中央に視点の接着されたパノラマ写真を適用します。 ところで、注意してください。撮影の開始位置を忘れて戻ってこなかった場合、スクリーンショットのように、結果としてパノラマ写真は「閉じません」:







次に、Three.jsを使用して、カメラを回転できる球体のテクスチャとして撮影されたパノラマ写真を使用します。 各ビューポイントで写真を1行だけ撮影したため、垂直軸の周りでのみカメラを回転させることができることに注意してください。 ただし、必要に応じて、撮影角度を変更することで、広視野角のパノラマ写真を撮影できます。つまり、上下を見ることができます。 ただし、これは原則の問題ではなく、パノラマを作成するプロセスは劇的に変わりません。



index.htmlファイルコードをご覧ください。



index.html
<!DOCTYPE html> <html lang="en"> <head> <title>  ,  ,    Nokia Lumia 1520</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> <style> body { background-color: #000000; margin: 0px; overflow: hidden; } a { color: #ffffff; } #footer { position: absolute; bottom: 100px; left:100px; } #text { text-shadow: -10px 10px 0px #00e6e6, -20px 20px 0px #01cccc, -30px 30px 0px #00bdbd; position: absolute; top: 100px; right:100px; } .button { -moz-box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.5), 0px 1px 2px rgba(0, 0, 0, 0.15); -webkit-box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.5), 0px 1px 2px rgba(0, 0, 0, 0.15); box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.5), 0px 1px 2px rgba(0, 0, 0, 0.15); background-color: #EEE; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZiZmJmYiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2UxZTFlMSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #fbfbfb), color-stop(100%, #e1e1e1)); background: -moz-linear-gradient(top, #fbfbfb, #e1e1e1); background: -webkit-linear-gradient(top, #fbfbfb, #e1e1e1); background: linear-gradient(to bottom, #fbfbfb, #e1e1e1); display: inline-block; vertical-align: middle; *vertical-align: auto; *zoom: 1; *display: inline; border: 1px solid #d4d4d4; height: 32px; line-height: 30px; padding: 0px 25.6px; font-weight: 300; font-size: 14px; font-family: "Helvetica Neue Light", "Helvetica Neue", "Helvetica", "Arial", "Lucida Grande", sans-serif; color: #666; text-shadow: 0 1px 1px white; margin: 0; text-decoration: none; text-align: center; } /* line 44, ../scss/partials/_buttons.scss */ .button:hover, .button:focus { background-color: #EEE; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2RjZGNkYyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #dcdcdc)); background: -moz-linear-gradient(top, #ffffff, #dcdcdc); background: -webkit-linear-gradient(top, #ffffff, #dcdcdc); background: linear-gradient(to bottom, #ffffff, #dcdcdc); } /* line 48, ../scss/partials/_buttons.scss */ .button:active { -moz-box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.3), 0px 1px 0px white; -webkit-box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.3), 0px 1px 0px white; box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.3), 0px 1px 0px white; text-shadow: 0px 1px 0px rgba(255, 255, 255, 0.4); background: #eeeeee; color: #bbbbbb; } /* line 54, ../scss/partials/_buttons.scss */ .button:focus { outline: none; } /* line 60, ../scss/partials/_buttons.scss */ input.button, button.button { height: 34px; cursor: pointer; -webkit-appearance: none; } /* line 67, ../scss/partials/_buttons.scss */ .button-block { display: block; } /* line 72, ../scss/partials/_buttons.scss */ .button.disabled, .button.disabled:hover, .button.disabled:focus, .button.disabled:active, input.button:disabled, button.button:disabled { -moz-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.1); -webkit-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.1); box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.1); filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80); opacity: 0.8; background: #EEE; border: 1px solid #DDD; text-shadow: 0 1px 1px white; color: #CCC; cursor: default; -webkit-appearance: none; } /* line 89, ../scss/partials/_buttons.scss */ .button-wrap { background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2UzZTNlMyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2YyZjJmMiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #e3e3e3), color-stop(100%, #f2f2f2)); background: -moz-linear-gradient(top, #e3e3e3, #f2f2f2); background: -webkit-linear-gradient(top, #e3e3e3, #f2f2f2); background: linear-gradient(to bottom, #e3e3e3, #f2f2f2); -moz-border-radius: 200px; -webkit-border-radius: 200px; border-radius: 200px; -moz-box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.04); -webkit-box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.04); box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.04); padding: 10px; display: inline-block; } /* line 195, ../scss/partials/_buttons.scss */ .button-circle { -moz-border-radius: 240px; -webkit-border-radius: 240px; border-radius: 240px; -moz-box-shadow: inset 0px 1px 1px rgba(255, 255, 255, 0.5), 0px 1px 2px rgba(0, 0, 0, 0.2); -webkit-box-shadow: inset 0px 1px 1px rgba(255, 255, 255, 0.5), 0px 1px 2px rgba(0, 0, 0, 0.2); box-shadow: inset 0px 1px 1px rgba(255, 255, 255, 0.5), 0px 1px 2px rgba(0, 0, 0, 0.2); width: 50px; line-height: 50px; height: 50px; padding: 0px; border-width: 4px; font-size: 16px; } </style> </head> <body> <script> function loadPoint(file){ texture = THREE.ImageUtils.loadTexture( 'textures/' + file + '.jpg', {}, function() { mesh.material.map = texture; } ); } //    :      . function switchView () { oculus_enabled =! oculus_enabled; renderer.setSize( window.innerWidth, window.innerHeight ); effect.setSize( window.innerWidth, window.innerHeight ); } </script> <div id="container"> <div id="footer"> <!--     --> <a href="#" class="button button-circle" onclick="loadPoint(1)">1</a> <a href="#" class="button button-circle" onclick="loadPoint(2)">2</a> <a href="#" class="button button-circle" onclick="loadPoint(3)">3</a> <a href="#" class="button button-circle" onclick="loadPoint(4)">4</a> <a href="#" class="button button-circle" onclick="loadPoint(5)">5</a> <a href="#" class="button button-circle" onclick="switchView()">OR</a> </div> </div> <script src="js/three.min.js"></script> <script src="js/OculusRiftEffect.js"></script> <script> var camera, scene, renderer, mesh, oculus_enabled = false; var isUserInteracting = false, onMouseDownMouseX = 0, onMouseDownMouseY = 0, lon = 0, onMouseDownLon = 0, lat = 0, onMouseDownLat = 0, phi = 0, theta = 0; init(); animate(); function init() { var container; container = document.getElementById( 'container' ); camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 1100 ); camera.target = new THREE.Vector3( 0, 0, 0 ); scene = new THREE.Scene(); var geometry = new THREE.SphereGeometry( 500, 60, 40 ); geometry.applyMatrix( new THREE.Matrix4().makeScale( -1, 1, 1 ) ); var material = new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'textures/1.jpg' ) // ,     ,       ,      . } ); mesh = new THREE.Mesh( geometry, material ); scene.add( mesh ); renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); container.appendChild( renderer.domElement ); effect = new THREE.OculusRiftEffect( renderer, {worldScale: 100} ); effect.setSize( window.innerWidth, window.innerHeight ); document.addEventListener( 'mousedown', onDocumentMouseDown, false ); document.addEventListener( 'mousemove', onDocumentMouseMove, false ); document.addEventListener( 'mouseup', onDocumentMouseUp, false ); window.addEventListener( 'resize', onWindowResize, false ); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); effect.setSize( window.innerWidth, window.innerHeight ); } function onDocumentMouseDown( event ) { event.preventDefault(); isUserInteracting = true; onPointerDownPointerX = event.clientX; onPointerDownPointerY = event.clientY; onPointerDownLon = lon; onPointerDownLat = lat; } function onDocumentMouseMove( event ) { if ( isUserInteracting === true ) { lon = ( onPointerDownPointerX - event.clientX ) * 0.1 + onPointerDownLon; lat = ( event.clientY - onPointerDownPointerY ) * 0.1 + onPointerDownLat; } } function onDocumentMouseUp( event ) { isUserInteracting = false; } function animate() { requestAnimationFrame( animate ); update(); } function update() { if ( isUserInteracting === false ) { lon += 0.1; } lat = Math.max( - 85, Math.min( 85, lat ) ); phi = THREE.Math.degToRad( 90 - lat ); theta = THREE.Math.degToRad( lon ); camera.target.x = 500 * Math.sin( phi ) * Math.cos( theta ); //camera.target.y = 500 * Math.cos( phi ); //           ,    ""   . camera.target.z = 500 * Math.sin( phi ) * Math.sin( theta ); camera.lookAt( camera.target ); if (oculus_enabled) { effect.render( scene, camera ); } else { renderer.render( scene, camera ); } } //, ,      andreyazbarov@gmail.com </script> </body> </html>
      
      









ヘッドセクションには、 http://alexwolfe.github.io/Buttons/から取得したボタンスタイルが表示されます。



水平軸を中心とした回転は、パノラマの欠落部分が表示されないようにブロックされます。 大きな垂直視野角のパノラマ写真を準備する場合は、更新機能で1行のコメントを解除する必要があります。



最初に、コードはサンプルthree.jsから取得されます-パノラマデモと補足。



Oculus Riftヘルメットのバーチャルツアーを表示するために、troffmo5のライブラリ( http://github.com/troffmo5 )を使用しました。



このリンクで Googleドライブからすべてのコードをダウンロードできます。



このリンクから結果を見ることができます。







神秘的な文字を含むボタンORを使用すると、Oculus Riftヘルメットの表示を有効にしたり、数字のあるボタンを使用したりできます。



将来的には、各視点および提示された各展示に説明を追加することにより、このようなミニツアーを改善することができます。たとえば、視聴を制御する便利な方法を追加することもできます。



この例が誰かの日常活動に役立つことを願っています。



All Articles