前言
threejs官網:https://threejs.org/
github各個版本:https://github.com/mrdoob/three.js/tags
版本更迭很快,我用的時候還是r90秒秒鍾r91出來了
剛入門的時候可以看看這個教程:http://www.hewebgl.com/article/articledir/1
初級教程是免費的,中高級是收費的,寫的不是很枯燥,也不用跟着敲代碼,有個相機,渲染器,場景,光源的概念就可以了。它適配的版本是比較舊的,我當時用新版本跟着寫有些demo無法完成,新手更談不上根據版本之間的差別找到問題,慢慢就失去興趣了。
我的這個教程用的是r90版本,它有個比較好的地方是,當你使用高版本的threejs卻使用低版本的語法時,瀏覽器console會有警告告訴你新的寫法是什么樣的。
我的DEMO下載
演示地址: 太窮了,帶寬很小,加載卡頓請諒解(哭泣)
相機轉動做的比較倉促沒有考慮細節,導致鼠標拖拽或是放大縮小卡頓,emmm小問題暫時不想改了
https://www.hugeoyzy.top/threejs/src/demo1.html
https://www.hugeoyzy.top/threejs/src/demo2.html
下載:https://download.csdn.net/download/u010588262/10288776
開發工具
因為我主要是做java開發,所以就用idea了,因為idea寫js也是有提示的,所以還挺方便,就建個Static Web項目就可以了:
我的項目結構是這樣的:
調試需要做兩點配置:
1. 瀏覽器路徑:
瀏覽器啟動參數,因為threejs要加載本地的圖片文件,obj文件之類的,看網上教程都說是不允許的,要部署在apache和tomcat之類的,我沒試,看到說可以加啟動參數就加了,調試沒問題
–allow-file-access-from-files
調試的時候就在html文件上鼠標移到右上角,點擊相應的瀏覽器圖標就可以了
如果修改了html的話直接保存文件在瀏覽器刷新就能看到效果了,但是修改了js的話就要關閉頁面重新點擊瀏覽器圖標。
官方DEMO(肥腸重要)
我入門最快的時候就是找到了官方DEMO之后
一個是:http://stemkoski.github.io/Three.js/#hello-world
這里的demo很入門,很好理解,頁面最上方有github地址,可以去下載。唯一不好的是使用的版本很老。r60版本的,不過對於初學者一開始最重要的是理解大概的使用,不用糾結於最新版本。
還有一個是:https://threejs.org/examples/#webgl_animation_cloth
這里的demo相比第一個是比較高大上的,源代碼就在你下載的threejs包里的examples目錄下
代碼講解
這一塊針對初學者中的初學者了,只要看過前言中的初級教程之后再看我的demo應該沒問題了,demo的代碼也比較整潔。
demo中的css,fonts,images和objs基本都是從官方demo中搞過來的,頁面上引用的js分別是什么作用也做了注釋了:
util.js是我把公用的東西放在一起了,要看懂util.js需要對threejs基本的組件有一些了解。
threejs的三圍坐標是這樣的,比如你現在對着電腦屏幕吧,把你屏幕的左下角想象成二維坐標原點,那往右就是x正方向,網上就是y正方向,從屏幕往外直蓋臉的就是z正方向了。
最上面的src()是用來初始化場景,相機和渲染器的,最開始的textureLoader = new THREE.TextureLoader();是用來加載圖片材質的,在大多數教程里用的都是ImageUtils來加載的,這個是新版本的改動,我說一下我對這仨玩意兒的理解吧
場景:就是一個黑盒子唄,把你要展示的東西全往里面裝,地板啊,天花板啊,盒子啊,球啊,貨架啊,三D字體啊全塞進去。為什么是黑盒子?因為你看不見它,也不看不見塞進去的東西。場景的初始化很簡答就一句話scene = new THREE.Scene();
相機:相機可以在任意的位置,任意的角度”拍攝”黑盒子里的場景,相機的初始化稍微復雜一些。
var SCREEN_WIDTH = window.innerWidth; var SCREEN_HEIGHT = window.innerHeight; var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000; camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR); scene.add(camera); camera.position.set(0, 600, 1000); cameraRadius = Math.sqrt(camera.position.x * camera.position.x + camera.position.z * camera.position.z); camera.up.set(0, 1, 0); camera.lookAt(new THREE.Vector3(0, 0, 0));
關於相機比較難理解的是position,up和lookAt的關系。你可看看這個視頻http://www.hewebgl.com/video/2,雖然我看完了也沒怎么看懂。
嘗試解釋一下吧。
現在,把你的手機打開攝像頭,正對着屏幕,想象它就像單反一樣上面右邊有個快門,這就是threejs中相機的初始方向,就是從z軸正方向看向z軸負方向。position是什么意思呢,現在把手機往右平移,意思是position.x在增加。lookAt也很容易,就是相機往哪兒拍唄。比較難理解的是up,好現在把手機位置固定,只能旋轉哦,也就是說position固定了,好了現在把手機旋轉鏡頭對着屏幕左下角,也就是咱們的三維坐標原點,也就是說lookAt也固定了。up的作用就是你現在可以旋轉手機了,保持鏡頭對着屏幕左下角的方向,手機的中心點位置不要變,旋轉手機,拍出來的畫面就會是正的,或者斜着的,或者上下倒過來的。隨着你旋轉手機快門的朝向也在變是吧,默認快門是對着Y軸正方向的,也就是(0,1,0)如果把手機顛倒,up就變成了(0,-1,0),拍出來的畫面也就是上下顛倒的,因為你雖然可以倒着手機拍,但是看照片的時候得把手機正過來看,畫面也就是反的咯。
渲染器
可以看到相機的初始化過程中沒有與場景綁定,他們倆就像在兩個平行世界一樣,看似在一起,其實沒在一起,需要用渲染器把他們倆關聯起來,這樣,相機就拍到了場景里的東西,再通過渲染器展示在瀏覽器上被我們看到了。Decetor是通過Detector.js引入進來的東西,用於判斷瀏覽器兼容性的。
if (Detector.webgl) renderer = new THREE.WebGLRenderer({antialias: true}); else renderer = new THREE.CanvasRenderer(); renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); container = document.getElementById('ThreeJS'); container.appendChild(renderer.domElement);
以地板為例怎么搞一個東西放在場景里,CSDN的代碼注釋沒有這么詳細,寫博客的時候加的,對不住只下資源不看文章的朋友。PlaneGeometry就是一個平面,SphereGeometry就是一個球,CubeGeometry就是一個立方體…….感興趣的可以百度多了解一下其他組件。材質就是各種Material,也有很多種,有受光照影響的,有不受光照影響的。把組件和材質組合起來就是一個完整的物體啦,就是THREE.Mesh這個方法把他們組合起來,第一個參數是組件,第二個參數是材質,材質還可以傳入一個數組,你可能覺得設置多個有啥意義啊一個蓋住一個的,材質是可以設置透明度的,明白了吧。
function addfloor() { // 加載圖片作為地板材質 var floorTexture = textureLoader.load('images/checkerboard.jpg'); // 沿x方向和Y方向都重復填充 floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping; // x方向和y方向各重復幾次,不理解的話改改這個值看看效果就知道了 floorTexture.repeat.set(4, 4); // 將材質包裝成表面材料,設置正反兩面都要鋪上 var floorMaterial = new THREE.MeshBasicMaterial({map: floorTexture, side: THREE.DoubleSide}); // 平面對象 var floorGeometry = new THREE.PlaneGeometry(1000, 1000, 1, 1); // 把對象和材料包裝成Mesh floor = new THREE.Mesh(floorGeometry, floorMaterial); // 設置位置放入場景啦 floor.position.y = -0.5; floor.rotation.x = Math.PI / 2; scene.add(floor); }
others()方法里是一些可有可無的輔助,看注釋就明白了。
animate()就是開始持續渲染,渲染就是通過renderer.render(scene, camera);把渲染器搞到的東西放在瀏覽器上給你看,持續渲染就是通過重復調用方法本身可以實現動畫的效果requestAnimationFrame(animate);,除了這兩個東西還調用了update();方法,update方法里就是慢慢的移動組件或者刷新其他需要更新得組件。requestAnimationFrame方法是比較平滑的動畫,我之前想用Tween.js實現相機轉動的效果,很卡頓,不流暢。
woodycube()就是封裝了獲得木箱,因為兩個demo里都用到了木頭箱子。
后面有空再說一下demo中的代碼
2018年3月16日
更新了一下util.js中的代碼,把鼠標點擊選中事件和getMeshed方法從demo2中移到util里面了
點擊事件
document.addEventListener("mousedown", onDocumentMouseDown, false); function onDocumentMouseDown(e) { e.preventDefault(); // 點擊頁面時停止相機旋轉,因為相機旋轉的時候無法選中物體,這個問題沒有解決,有點麻煩 rotate = false; //將鼠標點擊位置的屏幕坐標轉成threejs中的標准坐標,具體解釋見代碼釋義 var mouse = {}; mouse.x = (e.clientX / window.innerWidth) * 2 - 1; mouse.y = -(e.clientY / window.innerHeight) * 2 + 1; //新建一個三維單位向量 假設z方向就是0.5 //根據照相機,把這個向量轉換到視點坐標系 var vector = new THREE.Vector3(mouse.x, mouse.y, 0.5).unproject(camera); var sub = vector.sub(camera.position);= var param2 = sub.normalize(); var raycaster = new THREE.Raycaster(camera.position, param2); //射線和模型求交,選中一系列直線,getMeshs方法用於獲得場景中可以被選中的所有Mesh var intersects = raycaster.intersectObjects(getMeshs()); if (intersects.length > 0) { // outlineMesh是物體邊框組件,每次只會有一個物體被選中,就把這個東西定義為全局的 // 每次選中另一個物體之前先刪除,相當於取消前一個物體的選中 scene.remove(outlineMesh); //選中第一個射線相交的物體 var SELECTED = intersects[0].object; var outlineMaterial2 = new THREE.MeshBasicMaterial({color: 0x00ff00, side: THREE.BackSide}); outlineMesh = new THREE.Mesh(SELECTED.geometry.clone(), outlineMaterial2); outlineMesh.position.set(SELECTED.position.x, SELECTED.position.y, SELECTED.position.z); outlineMesh.scale.multiplyScalar(1.05); scene.add(outlineMesh); } }