1. 程式人生 > >10.ThreeJs開發指南-第十章-載入和使用紋理

10.ThreeJs開發指南-第十章-載入和使用紋理

第十章 載入和使用紋理

在材質中使用紋理

function createMesh(geom,imageFile){

    var texture = THREE.ImageUtils.loadTexture('../assets/textures/general/' + imageFile);

    var mat = new THREE.MeshPhongMaterial();
    mat.map = texture;

    var mesh = new THREE.Mesh(geom,mat);

    return mesh;
}

這裡的紋理圖片可以是:png、gif、jpeg

紋理載入是非同步的。這是沒有問題的,因為我們有一個render迴圈,每秒大概渲染場景60次,所以一旦紋理載入完畢就會立即在場景中顯示。

如果你想紋理載入前一致等待。

texture = THREE.ImageUtils.loadTexture('texture.png',{},function(){
    renderer.render(scene);
});

幾乎所有的圖片都可以作為紋理。

為了達到最好的效果,最好使用正方形的圖片,其長寬都是2的次方。

由於紋理需要放大和縮小,所以通常紋理上的畫素(紋理單元:texel)不會一對一地對映成面上的畫素。因此WebGL和Three.js提供了幾種選擇。

magFilter屬性:指定紋理如何放大。預設值:THREE.LinearFilter
minFilter屬性:指定紋理如何縮小。預設值:THREE.LinearMipMapLinearFilter

屬性值:
THREE.NearestFilter 最鄰近過濾
THREE.LinearFilter 線性過濾

mipmap:一個mipmap是一組紋理圖片,每個圖片的尺寸都是前一張圖片的一半。這些圖片實在載入紋理時建立的,可以生成較平滑的過濾效果。

mipmap的屬性值:

THREE.NearestMipMapNearestFilter:選擇最貼近目標解析度的mipmap,然後使用最鄰近過濾原則。
THREE.NearestMipMapLinarFilter:選擇層次最近的兩個mipmap,然後在這兩層之間使用最鄰近過濾原則獲取兩個中間值,然後這兩個中間值傳遞給線性過濾器,以獲得最終效果。
THREE.LinearMipMapNearestFilter:選擇最貼近目標解析度的mipmap,然後使用線性過濾原則。
THREE.LinearMipMapLinearFilter:選擇層次最近的兩個mipmap,然後在這兩層之間使用線性過濾原則獲得兩個中間值,然後這兩個中間值傳遞給線性過濾器,以獲得最終結果。

凹凸紋理貼圖建立皺紋

凹凸紋理的目的是為材質增加厚度。

function createMesh(geom,imageFile,bump){

    var texture = THREE.ImageUtils.loadTexture('../assets/textures/general/'+imageFile);

    var mat = new THREE.MeshPhongMaterial();
    mat.map = texture;

    var bump = THREE.ImageUtils.loadTexture('../assets/textures/general/'+bump);
    mat.bumpMap = bump;
    mat.bumpScale = 0.2;//凹凸的高度,負數表示凹下去的深度。

    var mesh = new THREE.Mesh(geom,mat);

    return mesh;

}

凹凸貼圖中只有畫素的相對高度,沒有任何坡度的方向性資訊。所以凹凸貼圖所能達到的厚度和細節程度是有限的。更多的細節可以使用法向貼圖。

使用法向貼圖建立更加細緻的凹凸和皺紋

法向貼圖中儲存的不是每個畫素的高度,而是畫素的法向向量。使用法向貼圖,只需使用很少的頂點和麵,就可以創建出細節非常豐富的模型。

function createMesh(geom,imageFile,normal){

    var t = THREE.ImageUtils.loadTexture('../assets/textures/general/'+imageFile);

    var m = THREE.ImageUtils.loadTexture('../assets/textures/general/'+normal);

    var mat2 = new THREE.MeshPhongMaterial({
        mat:t,
        normalMap:m
    });

    var mesh = new THREE.Mesh(geom,mat2);

    return mesh;
}

我們還可以指定凹凸的程度,方法是設定:mat.normalScale.set(1,1);通過這個屬性,可以沿著x軸和y軸縮放。不過最好將他們設定成一樣的。

