<!DOCTYPE html>
<
html
>
<
head
>
<
title
>three.js webgl - draggable cubes</
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 {
font-family: Monospace;
background-color: #f0f0f0;
margin: 0px;
overflow: hidden;
}
</
style
>
</
head
>
<
body
>
<
script
src
=
"script/three.js"
></
script
>
<
script
src
=
"script/jquery-1.7.2.min.js"
></
script
>
<
script
src
=
"script/libs/tween.min.js"
></
script
>
<
script
src
=
"script/controls/TrackballControls.js"
></
script
>
<
script
src
=
"script/libs/stats.min.js"
></
script
>
<
script
>
//container是指頁面放置這個webGL的一個層 stats是指
var container, stats;
//camera是指場景相機 controls是指控制器,比如一般的滑鼠視角控制等 scene是場景,就好像一個大的舞臺
//projector是可能指螢幕和場景轉換工具 renderer是指場景渲染,能把場景呈現到瀏覽器裡
var camera, controls, scene, projector, renderer;
//objects是指場景中的實體集合 plane是一個水平面網格,當選中一個物體時,可以通過這個水平面,看到和它在同一平面內的其他物理
var objects = [], plane;
//mouse,滑鼠所對應的二維向量 offset 是指三維偏移向量 INTERSECTED是指相交的物件 SELECTED選中的物件
var mouse = new THREE.Vector2(),
offset = new THREE.Vector3(),
INTERSECTED, SELECTED;
//初始化整個場景
init();
//開始每秒最大60幀的渲染
animate();
function init() {
//建立一個放置webGL的層
container = document.createElement( 'div' );
//把上面的層放到body裡
document.body.appendChild( container );
//建立一個透視相機 可視角度70度 瀏覽器的全屏的寬高 水平視錐 最近1 最遠10000
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
//相機的位置z正半軸3000
camera.position.z = 3000;
//軌跡球控制 滑鼠左擊旋轉 右擊平移 滾輪遠近
controls = new THREE.TrackballControls( camera );
//旋轉速度
controls.rotateSpeed = 1.0;
//變焦速度
controls.zoomSpeed = 1.2;
//平移速度
controls.panSpeed = 0.8;
//是否不變焦
controls.noZoom = false;
//是否不平移
controls.noPan = true;
//可能是慣性 true沒有慣性
controls.staticMoving = false;
//動態阻尼係數 就是靈敏度
controls.dynamicDampingFactor = 0.3;
//新建一個場景
scene = new THREE.Scene();
//新建一個環境光 就是正常物體都能照到的光
var ambientLight = new THREE.AmbientLight( Math.random() *0xffffff );
//把環境光加到場景中
scene.add( ambientLight );
//再新建一個無線遠的平行光,就是像太陽光一樣的,
var directionalLight = new THREE.DirectionalLight( Math.random() * 0xffffff );
//把平行光放在y軸正方向上的無窮遠處
directionalLight.position.set( 0, 1, 0 );
//把平行光加到場景中
scene.add( directionalLight );
//再建一個點光源 顏色 強度 照射距離
var pointLight = new THREE.PointLight( 0xff0000, 1, 500 );
//設定點光源的位置
pointLight.position.set(0,0,-200);
//把點光源加入到場景中
scene.add( pointLight );
//定義一個球體
var geometry = new THREE.SphereGeometry( 70, 32, 16 );
for ( var i = 0; i < 50; i ++ ) {
//把這個球體加上網格材質,隨機生成一種顏色的網格物件
var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );
//把材質顏色放到物件的環境顏色上,還沒有理解
object.material.ambient = object.material.color;
//隨機放置一個位置
object.position.x = Math.random() * 3000 - 1500;
object.position.y = Math.random() * 1800 - 1000;
object.position.z = Math.random() * 2400 - 1200;
//加到場景中去
scene.add( object );
//放到物件陣列中
objects.push( object );
}
//建立一個長2000寬2000,8*8的網格物件並加上一種基本材質
plane = new THREE.Mesh( new THREE.PlaneGeometry( 2000, 2000, 8, 8 ), new THREE.MeshBasicMaterial( { color: 0x000000, opacity: 0.25, transparent: true, wireframe: true } ) );
//網格物件是否可見
plane.visible = true;
//把網格物件加到場景中
scene.add( plane );
//建立一個螢幕和場景轉換工具
projector = new THREE.Projector();
//建立一個抗鋸齒的webgl渲染器
renderer = new THREE.WebGLRenderer( { antialias: true } );
//是否排列物件 預設是true
renderer.sortObjects = false;
//設定渲染的範圍
renderer.setSize( window.innerWidth, window.innerHeight );
//是否啟用陰影地圖
renderer.shadowMapEnabled = true;
//設定陰影地圖是紋理陰影
renderer.shadowMapType = THREE.PCFShadowMap;
//把渲染成的所有頁面標籤加入到webgl層中
container.appendChild( renderer.domElement );
//顯示了一個左上角的效能監視視窗
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild( stats.domElement );
//加入滑鼠拖動物件的一系列監聽事件
renderer.domElement.addEventListener( 'mousemove', onDocumentMouseMove, false );
renderer.domElement.addEventListener( 'mousedown', onDocumentMouseDown, false );
renderer.domElement.addEventListener( 'mouseup', onDocumentMouseUp, false );
//加入視窗改變大小時的監聽事件
window.addEventListener( 'resize', onWindowResize, false );
}
//視窗改變大小時的觸發事件
function onWindowResize() {
//改變相機的aspect屬性為當前視窗的寬高
camera.aspect = window.innerWidth / window.innerHeight;
//更新相機的投影矩陣
camera.updateProjectionMatrix();
//重新設定場景寬高
renderer.setSize( window.innerWidth, window.innerHeight );
}
//當滑鼠移動時觸發的事件
function onDocumentMouseMove( event ) {
//阻止本來的預設事件,比如瀏覽器的預設右鍵事件是彈出瀏覽器的選項
event.preventDefault();
//mouse.x是指 滑鼠的x到螢幕y軸的距離與螢幕寬的一半的比值 絕對值不超過1
//mouse.y是指 滑鼠的y到螢幕x軸的距離與螢幕寬的一半的比值 絕對值不超過1
//
//下面的矩形是顯示器螢幕,三維空間座標系的佈局以及螢幕的二維座標系
//
// 滑鼠是從 二維座標系
// 這個點 .-------------------------------------------|-->滑鼠x正半軸
// 開始算| 個 y / |
// x,y | | / |
// | | / |
// | 三維座標系| / |
// | -------------------/-------------------->x|
// | / | |
// | / | |
// | / | |
// |__________Z_匕______|______________________|
// |
// 滑鼠y \/
// 正半軸
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
//新建一個三維變換半單位向量 假設z方向就是0.5,這樣我左右移的時候,還會有前後移的效果
var vector = new THREE.Vector3( mouse.x, mouse.y, 0.5 );
//螢幕和場景轉換工具根據照相機,把這個向量從螢幕轉化為場景中的向量
projector.unprojectVector( vector, camera );
//vector.sub( camera.position ).normalize()變換過後的向量vector減去相機的位置向量後標準化
//新建一條從相機的位置到vector向量的一道光線
var raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
//是否有東西被選中
if ( SELECTED ) {
//有的話取到這條光線射到的物體所在水平面上所有相交元素的集合,所以這樣就可以限制每次拖動距離不能超出水平面panel
var intersects = raycaster.intersectObject( plane );
//這個滑鼠點中的點的位置減去偏移向量,新位置賦值給選中物體
if(intersects.length > 0){
SELECTED.position.copy( intersects[ 0 ].point.sub( offset ) );
}
return;
}
//否則的話,光線和所有物體相交,返回相交的物體
var intersects = raycaster.intersectObjects( objects );
//如果有物體相交了
if ( intersects.length > 0 ) {
//並且相交物體不是上一個相交物體
if ( INTERSECTED != intersects[ 0 ].object ) {
//將這個物件放到INTERSECTED中
INTERSECTED = intersects[ 0 ].object;
//改變水平面的位置
plane.position.copy( INTERSECTED.position );
//並把水平面指向到相機的方向
plane.lookAt( camera.position );
}
//改變滑鼠的樣式
container.style.cursor = 'pointer';
} else {
//改變滑鼠的樣式
container.style.cursor = 'auto';
}
}
//當滑鼠按下時觸發的事件
function onDocumentMouseDown( event ) {
//阻止本來的預設事件,比如瀏覽器的預設右鍵事件是彈出瀏覽器的選項
event.preventDefault();
var vector = new THREE.Vector3( mouse.x, mouse.y, 0.5 );
projector.unprojectVector( vector, camera );
var raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObjects( objects );
if ( intersects.length > 0 ) {
//不能改變視角了
controls.enabled = false;
//把選中的物件放到全域性變數SELECTED中
SELECTED = intersects[ 0 ].object;
//再和水平面相交
var intersects = raycaster.intersectObject( plane );
//選中位置和水平面位置(物體中心)的偏移量
offset.copy( intersects[ 0 ].point ).sub( plane.position );
//改變滑鼠的樣式
container.style.cursor = 'move';
}
}
function onDocumentMouseUp( event ) {
event.preventDefault();
//又能改變視角了
controls.enabled = true;
//如果有相交物體
if ( INTERSECTED ) {
//把位置給水平面
plane.position.copy( INTERSECTED.position );
//選中物體置空
SELECTED = null;
}
//改變滑鼠的樣式
container.style.cursor = 'auto';
}
//每秒最大60幀的渲染,一直在迴圈
function animate() {
//迴圈本身
requestAnimationFrame( animate );
//渲染場景
render();
//更新效能監視視窗
stats.update();
}
function render() {
//更新控制器
controls.update();
//渲染場景和相機
renderer.render( scene, camera );
}
</
script
>
</
body
>
</
html
>