欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何用threejs實現(xiàn)實時多邊形折射

 更新時間:2021年05月07日 15:35:39   作者:淺笑·  
這篇文章主要介紹了如何用threejs實現(xiàn)實時多邊形折射,對three.js庫感興趣的同學(xué),可以參考下

前言

在本教程中,您將學(xué)習(xí)如何使用Three.js在三個步驟中使對象看起來像玻璃。

渲染3D對象時,無論使用某種3D軟件還是使用WebGL進行實時顯示,始終都必須為其分配材料以使其可見并具有所需的外觀。

可以使用Three.js之類的庫中的現(xiàn)成程序來模仿許多類型的材料,但是在本教程中,我將向您展示如何使用三個對象(三個步驟)使對象看起來像玻璃一樣。

步驟1:設(shè)定和正面折射

在本演示中,我將使用菱形幾何圖形,但是您可以跟隨一個簡單的盒子或任何其他幾何圖形。

讓我們建立我們的項目。我們需要一個渲染器,一個場景,一個透視相機和我們的幾何圖形。為了渲染我們的幾何圖形,我們需要為其分配材質(zhì)。創(chuàng)建此材料將是本教程的主要重點。因此,繼續(xù)創(chuàng)建具有基本頂點和片段著色器的新ShaderMaterial。

與您期望的相反,我們的材料將不是透明的,實際上,我們將對鉆石后面的任何東西進行采樣和變形。為此,我們需要將場景(沒有菱形)渲染為紋理。我只是使用正交攝影機渲染全屏平面,但這也可能是充滿其他對象的場景。在Three.js中從菱形分割背景幾何圖形的最簡單方法是使用“圖層”。

this.orthoCamera = new THREE.OrthographicCamera( width / - 2,width / 2, height / 2, height / - 2, 1, 1000 );
// assign the camera to layer 1 (layer 0 is default)
this.orthoCamera.layers.set(1);

const tex = await loadTexture('texture.jpg');
this.quad = new THREE.Mesh(new THREE.PlaneBufferGeometry(), new THREE.MeshBasicMaterial({map: tex}));

this.quad.scale.set(width, height, 1);
// also move the plane to layer 1
this.quad.layers.set(1);
this.scene.add(this.quad);

我們的渲染循環(huán)如下所示:

this.envFBO = new THREE.WebGLRenderTarget(width, height);

this.renderer.autoClear = false;

render() {
    requestAnimationFrame( this.render );

    this.renderer.clear();

    // render background to fbo
    this.renderer.setRenderTarget(this.envFbo);
    this.renderer.render( this.scene, this.orthoCamera );

    // render background to screen
    this.renderer.setRenderTarget(null);
    this.renderer.render( this.scene, this.orthoCamera );
    this.renderer.clearDepth();

    // render geometry to screen
    this.renderer.render( this.scene, this.camera );
};

好吧,現(xiàn)在該花一點點理論了。透明材料(如玻璃)可以彎曲,因此可見。那是因為光在玻璃中的傳播要比空氣中的傳播慢,因此當光波以一定角度撞擊玻璃物體時,這種速度變化會導(dǎo)致光波改變方向。波浪方向的這種變化描述了折射現(xiàn)象。

為了在代碼中復(fù)制這一點,我們將需要知道我們的眼睛向量與世界空間中鉆石表面(法線)向量之間的角度。讓我們更新頂點著色器以計算這些向量。

varying vec3 eyeVector;
varying vec3 worldNormal;

void main() {
    vec4 worldPosition = modelMatrix * vec4( position, 1.0);
    eyeVector = normalize(worldPos.xyz - cameraPosition);
    worldNormal = normalize( modelViewMatrix * vec4(normal, 0.0)).xyz;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

在片段著色器中,我們現(xiàn)在可以將eyeVector和worldNormal用作glsl內(nèi)置折射函數(shù)的前兩個參數(shù)。第三個參數(shù)是折射率的比率,即我們的快速介質(zhì)(空氣)的折射率(IOR)除以我們的慢速介質(zhì)(玻璃)的IOR。在這種情況下,該值為1.0 / 1.5,但是您可以調(diào)整該值以獲得所需的結(jié)果。例如,水的IOR為1.33,鉆石的IOR為2.42。

uniform sampler2D envMap;
uniform vec2 resolution;

varying vec3 worldNormal;
varying vec3 viewDirection;

void main() {
    // get screen coordinates
    vec2 uv = gl_FragCoord.xy / resolution;

    vec3 normal = worldNormal;
    // calculate refraction and add to the screen coordinates
    vec3 refracted = refract(eyeVector, normal, 1.0/ior);
    uv += refracted.xy;
    
    // sample the background texture
    vec4 tex = texture2D(envMap, uv);

    vec4 output = tex;
    gl_FragColor = vec4(output.rgb, 1.0);
}

真好!我們成功編寫了折射著色器。但是我們的鉆石幾乎看不見……部分原因是我們只處理了玻璃的一種視覺特性。并非所有的光都會穿過要折射的材料,實際上,一部分光會被反射。讓我們看看如何實現(xiàn)它!

步驟2:反射和菲涅耳方程

為了簡單起見,在本教程中,我們將不計算適當?shù)姆瓷洌鴥H將白色用作反射光?,F(xiàn)在,我們怎么知道什么時候該反思,什么時候該折射?理論上,這取決于材料的折射率,當入射矢量和表面法線之間的角度大于臨界角時,光波將被反射。

