基於 GPU 渲染的高性能空間包圍計算

空間包圍檢測在計算機圖形學、虛擬仿真、工業生產等有着廣泛的應用。

現代煤礦開採過程中,安全一直是最大的挑戰之一。地質空間中存在諸多如瓦斯積聚、地質構造異常、水文條件不利等隱蔽致災因素,一旦被觸發,可能引發災難性的後果。因此在安全生產過程中有效的管理和規避各隱蔽致災因素,有着重要的意義。

通過對煤礦地質空間中各地質因素建模,建立空間數據庫,還原地下真實場景,使用計算機圖形學進行空間計算,可以實時監測各隱蔽致災因素的位置和距離,指導安全生產,並進行可視化展示。

空間包圍檢測有多種方法,比如基於包圍盒的檢測,三角面碰撞檢測等。本文提出了一種基於 GPU 渲染的高效計算方法。

假定待檢測球體範圍的半徑爲r。兩種檢測方法如下:

  • 方法 1:遍歷模型所有的點,計算點和球心的距離。如果有距離小於 r,模型在球體範圍內。
  • 方法 2:以檢測區域的包圍盒爲正交投影空間,渲染所有需要檢測的模型。渲染過程中計算每個渲染點到球心的距離,如果有距離小於r的渲染點,模型在球體範圍內。

模型和檢測區域有以下幾種位置關係:

 

  • 圖 1:模型完全在球體範圍內:方法 1 可檢測
  • 圖 2:模型部分點在球體範圍內:方法 1 可檢測
  • 圖 3:模型點不在球體範圍內,部分三角面在球形範圍內:方法 2 可檢測
  • 圖 4:模型不在球體範圍內:方法 1 + 2 可檢測
  • 圖 5:模型完全包含球體範圍:模型如果是空心的,方法 1 + 2 可檢測模型不在球體範圍內。如果需要計算結果是模型在球體範圍內,也就是模型是實心的,建模時需要在模型內部加上額外的輔助計算的三角面,用於表達內部信息。此時用方法 1 + 2 可檢測模型在球體範圍內。

以上方法使用 WebGL 渲染到紋理(Render To Texture) 和 readPixels 功能。圖撲 HT for Web SDK 組件庫對 WebGL 底層複雜操作做了封裝, 爲用戶省掉了繁瑣的底層 WebGL 操作,可以方便快捷的實現正交透視、渲染到紋理和異步 readPixels 等高級 WebGL 功能。

 

方法 1:點檢測法

準備一張 N X N 紋理圖 texture1(HT RenderTarget),保證要檢測的模型的數量不大於 N X N。每一個模型在紋理上分配一個像素,像素的位置爲 (x,y)。

創建點渲染模式着色器程序,實現以下功能:

  • 頂點着色器:檢測每個點到球心的距離,將距離是否小於r的信息傳給片段着色器。指定的位置 (x,y) 賦給 gl_Position。
  • 片段着色器:如果距離小於 r, 渲染紅色,否則不渲染顏色。

JavaScript 程序遍歷每一個待檢測模型,將模型的頂點和模型在紋理上的位置 (x,y) 通過 attribute 和 uniform 傳給頂點着色器。所有模型渲染結束後,使用異步 readPixels 將渲染結果讀出來。通過判斷讀取結果裏每個像素點顏色值,獲得模型是否在球體內部信息。

主要代碼:

// 創建渲染材質1
const texture1 = new ht.graph3d.RenderTarget(g3d, g3d.getGL(), 100, 100);

// 循環渲染所有的模型,結果保存到texture1。
for (let i = 0; i < nodeCount; i++) {
    data = datas[i];
    tModel = getDataMesh(data); // 獲取模型網格信息
    // 準備着色器數據
    tModel.mat = model4.mat;
    tModel.matDef[DEFAULT_MAT_NAME] = model4.mat;
    tModel.mat.modelMat = data.getMatrix4().toArray();
    x = i % 100;
    y = Math.floor(i / 100);
    model4.mat.uPos = [x / 100 * 2 - 1 + 1 / 200, 2 * y / 100 - 1 + 1 / 200];
    // 渲染到texture1
    g3d.setViewport(gl, 0, 0, 100, 100);
    g3d.renderModel(texture1, model4, {
        clear: false
    });
}