法向貼圖的問題是不容易建立。需要使用特殊的工具,例如:Blender和Photoshop,它們可以將高解析度的渲染結果或圖片作為輸入,從中建立法向貼圖。

使用光照貼圖建立假陰影

光照貼圖是預先渲染好的陰影,可以用它來模擬真實的陰影。

只對靜態場景起效果。

var lm = THREE.ImageUtils.loadTexture('../assets/textures/lightmap/lm-1.png');
var wood = THREE.ImageUtils.loadTexture('../assets/textures/general/floor-wood.jpg');

var groundMaterial = new THREE.MeshBasicMaterial({
    lightMap:lm,
    map:wood
});

groundGeom.faceVertexUvs[1] = groundGeom.faceVertexUvs[0];

要想光照貼圖顯示出來,我們需要為光照貼圖明確指定UV對映(將紋理的哪一部分應用到表面)。只有這樣,我們才能將光照貼圖和其他的紋理獨立開來。

用環境貼圖建立虛假的反光效果

計算反光非常消耗CPU,而且通常會使用法線追蹤演算法。

所以,我們可以建立一個物件所處環境的紋理來偽裝反光,並將它應用到特定的物件上。

步驟:

1、建立一個CubeMap物件
2、建立一個帶有這個CubeMap物件的方塊(天空盒)
3、將CubeMap作為紋理

function createCubeMap(){

    var path = "../assets/textures/cubemap/parliament/";

    var format = '.jpg';

    var urls= [
        path + 'posx' + format , path + 'negx' + format,
        path + 'posy' + format , path + 'negy' + format,
        path + 'posz' + format , path + 'negz' + format,
    ];


    var textureCube = THREE.ImageUtils.loadTextureCube(urls);

    return textureCube;
}

建立一個方塊:

var textureCube = createCubeMap();
var shader = THREE.ShaderLib['cube'];
shader.uniforms['tCube'].value = textureCube;
var material = new THREE.ShaderMaterial({
    fragmentShader:shader.fragmentShader,
    vertexShader:shader.vertexShader,
    uniforms:shader.uniforms,
    depthWrite:false,
    side:THREE.BackSide
});

cubeMesh = new THREE.Mesh(new THREE.CubeGeometry(100,100,100),material);

同一個Cube物件可以應用到某個網格上,用來建立虛假的反光。

var sphere1 = createMesh(new THREE.SphereGeometry(10,15,15),'plaster.jpg');

sphere1.material.envMap = textureCube;
sphere1.rotation.y = -0.5;
sphere1.position.x = 12;
sphere1.position.y = 5;
scene.add(sphere1);

var sphere2 = createMesh(new THREE.CubeGeometry(10,15,15),'plaste.jpg','plaster-normal.jpg');
sphere2.material.envMap = textureCube;
sphere2.rotation.y = 0.5;
sphere2.position.x = -12;
sphere2.position.y = 5;
scene.add(sphere2);

折射:

var textureCube = THREE.ImageUtils.loadTextureCube(urls,new THREE.CubeRefractionMapping);

通過材質的refraction屬性可以控制折射率。

在這個示例中,我們使用的是靜態環境貼圖。即我們只能看到環境的反光,而無法看到其他網格。

高光貼圖

可以為材質指定一個閃亮的、色彩明快的貼圖。

一般高光貼圖會同specular屬性一起使用。

var specularTexture = THREE.ImageUtils.loadTexture('../assets/textures/planets/EarthSpec.png');
var normalTexture = THREE.ImageUtils.loadTexture('../assets/textures/planets/EarthNormal.png');

var planetMaterial = new THREE.MeshPhongMaterial();
planetMaterial.specularMap = specularTexture;
planetMaterial.specular = new THREE.Color(0xff0000);
planetMaterial.shininess = 1;

planetMaterial.normalMap = normalTexture;

最好的效果往往是使用低光亮度實現的,但高光貼圖還會受到光照的影響。

紋理的高階用途

一、定製UV對映

通過UV對映,可以指定紋理的哪一部分顯示在物體表面。當你在Three.js中建立幾何體時,根據幾何體的型別,這些對映也一併自動建立。

UV定製一般是在諸如Blender這樣的軟體中完成的,特別是在模型變得複雜的時候。

