跳轉到內容

OpenGL 程式設計/Glescraft 6

來自華夏公益教科書
一個體素世界。

現在我們可以在體素世界中四處移動,我們可能想新增、刪除或更改體素。在第一個教程中,我們實現了一個簡單的set()函式,它允許我們更改任意體素的內容。但是,訣竅是找出我們正在檢視的確切塊,更具體地說,是該塊的哪個面。

反投影座標

[編輯 | 編輯原始碼]

正如我們在物體選擇教程中看到的,我們可以檢索視窗中任意畫素的深度值,並將它的座標反投影回物體座標。給定當前視窗的寬度和高度,wwwh,以及檢視和投影矩陣,我們對位於中心的畫素執行以下操作

glReadPixels(ww / 2, wh / 2, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);

glm::vec4 viewport = glm::vec4(0, 0, ww, wh);
glm::vec3 wincoord = glm::vec3(ww / 2, wh / 2, depth);
glm::vec3 objcoord = glm::unProject(wincoord, view, projection, viewport);

int x = floorf(objcoord.x);
int y = floorf(objcoord.y);
int z = floorf(objcoord.z);

objcoord向量儲存中心畫素的座標,但我們只需將浮點數向下舍入到最接近的整數即可獲得體素座標。為了讓使用者清楚地瞭解他目前正在檢視哪個體素,您可以在它周圍繪製一個包圍盒。為了允許與世界進行互動,您可以在單擊滑鼠按鈕時註冊一個回撥函式,並使用set()在相應的塊上更改當前選定的體素。該set()函式將依次將塊標記為已更改,當渲染下一幀時,這將自動導致 VBO 更新以反映更改。

更改或刪除現有的體素很好,但人們也應該能夠新增新的體素。如果要新增體素,它們會根據我們正在檢視的體素的哪個面,新增在當前選定的體素旁邊,這是合乎邏輯的。這樣做的一種方法是意識到,由於我們始終看著體素表面的一個點,因此 x、y、z 座標之一應該始終恰好具有整數值。在實踐中,這實際上永遠不會發生,但我們可以檢查 x、y、z 中哪一個最接近整數值。如果 x 座標最接近,那麼我們知道它要麼是兩個指向 x 方向的面之一。如果 x 座標小於相機的 x 座標,我們知道我們正在檢視指向正 x 方向的面,如果 x 座標較小,那麼我們正在檢視另一個面。因此,為了確定新體素的座標,首先我們定義一個函式,該函式提供到最近整數的距離

float dti(float val) {
  return fabsf(val - roundf(val));
}

然後我們按以下方式使用它

int nx = x;
int ny = y;
int nz = z;

if(dti(objcoord.x) < dti(objcoord.y))
  if(dti(objcoord.x) < dti(objcoord.z))
    if(lookat.x > 0)
      nx--;
    else
      nx++;
  else
    if(lookat.z > 0)
      nz--;
    else
      nz++;
else
  if(dti(objcoord.y) < dti(objcoord.z))
    if(lookat.y > 0)
      ny--;
    else
      ny++;
  else
    if(lookat.z > 0)
      nz--;
    else
      nz++;

這種方法的優點是即使對於非常遠的體素也能夠很好地工作。缺點是它有時會由於舍入錯誤而選擇錯誤的體素。這種方法的另一個特性是,如果您正在透過具有部分透明紋理的體素檢視,並且透明畫素在片段著色器中被丟棄,並且您沒有精確地指向其紋理的不透明部分,它會選擇在部分透明體素後面可見的體素。這取決於您想要什麼,這是一個特性或一個錯誤。

射線投射

[編輯 | 編輯原始碼]

另一種技術是想象一條從相機開始並朝你正在檢視的方向前進的線。您可以沿著該線移動,直到遇到體素。

這種方法的優點是它更加精確。它將所有體素視為不透明,因此它不會透過部分透明的體素。缺點是它需要更多計算,並且計算量會隨著我們正在檢視的體素的距離增加而增加。最好限制最大距離,這樣您只能選擇附近的體素。

< OpenGL 程式設計

瀏覽和下載完整程式碼
華夏公益教科書