3D引擎“Babylon.JS”入門教程翻譯總結
歷程:
最早接觸游戲編程是在大三下學期,用匯編語言和實驗室里的單片機、觸摸屏、電機(提供聲效)編的打地鼠程序。后來因為大四的畢業設計與三維空間模擬有關,又自學了MFC編程和NEHE的OpenGL教程(國人翻譯版)(C++3D編程的復雜會給任何參與者留下深刻的印象,向先驅者們致敬)。工作后自學了JavaScript語言和前端知識,感覺js與C++相比極其簡單易用,如果能以js代替C++進行3D開發可以極大降低編程門檻。在有目的性的搜索下我開始學習html5引入的新技術WebGL(事實上WebGL來自Khronos Group組織,而html5規范來自WHATWG和W3C組織,二者并沒有從屬關系,只是實際應用時WebGL通常通過html5的canvas被調用)。
派系:
現在網上比較容易找到的WebGL資料分成三個派系:WebGL原生開發派(基礎教程參見:http://learningwebgl.com/blog/),Three.js派(《WebGL入門指南》這本書比較簡單易懂),Babylon.js派(官方網站:http://www.babylonjs.com/)。按順序學習了三個派系的基礎教程(前兩個派系在網上只能找到基礎教程)之后我決定以Babylon.js為主要方向。如果要對這三種開發方式做個比較的話:WebGL原生開發好比遠古時期的投石索,既原始又難以控制,但通過不斷使用投石索鍛煉出來的強壯體魄可以在任何情況下給予你支持;Three.js好比AK47,簡單粗暴又威力強大,在特定情況下能夠快速解決問題,但當你想深入研究時又發覺難以控制;Babylon.js好比絕地武士的光劍,外形簡約但變化無窮,一劍在手頓感原力與你同在(豐富的教程和社區)。
教程:
Babylon.js官網上有極其豐富的免費教程和示例,其官網教程區(http://doc.babylonjs.com/tutorials)共有網頁博客形式的教程81篇,視頻形式的教程10部,其中博客教程分為19篇初級教程、22篇中級教程、40篇高級教程。我翻譯的是初級教程前15篇針對入門者的教程,以后有時間會挑選后面重要的部分繼續翻譯。
語言:
在網絡詞典的幫助下,單詞的認讀并沒有多大障礙,翻譯的難點在于漢語和英語的習慣句式不同,比如中國人習慣說“向那里慢慢走”,而美國人則習慣說“走向那里慢慢地”,當這些被倒裝的元素變成詞組甚至子句時英漢互譯會變得非常復雜。面對這種情況,不浪費分數過英語六級的我只能選擇意譯或者將一個長句拆成多個短句,實在不行也只能按原文的順序一一羅列釋義,難免有失原文風采,請讀者多多原諒。再者,入門部分教程的原文編寫者在文中使用了許多表示情感的語氣詞和表示尷尬的省略號,限于水平,也無法一一完整譯出。
工具:
Babylonjs的教程網站使用不同樣式來突出強調重要內容,所以我同樣以網站方式進行翻譯(網站下載地址:https://github.com/ljzc002/ljzc002.github.io/tree/master/BABYLON,可以將文件放在本機的Tomcat中使用,通過githubio直接訪問的方法見最后)。為了提高翻譯效率我在網站的代碼中添加了一個自己編寫的全手動翻譯插件(限于時間只在IE11下詳細調試過,Chrome下肯定有bug)。該插件的基本思路是監聽網頁中每個可編輯標簽的鼠標移入、鼠標移出、鼠標單擊事件,在鼠標移入時對標簽高亮顯示,點擊標簽時彈出對話框修改本處文字,同時維護一個日志列表支持對錯誤修改的回滾,最后提供一個按鈕將修改完畢的網頁導出。我在發布版本中禁用了翻譯插件,如果希望使用可以在window.onload中注釋掉“if(true) return;”。順便說一下Babylonjs網站的“分布式程度”極高,其樣式表、字體、乃至JavaScript腳本都是通過CDN進行動態分配、組裝的,我們甚至可以看到有些標簽的類名是本地的地名!我做的翻譯網頁只是對其外形的粗略模仿。
版權:
上面所有文字的版權、翻譯插件的版權、譯文的版權歸我所有,使用MIT協議發布;原版官方教程和引用的各項資源的版權歸原作者所有,使用各自原有的許可協議。
githubio訪問方法:
使用url“"http://ljzc002.github.io/BABYLON/HTML/"+html文件名訪問”,比如“http://ljzc002.github.io/BABYLON/HTML/001Basic_scene.html”即為訪問/BABYLON/HTML/目錄下的“基礎場景”網頁。
20190619更新:在https://github.com/ljzc002/ljzc002.github.io/tree/master/BABYLON101上傳了4.0版本的官方教程翻譯,推薦下載mhtml文件后用word查看,也可以通過githubio訪問對應的htm頁面。
20190829更新:在https://github.com/ljzc002/community開源了一個綜合使用基礎教程中各項技術的實例工程,這是一個基于玩家自定義的聯網桌游框架,歡迎交流討論。
threejs 繪制地球、飛機、軌跡
首先我們來看下要實現的效果
這個縮小后的圖片,下面我們來看下近距離的動態效果。。
效果比較簡陋,需要后期再處理。。。
下面進入主題,代碼篇。。
HTML部分:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>全球航班</title>
<style>
html{overflow: hidden;}
body { margin: 0;}
</style>
<script src="js/jquery.min.js"></script>
</head>
<body>
<!-- 地國 -->
<div id="zh_globe_container"></div> <!-- 容器 -->
<script src="js/threejs/Detector.js"></script> <!-- webGL瀏覽器支持檢測 -->
<script src="js/threejs/three.min.js"></script> <!-- 核心js -->
<script src="js/threejs/stats.min.js"></script> <!-- 性能測試 -->
<script src="js/threejs/OrbitControls.js"></script> <!-- 地球控制 -->
<script src="js/socketio-1.4.5.js"></script> <!-- socket -->
<script src="js/globe.js"></script> <!-- -->
</body>
</html>
JS部分(globe.js)
1、實現地球
地球貼圖(可以在網上下載)
// 地球
function globe() {
var globeTextureLoader = new THREE.TextureLoader();
globeTextureLoader.load('images/textures/earth.jpg', function (texture) {
var globeGgeometry = new THREE.SphereGeometry(200, 100, 100);
var globeMaterial = new THREE.MeshStandardMaterial({map: texture});
var globeMesh = new THREE.Mesh(globeGgeometry, globeMaterial);
group.add(globeMesh);
group.rotation.x = THREE.Math.degToRad(35);
group.rotation.y = THREE.Math.degToRad(170);
});
}
2、添加球面光源(這里使用的是半球光)
// 光
function lights() {
var hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x333333, 2);
hemisphereLight.position.x = 0;
hemisphereLight.position.y = 0;
hemisphereLight.position.z = -200;
group.add(hemisphereLight);
}
3、添加星點
// 星點
function stars() {
var starsGeometry = new THREE.Geometry();
for (var i = 0; i < 2000; i ++) {
var starVector = new THREE.Vector3(
THREE.Math.randFloatSpread(2000),
THREE.Math.randFloatSpread(2000),
THREE.Math.randFloatSpread(2000)
);
starsGeometry.vertices.push(starVector);
}
var starsMaterial = new THREE.PointsMaterial({color: 0x888888})
var starsPoint = new THREE.Points(starsGeometry, starsMaterial);
group.add(starsPoint);
}
4、添加飛機
這里需要我們把 經緯度坐標 轉成 xyz 坐標
// 獲取position
function getPosition(lng, lat, alt) {
var phi = (90-lat)*(Math.PI/180),
theta = (lng+180)*(Math.PI/180),
radius = alt+200,
x = -(radius * Math.sin(phi) * Math.cos(theta)),
z = (radius * Math.sin(phi) * Math.sin(theta)),
y = (radius * Math.cos(phi));
return {x: x, y: y, z: z};
}
畫飛機
// 飛機形狀(不想畫的,可以下載個 飛機模型 使用加載器加載進來)
var planeShape = new THREE.Shape();
planeShape.moveTo( 0, 0);
planeShape.lineTo(0.2, -0.2);
planeShape.lineTo(0.2, -1.3);
planeShape.lineTo(1.6,-2.7);
planeShape.lineTo(1.6,-3);
planeShape.lineTo(0.2, -2.1);
planeShape.lineTo(0.2, -3);
planeShape.lineTo(0.5, -3.4);
planeShape.lineTo(0.5, -3.7);
planeShape.lineTo(0, -3.3);
planeShape.lineTo(-0.5, -3.7);
planeShape.lineTo(-0.5, -3.4);
planeShape.lineTo(-0.2, -3);
planeShape.lineTo(-0.2, -2.1);
planeShape.lineTo(-1.6,-3);
planeShape.lineTo(-1.6,-2.7);
planeShape.lineTo(-0.2, -1.3);
planeShape.lineTo(-0.2, -0.2);
var planeGeometry = new THREE.ShapeGeometry(planeShape);
// 飛機材質
var planeMaterial = new THREE.MeshPhongMaterial({color: 0x0FB4DD, side: THREE.DoubleSide, depthTest: true});
depthTest作用是能否透過球體看到飛機,如果是false則旋轉到球體另一面也能看到飛機
添加飛機
// 添加飛機
function addPlane(item) {
if(item.anum && item.lng && item.lat) {
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
// 旋轉
plane.rotation.z = THREE.Math.degToRad(item.ang);
// 定位
var position = getPosition(item.lng, item.lat, 5);
plane.position.set(position.x, position.y, position.z);
// 顯示/隱藏
// plane.visible = false;
// 保存
planeMarkers[item.anum] = plane;
// 添加到場景
group.add(plane);
// 繪制歷史軌跡
drawHistoryTrack(item.anum);
}
}
繪制軌跡(使用socket來獲取的飛行軌跡經緯度坐標點)
// 時間段
var curTime = Date.parse(new Date())/1000;
var depTime = curTime - 30*60;
// 軌跡線質
var trackMaterial = new THREE.LineBasicMaterial({color : 0x1B94B1});
// 繪制歷史軌跡
function drawHistoryTrack(anum) {
socket.emit("fullPath", anum, depTime, curTime, function(status, data){
if(status) {
var dLength = data.length;
if(dLength>=2) {
var trackCoordArr = [];
for(var i=0; i<dLength; i++) {
if(data[i].lng && data[i].alt) {
trackCoordArr.push({lng: data[i].lng, lat: data[i].lat});
}
}
var tcaLength = trackCoordArr.length;
if(tcaLength>=2) {
var tcaHalfLength = Math.ceil(tcaLength/2),
vertexArr = [];
// 這里只取了三個點(起點、中點、終點)
var p1 = getPosition(trackCoordArr[0].lng, trackCoordArr[0].lat, 0),
p2 = getPosition(trackCoordArr[tcaHalfLength].lng, trackCoordArr[tcaHalfLength].lat, tcaLength*0.01),
p3 = getPosition(trackCoordArr[tcaLength-1].lng, trackCoordArr[tcaLength-1].lat, 0);
var trackCurve = new THREE.CatmullRomCurve3([
new THREE.Vector3(p1.x, p1.y, p1.z),
new THREE.Vector3(p2.x, p2.y, p2.z),
new THREE.Vector3(p3.x, p3.y, p3.z)
]);
var trackGeometry = new THREE.Geometry(),
verticesArr = trackCurve.getPoints(tcaLength);
trackGeometry.vertices = verticesArr;
var trackLine = new THREE.Line(trackGeometry, trackMaterial);
group.add(trackLine);
// 動畫點
addLightPoint(p1, tcaLength, verticesArr);
}
}
}
});
}
如果要繪制所有點,且頭尾是在球面上的曲線,則需要兩次循環
var tcaRemainLength = tcaLength-tcaHalfLength
for(var j=0; j<tcaHalfLength; j++) { // 前一半
var p1 = getPosition(trackCoordArr[j].lng, trackCoordArr[j].lat, j*0.05);
vertexArr.push(new THREE.Vector3(p1.x, p1.y, p1.z));
}
for(var k=tcaRemainLength; k>0; k--) { // 后一半
var p2 = getPosition(trackCoordArr[tcaLength-k].lng, trackCoordArr[tcaLength-k].lat, k*0.05);
vertexArr.push(new THREE.Vector3(p2.x, p2.y, p2.z));
}
var trackCurve = new THREE.CatmullRomCurve3(vertexArr);
這個部分看看就行了。。
光點動畫
// 點動畫
var pointGeometry = new THREE.SphereGeometry(0.2, 20, 20);
var pointMaterial = new THREE.MeshBasicMaterial({color: 0x40E0D0});
function addLightPoint(pos, coordsNum ,verArr) {
var pointMesh = new THREE.Mesh(pointGeometry, pointMaterial);
pointMesh.position.set(pos.x, pos.y, pos.z);
group.add(pointMesh);
var index = 0;
function pointAnimate() {
index++;
if(index>coordsNum) {
index = 0;
}
pointMesh.position.set(verArr[index].x, verArr[index].y, verArr[index].z);
requestAnimationFrame(pointAnimate);
}
pointAnimate();
}
這個點使用的是sphere,,當然也可以用頂點來實現,如下
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(0, 0, 0))
geometry.colors.push(new THREE.Color(0xffffff));
var material = new THREE.PointsMaterial({size: 1, vertexColors: THREE.VertexColors, opacity: 0.75, sizeAttenuation: true, transparent: true});
var point = new THREE.Points(geometry, material);
point.position.set(x, y, z);
group.add(point);
另外不想用光點動畫的話,也可以用線動畫,實現原理是不斷更新頂點坐標,如下
var curveGeometry = new THREE.Geometry();
var curveData = new THREE.CatmullRomCurve3(verArr.slice(0, 10));
curveGeometry.vertices = curveData.getPoints(10);
var curveMaterial = new THREE.LineBasicMaterial({color: 0x40E0D0});
var curveLine = new THREE.Line(curveGeometry, curveMaterial);
group.add(curveLine);
var index = 0;
function lineAnimate() {
index++;
if(index>coordsNum-10) {
index = 0;
}
var offsetData = verArr.slice(index, 10+index);
if(offsetData.length > 0) {
curveData = new THREE.CatmullRomCurve3(offsetData);
curveLine.geometry.vertices = curveData.getPoints(10);
curveLine.geometry.verticesNeedUpdate = true;
}
requestAnimationFrame(lineAnimate);
}
lineAnimate();
最后就是布置場景和事件了
// 初始化
function init() {
container = document.getElementById('zh_globe_container');
scene = new THREE.Scene();
var bgTexture = new THREE.TextureLoader().load("images/textures/starfield.jpg");
scene.background = bgTexture;
camera = new THREE.PerspectiveCamera(50, winWth/winHgt, 1, 2000);
camera.up.x = 0;
camera.up.y = 1;
camera.up.z = 0;
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 400;
camera.lookAt(0,0,0);
group = new THREE.Group();
scene.add(group);
// 地球
globe();
// 飛機
plane();
// 星點
stars();
// 半球光
lights();
// 渲染器
renderer = new THREE.WebGLRenderer({antialias: true, preserveDrawingBuffer: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(winWth, winHgt);
container.appendChild(renderer.domElement);
// 盤旋控制
var orbitControl = new THREE.OrbitControls(camera, renderer.domElement);
orbitControl.minDistrance = 20;
orbitControl.maxDistrance = 50;
orbitControl.maxPolarAngle = Math.PI/2;
// 性能測試
stats = new Stats();
container.appendChild(stats.dom);
// resize事件
window.addEventListener('resize', onWindowResize, false);
}
// 窗口大小改變
function onWindowResize() {
camera.aspect = window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
// 渲染
function render() {
group.rotation.y -= 0.0005;
renderer.render(scene, camera);
}
// 動畫
function animate() {
requestAnimationFrame(animate);
render();
stats.update();
}
init();
animate();
完整代碼:
var log = console.log.bind(console);
var globeObj = (function() {
'use strict';
// 判斷瀏覽器是否支持webgl
if(!Detector.webgl) Detector.addGetWebGLMessage();
var container, stats;
var camera, scene, renderer;
var group;
var mouseX = 0, mouseY = 0;
var winWth = window.innerWidth, winHgt = window.innerHeight;
// 獲取position
function getPosition(lng, lat, alt) {
var phi = (90-lat)*(Math.PI/180),
theta = (lng+180)*(Math.PI/180),
radius = alt+200,
x = -(radius * Math.sin(phi) * Math.cos(theta)),
z = (radius * Math.sin(phi) * Math.sin(theta)),
y = (radius * Math.cos(phi));
return {x: x, y: y, z: z};
}
// 飛機
function plane() {
var socket = io('https://loc.variflight.com/*****此處接口地址不能給了', {transports: ['websocket']});
var clientBounds = [52.793056,72.427908,2.970897,135.181814];
// 連接
socket.on('connect', function() {
socket.emit("sub", clientBounds, -1, '', function(){});
});
// 飛機標記
var planeMarkers = {};
// 飛機形狀
var planeShape = new THREE.Shape();
planeShape.moveTo( 0, 0);
planeShape.lineTo(0.2, -0.2);
planeShape.lineTo(0.2, -1.3);
planeShape.lineTo(1.6,-2.7);
planeShape.lineTo(1.6,-3);
planeShape.lineTo(0.2, -2.1);
planeShape.lineTo(0.2, -3);
planeShape.lineTo(0.5, -3.4);
planeShape.lineTo(0.5, -3.7);
planeShape.lineTo(0, -3.3);
planeShape.lineTo(-0.5, -3.7);
planeShape.lineTo(-0.5, -3.4);
planeShape.lineTo(-0.2, -3);
planeShape.lineTo(-0.2, -2.1);
planeShape.lineTo(-1.6,-3);
planeShape.lineTo(-1.6,-2.7);
planeShape.lineTo(-0.2, -1.3);
planeShape.lineTo(-0.2, -0.2);
var planeGeometry = new THREE.ShapeGeometry(planeShape);
// 飛機材質
var planeMaterial = new THREE.MeshPhongMaterial({color: 0x0FB4DD, side: THREE.DoubleSide, depthTest: true});
// 添加飛機
function addPlane(item) {
if(item.anum && item.lng && item.lat) {
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
// 旋轉
plane.rotation.z = THREE.Math.degToRad(item.ang);
// 定位
var position = getPosition(item.lng, item.lat, 5);
plane.position.set(position.x, position.y, position.z);
// 顯示/隱藏
// plane.visible = false;
// 保存
planeMarkers[item.anum] = plane;
// 添加到場景
group.add(plane);
// 繪制歷史軌跡
drawHistoryTrack(item.anum);
}
}
// 時間段
var curTime = Date.parse(new Date())/1000;
var depTime = curTime - 30*60;
// 軌跡線質
var trackMaterial = new THREE.LineBasicMaterial({color : 0x1B94B1});
// 繪制歷史軌跡
function drawHistoryTrack(anum) {
socket.emit("fullPath", anum, depTime, curTime, function(status, data){
if(status) {
var dLength = data.length;
if(dLength>=2) {
var trackCoordArr = [];
for(var i=0; i<dLength; i++) {
if(data[i].lng && data[i].alt) {
trackCoordArr.push({lng: data[i].lng, lat: data[i].lat});
}
}
var tcaLength = trackCoordArr.length;
if(tcaLength>=2) {
var tcaHalfLength = Math.ceil(tcaLength/2),
tcaRemainLength = tcaLength-tcaHalfLength,
vertexArr = [];
/* 所有點
for(var j=0; j<tcaHalfLength; j++) {
var p1 = getPosition(trackCoordArr[j].lng, trackCoordArr[j].lat, j*0.05);
vertexArr.push(new THREE.Vector3(p1.x, p1.y, p1.z));
}
for(var k=tcaRemainLength; k>0; k--) {
var p2 = getPosition(trackCoordArr[tcaLength-k].lng, trackCoordArr[tcaLength-k].lat, k*0.05);
vertexArr.push(new THREE.Vector3(p2.x, p2.y, p2.z));
}
var trackCurve = new THREE.CatmullRomCurve3(vertexArr);
*/
// 三個點
var p1 = getPosition(trackCoordArr[0].lng, trackCoordArr[0].lat, 0),
p2 = getPosition(trackCoordArr[tcaHalfLength].lng, trackCoordArr[tcaHalfLength].lat, tcaLength*0.01),
p3 = getPosition(trackCoordArr[tcaLength-1].lng, trackCoordArr[tcaLength-1].lat, 0);
var trackCurve = new THREE.CatmullRomCurve3([
new THREE.Vector3(p1.x, p1.y, p1.z),
new THREE.Vector3(p2.x, p2.y, p2.z),
new THREE.Vector3(p3.x, p3.y, p3.z)
]);
var trackGeometry = new THREE.Geometry(),
verticesArr = trackCurve.getPoints(tcaLength);
trackGeometry.vertices = verticesArr;
var trackLine = new THREE.Line(trackGeometry, trackMaterial);
group.add(trackLine);
// 動畫點
addLightPoint(p1, tcaLength, verticesArr);
}
}
}
});
}
// 點動畫
var pointGeometry = new THREE.SphereGeometry(0.2, 20, 20);
var pointMaterial = new THREE.MeshBasicMaterial({color: 0x40E0D0});
function addLightPoint(pos, coordsNum ,verArr) {
var pointMesh = new THREE.Mesh(pointGeometry, pointMaterial);
pointMesh.position.set(pos.x, pos.y, pos.z);
group.add(pointMesh);
var index = 0;
function pointAnimate() {
index++;
if(index>coordsNum) {
index = 0;
}
pointMesh.position.set(verArr[index].x, verArr[index].y, verArr[index].z);
requestAnimationFrame(pointAnimate);
}
pointAnimate();
/*var curveGeometry = new THREE.Geometry();
var curveData = new THREE.CatmullRomCurve3(verArr.slice(0, 10));
curveGeometry.vertices = curveData.getPoints(10);
var curveMaterial = new THREE.LineBasicMaterial({color: 0x40E0D0});
var curveLine = new THREE.Line(curveGeometry, curveMaterial);
group.add(curveLine);
var index = 0;
function lineAnimate() {
index++;
if(index>coordsNum-10) {
index = 0;
}
var offsetData = verArr.slice(index, 10+index);
if(offsetData.length > 0) {
curveData = new THREE.CatmullRomCurve3(offsetData);
curveLine.geometry.vertices = curveData.getPoints(10);
curveLine.geometry.verticesNeedUpdate = true;
}
requestAnimationFrame(lineAnimate);
}
lineAnimate();*/
}
// 監聽數據(添加并更新)
socket.on('~', function(res) {
if($.isEmptyObject(planeMarkers)) {
$.each(res, function(i, item) {
addPlane(item);
});
} else {
$.each(res, function(i, item) {
if(planeMarkers[item.anum]) {
if(item.lng && item.lat) {
var pos = getPosition(item.lng, item.lat, 5);
planeMarkers[item.anum].position.set(pos.x, pos.y, pos.z);
}
} else {
addPlane(item);
}
});
}
});
}
// 地球
function globe() {
var globeTextureLoader = new THREE.TextureLoader();
globeTextureLoader.load('images/textures/earth.jpg', function (texture) {
var globeGgeometry = new THREE.SphereGeometry(200, 100, 100);
var globeMaterial = new THREE.MeshStandardMaterial({map: texture});
var globeMesh = new THREE.Mesh(globeGgeometry, globeMaterial);
group.add(globeMesh);
group.rotation.x = THREE.Math.degToRad(35);
group.rotation.y = THREE.Math.degToRad(170);
});
}
// 星點
function stars() {
var starsGeometry = new THREE.Geometry();
for (var i = 0; i < 2000; i ++) {
var starVector = new THREE.Vector3(
THREE.Math.randFloatSpread(2000),
THREE.Math.randFloatSpread(2000),
THREE.Math.randFloatSpread(2000)
);
starsGeometry.vertices.push(starVector);
}
var starsMaterial = new THREE.PointsMaterial({color: 0x888888})
var starsPoint = new THREE.Points(starsGeometry, starsMaterial);
group.add(starsPoint);
}
// 光
function lights() {
var hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x333333, 2);
hemisphereLight.position.x = 0;
hemisphereLight.position.y = 0;
hemisphereLight.position.z = -200;
group.add(hemisphereLight);
}
// 初始化
function init() {
container = document.getElementById('zh_globe_container');
scene = new THREE.Scene();
var bgTexture = new THREE.TextureLoader().load("images/textures/starfield.jpg");
scene.background = bgTexture;
camera = new THREE.PerspectiveCamera(50, winWth/winHgt, 1, 2000);
camera.up.x = 0;
camera.up.y = 1;
camera.up.z = 0;
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 400;
camera.lookAt(0,0,0);
group = new THREE.Group();
scene.add(group);
// 地球
globe();
// 飛機
plane();
// 星點
stars();
// 半球光
lights();
// 渲染器
renderer = new THREE.WebGLRenderer({antialias: true, preserveDrawingBuffer: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(winWth, winHgt);
container.appendChild(renderer.domElement);
// 盤旋控制
var orbitControl = new THREE.OrbitControls(camera, renderer.domElement);
orbitControl.minDistrance = 20;
orbitControl.maxDistrance = 50;
orbitControl.maxPolarAngle = Math.PI/2;
// 性能測試
stats = new Stats();
container.appendChild(stats.dom);
// resize事件
window.addEventListener('resize', onWindowResize, false);
}
// 窗口大小改變
function onWindowResize() {
camera.aspect = window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
// 渲染
function render() {
group.rotation.y -= 0.0005;
renderer.render(scene, camera);
}
// 動畫
function animate() {
requestAnimationFrame(animate);
render();
stats.update();
}
init();
animate();
})();
場景背景圖
寫到
浙公網安備 33010602011771號