博客 / 詳情

返回

JSAPIThree 渲染系統學習筆記:讓場景動起來

作為一個剛開始學習 mapvthree 的小白,今天要學習渲染系統了!聽説這個系統可以控制場景的渲染方式,還能開啓動畫循環、配置渲染特效等!想想就激動!

第一次聽説渲染系統

今天在文檔裏看到了 engine.rendering 這個詞,一開始我還以為是用來畫圖的,結果查了一下才知道,原來這是用來控制場景渲染的模塊!

文檔説渲染系統可以:

  • 控制動畫循環
  • 配置渲染特效(如 Bloom、抗鋸齒等)
  • 監聽渲染事件
  • 監控渲染性能
  • 控制渲染質量

我的理解:簡單説就是控制"怎麼渲染場景",比如要不要每幀都渲染、用什麼特效、怎麼優化性能等!

第一步:發現引擎的渲染屬性

作為一個初學者,我習慣先看看引擎有哪些屬性。文檔説 engine.rendering 就是渲染管理器!

我的發現:原來引擎創建後,就自動有了一個渲染管理器對象!不需要手動創建,直接用就行!

import * as mapvthree from '@baidumap/mapv-three';

const container = document.getElementById('container');
const engine = new mapvthree.Engine(container);

// 渲染管理器已經自動創建了!
console.log(engine.rendering); // 可以訪問渲染管理器

我的理解engine.rendering 就是渲染系統的入口,所有渲染相關的操作都通過它來完成。

第二步:開啓循環渲染

文檔説可以通過 engine.rendering.enableAnimationLoop 來開啓循環渲染。我試了試:

// 開啓循環渲染
engine.rendering.enableAnimationLoop = true;

我的疑問:什麼是循環渲染?為什麼要開啓?

看了文檔才知道,默認情況下,引擎只在需要時渲染(比如拖動地圖時)。開啓循環渲染後,引擎會每幀都渲染,適合有動畫的場景。

我的嘗試:我寫了個簡單的測試:

import * as mapvthree from '@baidumap/mapv-three';

const container = document.getElementById('container');

const engine = new mapvthree.Engine(container, {
    map: {
        center: [116.404, 39.915],
        range: 1000,
    },
    rendering: {
        enableAnimationLoop: true, // 開啓循環渲染
    },
});

我的發現:開啓後,如果有動畫效果(比如飛線、粒子等),會持續播放!

我的理解

  • 默認關閉:只在需要時渲染,節省性能
  • 開啓後:每幀都渲染,適合有動畫的場景

我的注意:如果場景沒有動畫,建議不要開啓,可以節省性能!

第三步:設置幀率

看到循環渲染後,我開始好奇:能不能控制渲染的幀率?

文檔説可以通過 engine.rendering.animationLoopFrameTime 來設置幀時間間隔!

// 設置幀時間間隔為 16 毫秒(約 60 FPS)
engine.rendering.animationLoopFrameTime = 16;

我的理解animationLoopFrameTime 是每幀之間的時間間隔(毫秒),默認是 16 毫秒,約等於 60 FPS。

我的嘗試

// 60 FPS(默認)
engine.rendering.animationLoopFrameTime = 16;

// 30 FPS
engine.rendering.animationLoopFrameTime = 33;

// 120 FPS(更快,但更耗性能)
engine.rendering.animationLoopFrameTime = 8;

我的發現

  • 16:約 60 FPS,流暢
  • 33:約 30 FPS,稍慢但省性能
  • 8:約 120 FPS,很快但耗性能

我的想法:如果做性能優化,可以降低幀率來節省性能!

第四步:監聽渲染事件

看到循環渲染後,我想:能不能在渲染時執行一些操作?

文檔説可以用 engine.rendering.addPrepareRenderListener() 來監聽渲染事件!

engine.rendering.addPrepareRenderListener((engine, renderState) => {
    // 每幀渲染前都會執行這裏的代碼
    console.log('正在渲染...');
});

我的理解addPrepareRenderListener() 會在每幀渲染前調用,可以在這裏執行一些操作。

我的嘗試