UV對映有兩個維度,U和V,分別取值範圍是0-1.

需要為構成面的每一個頂點指定u和v。

重複對映

cube.material.map.wrapS = THREE.RepeatWrapping;//沿x方向的行為
cube.material.map.wrapT = THREE.RepeatWrapping;//沿y方向的行為

THREE.RepeatWrapping 允許紋理重複自己
THREE.ClampToEdgeWrapping 預設設定,紋理邊緣的畫素會被拉伸,以填滿剩下的空間。

如果使用了THREE.RepeatWrapping,我們可以使用下面的程式碼代替repeat屬性:

cube.material.map.repeat.set(repeatX,repeatY);

引數:
repeatX:指定在x軸方向多久重複一次。
repeatY:指定在y軸方向多久重複一次。

如果設定為1,都不會重複。
如果設定<1,紋理就會被放大。
如果設定為負數,就會產生紋理映象。

當修改repeat屬性時,Three.js會自動更新紋理,並用新的設定進行渲染。

當然,你從 THREE.RepeatWrapping 切換到 THREE.ClampToEdgeWrapping 時,你就要明確更新紋理:
cube.material.map.needsUpdate = true;

在畫布上繪製圖案並作為紋理

互動式畫布:literally庫

<div class="fs-container">
    <div id="canvas-output" style='float:left'></div>
<div>

...


var canvas = document.createElement('canvas');
$('#canvas-output')[0].appendChild(canvas);

//建立繪圖工具
$('#canvas-output').literallycanvas(
    {imageURLPrefix:'../libs/literally/img'}
);


//將在畫布上的繪圖結果作為輸入建立一個紋理
function createMesh(geom){

    var canvasMap = new THREE.Texture(canvas);
    var mat = new THREE.MeshPhongMaterial();
    mat.map = canvasMap;
    var mesh = new THREE.Mesh(geom,mat);

    return mesh;
}

更新材質:

function render(){

    stats.update();

    cube.rotation.y += 0.01;
    cube.rotation.x += 0.01;

    cube.material.map.needsUpdate = true;
    requeatAnimationFrame(render);

    webGLRenderer.render(scene,camera);
}

用畫布作凹凸紋理

凹凸圖只是簡單的黑白圖片。

貼圖中的畫素的密集程度越高,貼圖看上去越皺。

在畫布上隨機產生一副灰度圖。

Perlin噪聲,可以產生看上去非常自然的隨機紋理。

var ctx = canvas.getContext('2d');
function fillWithPerlin(perlin,ctx){

    for(var x = 0 ;x < 512; x++){
        for(var y = 0 ;y <512;y++){

            var base = new THREE.Color(0xffffff);
            var value = perlin.noise(x/10,y/10,0);//在畫布x座標和y座標的基礎上生成一個0到1之間的值。該值可以在畫布上畫一個畫素點。
            base.multiplyScalar(value);

            ctx.fillStyle = '#' + base.getHexString();
            ctx.fillRect(x,y,1,1);
        }

    }
}


function createMesh(geom){
    var bumpMap = new THREE.Texture(canvas);

    var mat = new THREE.MeshPhongMaterial();
    mat.color = new THREE.Color(0x77ff77);
    mat.bumpMap = bumpMap;
    bumpMap.needsUpdate = true;

    var mesh = new THREE.Mesh(geom,mat);

    return mesh;
}

用視訊輸出作為紋理

webgl已經直接支援HTML視訊元素。

<video id = 'video' 
    style='display:none;position: absolute;left:15px;top:75px;'
    src='../assets/movies/Big_Buck_Bunny_small.ogv'
    controls='true'
    autoplay='true'
</video>


var video = document.getElementById('video');
texture = new THREE.Texture(video);
texture.minFilter = Three.LinearFilter;
texture.magFilter = THREE.LinearFilter;
texture.generateMipmaps = false;

注意:
1.由於我們的視訊不是正方形,所以要保證材質不會生成mipmap。
2.由於材質變化得很頻繁,所以我們需要設定簡單高效的過濾器。

