天天看點

使用WebGL + Three.js制作動畫場景 使用WebGL + Three.js制作動畫場景

使用WebGL + Three.js制作動畫場景

使用WebGL + Three.js制作動畫場景 使用WebGL + Three.js制作動畫場景

3D圖像,技術,打造産品,還有網際網路:這些隻是我愛好的一小部分。

現在,感謝WebGL的出現-一個新的JavaScriptAPI,它可以在不依賴任何插件的情況下渲染浏覽器中的3D圖像-這讓3D渲染操作變得異常簡單。

随着虛拟現實和增強現實應用的發展,大型廠商們開始轉向數字化觸覺體驗,這是令人動心的一項技術。

或者,至少那些已經投資的人這一年還抱有希望-

11億美金流入VR和AR領域

.

這是兩篇入門文章

Abbey Road Studios的谷歌互動之旅

拍攝Deadliest Catch用到的艦隊

,通過在非真實世界給觀衆沉浸式的體驗,所有的産品,服務和環境得以實作更好的配合。

由于人們能接觸到更多體驗性的技術,2D開始變得有些單調。這是事實。

讓我們現實點。目前看來,很多緻力于創造體驗的應用仍處在技術探索階段,對大多數商業領域而言前景不算明朗。

或者說他們真的創造了令人激動的體驗嗎?

走進WebGL:一項實用與靈活的技術,可以創造更強沉浸式3D内容。無論是Porsche展示

一輛新911的細節

,還是NASA重點介紹的

火星是什麼樣子

,或者是

J Dlla備受喜愛的甜甜圈專輯慶典

,WebGL能應用于很多領域來表現各種類型的故事。

為讓你熟悉這項強大的技術,我打算做一個關于它是如何工作的簡要概括,還有使用Three.js(一個基于WebGL API的JavaScript庫)一步步創造簡單3D環境的快速教程。

首先,什麼是WebGL?

WebGL是一項在浏覽器中展示基于硬體加速的3D圖像的web技術,不需要安裝額外插件或者下載下傳多餘的軟體。

是以,很多閱聽人可以更友善地接觸到WebGL。浏覽器支援程度也很不錯(

目前應用廣泛

),Chrome,Firefox,IE,Opera和Safari等主流移動端和桌面浏覽器都提供了很好的支援。

許多計算機和智能手機有先進的圖像渲染單元(GPUS),可直到最近,大多數網頁和移動網站都不能使用GPUS。這導緻裝置的加載速度緩慢,圖像品質低,并且對3D内容的支援程度也很低。

為了解決這個問題WebGL花了不少時間。基于著名的

OpenGL 3D 圖像标準

,WebGL賦予Javascript插件式的自由接入方式,通過HTML5 元素連接配接一個裝置的圖像硬體,并在浏覽器中直接應用3D技術。結果是360度的3D内容變得更容易建立—排除了使用獨立應用或插件的幹擾——同時使用者能更容易地在網上擁有高清體驗。

什麼是Three.js?

OpenGL和WebGL的複雜度相差不大。

Three.js是一個開源文法庫,簡化了WebGL工具和環境的建立工作。它支援大部分基于GPU加速的低代碼量3D動畫。

聊得差不多了,讓我們編寫代碼吧

示例用Three.js庫展示了更複雜的效果。為了練習需要,我會盡量寫的簡單,用低複雜度的環境來展示僅靠了解的基礎知識能實作什麼效果。

我打算建構一個我們已使用過的

例子
使用WebGL + Three.js制作動畫場景 使用WebGL + Three.js制作動畫場景

讓我們開始用了解的基礎知識做點東西吧。

一個渲染器,一個場景,還有一個相機

代碼連結

第一步

貢獻者 Matt Agar(

@agar

)

代碼釋出于

CodePen

點選并拖動這個例子,做點嘗試

CodePen上的例子相當于入門,現在我們開始使用Three.js。

Firstly we need a Scene — a group or stage containing all the objects we want to render. Scenes allow you to set up what and where is going to be rendered by Three.js. This is where you place objects, lights, and cameras.

首先我們需要一個場景 — 一個包含了我們要渲染的所有對象的群組。場景允許你設定Three.js要渲染的對象和渲染位置,以及如何進行渲染。這個場景指的就是你放置對象,光線和相機的地方。

`var scene = new THREE.Scene();`

           