// 監聽渲染事件
engine.rendering.addPrepareRenderListener((engine, renderState) => {
    // 獲取當前視野距離
    const range = engine.map.getRange();
    
    // 根據視野距離控制模型的顯示隱藏
    if (range > 1000) {
        // 視野太遠,隱藏模型
        mesh.visible = false;
    } else {
        // 視野較近,顯示模型
        mesh.visible = true;
    }
});

我的發現:可以在渲染時根據場景狀態動態調整物體!

我的想法:如果做性能優化,可以用這個方法來根據視野距離控制模型的顯示!

檢查視野是否變化

文檔説可以用 renderState.viewChanged 來判斷視野是否變化!

engine.rendering.addPrepareRenderListener((engine, renderState) => {
    if (renderState.viewChanged) {
        // 只有視野變化時才執行
        console.log('視野變化了!');
    }
});

我的理解viewChanged 可以判斷視野是否變化,避免不必要的操作。

我的發現:配合 viewChanged 使用,可以減少無效的回調調用,提升性能!

我的建議:使用 addPrepareRenderListener 時,建議配合 viewChanged 檢查,避免無效回調!

第五步:瞭解渲染特性

看到渲染監聽後,我開始好奇:什麼是渲染特性?

文檔説可以通過 engine.rendering.features 來配置渲染特性,比如抗鋸齒、Bloom 等!

開啓抗鋸齒

// 開啓抗鋸齒
engine.rendering.features.antialias.enabled = true;
engine.rendering.features.antialias.method = 'smaa';

我的理解:抗鋸齒可以讓畫面更平滑,減少鋸齒感。

我的發現:默認是開啓的,所以一般不需要手動設置。

開啓 Bloom 泛光

// 開啓 Bloom 泛光
engine.rendering.features.bloom.enabled = true;
engine.rendering.features.bloom.strength = 0.1;
engine.rendering.features.bloom.threshold = 1;
engine.rendering.features.bloom.radius = 0;

我的理解:Bloom 可以讓亮的地方更亮,產生泛光效果。

我的發現:這個功能我在之前的 Bloom 學習筆記裏學過,這裏可以統一配置!

第六步:設置像素比

看到渲染特性後,我想:能不能控制渲染的分辨率?

文檔説可以通過 engine.rendering.pixelRatio 來設置設備像素比!

// 設置像素比
engine.rendering.pixelRatio = 1.0;

我的理解pixelRatio 控制渲染的分辨率,默認是 window.devicePixelRatio

我的嘗試

// 正常分辨率(默認)
engine.rendering.pixelRatio = window.devicePixelRatio;

// 降低分辨率(節省性能)
engine.rendering.pixelRatio = 1.0;

// 提高分辨率(更清晰,但更耗性能)
engine.rendering.pixelRatio = 2.0;

我的發現

  • 1.0:標準分辨率,性能好
  • 2.0:高分辨率,更清晰但耗性能
  • window.devicePixelRatio:根據設備自動調整(默認)

我的想法:如果做性能優化,可以降低像素比來提升性能!

第七步:使用高質量緩衝區

看到像素比後,我開始好奇:什麼是高質量緩衝區?

文檔説可以通過 engine.rendering.useHighPrecisionBuffer 來開啓高質量緩衝區!

// 開啓高質量緩衝區
engine.rendering.useHighPrecisionBuffer = true;

我的理解:高質量緩衝區可以提升畫面質量,但會增加性能開銷。

我的發現

  • 默認是 false,使用低質量緩衝區
  • 開啓後使用高質量緩衝區,畫面更清晰
  • 某些渲染特性(如 Bloom、反射)會自動開啓

我的嘗試

// 開啓高質量緩衝區
engine.rendering.useHighPrecisionBuffer = true;

我的發現:開啓後,畫面質量確實提升了,但是性能也下降了。

我的建議:只有在需要高質量畫面或使用高級渲染特性時才開啓!

第八步:使用調試模式

看到高質量緩衝區後,我想:有沒有調試模式?

文檔説可以用 engine.rendering.debugMode 來開啓調試模式!

// 開啓調試模式
engine.rendering.debugMode = 1; // DEBUG_MODE_MESH

我的理解:調試模式可以顯示渲染的詳細信息,方便調試。

