成都58手機(jī)微信網(wǎng)站建設(shè)名錄青島快速排名
受到一些很棒的 three.js 演示、與 covid 相關(guān)的旅行禁令以及可能在 pinterest 上花太多時間看美麗的旅行照片的啟發(fā)——我開始看看我是否可以使用 three.js 和r3f在瀏覽器中渲染一個令人信服的風(fēng)景場景。
推薦:將 NSDT場景編輯器 加入你的3D開發(fā)工具鏈。
在過去一個月左右的時間里,我一直在嘗試不同的方法,并在互聯(lián)網(wǎng)上搜索有關(guān)如何使用瀏覽器技術(shù)渲染半現(xiàn)實景觀的技巧。 我發(fā)現(xiàn)它很有價值,但也比我預(yù)期的要難得多。
我整理了一份簡短指南,總結(jié)了我從 A 到 B 所使用的技巧和技巧:
TLDR:
我使用特定于地形的自定義實用程序擴(kuò)展了 three.js 標(biāo)準(zhǔn)材質(zhì)。 這種經(jīng)過修改的材質(zhì)使用多種紋理來為地形著色、塑造和照亮地形。 我對這些紋理中的一些進(jìn)行平鋪和分層,以確保地形在更大的觀看距離范圍內(nèi)看起來不錯。
我選擇從一個名為 world creator 的圖形用戶界面生成和導(dǎo)出地形數(shù)據(jù)。在線演示可以訪問這里,源碼托管在GitHub。
1、技術(shù)
我將這篇文章結(jié)構(gòu)化為我用來改進(jìn)渲染的技術(shù)列表,而不是冗長的教程。 對于每一種技術(shù),我都會提供一個簡短的描述,如果你不熟悉它,這應(yīng)該可以幫助你定位。 我還將提供一個提示/技巧部分,該部分應(yīng)該闡明我如何在示例場景中使用該技術(shù)。 我希望這將使它對不同技能水平的人更容易瀏覽和有用。
動機(jī)限制:
- 你的幾何圖形越多,gpu 必須做的工作就越多。 如果沒有尖端的幾何虛擬化技術(shù),在當(dāng)前的硬件上不可能以 100% 的保真度實時渲染地形大小的對象。
- 我們可以在合理時間內(nèi)下載的紋理大小有限制
- 可以使用 webGL 移動到 gpu 內(nèi)存中的紋理紋理的大小是有限制的。 在舊手機(jī)上它可以低至 1024x1024
- webGL 允許你在單個對象上使用的紋理數(shù)量有限制(16)
工具:
- React(抱歉我忍不住)
- Three.js(很棒的3d渲染庫)
- React-three-fiber(讓你在 jsx 中使用 three.js)
- World Creator(用于制作渲染場景所需的各種紋理)
React + r3f 并不是絕對必要的,但將這些概念應(yīng)用到 vanilla three.js 設(shè)置中應(yīng)該很容易。 另請注意,World creator 不是免費的,但有很多替代方法可以生成高度圖等。
2、高度/位移圖
高度圖將地形的垂直高度編碼為從 0(黑色)到 1(白色)的像素值。 Three.js 內(nèi)置處理位移的代碼(頂點著色器)。 位移貼圖(displacement map)可用于 3d 對象,但對于風(fēng)景,我只使用了一個簡單的平面。 最主要的是確保你的比例與生成高度圖的任何程序或工具相匹配。 可以使用 displacementScale 和 displacementBias 來使 three.js 的比例匹配。
高度圖是這樣的圖像:
提示:對于高度圖中的每個像素,你至少需要對平面進(jìn)行一次細(xì)分,否則three.js 著色器將不會有一個關(guān)聯(lián)的頂點來定位。
技巧:除非相機(jī)靠近地面,否則高度圖不需要特別高分辨率。 在上面的示例中,我使用了 1024*1024 高度圖。 使地形看起來不錯的大部分細(xì)節(jié)都來自法線貼圖和漫反射貼圖。
限制:2d 高度圖無法表示 3d 地形細(xì)節(jié),如洞穴或懸垂。
3、大氣霧
過度簡化天空“是藍(lán)色的”,所以當(dāng)你看它實際上把它背后的東西染成藍(lán)色。 霧在 three.js 場景中是一種常見的技術(shù),但它在風(fēng)景中尤為重要,因為它可以幫助觀眾理解山丘、樹木等的規(guī)模和排列:
<fog attach="fog" args={["#74bbd0", 300, 1800]} />
注意:使用的材質(zhì)需要知道如何正確渲染霧。 如果你不擴(kuò)展 three.js 材質(zhì),這將不會自動工作。
4、HDRI 照明
HDRI(高動態(tài)范圍圖像)是一種環(huán)繞場景的大紋理,可作為 PBR 材質(zhì)的更逼真光源。 我喜歡光線具有更自然的方向和顏色,但找到適合我的場景的光線有點費力。 也就是說,我認(rèn)為這是值得的,因為照明設(shè)置是對場景質(zhì)量和氣氛影響最大的因素之一。
// I used the Environment component from Drei with one of the preset HDRIs
import { Environment } from "@react-three/drei"export default Landscape(){return (// landape mesh ect...// hdri<Environment preset="park" />)
}
提示:
- polyhaven 是 HDRI 的重要來源網(wǎng)站。
- 嘗試使用不同的 HDRI 和環(huán)境光級別。
技巧:嘗試使用標(biāo)準(zhǔn)網(wǎng)格材質(zhì)的 normalScale、 envMapIntensity 和 metalness 參數(shù)。 它們使你可以對對比度、顏色深度和亮度進(jìn)行大量控制。
5、SPLAT紋理
對于大多數(shù) 3d 對象,我們使用稱為漫反射紋理的單一紋理來應(yīng)用顏色。 如果我們放大到接近地形大小的對象,這很快就會崩潰,因為每次將其尺寸加倍時,圖像的文件大小/內(nèi)存影響不會線性縮放。 在地形大小的對象上應(yīng)用單個漫反射紋理會變得模糊或過大。
從邏輯上講,我們應(yīng)該能夠通過混合和重復(fù)多個較小的紋理來解決這個問題,每個紋理對應(yīng)一種地形類型(草地、泥地、巖石等)。
紋理splat是一種方法。 它的工作原理是獲取多個紋理并將它們與另一個稱為 splat 紋理的紋理中的顏色通道相關(guān)聯(lián)。 splat 紋理中的一個像素被渲染為 100x100 圖像 - 取決于我們的紋理大小和它們重復(fù)的次數(shù) - 使得 splat 紋理明顯小于相同細(xì)節(jié)級別的漫反射紋理。
這是 splat 紋理:
例如,如果 splat 中的像素是純紅色,那么我們將該區(qū)域渲染為泥土,如果它是純綠色,則將其渲染為草地,如果它是紅色和綠色的混合,那么我們可以線性組合瓷磚紋理的 rgb 值以 更自然地融合在草地和泥濘地區(qū)之間。 混合對于防止它看起來像 Minecraft 地圖至關(guān)重要。
// inside the main function of the fragment shader ...
vec4 diffuse1 = texture2D(uDiffuse1, uv * 100.0);
vec4 diffuse2 = texture2D(uDiffuse2, uv * 100.0);
vec4 splat1 = texture2D(uSplat1, uv);vec4 color = diffuse1 * splat1.r + diffuse2 * splat1.g;diffuseColor = vec4( color.rgb, opacity );
提示:World creator 以 TGA 格式導(dǎo)出 splat 紋理,我以前從未聽說過這種格式,并且發(fā)現(xiàn)使用起來有些困難,但幸運的是,Three.js 示例包含一個 TGA 加載器,可以處理將 .tga 文件加載到數(shù)據(jù)紋理中。
局限性:紋理平鋪會導(dǎo)致明顯的紋理重復(fù),我們將在下面解決:
6、隱藏紋理重復(fù)
雖然這樣做很容易,但沒有什么要求我們將紋理放置在會產(chǎn)生不良平鋪效果的網(wǎng)格圖案類型中。 Inigo Quilez 有一篇關(guān)于修復(fù)紋理重復(fù)的多種方法的好文章,我在我的場景中使用了它。
簡單的說,該技術(shù)本質(zhì)上歸結(jié)為使用隨機(jī)噪聲紋理和一些數(shù)學(xué)來更有機(jī)地在網(wǎng)格表面上無縫地放置和混合紋理。
// use instead of texture2D
vec4 textureNoTile( sampler2D samp, vec2 uv ){// sample variation patternfloat k = texture2D( uNoise, 0.005*uv ).x; // cheap (cache friendly) lookup// compute indexfloat l = k*8.0;float f = fract(l);float ia = floor(l);float ib = ia + 1.0;// offsets for the different virtual patternsfloat v = 0.4;vec2 offa = sin(vec2(3.0,7.0)*ia); // can replace with any other hashvec2 offb = sin(vec2(3.0,7.0)*ib); // can replace with any other hash// compute derivatives for mip-mappingvec2 dx = dFdx(uv), dy = dFdy(uv);// sample the two closest virtual patternsvec3 cola = texture2DGradEXT( samp, uv + v*offa, dx, dy ).xyz;vec3 colb = texture2DGradEXT( samp, uv + v*offb, dx, dy ).xyz;// // interpolate between the two virtual patternsvec3 col = mix( cola, colb, smoothstep(0.2,0.8,f-0.1*sum(cola-colb)) );return vec4(col,1.0);
}
7、法線貼圖
他們讓我們在給定點對表面的“正常”角度進(jìn)行編碼。 這有點 hack,但它有助于確定從該物體反射到相機(jī)的光量,使我們能夠平滑或創(chuàng)建額外的細(xì)節(jié),而無需渲染額外的三角形。 同樣,這是 THREE.js 物理材質(zhì)中內(nèi)置的內(nèi)容。
提示:在巖石等細(xì)節(jié)物體上使用法線貼圖比使用你能找到的最高分辨率漫反射紋理重要得多。
8、多層次的法線
World Creator 導(dǎo)出整個地形的法線貼圖,但也導(dǎo)出我們在景觀中平鋪的巖石紋理的法線貼圖:
理想情況下,場景將受益于宏觀懸崖面法線以及水平法線的更細(xì)微的凹凸和裂縫。 不幸的是,如果只是將法線紋理添加在一起,它們會變得渾濁和/或在某些地方會出現(xiàn)超級亮點或暗點。
Stephen Hill 對問題和可能的解決方案有很好的總結(jié) , 它給著色器增加了一些復(fù)雜性,但在我看來這是值得的:
// adapted from original article so more than two normals can be blended
vec4 blend_normals(vec4 n1, vec4 n2){vec3 t = n1.xyz*vec3( 2, 2, 2) + vec3(-1, -1, 0);vec3 u = n2.xyz*vec3(-2, -2, 2) + vec3( 1, 1, -1);vec3 r = t*dot(t, u) /t.z -u;return vec4((r), 1.0) * 0.5 + 0.5;
}//usage:
vec4 blend = blend_normals(n1, n2);
vec4 triblend = blend_normals(blend, n3);
提示:當(dāng)以不同的比例重復(fù)時,使用多個法線往往效果最好。 盡管上述技術(shù)在數(shù)學(xué)上比簡單的線性組合更正確,但我發(fā)現(xiàn)應(yīng)用多個相同大小的法線最終看起來就像隨機(jī)噪聲。 在演示巖石中,我使用一個大的用于景觀細(xì)節(jié),一個中等的用于巖石中的峭壁,一個小的用于額外的凹凸紋理。
9、未來的改進(jìn)
實施基于四叉樹的細(xì)節(jié)系統(tǒng)和紋理流以在相機(jī)附近渲染更高清晰度的紋理可以提高質(zhì)量并減少初始加載時間。
用于渲染搖曳的草地或渲染攝像機(jī)附近的苔蘚的植被系統(tǒng)也有助于提高場景的保真度,使你可以使用第三人稱控制器在地圖上四處走動。
原文鏈接:Three渲染真實景觀 — BimAnt