iT邦幫忙

2025 iThome 鐵人賽

DAY 10
0
Modern Web

WebXR未來新視界:Babylon.js打造Web的VR/AR/XR體驗系列 第 10

[Day10] 認識模型 — 常用的模型格式類型和如何引入外部模型

  • 分享至 

  • xImage
  •  

Babylon.js常用的模型格式類型

Babylon.js 支援多種 3D 檔案格式,但建議優先使用glTF (.gltf) / GLB (.glb),以下是幾種常見的檔案類型。

glTF(.gltf) 被譽為「3D 界的 JPEG」glTF 本身是一個 JSON 檔案,包含一個 .bin 檔案加上多個紋理圖片檔。它是一個為了在網路上高效傳輸和載入 3D 場景而設計的開放標準格式。它支援 PBR (物理基礎渲染)和動畫高效、可擴展、可互通的 3D 內容網路標準格式。不僅僅包含模型的外觀,還可以打包:

  • 幾何體 (Geometry):模型的形狀。
  • 材質與紋理 (Materials & Textures):模型的外觀、顏色、金屬感、粗糙度等。
  • 動畫 (Animations):骨骼動畫、形變動畫等。
  • 骨架 (Skeletons):用於驅動角色動畫。
  • 整個場景結構

GLB(.glb)是將所有glTF相關內容打包成一個檔案。對於網頁應用,建議使用GLB。

OBJ (.obj)是個比較老的格式,不支援 PBR (物理基礎渲染)、不支援動畫。不適合需要動畫或複雜材質的網頁應用。

Babylon (.babylon)是元老級的 Babylon.js 專案用的原生場景檔案格式,這種檔案格式會 包含3D 場景的所有細節,且只支援Babylon.js 的專案。在官網教學裡有時會看到這種格式的範例,但目前官方與社群都強烈推薦使用 glTF (.gltf)。知道這個是就範例就好,請盡量不要使用。

就像寫網頁可以用css畫出圖案,也可以使用圖檔一樣。模型來源有兩種:引入外部模型用基本模型去組裝。一般由於效能和藝術細節複雜度考量,主要會用引入外部模型 的方式。

外部模型怎麼來

模型是由建模軟體製作出來,常聽到的有Blender、Maya、3ds Max、Cinema 4D、SketchUp。

或是有像是在SketchfabTurboSquid 這樣龐大的 3D 模型市集,提供了數百萬個現成的模型。這讓開發者可以快速地搭建場景,而不需要事事都從零開始。
https://sketchfab.com/feed

https://www.turbosquid.com/

3D模型的檔案格式並不能像jpg檔一樣直接用瀏覽器開啟。使用之前可以丟進babylon.js模型預覽沙盒確認模型正確性。也可以透過他直接將 FBX, OBJ, STL 等多種格式的檔案拖曳進去,自動轉換匯出為 .glb 檔案來使用。
babylon.js模型預覽沙盒:https://sandbox.babylonjs.com/
在上傳的時候要注意要把整個資料夾的檔案都框起來,拖曳丟進去一次上傳就應該要能讀出來。

在本地專案引入本地端的外部模型

我們延續(Feature - 單元一)的範例二程式碼,並做以下修改,執行後我們可以看到一個房子:

   // 註解這段
   // BABYLON.MeshBuilder.CreateBox("box", {});
   
   // 加上這段     
    BABYLON.ImportMeshAsync("https://assets.babylonjs.com/meshes/both_houses_scene.babylon", scene, {
        meshNames: "semi_house"
    });

ImportMeshAsync是個常見用來載入3D模型最主要、最常用的指令
這邊可以注意它是**回傳Promise,**目的就是確保先把模型先載入完成,因為模型比較大卻同時是主要要互動的對象,等他好再開始也是合理的吧。引入的方法參數中,

ImportMeshAsync(
    source: SceneSource,
    scene: Scene,
    options?: ImportMeshOptions,
): Promise<ISceneLoaderAsyncResult>