我的發現

  • 0:無調試模式(默認)
  • 1:網格調試模式
  • 2:材質調試模式
  • 3:物體調試模式

我的嘗試

// 開啓網格調試模式
engine.rendering.debugMode = 1;

我的發現:開啓後,可以看到網格的詳細信息,方便調試!

我的注意:調試模式主要用於開發,生產環境建議關閉!

第九步:凍結更新

看到調試模式後,我想:能不能暫停渲染更新?

文檔説可以用 engine.rendering.freezeUpdate 來凍結更新!

// 凍結更新
engine.rendering.freezeUpdate = true;

// 恢復更新
engine.rendering.freezeUpdate = false;

我的理解:凍結更新後,場景不會更新,但可以繼續交互。

我的嘗試

// 凍結更新
engine.rendering.freezeUpdate = true;

// 做一些操作...

// 恢復更新
engine.rendering.freezeUpdate = false;

我的發現:凍結後,場景會停止更新,適合做批量操作!

我的想法:如果做批量更新,可以先凍結,更新完再恢復,可以提升性能!

第十步:獲取渲染對象

看到這麼多屬性後,我想:能不能直接訪問底層的渲染對象?

文檔説可以通過 engine.rendering.rendererengine.rendering.sceneengine.rendering.camera 來訪問底層對象!

// 獲取渲染器
const renderer = engine.rendering.renderer;

// 獲取場景
const scene = engine.rendering.scene;

// 獲取相機
const camera = engine.rendering.camera;

我的理解:這些是 Three.js 的底層對象,可以直接操作。

我的發現:可以直接訪問 Three.js 對象,可以做更底層的操作!

我的注意:直接操作底層對象需要了解 Three.js,建議謹慎使用!

獲取渲染分辨率

文檔説可以用 engine.rendering.resolution 來獲取渲染分辨率!

// 獲取渲染分辨率
const resolution = engine.rendering.resolution;
console.log(resolution); // 分辨率信息

我的發現:可以獲取當前渲染的分辨率,方便做適配!

獲取動畫管理器

文檔説可以用 engine.rendering.animation 來獲取動畫管理器!

// 獲取動畫管理器
const animation = engine.rendering.animation;

我的理解:動畫管理器可以用來控制場景中的動畫效果。

我的發現:可以通過動畫管理器來統一管理場景中的動畫!

獲取標籤實例

文檔説可以用 engine.rendering.label 來獲取標籤實例!

// 獲取標籤實例
const label = engine.rendering.label;

我的理解:標籤實例可以用來管理場景中的標籤。

我的發現:可以通過標籤實例來統一管理場景中的標籤!

獲取拾取器實例

文檔説可以用 engine.rendering.picking 來獲取拾取器實例!

// 獲取拾取器實例
const picking = engine.rendering.picking;

我的理解:拾取器可以用來檢測鼠標點擊的對象。

我的發現:可以通過拾取器來實現點擊交互功能!

獲取天氣實例

文檔説可以用 engine.rendering.weather 來獲取天氣實例!

// 獲取天氣實例
const weather = engine.rendering.weather;

我的理解:如果場景中有天氣效果,可以通過這個屬性訪問。

我的發現:可以獲取天氣實例,然後控制天氣效果!

獲取天空實例

文檔説可以用 engine.rendering.sky 來獲取天空實例!

// 獲取天空實例
const sky = engine.rendering.sky;

我的理解:如果場景中有天空,可以通過這個屬性訪問。

我的發現:可以獲取天空實例,然後控制天空效果!

第十一步:獲取渲染狀態

看到底層對象後,我想:能不能獲取渲染狀態?

文檔説可以用 engine.rendering.renderState 來獲取渲染狀態!

// 獲取渲染狀態
const renderState = engine.rendering.renderState;

// 檢查視野是否變化
if (renderState.viewChanged) {
    console.log('視野變化了!');
}

我的理解renderState 保存了渲染狀態信息,比如視野變化、相機位置等。

我的發現:可以在渲染監聽中使用 renderState 來獲取狀態信息!

第十二步:自動偏移相機和場景

看到渲染狀態後,我想:什麼是自動偏移?