-用一個好方法建立場景

接下來我們在這個例子中添加一個相機。我添加的是透視相機,但也有其他可用的選項。頭兩個參數分别指明了相機的視野區域和寬高比。後兩個參數代表相機渲染對象的截止距離。

var camera = new THREE.PerspectiveCamera(
    75,                                   // Field of view
    window.innerWidth/window.innerHeight, // Aspect ratio
    0.1,                                  // Near clipping pane
    1000                                  // Far clipping pane
);

// Reposition the camera
camera.position.set(5,5,0);

// Point the camera at a given coordinate
camera.lookAt(new THREE.Vector3(0,0,0));

           

-添加相機,視場,寬高比和截止距離

最後至關重要的部分是渲染器本身,它掌握着一個來自給定相機視角場景的渲染。Three.js提供了很多種渲染器以供選擇,但我決定在這個練習中使用标準的WebGL渲染器。

var renderer = new THREE.WebGLRenderer({ antialias: true });

// Size should be the same as the window
renderer.setSize( window.innerWidth, window.innerHeight );

// Set a near white clear color (default is black)
renderer.setClearColor( 0xeeeeee );

// Append to the document
document.body.appendChild( renderer.domElement );

// Render the scene/camera combination
renderer.render(scene, camera);

           

-添加渲染器

這個例子也包括了一些基礎的幾何結構— 在這裡是一個扁平的平面 — 我們可以看到一些特征以深度形式被渲染出來。如果沒有它,我們隻能看到空空的螢幕。我接下來會簡短介紹關于Geometry(幾何結構),Materials(材質)和Meshes(網格)。

// A mesh is created from the geometry and material, then added to the scene
var plane = new THREE.Mesh(
  new THREE.PlaneGeometry( 5, 5, 5, 5 ),
  new THREE.MeshBasicMaterial( { color: 0x222222, wireframe: true } )
);
plane.rotateX(Math.PI/2);
scene.add( plane );

           

-添加一個扁平的平面

一個關于控制相機的小貼士

你可能已經意識到我在這個例子裡使用了外部子產品。這個是Three.js 的Github repo裡能找到的衆多可用子產品的一個。

在這個例子裡是

軌道控制

它允許我們捕獲canvas元素上的滑鼠事件以重新定位圍繞着場景的相機。

var controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', function() { renderer.render(scene, camera); } );

           

-實作軌道控制

在CodePen例子中從動作,點選和拖放或者滾動滑鼠輪等方面檢驗軌道控制。在這個示例中,由于我們沒有設定動作循環(一旦我開始裝飾我的聖誕樹,我就會介紹動作循環),當控制發生更新時我們同樣需要重新渲染場景。

準備渲染

好吧,之前的例子現在可能看着有點蠢,但你在沒有硬體基礎的情況下無法建構一個更好的屋子或聖誕樹。

是時候給我們的場景添些東西了,現在有三件事需要我們去探索:Geometries,Materials,還有Meshes。

第二步

