<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
最近的天氣有幾分酷熱,去實驗室的道路也有幾分漫長,走著走著,小包感覺靈魂已經被熱出竅了。回到實驗室,把空調開啟,雪糕吃上,靜坐了幾分鐘,才重新感覺到靈魂的滋味,葛優躺在實驗室的小床上,思維開始天馬行空,世上有一萬種方式能讓小包涼快,但地球母親吶,她卻日漸炎熱,誰能來給她降降溫?
躺著躺著,進入了夢鄉,小包夢到未來有一天,人類超級發達,可以穿梭時空,但發展的代價也是巨大的,地球母親不堪重負,熱度超標,我們卻束手無策,科學家最後想出一個古老的辦法,將地球的一週用冰包裹起來,進行物理降溫。這很讓人驚悚,小包醒來後,枯坐了一會,決定做一個雪糕地球,不只是一種整活調侃,也是一種反思與警示,保護地球,人人有責。
* { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } body { height: 100vh; background-color: hotpink; margin: 0; padding: 0; overflow: hidden; } .loader { display: flex; color: white; display: flex; justify-content: center; align-items: center; font-size: 5em; width: 100%; height: 100%; font-family: "Baloo Bhaijaan", cursive; } .loader span { text-shadow: 0 1px #bbb, 0 2px #bbb, 0 3px #bbb, 0 4px #bbb, 0 5px #bbb, 0 6px transparent, 0 7px transparent, 0 8px transparent, 0 9px transparent, 0 10px 10px rgba(0, 0, 0, 0.4); text-shadow: 0 1px #bbb, 0 2px #bbb, 0 3px #bbb, 0 4px #bbb, 0 5px #bbb, 0 6px #bbb, 0 7px #bbb, 0 8px #bbb, 0 9px #bbb, 0 50px 25px rgba(0, 0, 0, 0.2); transform: translateY(-20px); }
/* * 基礎設定 */ let isLoaded = false; // 紋理資源是否載入完畢 const loadingScreen = { scene: new THREE.Scene(), camera: new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ), // 移除載入標誌的函數 removeText() { const loadingText = document.querySelector("#canvas-loader"); if (loadingText.parentNode) { loadingText.parentNode.removeChild(loadingText); } }, }; // 初始化載入器 let loadingManager = new THREE.LoadingManager(); // 監聽載入器 onLoad 事件 loadingManager.onLoad = () => { loadingScreen.removeText(); isLoaded = true; }; // 建立場景 const scene = new THREE.Scene(); // 建立渲染器 const renderer = new THREE.WebGLRenderer({ antialias: true }); // 渲染器基本設定 renderer.setClearColor("hotpink"); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); // canvas 外部容器 const canvasWrapper = document.querySelector("#canvas-wrapper"); // 建立透視相機 const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); // 設定相機位置 camera.position.set(0, 0, 220); // 建立平行光源 const light = new THREE.DirectionalLight(); light.position.set(0, 0, 1); scene.add(light); // 建立點光源 const point = new THREE.PointLight(0xeeeeee); point.position.set(400, 200, 300); //點光源位置 scene.add(point); //點光源新增到場景中 // 建立球體 const cRadius = 100; const geometry = new THREE.SphereBufferGeometry( cRadius, cRadius * 6.4, cRadius * 6.4 ); // 紋理圖 const textureLoader = new THREE.TextureLoader(loadingManager); const textureSurface = textureLoader.load( "https://s3-us-west-2.amazonaws.com/s.cdpn.io/249663/world-surface.jpg" ); const textureElevation = textureLoader.load( "https://s3-us-west-2.amazonaws.com/s.cdpn.io/249663/world-elevation.jpg" ); const textureSpecular = textureLoader.load( "https://s3-us-west-2.amazonaws.com/s.cdpn.io/249663/world-specular.jpg" ); // 材質資訊 const materialOpt = { map: textureSurface, normalMap: textureElevation, specularMap: textureSpecular, shininess: 80, }; const material = new THREE.MeshPhongMaterial(materialOpt); // 建立網格體 const sphere = new THREE.Mesh(geometry, material); // 設定環境貼圖的顏色深淺 sphere.material.normalScale.set(0.5, 0.5); // 將模型新增到場景中 scene.add(sphere); // 將 canvas 元素新增到頁面中 canvasWrapper.appendChild(renderer.domElement); /* * 事件監聽實現動效 */ let mouseX = 0; let mouseY = 0; const moveAnimate = { coordinates(clientX, clientY) { const limit = 270; const limitNeg = limit * -1; mouseX = clientX - window.innerWidth / 2; mouseY = clientY - window.innerHeight / 2; mouseX = mouseX >= limit ? limit : mouseX; mouseX = mouseX <= limitNeg ? limitNeg : mouseX; mouseY = mouseY >= limit ? limit : mouseY; mouseY = mouseY <= limitNeg ? limitNeg : mouseY; }, onMouseMove(e) { moveAnimate.coordinates(e.clientX, e.clientY); }, onTouchMove(e) { const touchX = e.changedTouches[0].clientX; const touchY = e.changedTouches[0].clientY; moveAnimate.coordinates(touchX, touchY); }, }; document.addEventListener("mousemove", moveAnimate.onMouseMove); document.addEventListener("touchmove", moveAnimate.onTouchMove); const onWindowResize = () => { const w = window.innerWidth; const h = window.innerHeight; camera.aspect = w / h; camera.updateProjectionMatrix(); renderer.setSize(w, h); }; window.addEventListener("resize", onWindowResize); const createAnimRotation = () => { const speed = 0.005; sphere.rotation.z += speed / 2; sphere.rotation.y += speed; }; // 渲染函數 const render = () => { if (!isLoaded) { renderer.render(loadingScreen.scene, loadingScreen.camera); requestAnimationFrame(render); return; } camera.position.x += (mouseX * -1 - camera.position.x) * 0.05; camera.position.y += (mouseY - camera.position.y) * 0.05; camera.lookAt(scene.position); createAnimRotation(); renderer.render(scene, camera); requestAnimationFrame(render); }; render();
Three.js
是一款執行在瀏覽器中的 3D
引擎,你可以用它建立各種三維場景,包括了攝影機、光影、材質等各種物件,大家或多或少應該都見識過 Three
的傳說。這是小包第一次使用 Three
,因此小包會圍繞雪糕地球實現的各種細節講起。
下面首先來看一下 Three
框架的基本組成要素(圖源: Three.js 教學)
Three
中最重要的三個物件即場景、相機和渲染器。場景即放置模型、光照的場地;相機設定以何種方式何種角度來觀看場景,渲染器將效果渲染到網頁中。這三個概念都不難理解,下面我們用程式碼實現這三個物件。
// 場景 const scene = new THREE.Scene(); // 透視相機 const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); // 渲染器 const renderer = new THREE.WebGLRenderer(); // 設定渲染區域尺寸 renderer.setSize(window.innerWidth, window.innerHeight); // body元素中插入canvas物件 document.body.appendChild(renderer.domElement); // 設定背景顏色 renderer.setClearColor("hotpink"); // 執行渲染操作 指定場景、相機作為引數 renderer.render(scene, camera);
Three
中有多種相機,本文章主要使用透視相機(PerspectiveCamera
),其原理與人眼所看的景象類似,共有四個引數:
PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )
fov
: 表示能看到的角度範圍,值為角度,類似於人的視角。
aspect
: 表示渲染視窗的長寬比,如果網頁中只有一個 canvas
,其值通常設定為網頁視口的寬高比
near/far
: near/far
分別代表攝像機的近剪下面和遠剪下面
文字有些難以理解,可以參考一下下圖:
開啟瀏覽器,看一下會渲染出什麼?目前只能看到全粉色的網頁,這是因為目前的場景中並沒有新增 3D
模型物件。
接下來我們來新增一個球體模型,作為地球的基底。
const cRadius = 100; const geometry = new THREE.SphereBufferGeometry( cRadius, cRadius * 6.4, cRadius * 6.4 );
SphereBufferGeometry
是 Three
中實現球體的 API
,引數非常多,這裡只介紹前三個引數
radius
: 球體半徑
widthSegments
: 沿經線方向分段數
heightSegments
: 沿緯線方向分段數
為球體新增材質 Material
,目前我們只新增一個顏色屬性。
// 材質物件Material const material = new THREE.MeshLambertMaterial({ color: 0x0000ff, });
渲染網格體 Mesh
,並將其新增到場景 Scene
中。
// 網格體 Mesh,兩個引數分別為幾何體和材質 const sphere = new THREE.Mesh(geometry, material); scene.add(sphere);
重新開啟網站,並沒有看到球體,還是一片粉茫茫的寂寥,天理何在?
Three
相機的初始位置預設為 (0,0,0)
,相機焦點預設為 Z
軸負半軸方向,球體的半徑是 100
,也就是說目前相機位於球體內部,因此我們需要調整相機位置。
// 設定相機的位置 camera.position.set(0, 0, 220); // 設定相機焦點的方向 camera.lookAt(scene.position);
噹噹噹當,網頁中就可以成功看到一個黑色球體了,額有點奇怪,我們明明設定的是 0x0000ff
顏色,怎麼會顯示一個黑色模型?
小包苦思冥想: 萬物本沒有顏色,顏色是光的反射。在整個場景中,目前是沒有光源的,因此下面分別新增平行光(DirectionalLight
)和點光源(PointLight
)
平行光是沿著特定方向發射的光,其表現類似無限遠的陽光,文章使用它來模擬太陽光。點光源是從一個點向各個方向發射的光源,使用它來增加整體的亮度。
// 宣告平行光 const light = new THREE.DirectionalLight(); // 設定平行光源位置 light.position.set(0, 0, 1); // 將平行光源新增到場景中 scene.add(light); // 宣告點光源 const point = new THREE.PointLight(0xeeeeee); // 設定點光源位置 point.position.set(400, 200, 300); // 點光源新增到場景中 scene.add(point);
立體效果看起來不明顯,沒事,接下來我們讓球體動起來。接下來,給球體新增一個 z
軸和 y
軸的轉動。
const createAnimRotation = () => { const speed = 0.005; sphere.rotation.z += speed / 2; sphere.rotation.y += speed; }; const render = () => { createAnimRotation(); renderer.render(scene, camera); requestAnimationFrame(render); }; render();
由於球體是對稱的,轉動看起來並不明顯,如果你特別想看到轉動效果,可以將 SphereBufferGeometry
暫時更換為 BoxBufferGeometry
。
上文已經成功實現地球,接下來我們來為地球披上衣服。本文實現的是雪糕地球,因此小包直接為其披上雪糕外衣。
Three
可以將一張紋理圖對映到幾何體上,具體的對映原理我們不做探究,對映的思想可以參考下圖。
選取一張雪糕地球的紋理圖,使用下面的程式碼實現紋理貼圖效果。
// 紋理載入器物件 const textureLoader = new THREE.TextureLoader(); const textureSurface = textureLoader.load( "https://s3-us-west-2.amazonaws.com/s.cdpn.io/249663/world-surface.jpg" ); // 設定紋理貼圖 const material = new THREE.MeshLambertMaterial({ map: textureSurface });
只使用普通貼圖的雪糕地球看起來已經非常不錯了,但還有進一步美化的空間,Three
提供了高光貼圖,使用高光貼圖,會有高亮部分顯示。
const textureSpecular = textureLoader.load( "https://s3-us-west-2.amazonaws.com/s.cdpn.io/249663/world-specular.jpg" ); const material = new THREE.MeshPhongMaterial({ map: textureSurface, specularMap: textureSpecular, shininess: 80, // 高光部分的亮度 });
雖然動圖錄制的幀數太低,還是依稀可以看到一些高亮區域。
Three
還提供了環境貼圖,環境貼圖可以增加表面的細節,使三維模型更加立體。
const textureElevation = textureLoader.load( "https://s3-us-west-2.amazonaws.com/s.cdpn.io/249663/world-elevation.jpg" ); const material = new THREE.MeshPhongMaterial({ map: textureSurface, normalMap: textureElevation, specularMap: textureSpecular, shininess: 80, });
立體效果是有了,但是具體看起來一言難盡,顏色有些許暗淡,不符合雪糕的風格。
小包繼續開始檢視檔案,環境貼圖中有 normalScale
屬性,可以設定顏色的深淺程度,減少對應屬性值為 0.5,0.5
。
sphere.material.normalScale.set(0.5, 0.5);
給地球加一些互動效果:
上述動效我們可以通過移動相機位置實現。首先設定地球旋轉的最大正負角度為 270
。
// 定義動效物件 let mouseX = 0; let mouseY = 0; const moveAnimate = { coordinates(clientX, clientY) { const limit = 270; const limitNeg = limit * -1; mouseX = clientX - window.innerWidth / 2; mouseY = clientY - window.innerHeight / 2; mouseX = mouseX >= limit ? limit : mouseX; mouseX = mouseX <= limitNeg ? limitNeg : mouseX; mouseY = mouseY >= limit ? limit : mouseY; mouseY = mouseY <= limitNeg ? limitNeg : mouseY; }, onMouseMove(e) { moveAnimate.coordinates(e.clientX, e.clientY); }, }; document.addEventListener("mousemove", moveAnimate.onMouseMove);
通過上述事件計算出 mouseX
與 mouseY
的值,在 render
函數中,修改 camera
的位置。
camera.position.x += (mouseX * -1 - camera.position.x) * 0.05; camera.position.y += (mouseY - camera.position.y) * 0.05; camera.lookAt(scene.position);
行動端同步監聽 touchmove
事件,手機也可以看到雪糕地球的動態效果。
const moveAnimate = { onTouchMove(e) { const touchX = e.changedTouches[0].clientX; const touchY = e.changedTouches[0].clientY; moveAnimate.coordinates(touchX, touchY); }, }; document.addEventListener("touchmove", moveAnimate.onTouchMove);
紋理的載入需要一定的時間,因此新增一個轉場 loading
效果。
loading
效果使用小包前面的實現躍動的文字中的效果。
.loader { display: flex; color: white; display: flex; justify-content: center; align-items: center; font-size: 5em; width: 100%; height: 100%; font-family: "Baloo Bhaijaan", cursive; } .loader span { text-shadow: 0 1px #bbb, 0 2px #bbb, 0 3px #bbb, 0 4px #bbb, 0 5px #bbb, 0 6px transparent, 0 7px transparent, 0 8px transparent, 0 9px transparent, 0 10px 10px rgba(0, 0, 0, 0.4); text-shadow: 0 1px #bbb, 0 2px #bbb, 0 3px #bbb, 0 4px #bbb, 0 5px #bbb, 0 6px #bbb, 0 7px #bbb, 0 8px #bbb, 0 9px #bbb, 0 50px 25px rgba(0, 0, 0, 0.2); transform: translateY(-20px); }
Three
提供了 LoadingManager
,其功能是處理並跟蹤已載入和待處理的資料。當所有載入器載入完成後,會呼叫 LoadingManager
上的 onLoad
事件。
因此我們定義一個 LoadingManager
,當觸發 onLoad
事件後,將頁面中的載入標誌移除。
const loadingScreen = { scene: new THREE.Scene(), camera: new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ), // 移除載入標誌的函數 removeText() { const loadingText = document.querySelector("#canvas-loader"); if (loadingText.parentNode) { loadingText.parentNode.removeChild(loadingText); } }, }; // 初始化載入器 let loadingManager = new THREE.LoadingManager(); // 監聽載入器 onLoad 事件 loadingManager.onLoad = () => { loadingScreen.removeText(); isLoaded = true; }; // 紋理圖載入器傳入 loadingManager const textureLoader = new THREE.TextureLoader(loadingManager);
參考連結
以上就是Three.js實現雪糕地球的使用範例詳解的詳細內容,更多關於Three.js雪糕地球的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45