interface ImportMeshOptions {
    meshNames?: null | string | readonly string[];
    name?: string;
    onProgress?: (event: ISceneLoaderProgressEvent) => void;
    pluginExtension?: string;
    pluginOptions?: {
        bvh?: { enabled?: boolean; loopMode?: number };
        gltf?: {
            alwaysComputeBoundingBox?: boolean;
            alwaysComputeSkeletonRootNode?: boolean;
            animationStartMode?: GLTFLoaderAnimationStartMode;
            capturePerformanceCounters?: boolean;
            compileMaterials?: boolean;
            compileShadowGenerators?: boolean;
            coordinateSystemMode?: GLTFLoaderCoordinateSystemMode;
            createInstances?: boolean;
            customRootNode?: Nullable<TransformNode>;
            enabled?: boolean;
            extensionOptions?: {
                EXT_lights_ies?: { enabled?: boolean };
                EXT_lights_image_based?: { enabled?: boolean };
                EXT_materials_diffuse_roughness?: { enabled?: boolean };
                EXT_mesh_gpu_instancing?: { enabled?: boolean };
                EXT_meshopt_compression?: { enabled?: boolean };
                EXT_texture_avif?: { enabled?: boolean };
                EXT_texture_webp?: { enabled?: boolean };
                ExtrasAsMetadata?: { enabled?: boolean };
                KHR_animation_pointer?: { enabled?: boolean };
                KHR_draco_mesh_compression?: { enabled?: boolean };
                KHR_interactivity?: { enabled?: boolean };
                KHR_lights_punctual?: { enabled?: boolean };
                KHR_materials_anisotropy?: { enabled?: boolean };
                KHR_materials_clearcoat?: { enabled?: boolean };
                KHR_materials_diffuse_transmission?: { enabled?: boolean };
                KHR_materials_dispersion?: { enabled?: boolean };
                KHR_materials_emissive_strength?: { enabled?: boolean };
                KHR_materials_ior?: { enabled?: boolean };
                KHR_materials_iridescence?: { enabled?: boolean };
                KHR_materials_pbrSpecularGlossiness?: { enabled?: boolean };
                KHR_materials_sheen?: { enabled?: boolean };
                KHR_materials_specular?: { enabled?: boolean };
                KHR_materials_transmission?: { enabled?: boolean };
                KHR_materials_unlit?: { enabled?: boolean };
                KHR_materials_variants?: {
                    defaultVariant?: string;
                    enabled?: boolean;
                    onLoaded?: (controller: MaterialVariantsController) => void;
                };
                KHR_materials_volume?: { enabled?: boolean };
                KHR_mesh_quantization?: { enabled?: boolean };
                KHR_node_hoverability?: { enabled?: boolean };
                KHR_node_selectability?: { enabled?: boolean };
                KHR_node_visibility?: { enabled?: boolean };
                KHR_texture_basisu?: { enabled?: boolean };
                KHR_texture_transform?: { enabled?: boolean };
                KHR_xmp_json_ld?: { enabled?: boolean };
                MSFT_audio_emitter?: { enabled?: boolean };
                MSFT_lod?: { enabled?: boolean; maxLODsToLoad?: number };
                MSFT_minecraftMesh?: { enabled?: boolean };
                MSFT_sRGBFactors?: { enabled?: boolean };
                [key: string]: undefined | { enabled?: boolean; [key: string]: unknown };
            };
            loadAllMaterials?: boolean;
            loadMorphTargets?: boolean;
            loadNodeAnimations?: boolean;
            loadOnlyMaterials?: boolean;
            loadSkins?: boolean;
            loggingEnabled?: boolean;
            onCameraLoaded?: (camera: Camera) => void;
            onMaterialLoaded?: (material: Material) => void;
            onMeshLoaded?: (mesh: AbstractMesh) => void;
            onParsed?: (loaderData: IGLTFLoaderData) => void;
            onSkinLoaded?: (node: TransformNode, skinnedNode: TransformNode) => void;
            onTextureLoaded?: (texture: BaseTexture) => void;
            onValidated?: (results: IGLTFValidationResults) => void;
            preprocessUrlAsync?: (url: string) => Promise<string>;
            skipMaterials?: boolean;
            targetFps?: number;
            transparencyAsCoverage?: boolean;
            useClipPlane?: boolean;
            useGltfTextureNames?: boolean;
            useRangeRequests?: boolean;
            useSRGBBuffers?: boolean;
            validate?: boolean;
        };
        obj?: {
            computeNormals?: boolean;
            enabled?: boolean;
            importVertexColors?: boolean;
            invertTextureY?: boolean;
            invertY?: boolean;
            materialLoadingFailsSilently?: boolean;
            optimizeNormals?: boolean;
            optimizeWithUV?: boolean;
            skipMaterials?: boolean;
            useLegacyBehavior?: boolean;
            UVScaling?: Vector2;
        };
        splat?: { enabled?: boolean; flipY?: boolean; keepInRam?: boolean };
        stl?: { enabled?: boolean };
        [key: string]: undefined | { enabled?: boolean; [key: string]: unknown };
    };
    rootUrl?: string;
}

更詳細的文件在這裡

接下來下篇來說說用基本模型組裝的模型。


上一篇
[Day09] 用Babylon.js在網頁上放置模型(Feature - 單元一)
下一篇
[Day11] 用Babylon.js基礎模型組裝模型時常用的操作(Feature - 單元二)
系列文
WebXR未來新視界:Babylon.js打造Web的VR/AR/XR體驗12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言