在這個課程里主要完成講解兩個demo:
一個是電視牆:用視頻做紋理
一,用視頻做紋理
首先我們用video標簽把視頻源引入:
<video id="video" autoplay loop style="display:none"> <source src="http://10.1.26.29:84/assets/video/sintel_trailer-480p.ogv" type="video/ogg"> <source src="http://10.1.26.29:84/assets/video/sintel_trailer-480p.mp4" type="video/mp4"> <source src="http://10.1.26.29:84/assets/video/sintel_trailer-480p.webm" type="video/webm"> </video>
設置視頻紋理
在這里引入一個概念mipmap:一個mipmap是一組紋理圖片,每個圖片的尺寸都是前一張圖片的一半。這些圖片實在加載紋理時創建的,可以生成較平滑的過濾效果。
注意:
1.由於我們的視頻不是正方形,所以要保證材質不會生成mipmap。
2.由於材質變化得很頻繁,所以我們需要設置簡單高效的過濾器。LinearFilter 線性過濾
var video = document.getElementById('video'); //獲取頁面dom元素 videoTexture = new THREE.Texture(video); //minFilter屬性:指定紋理如何縮小。默認值:THREE.LinearMipMapLinearFilter videoTexture.minFilter = THREE.LinearFilter; //magFilter屬性:指定紋理如何放大。默認值:THREE.LinearFilter videoTexture.magFilter = THREE.LinearFilter; videoTexture.format = THREE.RGBFormat; videoTexture.generateMipmaps = false;
THREE.Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy )
Image:這是一個圖片類型,基本上它有ImageUtils來加載,如下代碼
var image = THREE.ImageUtils.loadTexture(url); // url 是一個http://xxxx/aaa.jpg 的類似地址,javascript沒有從本地加載數據的能力,所以沒有辦法從您電腦的C盤加載數據。
Mapping:是一個THREE.UVMapping()類型,它表示的是紋理坐標。下一節,我們將說說紋理坐標。
wrapS:表示x軸的紋理的回環方式,就是當紋理的寬度小於需要貼圖的平面的寬度的時候,平面剩下的部分應該p以何種方式貼圖的問題。
wrapT:表示y軸的紋理回環方式。
magFilter和minFilter表示過濾的方式,這是OpenGL的基本概念。當您不設置的時候,它會取默認值。
format:表示加載的圖片的格式,這個參數可以取值THREE.RGBAFormat,RGBFormat等。THREE.RGBAFormat表示每個像素點要使用四個分量表示,分別是紅、綠、藍、透明來表示。RGBFormat則不使用透明,也就是說紋理不會有透明的效果。
type:表示存儲紋理的內存的每一個字節的格式,是有符號,還是沒有符號,是整形,還是浮點型。不過這里默認是無符號型(THREE.UnsignedByteType)。暫時就解釋到這里,有需要時,我們在仔細分析,或者給作者留言詢問。
anisotropy:各向異性過濾。使用各向異性過濾能夠使紋理的效果更好,但是會消耗更多的內存、CPU、GPU時間。
添加一個長方體作為電視牆,設置紋理時,我們采用兩種紋理,有四面是純色的,有兩面是視頻紋理,我們這里用數組materials存放六個面的紋理,然后通過THREE.MeshFaceMaterial()設置每個面的紋理,你也可以自己設置,六個面都是視頻紋理
var cubeGeometry = new THREE.BoxGeometry(1, 10, 20); var cubeMaterial = new THREE.MeshBasicMaterial({map: videoTexture}); var materials = []; materials.push(cubeMaterial); materials.push(cubeMaterial); for (var i = 1; i < 5; i++) { materials.push(new THREE.MeshLambertMaterial({color: 0xff0000})); } //MeshFaceMaterial這是一種容器,可以在該容器中為物體的各個表面上設置不同的顏色 var cube = new THREE.Mesh(cubeGeometry, new THREE.MeshFaceMaterial(materials)); cube.position.set(-10.05,0,0); cube.rotation.x = -0.3; cube.rotation.y = 23.5; cube.name = 'cube'; scene.add(cube);
MeshFaceMaterial方法根據參數materials創建mesh(網格)的復合材質類型,參數material是一個Material類型的數組對象,網格中的三角面屬性materialindex定義了該三角面使用的參數material中材質對象的索引號.
添加控制器:
我們之前已經使用過一個可以縮放的控制器,這里我們使用一個可以旋轉的控制器
用script標簽引入一個庫文件 OrbitControls.js
創建一個實例對象,就可以使用了。
orbit = new THREE.OrbitControls(camera);
完整代碼下載:github(threejs-five) 如果你覺得我寫的對你有幫助的話,請給個star,謝謝
二,玻璃質感反射一個空間環境
首先我們從原理上進行分析,這種可以表現立體空間環境的實現,實際上是構造了一個大的正方體,(skybox)正方體的六個面的背景是符合實際場景的六張圖片,在這個demo里,就是汽車內部的前后左右上下,然后我們在正方體的內部進行觀看,從而達到立體環繞的效果;
使用ShaderMaterial(共享材質)實現漫光反射,根據參數parameters創建為自定義着色器創建材質類型,這樣的材質對象讓用戶擴充材質類型,有了無限的可能.
參數的格式: parameters = { defines: { "label" : "value" }, uniforms: { "parameter1": { type: "f", value: 1.0 }, "parameter2": { type: "i" value2: 2 } }, fragmentShader: <string>, vertexShader: <string>, shading: THREE.SmoothShading, blending: THREE.NormalBlending, depthTest: <bool>, depthWrite: <bool>, wireframe: <boolean>, wireframeLinewidth: <float>, lights: <bool>, vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, skinning: <bool>, morphTargets: <bool>, morphNormals: <bool>, fog: <bool> }
接下來我們從代碼進行分析:
首先我們先引入正方體六個面的圖片,依然是異步引入
var urls = [ 'http://10.1.26.29:84/assets/cubemap/car/right.png', 'http://10.1.26.29:84/assets/cubemap/car/left.png', 'http://10.1.26.29:84/assets/cubemap/car/top.png', 'http://10.1.26.29:84/assets/cubemap/car/bottom.png', 'http://10.1.26.29:84/assets/cubemap/car/front.png', 'http://10.1.26.29:84/assets/cubemap/car/back.png' ];
用THREE.ImageUtils.loadTextureCube()方法載入紋理
定義一個着色器,基於THREE.ShaderLib來創建自定義的ShaderMaterial材質
var cubemap = THREE.ImageUtils.loadTextureCube(urls); cubemap.format = THREE.RGBFormat; //一個特別的着色器(Three.ShaderLib[“cube”]),結合THREE.ShaderMaterial類,我們可以基於CubeMap對象創建一個環境 var shader = THREE.ShaderLib["cube"]; shader.uniforms["tCube"].value = cubemap;
//ShaderMaterial創建自己的着色器,要使用ShaderMaterial,必須傳入兩個着色器:fragmentShader,vertexShader var material = new THREE.ShaderMaterial({ fragmentShader: shader.fragmentShader, //定義每個傳入的像素的顏色 vertexShader: shader.vertexShader, //允許你修改每一個傳入的頂點的位置 uniforms: shader.uniforms, //該屬性可以向你的着色器發送消息,將同樣的消息發送到每個頂點和片段 depthWrite: false, side: THREE.DoubleSide //實現一個立體雙面的效果 });
接下來,我們就可以創建天空盒子
var skybox = new THREE.Mesh(new THREE.BoxGeometry(10000, 10000, 10000), material); scene.add(skybox);
CubeCamera方法根據 near, far ,cubeResolution 生成立方體相機.CubeCamera對象的功能函數采用 定義構造的函數原型對象來實現. CubeCamera經常用來創建天空盒子.由六張圖片拼接成一個場景.
near 指明相對於深度剪切面的近的距離,必須為正數,可選參數,如果未指定,初始化為0.1
far 指明相對於深度剪切面的遠的距離,必須為正數,可選參數,如果未指定,初始化為2000
cubeResolution 設置立方體的寬度
Matrix4 返回PerspectiveCamera,透視投影相機
cubeCamera = new THREE.CubeCamera(0.1, 20000, 256); cubeCamera.renderTarget.minFilter = THREE.LinearMipMapLinearFilter; scene.add(cubeCamera);
接下來,我們可以創建鏡面反射的立體圖形
先以球體為例,
我們用立體相機的渲染目標,cubeCamera.renderTarget,也就是整個環境
var sphereGeometry = new THREE.SphereGeometry(4, 15, 15);
////envMap 設置環境貼圖,默認是null var dynamicEnvMaterial = new THREE.MeshBasicMaterial({envMap: cubeCamera.renderTarget, side: THREE.DoubleSide}); sphere = new THREE.Mesh(sphereGeometry, dynamicEnvMaterial); sphere.name = 'sphere'; scene.add(sphere);
再添加一個旋轉立方體,可以顯示變化的環境
用 cubemap作為紋理貼圖,就是上面加載的六張圖片
var cubeGeometry = new THREE.BoxGeometry(5, 5, 5); var envMaterial = new THREE.MeshBasicMaterial({envMap: cubemap, side: THREE.DoubleSide}); var cube = new THREE.Mesh(cubeGeometry, envMaterial); cube.name = 'cube'; scene.add(cube); cube.position.set(-10, 0, 0);
最后,同樣使用上面提到的控制器:OrbitControls
orbit = new THREE.OrbitControls(camera);
完整代碼下載: github(threejs-six) 如果你覺得我寫的對你有幫助的話,請給我個star,