相關代碼能夠由此github查看html
經過前三篇的學習基礎知識咱們已經儲備差很少了, 這一篇咱們要作一個貼圖地球
, 這種地球也是很多公司如今在使用的方案, 但缺點也比較明顯就是它沒法精準的選中某個國家, 因此一些精細的操做它作不到, 可是學習這個技術依舊是一件使人愉快的事情, 也沒準你不須要選中國家的功能, 閒言少敘咱們全軍出擊吧。vue
咱們這一篇只討論規則的矩形木塊, 生活中更常見的不規則木塊咱們在3d模型
篇再聊, 繪製木塊的原理就是先生成geometry
, 把它的材質定義爲木頭圖片, 使其材質均勻有規則的分佈在geometry
表面, 這樣在咱們眼裏就成了木塊。git
我是直接百度的一張木頭紋理圖片, 你也能夠用我這張, 同時新建一個img
文件夾用來存放圖片。
github
const loader = new THREE.TextureLoader();
上面代碼咱們生成了一個加載器, 能夠用這個實例進行一系列的加載操做, 內部使用ImageLoader
來加載文件, 顧名思義Texture
是紋理的意思因此能夠叫它紋理加載器
,後面章節降到加載3d模型的時候還會介紹更多的加載器
。
ajax
const loader = new THREE.TextureLoader(); loader.load( './img/木塊.jpeg', (texture) => { const material = new THREE.MeshBasicMaterial({ map: texture }) const geometry = new THREE.BoxGeometry(2, 2, 1); // 加入紋理 const mesh = new THREE.Mesh(geometry, material) // 放入幾何 scene.add(mesh); }, (xhr) => { // 進度 console.log(`${xhr.loaded / xhr.total * 100}%`) }, (err) => { // 錯誤 console.log(err) } )
第一個參數
要加載的資源的路徑。第二個參數
加載成功後的回調, 會返回紋理對象。第三個參數
進度, 將在加載過程當中進行調用。參數爲XMLHttpRequest實例,實例包含total和loaded字節, 請注意three.js r84遺棄了TextureLoader進度事件, 咱們其實能夠填undefined
。第四個參數
錯誤的回調。當前咱們直接打開咱們的html
文件他會報以下的錯誤:跨域
每次遇到這種跨域報錯, 咱們第一時間應該想到把資源放在服務器上, 可是當前有更簡潔的方式。服務器
vscode
插件
在咱們的項目頁面點擊右下角的 Go live
啓動一個服務。
此時咱們就能夠獲得以下的效果:
app
完整代碼:dom
<html> <body> <script src="https://cdn.bootcdn.net/ajax/libs/three.js/r122/three.min.js"></script> <script src="../utils/OrbitControls.js"></script> <script> const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 1000); camera.position.z = 20; const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setClearColor(0xffffff) orbitControls = new THREE.OrbitControls(camera, renderer.domElement); document.body.appendChild(renderer.domElement); const axisHelper = new THREE.AxisHelper(4) scene.add(axisHelper) // 爲物體增長材質 const loader = new THREE.TextureLoader(); loader.load( './img/木塊.jpeg', (texture) => { console.log(texture) const material = new THREE.MeshBasicMaterial({ map: texture }) const geometry = new THREE.BoxGeometry(2, 2, 1); // 加入紋理 const mesh = new THREE.Mesh(geometry, material) // 放入幾何 scene.add(mesh); }, (xhr) => { // 進度(已廢棄) console.log(`${xhr.loaded / xhr.total * 100}%`) }, (err) => { // 錯誤 console.log(err) } ) const animate = function () { requestAnimationFrame(animate); renderer.render(scene, camera); }; animate(); </script> </body> </html>
咱們來談談紋理的幾個屬性吧, 木塊的圖片想看出差異不明顯, 咱們在img
文件夾裏面再放一張鳴人
的圖片。
函數
代碼裏面咱們只改路徑便可。
loader.load( './img/螺旋丸.jpeg', (texture) => { ...//
從上圖咱們能夠看出, 六個面上都是完整的圖片, 可是因爲寬高比的不一樣圖像被相應的壓縮, 接下來咱們就介紹幾個比較經常使用的屬性。
repeat
咱們把加載到的紋理進行處理texture.repeat.x = 0.5
定義他的x軸重複值。
把它的數值調大至5。
從上面的效果能夠看得出, 這個repeat.x
相似在物體x軸方向的畫面個數, 也就是說0.5就是x軸方向鋪滿須要0.5個圖片, 5就是須要5張圖片才能充滿, 那麼與之相對的就是y軸的重複正以下圖:
這看起來像個禮品盒的繩子, 那麼接下來咱們讓這個圖鋪滿表面。
wrapS
wrapT
t 是圖片的y軸咱們設置一下:
texture.wrapT = THREE.RepeatWrapping;
同理設置x軸, 注意x軸叫s
:
textureObj.wrapS = THREE.RepeatWrapping
紋理不是咱們這個系列的重點就不擴展了, 有興趣的同窗本身玩一玩
完整代碼以下:
<html> <body> <script src="https://cdn.bootcdn.net/ajax/libs/three.js/r122/three.min.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/dat-gui/0.7.7/dat.gui.min.js"></script> <script src="../utils/OrbitControls.js"></script> <script> const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 1000); camera.position.z = 14; const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setClearColor(0xffffff) orbitControls = new THREE.OrbitControls(camera, renderer.domElement); document.body.appendChild(renderer.domElement); const axisHelper = new THREE.AxisHelper(4) scene.add(axisHelper) // 爲物體增長材質 let textureObj = null; const loader = new THREE.TextureLoader(); loader.load( './img/螺旋丸.jpeg', (texture) => { textureObj = texture; const material = new THREE.MeshBasicMaterial({ map: texture }) const geometry = new THREE.BoxGeometry(2, 2, 1); // 加入紋理 const mesh = new THREE.Mesh(geometry, material) // 放入幾何 scene.add(mesh); }, (xhr) => { // 進度(已廢棄) console.log(`${xhr.loaded / xhr.total * 100}%`) }, (err) => { // 錯誤 console.log(err) } ) const pames = { repeatx: 5, repeaty: 5, } function createUI() { const gui = new dat.GUI(); gui.add(pames, "repeatx", 0, 5).name("repeatx") gui.add(pames, "repeaty", 0, 5).name("repeaty") } const animate = function () { if (textureObj) { textureObj.repeat.x = pames.repeatx textureObj.repeat.y = pames.repeaty textureObj.wrapT = THREE.RepeatWrapping; textureObj.wrapS = THREE.RepeatWrapping } requestAnimationFrame(animate); renderer.render(scene, camera); }; createUI() animate(); </script> </body> </html>
主線任務
終於開始) 初始化一個乾淨的vue
項目, 這個過程我就不在這裏說了, 咱們就從引入three.js
開始, 這裏要十分注意three.js
的版本很重要, 一樣的邏輯在不一樣版本里面效果居然不同, 因此想要和本篇同樣編寫代碼的同窗能夠和我暫時統一版本:
yarn add three@0.123.2
把App.vue
改裝成以下的樣子
<template> <div id="app"> <cc-map id="map"></cc-map> </div> </template> <script> import ccMap from "./components/cc_map.vue"; export default { name: "App", components: { ccMap, }, }; </script> <style> #app { overflow: hidden; border: 1px solid #ccc; width: 700px; height: 600px; margin: 20px auto; } </style>
從上面代碼能夠看出, <cc-map></cc-map>
這個就是我第一篇文章
裏提到的專門的vue組件
, 接下來的篇章裏咱們就都是圍繞着開發這個組件的功能了, 除非零散的知識點我會單開一個html
文件講, 大部分都是主線任務了。
暫時新建這樣三個文件夾與文件。
思否
不讓上傳超過4M
的圖, 因此下面是個模糊的截圖, 想看原圖的盆友能夠看我項目裏的, 這裏的圖片處於assets > images
的位置。
config > earth.config.js
內配置兩個參數。
export default { r: 80, // 半徑 earthBg: require("../assets/images/地圖.png"), // 貼圖路徑 }
當前初步components > cc_map.vue
的模板結構, 注意習慣引入'three'的方式。
<template> <div class="map" ref="map"></div> </template> <script> import * as THREE from "three"; import envConifg from "../config/earth.config"; export default { name: "ccMap", data() { return { }; }, methods: { }, mounted() { }, }; </script> <style scoped> .map { box-sizing: border-box; width: 100%; height: 100%; } </style>
都是以前篇章提到的方法, 先把data數據初始化好
data() { return { scene: null, camera: null, mapDom: null, renderer: null, orbitControls: null, object: new THREE.Object3D(), axisHelper: new THREE.AxesHelper(120), textureLoader: new THREE.TextureLoader(), }; },
場景
使用initTHREE
(以後基本不改)initTHREE() { this.renderer = new THREE.WebGLRenderer({ antialias: true, }); this.mapDom = this.$refs.map; this.renderer.setSize(this.mapDom.clientWidth, this.mapDom.clientHeight); this.renderer.setClearColor(0xffffff, 1.0); this.mapDom.appendChild(this.renderer.domElement); },
相機
使用initCamera
(以後基本不改)initCamera() { this.camera = new THREE.PerspectiveCamera( 45, this.mapDom.clientWidth / this.mapDom.clientHeight, 1, 2000 ); this.camera.position.z = 300; this.camera.up.set(0, 1, 0); this.camera.lookAt(0, 0, 0); },
容器
使用initScene
(以後基本不改)this.scene = new THREE.Scene();
輔助線
使用initAxisHelper
(以後基本不改)this.scene.add(this.axisHelper);
光源
使用initLight
(以後基本不改)const ambientLight = new THREE.AmbientLight(0xffffff); this.scene.add(ambientLight);
後期能夠模擬太陽光照射, 到時候咱們加個平型光就很像回事了。
軌道
使用initOrbitControls
(以後基本不改)import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"; // ... initOrbitControls() { const os = new OrbitControls(this.camera, this.renderer.domElement); os.target = new THREE.Vector3(0, 0, 0); //控制焦點 os.autoRotate = false; //將自動旋轉關閉 os.enablePan = false; // 不由止鼠標平移, 能夠用鍵盤來平移 os.maxDistance = 1000; // 最大外移動 os.minDistance = 100; // 向內最小外移動 this.orbitControls = os; },
地球背景
使用initBg
以後會有一張專門講物體的繪製的, 到時候咱們再詳聊圓形
initBg() { // 把背景圖加載過來當作紋理。 const texture = this.textureLoader.load(envConifg.earthBg); // 這個繪製球體 const geometry = new THREE.SphereGeometry(envConifg.r, 50, 50); // 放入紋理 const material = new THREE.MeshLambertMaterial({ map: texture, }); const mesh = new THREE.Mesh(geometry, material); this.scene.add(mesh); },
渲染函數
使用glRender
this.renderer.render(this.scene, this.camera); requestAnimationFrame(this.glRender);
這裏確定不能直接叫render
mounted() { this.initTHREE(); this.initCamera(); this.initScene(); this.initAxisHelper(); this.initLight(); this.initOrbitControls(); this.initBg(); this.glRender(); },
這裏的貼圖地圖其實已經能夠知足部分的需求場景了, 不要看它簡單它也能夠很炫的。
貼圖地球有它的侷限性, 好比上面地圖上如今是空空的沒有相應的國家名, 可是若是我在圖片中ps上國家名, 讓咱們看看效果。
ps上終究不是最靈活的辦法, 並且若是你仔細看會發現文字有點向上彎曲, 由於圖片是附着在球體上的, 因此越靠近南北極越會聚成一個點, 因此這樣加文字的模式只針對少數面積大並在赤道附近的國家有用。
上面咱們設置的球體咱們單獨拿出來玩一下, 這裏咱們只聊前三個參數, 後面會有專門介紹幾何體的文章
。
![image.png](/img/bVcRbTv)
來吧展現: 當我把水平分段數變成5new THREE.SphereGeometry(envConifg.r, 5, 50);
來吧展現: 當我把垂直分段數變成5new THREE.SphereGeometry(envConifg.r, 50, 5);
1x
2x
3x
下一篇開始正式繪製咱們的矢量3d地球
了, 會涉及一些數學知識, 好比三角函數你是否已經不會背了, 那我就帶你研究? 此次就是這樣, 但願和你一塊兒進步。