在片段著色器中,我們將使用菲涅耳方程來計算反射光線與折射光線之間的比率。不幸的是,glsl也沒有內(nèi)置此方程式,但是您可以從這里復(fù)制它:

float Fresnel(vec3 eyeVector, vec3 worldNormal) {
    return pow( 1.0 + dot( eyeVector, worldNormal), 3.0 );
}

現(xiàn)在,我們可以根據(jù)剛計算出的菲涅耳比,簡單地將折射紋理顏色與白色反射顏色混合。

uniform sampler2D envMap;
uniform vec2 resolution;

varying vec3 worldNormal;
varying vec3 viewDirection;

float Fresnel(vec3 eyeVector, vec3 worldNormal) {
    return pow( 1.0 + dot( eyeVector, worldNormal), 3.0 );
}

void main() {
    // get screen coordinates
    vec2 uv = gl_FragCoord.xy / resolution;

    vec3 normal = worldNormal;
    // calculate refraction and add to the screen coordinates
    vec3 refracted = refract(eyeVector, normal, 1.0/ior);
    uv += refracted.xy;

    // sample the background texture
    vec4 tex = texture2D(envMap, uv);

    vec4 output = tex;

    // calculate the Fresnel ratio
    float f = Fresnel(eyeVector, normal);

    // mix the refraction color and reflection color
    output.rgb = mix(output.rgb, vec3(1.0), f);

    gl_FragColor = vec4(output.rgb, 1.0);
}

看起來已經(jīng)好多了,但是還有一些不足之處……嗯,我們看不到透明對象的另一面。讓我們解決這個問題!

步驟3:多邊折射

到目前為止,我們已經(jīng)了解了有關(guān)反射和折射的知識,我們可以理解,光在離開對象之前可以在對象內(nèi)部來回反彈幾次。

為了獲得物理上正確的結(jié)果,我們將必須跟蹤每條光線,但是不幸的是,這種計算量太大,無法實時渲染。因此,我將向您展示一個簡單的近似值,至少可以直觀地看到我們鉆石的背面。

在一個片段著色器中,我們需要幾何圖形的正面和背面的世界法線。由于我們不能同時渲染兩側(cè),因此需要首先將背面法線渲染為紋理。

讓我們像在步驟1中一樣制作一個新的ShaderMaterial,但是這次我們將世界法線渲染為gl_FragColor。

varying vec3 worldNormal;

void main() {
    gl_FragColor = vec4(worldNormal, 1.0);
}

接下來,我們將更新渲染循環(huán)以包括背面通道。

this.backfaceFbo = new THREE.WebGLRenderTarget(width, height);

...

render() {
    requestAnimationFrame( this.render );

    this.renderer.clear();

    // render background to fbo
    this.renderer.setRenderTarget(this.envFbo);
    this.renderer.render( this.scene, this.orthoCamera );

    // render diamond back faces to fbo
    this.mesh.material = this.backfaceMaterial;
    this.renderer.setRenderTarget(this.backfaceFbo);
    this.renderer.clearDepth();
    this.renderer.render( this.scene, this.camera );

    // render background to screen
    this.renderer.setRenderTarget(null);
    this.renderer.render( this.scene, this.orthoCamera );
    this.renderer.clearDepth();

    // render diamond with refraction material to screen
    this.mesh.material = this.refractionMaterial;
    this.renderer.render( this.scene, this.camera );
};

現(xiàn)在,我們在折射材料中采樣背面法線紋理。

vec3 backfaceNormal = texture2D(backfaceMap, uv).rgb;

最后,我們結(jié)合了正面和背面法線。

float a = 0.33;
vec3 normal = worldNormal * (1.0 - a) - backfaceNormal * a;

在此等式中,a只是一個標量值,指示應(yīng)應(yīng)用背面法線的數(shù)量。