文檔説可以通過 engine.rendering.autoOffsetRelativeCenter 來設置自動偏移!

// 開啓自動偏移
engine.rendering.autoOffsetRelativeCenter = true;

我的理解:自動偏移可以讓相機和場景自動偏移,使 worldMatrix 偏移量較小,提升精度。

我的發現:開啓後,可以提升座標精度,適合大範圍場景!

我的注意:這個功能主要用於大範圍場景,一般場景不需要開啓!

第十三步:保留繪圖緩衝區

看到自動偏移後,我想:什麼是保留繪圖緩衝區?

文檔説可以通過 contextParameters.preserveDrawingBuffer 來設置保留繪圖緩衝區!

const engine = new mapvthree.Engine(container, {
    rendering: {
        contextParameters: {
            preserveDrawingBuffer: true, // 開啓保留繪圖緩衝區
        },
    },
});

我的理解:保留繪圖緩衝區可以讓截圖功能正常工作。

我的發現:如果要做截圖功能,需要開啓這個選項!

我的嘗試

const engine = new mapvthree.Engine(container, {
    rendering: {
        contextParameters: {
            preserveDrawingBuffer: true, // 開啓截圖功能
        },
    },
});

// 截圖
const dataURL = engine.rendering.renderer.domElement.toDataURL();

我的發現:開啓後,可以正常截圖了!

我的注意:開啓後會稍微影響性能,只在需要截圖時開啓!

第十四步:初始化時配置渲染

看到這麼多屬性後,我想:能不能在創建引擎時就配置渲染?

文檔説可以在引擎初始化時通過 rendering 配置項來設置渲染參數!

const engine = new mapvthree.Engine(container, {
    map: {
        center: [116.404, 39.915],
        range: 1000,
    },
    rendering: {
        enableAnimationLoop: true,
        animationLoopFrameTime: 16,
        pixelRatio: 1.0,
        features: {
            antialias: {
                enabled: true,
                method: 'smaa',
            },
            bloom: {
                enabled: true,
                strength: 0.1,
                threshold: 1,
                radius: 0,
            },
        },
    },
});

我的發現:可以在初始化時一次性配置所有渲染參數,更方便!

我的理解

  • enableAnimationLoop:是否開啓循環渲染
  • animationLoopFrameTime:幀時間間隔
  • pixelRatio:像素比
  • features:渲染特性配置

我的嘗試

const engine = new mapvthree.Engine(container, {
    map: {
        center: [116.404, 39.915],
        range: 1000,
    },
    rendering: {
        enableAnimationLoop: true,
        animationLoopFrameTime: 16,
        features: {
            bloom: {
                enabled: true,
            },
        },
    },
});

我的發現:初始化時就配置好了,代碼更簡潔!

第十五步:實際應用場景

學到這裏,我開始想:渲染系統能用在什麼地方呢?

場景 1:動畫場景

如果場景中有動畫效果(如飛線、粒子等),需要開啓循環渲染:

engine.rendering.enableAnimationLoop = true;

我的想法:這樣動畫才能持續播放!

場景 2:性能優化

如果場景性能不好,可以優化渲染設置:

// 降低幀率
engine.rendering.animationLoopFrameTime = 33;

// 降低像素比
engine.rendering.pixelRatio = 1.0;

// 關閉不必要的特效
engine.rendering.features.bloom.enabled = false;

我的想法:這樣可以提升性能,讓場景更流暢!

場景 3:根據視野控制顯示

如果場景中有很多模型,可以根據視野距離控制顯示:

engine.rendering.addPrepareRenderListener((engine, renderState) => {
    if (renderState.viewChanged) {
        const range = engine.map.getRange();
        
        // 根據視野距離控制模型顯示
        models.forEach(model => {
            model.visible = range < 5000;
        });
    }
});

我的想法:這樣可以根據視野距離動態控制模型,提升性能!

第十六步:做一個完整的示例

我想寫一個完整的示例,把學到的都用上:

import * as mapvthree from '@baidumap/mapv-three';
import {Mesh, BoxGeometry, MeshStandardMaterial, Color} from 'three';

const container = document.getElementById('container');