// 讀取檢測結果
texture1.readPixelsAsync(0, 0, 100, 100, null, (result) => {
    for (let y = 0; y < 100; y++) {
        for (let x = 0; x < 100; x++) {
            // 遍歷像素點,檢測是否是紅色
            // ......
        }
    }
});

 

方法 2:面檢測法

準備兩張紋理貼圖 texture1 和 texture2。Texture1 的要求同方法 1。Texture2 默認使用 1000 X 1000 的分辨率。

創建兩套着色器。第一套着色器使用三角面渲染:

  • 頂點着色器:正常計算頂點投影信息
  • 片段着色器:檢測每一個點到球心的距離,如果小於 r,渲染紅色

第二套着色器使用點渲染:

  • 頂點着色器:根據輸入的 texture2 座標(attribute),使用 texture2D 獲取對應位置的顏色值,如果是紅色,表示模型在球體內部,將此信息傳給片段着色器。模型在 texture1 上的位置信息 (x,y) 賦給 gl_Position。
  • 片段着色器:如果距離小於 r, 渲染紅色,否則不渲染顏色。

JavaScript 程序遍歷每一個模型,使用着色器 1 將結果渲染到 texture2。渲染過程使用正交透視矩陣,視錐是球體的包圍盒。JavaScript 將 texture2 (uniform sampler2D)、texture2 每個像素的 x, y位置信息 (attribute)、模型在 texture1 上的位置信息 (uniform) 傳給頂點着色器 2。片段着色器 2 將模型是否在球體內的信息渲染到 texture1。所有模型渲染結束後,使用異步 readPixels 將渲染結果讀出來。通過判斷讀取結果裏每個像素點顏色值,獲得模型是否在球體內部信息。

 

主要代碼:


// 創建渲染材質1,2


const texture1 = new ht.graph3d.RenderTarget(g3d, g3d.getGL(), 100, 100);


const texture2 = new ht.graph3d.RenderTarget(g3d, g3d.getGL(), 1000, 1000);


 


// 循環渲染所有的模型到texture2。texture2信息渲染到texture1


for (let i = 0; i < nodeCount; i++) {


data = datas[i];


     tModel = getDataMesh(data); // 獲取模型網格信息


  // 準備着色器1數據


     tModel.mat = model2.mat;


     tModel.matDef[DEFAULT_MAT_NAME] = model2.mat;


     tModel.mat.modelMat = data.getMatrix4().toArray();


// 渲染到texture2


     g3d.setViewport(gl, 0, 0, 1000, 1000);


     g3d.renderModel(texture2, tModel, { clear: true });


// 準備着色器2數據


model3.mat.uImage = texture2.texture;


     x = i % 100;


     y = Math.floor(i / 100);


     model3.mat.uPos = [x / 100 * 2 - 1 + 1 / 200, 2 * y / 100 - 1 + 1 / 200];


// 渲染到texture1


     g3d.setViewport(gl, 0, 0, 100, 100);


     g3d.renderModel(texture1, model3, { clear: false });


 }

 

 

方法 1 簡單快速。但檢測結果不準確。方法 2 檢測結果準確,但計算過程複雜。實際使用中兩種方法結合使用。首先使用方法 1 檢測。對於不在球體範圍內的模型,再使用方法 2 檢測。

如果需要檢測橢球體範圍或者長方體的範圍,可以獲取橢球體或長方的變換矩陣,計算獲得逆矩陣。將逆矩陣應用於每一個待檢測模型的節點。此時只需要檢測變換後的模型是否在單位圓或單位立方體內即可。HT SDK 3D 引擎庫提供了豐富的數學計算 API,可以非常直觀簡潔的實現以上功能。

 

您可以至圖撲軟件官網查看更多案例及效果:https://www.hightopo.com/demos/index.html

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章