var materialArray =[];
materialArray.push(new THREE.MeshBasicMaterial(color:0x0051ba));
materialArray.push(new THREE.MeshBasicMaterial(color:0x0051ba));
materialArray.push(new THREE.MeshBasicMaterial(color:0x0051ba));
materialArray.push(new THREE.MeshBasicMaterial(color:0x0051ba));
materialArray.push(new THREE.MeshBasicMaterial(color:0x0051ba));
materialArray.push(new THREE.MeshBasicMaterial(color:0x0051ba));

var faceMaterial = new THREE.MeshFaceMaterial(materialArray);
var mesh = new THREE.Mesh(geom,faceMaterial);

在幀迴圈中更新材質

if(video.readyState === video.HAVE_ENOUGH_DATA){
    if(texture)
        texture.needsUpdate = true;
}

小結:

使用紋理時的注意事項:
1.紋理圖片的型別可以是png、jpg或gif格式的。圖片載入是非同步的,所以要麼使用渲染迴圈,要麼在載入紋理時提供一個回撥函式。
2.使用正方形紋理。這樣就可以使用mipmap來達到更好的效果。
3.標準情況下,Three.js並不支援反光,可以使用環境貼圖創建出虛假的反光。
4.要想直接控制物體表面的光亮貼圖,可以使用高光貼圖。,
5.紋理的repeat屬性,可以讓紋理自我複製。還要記住將材質的包裹屬性從 ClampToEdgeWrapping 改成 RepeatWrapping
6.Thee.js中可以用HTML5畫布元素或者視訊元素建立動態紋理。

相關推薦

10.ThreeJs開發指南--載入使用紋理