const engine = new mapvthree.Engine(container, {
    map: {
        center: [116.404, 39.915],
        range: 1000,
        pitch: 60,
    },
    rendering: {
        enableAnimationLoop: true,
        animationLoopFrameTime: 16,
        pixelRatio: 1.0,
        features: {
            bloom: {
                enabled: true,
                strength: 0.1,
            },
        },
    },
});

// 創建一個模型
const geometry = new BoxGeometry(50, 50, 50);
const material = new MeshStandardMaterial({
    color: new Color(2, 2, 0),
});
const mesh = new Mesh(geometry, material);
engine.add(mesh);

const position = engine.map.projectArrayCoordinate([116.404, 39.915]);
mesh.position.set(position[0], position[1], 0);

// 監聽視野變化,控制模型顯示
engine.rendering.addPrepareRenderListener((engine, renderState) => {
    if (renderState.viewChanged) {
        const range = engine.map.getRange();
        
        // 視野太遠時隱藏模型
        if (range > 1000) {
            mesh.visible = false;
        } else {
            mesh.visible = true;
        }
    }
});

我的感受:寫一個完整的示例,把學到的都用上,感覺很有成就感!

我的發現

  • 可以控制渲染循環
  • 可以配置渲染特性
  • 可以監聽渲染事件
  • 可以監控渲染性能

雖然代碼還很簡單,但是已經能做出一個基本的渲染控制系統了!

第十七步:踩過的坑

作為一個初學者,我踩了不少坑,記錄下來避免再犯:

坑 1:動畫不播放

原因:沒有開啓循環渲染。

解決:設置 engine.rendering.enableAnimationLoop = true

坑 2:性能不好

原因:開啓了循環渲染但沒有動畫,或者像素比太高。

解決

  1. 如果沒有動畫,不要開啓循環渲染
  2. 降低像素比
  3. 降低幀率

坑 3:修改數據後場景不更新

原因:沒有開啓循環渲染,或者需要手動請求渲染。

解決

  1. 開啓循環渲染(如果有動畫)
  2. 或者調用 engine.requestRender() 手動請求渲染

坑 4:渲染監聽回調執行太頻繁

原因:沒有檢查 viewChanged,導致每次渲染都執行。

解決:配合 renderState.viewChanged 檢查,只在視野變化時執行。

坑 5:高質量緩衝區影響性能

原因:開啓了高質量緩衝區但沒有必要。

解決:只有在需要高質量畫面或使用高級渲染特性時才開啓。

坑 6:截圖功能不工作

原因:沒有開啓 preserveDrawingBuffer

解決:在初始化時設置 contextParameters.preserveDrawingBuffer = true

坑 7:渲染監聽回調執行太頻繁

原因:沒有檢查 viewChanged,導致每次渲染都執行。

解決:配合 renderState.viewChanged 檢查,只在視野變化時執行。

我的學習總結

經過這一天的學習,我掌握了:

  1. 渲染系統的作用:控制場景的渲染方式、動畫循環、渲染特效等
  2. 如何開啓循環渲染:通過 enableAnimationLoop 開啓
  3. 如何設置幀率:通過 animationLoopFrameTime 設置
  4. 如何監聽渲染事件:通過 addPrepareRenderListener 監聽
  5. 如何配置渲染特性:通過 features 配置 Bloom、抗鋸齒等
  6. 如何優化性能:通過調整像素比、幀率、關閉不必要的特效等
  7. 如何開啓截圖功能:通過 contextParameters.preserveDrawingBuffer 開啓
  8. 如何獲取渲染對象:通過 rendererscenecamera 等屬性訪問
  9. 如何獲取渲染狀態:通過 renderState 獲取渲染狀態信息

我的感受:渲染系統真的很強大!雖然功能很多,但是用起來其實不難。關鍵是要理解每個功能的作用,然後根據需求合理配置!

下一步計劃

  1. 學習更多渲染特性的配置
  2. 嘗試做性能優化
  3. 做一個完整的渲染控制項目

學習筆記就到這裏啦!作為一個初學者,我覺得渲染系統雖然功能很多,但是用起來其實不難。關鍵是要理解每個功能的作用,然後根據需求合理配置!希望我的筆記能幫到其他初學者!大家一起加油!
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.