貢獻者Matt Agar(

來自

Codepen

-鈴兒叮當響。是,它們一定會響的

不論是點選還是拖拽,快嘗試一下吧。

使用平面陰影來添加一些簡單的多邊形

首先我們需要一些Geometry。它可以是包含點和線的任何立方形狀。

Three.js簡化了一系列可實作的

建構基礎多邊形

操作。這裡有很多種适合3D格式的檔案加載器。你也可以選擇通過指定頂點和表面建立你自己的幾何結構。

現在,我們将以一個基礎的八面體作為開始。

`var geometry = new THREE.OctahedronGeometry(10,1);`

           

-添加Geometry

Materials描繪了對象的外觀。它們的定義不受渲染器影響(大部分情況下),是以當你決定使用不同的渲染器時不必重寫它。

這裡是

可實作的各種Materials

,所有的Materials都使用一個包含各種屬性的對象,屬性會被應用于這些Materials。

下面的例子實作了一個扁平帶陰影的Material,這展示了我們的多邊形對象,而不是打算對它們進行平滑處理。

var material = new THREE.MeshStandardMaterial( {
    color: 0xff0051,
    shading: THREE.FlatShading, // default is THREE.SmoothShading
    metalness: 0,
    roughness: 1
} );

           

-用Materials确定對象的紋理

第三個我們需要的是Mesh(網格)。一個Mesh就是一個對象,它得到一個多面體并給它應用Material,我們可以把網格插入我們的場景中并自由移動它。

下面是如何來合并Geometry和Material并放入一個Mesh,然後添加到場景中。需要指明的是,将Mesh添加進場景後,我們可以自由地重新定位或者旋轉它。

var shapeOne = new THREE.Mesh(geometry, material);
shapeOne.position.y += 10;
scene.add(shapeOne);

           

-将Geometry和Material合并進一個Mesh中,并将Mesh加入場景

添加光線

一旦我們在場景中擁有了對象,我們需要照亮它們。為了實作這種效果,我們會添加兩類不同的光線:環境光和點狀光。

環境光的色彩會全局應用到場景中所有的對象。

var ambientLight = new THREE.AmbientLight( 0xffffff, 0.2 );
scene.add( ambientLight );

           

-給場景添加環境光

點狀光在場景中某特定位置建立光。光在任何方向都會閃爍,大概和燈泡的效果類似。

var pointLight = new THREE.PointLight( 0xffffff, 1 );
pointLight.position.set( 25, 50, 25 );
scene.add( pointLight );

           

-為場景添加點狀光

如果這些不能滿足你的需求,還有其他種類的光可以選擇,包括定向光和斑點光。檢視

Three.js 光線手冊

來獲得更多資訊。

制造并接收陰影

陰影預設是不能使用的,但對建立視覺上的深度很有幫助 — 是以我們需要在渲染器上啟用它們。

renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;

           

-在渲染器中啟用陰影

下一步是指定哪些光線可以形成陰影,還有要渲染的陰影範圍有多大。

pointLight.castShadow = true;
pointLight.shadow.mapSize.width = 1024;
pointLight.shadow.mapSize.height = 1024;

           

-啟用光線。相應地,陰影也會出現

最終我們指定哪些網格應該接收陰影。需要指明的是任何網格在不依賴場景的情況下都能制造和接收陰影。

shapeOne.castShadow = true;
shapeOne.receiveShadow = true;

           

-利用陰影來突出Mesh

在這個場景裡,我們使用了一個特殊的陰影Material。它允許一個Mesh僅展示陰影,而非對象本身。

var shadowMaterial = new THREE.ShadowMaterial( { color: 0xeeeeee } );
shadowMaterial.opacity = 0.5;

           

-實作陰影效果

利用簡單要素建構複雜物體

目前為止我們做的一些簡單的例子還不錯,可如果我們能實作元素複用的話事情會更簡單。

第三步

代碼貢獻者 (

-能确定的是,這些拼合的多邊形變得更小巧了

在codepen中點選并拖拽以獲得更清晰的效果。

通過對多邊形對象進行合并和分層操作,我們可以開始建立更多的複雜形狀。

下面的操作是擴充Three.Group對象以求在構造器中建立複雜形狀。

var Decoration = function() {

    // Run the Group constructor with the given arguments
    THREE.Group.apply(this, arguments);

    // A random color assignment
    var colors = ['#ff0051', '#f56762','#a53c6c','#f19fa0','#72bdbf','#47689b'];

    // The main bauble is an Octahedron
    var bauble = new THREE.Mesh(
        addNoise(new THREE.OctahedronGeometry(12,1), 2),
        new THREE.MeshStandardMaterial( {
            color: colors[Math.floor(Math.random()*colors.length)],
            shading: THREE.FlatShading ,
            metalness: 0,
            roughness: 1
    } )
    );
    bauble.castShadow = true;
    bauble.receiveShadow = true;
    bauble.rotateZ(Math.random()*Math.PI*2);
    bauble.rotateY(Math.random()*Math.PI*2);
    this.add(bauble);

    // A cylinder to represent the top attachment
    var shapeOne = new THREE.Mesh(
        addNoise(new THREE.CylinderGeometry(4, 6, 10, 6, 1), 0.5),
        new THREE.MeshStandardMaterial( {
            color: 0xf8db08,
            shading: THREE.FlatShading ,
            metalness: 0,
            roughness: 1
        } )
    );
    shapeOne.position.y += 8;
    shapeOne.castShadow = true;
    shapeOne.receiveShadow = true;
    this.add(shapeOne);
};
Decoration.prototype = Object.create(THREE.Group.prototype);
Decoration.prototype.constructor = Decoration;

           

-在構造器中建立複雜形狀

我們現在能多次複用拼合得到的多邊形來給我們的場景添加多重距離,用比單獨建立每一個元素更少的工作量讓樹木更真實。

var decoration = new Decoration();
decoration.position.y += 10;
scene.add(decoration);

           

-裝飾樹幹

另一個建議是給建立的對象添加一個随機的元素。

在對象的Geometry内移動頂點,以添加一個随機組織的元素來降低形狀複雜度。若沒有這些小缺陷,做出來的物體會有點合成的感覺。我使用了一個輔助函數來給Geometry的頂點随機添加噪點。

function addNoise(geometry, noiseX, noiseY, noiseZ) {
    var noiseX = noiseX || 2;
    var noiseY = noiseY || noiseX;
    var noiseZ = noiseZ || noiseY;
    for(var i = 0; i < geometry.vertices.length; i++){
        var v = geometry.vertices[i];
        v.x += -noiseX / 2 + Math.random() * noiseX;
        v.y += -noiseY / 2 + Math.random() * noiseY;
        v.z += -noiseZ / 2 + Math.random() * noiseZ;
    }
    return geometry;
}

           

-添加噪點可以使對象更真實

實作動作

目前為止我們隻為WebGLRender實作了一個單獨的渲染調用。為了向我們的場景中添加一些動作,我們需要做出一些更新。

第四步

-觀察下多面體催眠式的緩慢旋轉

渲染循環

為了使浏覽器适應我們的更新速度,我們正在使用浏覽器

動作請求架構

API來調用一個新的渲染函數。

requestAnimationFrame(render);
function render() {
    // Update camera position based on the controls
    controls.update();

    // Re-render the scene
    renderer.render(scene, camera);

    // Loop
    requestAnimationFrame(render);
}

           

-利用動作請求架構建立一個渲染循環

逾時更新元素

現在,我會對複雜對象做出一些改變,每次建立距離時給裝飾物初始化一個随機旋轉速度。

this.rotationSpeed = Math.random() * 0.02 + 0.005;
this.rotationPosition = Math.random();

           

-進入旋轉

我們同樣設定了一個可以被調用來基于目前值值繞Y軸旋轉的新函數。需要指出的是旋轉速度基于浏覽器取得的幀率,但對這個簡單的例子來說還好。對處理這個過程而言,你一定會用到數學函數。

Decoration.prototype.updatePosition = function() {
    this.rotationPosition += this.rotationSpeed;
    this.rotation.y = (Math.sin(this.rotationPosition));
};

           

-觀察裝飾物旋轉情況

随着一個更新函數的定義,每運作一次我們就能通過更新渲染循環來重新計算每個元素每次被建立的位置。

function render() {
    // Update camera position based on the controls
    controls.update();

    // Loop through items in the scene and update their position
    for(var d = 0; d < decorations.length; d++) {
        decorations[d].updatePosition();
    }

    // Re-render the scene
    renderer.render(scene, camera);

    // Loop
    requestAnimationFrame(render);
}

           

-重新計算元素位置

把以上的幾個例子結合在一起

第五步

-3D聖誕樹:完全成型,裝飾完美

最終的産品總算出來了。僅僅使用了基礎功能,我們已經建構出一個互動式的3D聖誕樹,并且建立了一個平面的二維場景。

但這隻是使用WebGL的開始。當這項技術快速發展的時候,會出現許多可供選擇的資源,還有能正确指導你的教程。以下是資源連結:

你還在等什麼?嘗試下WebGL和Three.js吧,開始建立你自己的3D效果。如果你做了一些有趣的玩意,請告訴我。我很樂意欣賞一下。

分享

使用WebGL + Three.js制作動畫場景 使用WebGL + Three.js制作動畫場景

關于作者

擁有超過15年的工程經驗,Matt是August的創始成員之一,并團結了世界上一批很優秀的前後端開發者。作為能适應任何情景的問題解決者,Matt從實用角度和大方向上審視項目的技術問題。當他不在解決問題時,他一定在搭建一個虛構的動物王國并和年輕的家人一起探索戶外。

聯系方式

本文轉載自: 衆成翻譯 譯者: VicSusi 連結: http://www.zcfy.cc/article/4705 原文: https://www.august.com.au/blog/animating-scenes-with-webgl-three-js

繼續閱讀