第十章 載入和使用紋理 在材質中使用紋理 function createMesh(geom,imageFile){ var texture = THREE.ImageUtils.loadTexture('../assets/textures/g

05.ThreeJs開發指南--幾何體

第五章 學習使用幾何體 二維幾何體: 一、PlaneGeometry:平面 var plane = new THREE.PlaneGeometry(width,height,widthSegments,heightSegments); width:

07.ThreeJs開發指南--粒子系統

第七章 粒子系統 function createParticles(){ var meaterial = new THREE.ParticleBasicMaterial(); for(var x = -5 ; x < 5 ; x +

Unix/Linux下的Curses庫開發指南——curses庫視窗

第3 章 curses 庫視窗 3 .1 curses 視窗簡介 3.1.1視窗概念 視窗是 curses 庫中最重要的一個元件,它實際上是螢幕上的一塊矩形區域,在上面我們可以進行各種輸出以及操作。 curses 庫中提供了大量的函式以允許我們建立和操作自己的視

Vert.x Java開發指南—— 利用RxJava進行響應式程式設計

第九章 利用RxJava進行響應式程式設計 截止目前,我們已經探索了Vert.x技術棧的多個部分,使用基於回撥的API。它僅僅可以正常工作,而且這個程式設計模型對於開發者在許多語言中是非常熟悉的。儘管如此,它可能有點繁瑣,尤其當你組合幾個事件源或者處理複雜

PHPUnit袖珍指南 程式碼覆蓋率分析

第十章程式碼覆蓋率分析 你已經學會了怎麼使用單元測試程式碼,但你怎麼測試你的測試呢?你怎麼發現沒被測試的程式碼,換句話說,沒被測試覆蓋的程式碼?怎麼衡量測試的完整性?所有這些問題的答案就是程式碼覆蓋率分析。程式碼覆蓋率分析告訴你當測試進行時,那些產品程式碼執行過了。 PH

CALLRET指令

一、ret和retf ①ret:用棧中的資料修改IP的值,從而實現近轉移。 ret指令的兩步操作: (IP)=((SS)*16+(SP));(SP)=(SP)+2。 ②retf:用棧中的資料修改CS和IP的值,從而實現遠轉移。 retf指令的四步操作: (IP)=((SS)*16+(SP));(S

——陣列指標

陣列初始化 當初始化列表中的值少於陣列元素個數時,編譯器會把剩餘的元素都初始化為0;個數多於陣列元素個數時,會視為錯誤 省略陣列中括號中的數字,編譯器會根據初始化列表中的專案來確定陣列的大小。 days是陣列 sizeof days是整個陣列的大小 sizeof

C++ primer plus 物件

重要的面向物件程式設計特性:抽象、封裝和資料隱藏、多型、繼承和程式碼的可重用性。為了實現這些特性並將它們組合在一起,C++所做的最重要的改進是提供了類。 類是一種將抽象轉換為使用者定義型別的C++工具,它將資料表示和操縱資料的方法組合成一個整潔的包。 類宣告:以資料成員的方式描述資料部分,以

【翻譯】Sklearn與TensorFlow機器學習實用指南 ——12 裝置伺服器上的分散式TensorFlow(上)

在第 11 章,我們討論了幾種可以明顯加速訓練的技術:更好的權重初始化,批量標準化,複雜的優化器等等。 但是,即使採用了所有這些技術,在具有單個 CPU 的單臺機器上訓練大型神經網路可能需要幾天甚至幾周的時間。在本章中,我們將看到如何使用 TensorFlow 在多個裝置(C

組合語言學習-CALLRET指令

     (sp)=(sp)-2      ((ss)*16+(sp))=(IP)(IP與CS壓棧) (2) (CS)=標號處所在的段地址       (IP)=標號處所在的偏移地址 call far ptr 標號相當於: push CS push IP jmp far ptr 標號      10.

C Primer Plus ——陣列指標

 與普通變數相似,在初始化之前陣列元素的數值是不定的。編譯器使用的數值是儲存單元中已有的數值。初始化陣列元素時(int),當數值數目少於陣列元素數目時(部分初始化),多餘的陣列元素被初始化為0。如果初始化列表中專案的個數大於陣列大小,編譯器則會認為這是一個錯誤。可以在初始化

thinkpython練習10-5

Points: >sorted()內建函式,不改變原來列表,新建一個升序排列的列表,並把這個新列表返回。 >.sort()方法,直接改變原來列表,返回值為None。 編寫一個名為is_sorted的函式,接收一個列表為形參,並當列表是按照升序排好序的時候返回True,否則返回

.NET Core實戰專案之CMS 設計篇-系統開發框架設計

這兩天比較忙,週末也在加班,所以更新的就慢了一點,不過沒關係,今天我們就進行千呼萬喚的系統開發框架的設計。不知道上篇關於架構設計的文章大家有沒有閱讀,如果閱讀後相信一定對架構設計有了更近一部的理解,如果你沒有閱讀也希望大家能好好閱讀一下!其實說白了,架構是為了應對軟體系統複雜度而提出的一個解決方案,架構設計的

2018.10.16——-10.1概述-10.2初識泛型演算法

10.1 #include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { int val = 1;

C++Primer五版 習題答案(1~10

1:這個比較簡單,類比下 find() 函式也就知道了。 #include<iostream> #include<algorithm> #include<vector> using namespace std; void main() {

【《Real-Time Rendering 3rd》 提煉總結】(九) · 遊戲開發中基於影象的渲染技術總結

毛星雲,網路ID「淺墨」,90後,熱愛遊戲開發、遊戲引擎、計算機圖形、實時渲染等技術,就職於騰訊互娛。 微軟最有價值專家 著作《Windows遊戲程式設計之從零開始》、《OpenCV3程式設計入門》 碩士就讀於南京航空航天大學航天學院(2013級碩士研究生),已於2016年三月畢業。本科

k-均值演算法 10.4 對地圖上的點進行聚類

將地圖上的點進行聚類,安排交通工具抵達這些簇的質心,然後步行到每個簇內地址。 這裡我們直接用給出的檔案進行操作,跳過10.4.1節。 新增程式碼: def distSLC(vecA, vecB):

從零開始Desire HD刷機指南——:利用第三方recovery備份與還原系統

原文地址:http://blog.sina.com.cn/s/blog_722b43a60100q5jf.html 本教程由symen 原創,轉載請註明出處。 上一章我們介紹瞭如何把第三方recovery 刷進手機,本章來介紹如何利用它來備份與還原系統。 先來看一下

從零開始Desire HD刷機指南——:如何給DHD進行廣告免疫

原文地址:http://blog.sina.com.cn/s/blog_722b43a60100q5kf.html 本教程由symen 原創,轉載請註明出處。 很多免費軟體都會帶有廣告條幅,看起來甚是惱人,下面教大家一種方法,來給你的DHD 進行廣告免疫。 原理