「譯」如何實現交互式 WebGL 懸停效果

本文主要內容是介紹如何經過一些簡單的步驟,在圖像上實現交互式鼠標懸停效果。css

查看 Demo下載源碼html

我很喜歡 WebGL,在本文中,我將介紹如何在掌握着色器基礎上作出炫酷效果。我想要重現的這個效果,源自 Jesper Landberg 的網站,他是一個很是酷的傢伙,請務必查看一下他網站上的東西。git

image

讓咱們開始吧!讓咱們從編寫簡單的 HTML 開始:github

<div class="item">
    <img src="img.jpg" class="js-image" alt="">
    <h2>Some title</h2>
    <p>Lorem ipsum.</p>
</div>
<script src="app.js"></script>

這個例子再簡單不過了!接下來,讓咱們來添加些樣式,以使其它起來更漂亮:web

image

全部動畫效果將在 Canvas 元素中呈現,同時咱們還須要添加部分 JavaScript 代碼。我在這裏使用 Parcel,由於學習起來很是簡單,我還將在 WebGL 部分中使用 Three.jscanvas

到這裏,咱們開始編寫 JavaScript 代碼,並按照官方文檔着手進行基本的 Three.js 設置:api

import * as THREE from "three";

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );

var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );


camera.position.z = 5;

var animate = function () {
    requestAnimationFrame( animate );

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

    renderer.render( scene, camera );
};

animate();

讓咱們設置 Canvas 元素的樣式:app

body { margin: 0; }

canvas { 
    display: block; 
    position: fixed;
    z-index: -1; // 將其設置爲背景
    left: 0; // 鋪滿整個屏幕
    top: 0; // 鋪滿整個屏幕
}

當你完成全部這些操做,就可使用 parcel index.html 運行它。如今,您不會看到太多,到目前爲止,它是一個空的 3D 場景。讓咱們暫時擱置 HTML,專一於 3D 場景操做。composer

讓咱們建立一個帶有圖像的簡單 PlaneBufferGeometry 對象。像這樣:dom

let TEXTURE = new TextureLoader().load('supaAmazingImage.jpg'); 
let mesh = new Mesh(
    new PlaneBufferGeometry(), 
    new MeshBasicMaterial({map: TEXTURE})
)

如今,咱們將看到如下內容:

image

顯然咱們尚未實現效果,咱們須要追蹤鼠標的顏色軌跡。固然,咱們還須要着色器。若是您對着色器感興趣,或者您可能已經學過一些有關如何放置圖像的教程,例如如何將鼠標放在懸停位置上?液體變形效果?

可是咱們有一個問題:咱們只能在上面的示例中在該圖像上(和內部)使用着色器。可是效果並不侷限於任何圖像邊界,而是流動的,覆蓋整個區域,就像整個屏幕同樣。

後期處理進行渲染

事實證實 Three.js 渲染器的輸出只是另外一幅圖像。咱們能夠利用它,並在該輸出上應用着色器位移!

這是代碼的補充部分:

// 設置後期處理
let composer = new EffectComposer(renderer);
let renderPass = new RenderPass(scene, camera);
// 用圖像渲染場景
composer.addPass(renderPass);

// 咱們的自定義着色器傳遞整個屏幕,以替換之前的渲染
let customPass = new ShaderPass({vertexShader,fragmentShader});
// 確保咱們正在渲染它
customPass.renderToScreen = true;
composer.addPass(customPass);

// 最後真正使用咱們的着色器渲染場景
composer.render()
// 而不是之前的 render()
// renderer.render(scene, camera);

但整個過程用一句話歸納就是,着色器被應用到了整個屏幕上。

接下來,讓咱們完成具備炫酷效果的最終着色器:

// 在鼠標周圍留一個小圓圈,並保持必定距離
float c = circle(uv, mouse, 0.0, 0.2);
// 獲取 3 次紋理,每次具備不一樣的偏移量,具體取決於鼠標速度:
float r = texture2D(tDiffuse, uv.xy += (mouseVelocity * .5)).x;
float g = texture2D(tDiffuse, uv.xy += (mouseVelocity * .525)).y;
float b = texture2D(tDiffuse, uv.xy += (mouseVelocity * .55)).z;
// 將全部內容合併到最終輸出
color = vec4(r, g, b, 1.);

您能夠在第一個演示中看到此結果。

將效果應用於多張圖像

屏幕具備不一樣尺寸,3D 圖像也各自具備尺寸。所以,咱們如今要作的是計算這二者之間的某種關係。

就像我同樣嗎?在上一篇文章中,咱們能夠製做一個寬度爲 1 的平面,並將其徹底適配屏幕寬度。因此實際上,咱們使用了: WidthOfPlane=ScreenSize

對於咱們的 Three.js 場景,這意味着若是要在屏幕上顯示 100px 寬的圖像,咱們將建立一個 Three.js 對象,其寬度爲 100*(WidthOfPlane/ScreenSize)。經過這種數學運算,咱們還能夠輕鬆設置一些邊距和位置。

頁面加載後,我將遍歷全部圖像,獲取它們的尺寸,並將它們添加到個人 3D 世界中:

let images = [...document.querySelectorAll('.js-image')];
images.forEach(image=>{
    // 如今,咱們有了圖像的大小和左邊、上邊的位置
    let dimensions = image.getBoundingClientRect();
    // 隱藏原始圖像
    image.style.visibility = hidden;
    // 根據其 HTML 將 3D 對象添加到場景中
    createMesh(dimensions);
})

如今,製做這個HTML-3D混合結構很是簡單。

關於 mouseVelocity 我想補充的是,我用它來改變效果的半徑,鼠標移動得越快,半徑越大。

要使其可滾動,咱們只須要移動整個場景便可,與滾動屏幕的數量相同。使用我以前提到的相同公式:NumberOfPixels*(WidthOfPlane/ScreenSize)

有時,WidthOfPlane 等於甚至更容易 ScreenSize。這樣,您最終在兩個世界中獲得的數字徹底相同!

探索不一樣的效果

使用不一樣的着色器,您可使用此方法產生任何效果。所以,我決定使用一些參數。

無需將圖像分爲三個顏色層,咱們能夠根據與鼠標的距離來簡單地移動圖像:

vec2 newUV = mix(uv, mouse, circle); 
color = texture2D(tDiffuse,newUV);

對於最後一個效果,我使用了一些隨機性,以在鼠標光標周圍得到像素化效果。

最後一個演示中,您能夠在效果之間切換以查看能夠進行的一些修改。有了「縮放」效果,我只使用了一個位移,可是在最後一箇中,我還對像素進行了隨機化,這對我來講看起來很酷!

很高興看到您對此動畫的想法。您將用這種技術產生什麼樣的效果?

GitHub 上找到這個項目。

相關文章
相關標籤/搜索