我們做到了!我們可以看到鉆石的所有側(cè)面,僅是因為我們對鉆石的材質(zhì)進行了折射和反射。

局限性

正如我已經(jīng)解釋的那樣,用這種方法實時渲染物理上正確的透明材料是不可能的。當在彼此前面渲染多個玻璃對象時會發(fā)生另一個問題。由于我們僅對環(huán)境采樣一次,因此無法看透一連串的對象。最后,我在這里演示的屏幕空間折射在畫布的邊緣附近效果不佳,因為光線可能會折射到其邊界之外的值,并且在將背景場景渲染到渲染目標時我們沒有捕獲到該數(shù)據(jù)。

當然,有多種方法可以克服這些限制,但是對于您在WebGL中進行實時渲染,它們可能并不是全部很好的解決方案。

以上就是如何用threejs實現(xiàn)實時多邊形折射的詳細內(nèi)容,更多關(guān)于JS庫的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • uniapp介紹與使用以及小程序?qū)崟r獲取視頻播放時間

    uniapp介紹與使用以及小程序?qū)崟r獲取視頻播放時間

    這篇文章主要給大家介紹了關(guān)于uniapp介紹與使用以及小程序?qū)崟r獲取視頻播放時間的相關(guān)資料,文中通過實例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用uniapp具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2023-02-02
  • 詳解瀏覽器緩存和webpack緩存配置

    詳解瀏覽器緩存和webpack緩存配置

    這篇文章主要介紹了瀏覽器緩存和webpack緩存配置,瀏覽器緩存分為兩種類型,強緩存和協(xié)商緩存,具體內(nèi)容詳情大家參考下本文
    2018-07-07
  • js實現(xiàn)上傳按鈕并顯示縮略圖小輪子

    js實現(xiàn)上傳按鈕并顯示縮略圖小輪子

    這篇文章主要為大家詳細介紹了js實現(xiàn)上傳按鈕并顯示縮略圖小輪子,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • JavaScript實現(xiàn)旋轉(zhuǎn)輪播圖

    JavaScript實現(xiàn)旋轉(zhuǎn)輪播圖

    這篇文章主要為大家詳細介紹了JavaScript實現(xiàn)旋轉(zhuǎn)輪播圖,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • JS面向?qū)ο缶幊虒崿F(xiàn)的Tab選項卡案例詳解

    JS面向?qū)ο缶幊虒崿F(xiàn)的Tab選項卡案例詳解

    這篇文章主要介紹了JS面向?qū)ο缶幊虒崿F(xiàn)的Tab選項卡,結(jié)合具體案例形式詳細分析了JS基于面向?qū)ο蟪绦蛟O(shè)計實現(xiàn)Tab選項卡的相關(guān)操作技巧,需要的朋友可以參考下
    2020-03-03
  • js實現(xiàn)input密碼框顯示/隱藏功能

    js實現(xiàn)input密碼框顯示/隱藏功能

    這篇文章主要為大家詳細介紹了js實現(xiàn)input密碼框顯示和隱藏功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-10-10
  • 使用JavaScript實現(xiàn)網(wǎng)頁版Pongo設(shè)計思路及源代碼分享

    使用JavaScript實現(xiàn)網(wǎng)頁版Pongo設(shè)計思路及源代碼分享

    Pongo是款很好玩的手欠類游戲,由于在玩的過程中發(fā)現(xiàn)了一些BUG,所以就打算自己也弄個,經(jīng)過一番思索,就有了本文,主要說說實現(xiàn)這款游戲的設(shè)計思路以及分享源碼給大家
    2014-06-06
  • JavaScript焦點事件、鼠標事件和滾輪事件使用詳解

    JavaScript焦點事件、鼠標事件和滾輪事件使用詳解

    這篇文章主要介紹了JavaScript焦點事件、鼠標事件和滾輪事件使用詳解,通過示例給大家講解的非常細致,有需要的小伙伴可以參考下。
    2016-01-01
  • javascript實現(xiàn)貪吃蛇經(jīng)典游戲

    javascript實現(xiàn)貪吃蛇經(jīng)典游戲

    這篇文章主要為大家詳細介紹了javascript實現(xiàn)貪吃蛇經(jīng)典游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-04-04
  • js innerHTML 的一些問題的解決方法

    js innerHTML 的一些問題的解決方法

    innerHTML 屬性的使用非常流行,因為他提供了簡單的方法完全替代一個 HTML 元素的內(nèi)容。另外一個方法是使用 DOM Level 2 API(removeChild, createElement, appendChild)。但很顯然,使用 innerHTML 修改 DOM tree 是非常容易且有效的方法。
    2008-06-06

最新評論