2012-03-07 291 views
6

我正在開發一種用於分子三維可視化的小工具。 對於我的項目,我選擇以「Brad Larson先生」用他的蘋果軟件「Molecules」做的事情。一個鏈接,你可以找到使用該技術的一個小演講:Brad Larsson software presentationGLSL中的圓柱體冒充者

對於做我的工作,我必須計算球騙子缸冒名頂替

目前我有成功做了「球冒名頂替」與另一個教程Lies and Impostors

的幫助下總結球體冒名頂替的計算:首先我們發送一個「球位置」和「球半徑「到」頂點着色器「,它將在相機空間中創建一個始終面向相機的正方形,之後我們將我們的正方形發送到片段着色器,我們使用簡單的光線追蹤來查找正方形的哪個片段包含在最後我們計算片段的法線和位置來計算光照。 (另一件事我們也寫gl_fragdepth給我們的冒名頂尖球體一個很好的深度!)

但是現在我被封鎖在缸冒充者的計算中,我嘗試在球冒充者和缸冒充者之間做一個平行但我沒有找到任何東西,我的問題是,對於球體來說這很容易,因爲無論我們如何看待它,球體總是相同的,我們總會看到同樣的事情:「一個圓圈」,另一件事是球體完全由數學定義,那麼我們可以輕鬆找到計算照明的位置和法線,並創建我們的冒名頂替者。

對於圓柱體來說,它不是一回事,我也沒有找到一個暗示來塑造一個可以用作「圓柱體冒名頂替者」的形式,因爲圓柱體根據我們所看到的角度顯示了許多不同的形式!

所以我的要求是問你一個解決方案或指示我的問題「缸冒名頂替者」。

+3

你爲什麼會使用冒充者爲此?爲什麼不畫一個圓柱?另外,當我編寫該教程時,我選擇球體而不是圓柱體是有原因的。球體是對稱的;它們由位置和半徑來定義。光線追蹤數學很簡單。氣缸遠遠更復雜。如果您只是將我在教程中使用的圓柱體模型拉出並渲染該模型,將會容易得多。 – 2012-03-07 03:25:45

+0

正如我所說,我開發了一個用於學校項目3D分子可視化的小工具,所以我決定首先根據Brad Larson在其應用程序Brad Larson應用程序中使用的技術執行3D球體冒名頂替者和圓柱體冒名頂替者,是假冒僞造者比繪製一個包含百多個多邊形的真正圓柱體更光明,而這對於分子的三維可視化來說非常重要,因爲將要計算大量的分子! 但如果你說我太難了,我開始有些害怕了? – nadir 2012-03-07 12:59:54

+0

我無法回答您的問題,但您鏈接到Larsson的論文非常有趣,所以非常感謝。如果我要提供建議,我會說只是把它留在領域而忽略了圓柱體:p。 – Robinson 2012-03-07 13:29:23

回答

1

從我能理解的論文中,我會解釋如下。

從任何角度看,冒充者滾筒具有以下特徵。

  1. 從頂部看,它是一個圓圈。所以考慮到你永遠不需要查看圓柱體頂部,你不需要渲染任何東西。
  2. 從側面看,它是一個矩形。像素着色器只需要正常計算照度。
  3. 從任何其他角度來看,它都是一個矩形(與步驟2中計算的相同)曲線。它的曲率可以在像素着色器內建模爲頂部橢圓的曲率。根據視角,這個曲率可以被認爲只是紋理空間中每個「列」的偏移量。假設0意味着您正在側向觀察圓柱體,則可以通過將長軸(圓柱體的厚度)乘以當前視角的一個因子(角度/ 90)來計算此橢圓的短軸。

Fig 1. 視角。我在下面的數學中只考慮了0-90的情況,但其他情況則是微不足道的。由於視角(φ)和圓柱體的直徑(a),這裏是着色器需要在紋理空間Y = b'sin(φ)中扭曲Y軸的方式。和b'= a *(phi/90)。絕不應渲染phi = 0和phi = 90的情況。

當然,我沒有考慮到這個柱面的長度 - 這取決於你的特定投影,而不是圖像空間問題。

+0

謝謝你的解釋,我明白我的問題更好一些,但我總是不知道如何鏈接所有你所說的並實施它,所以如果你有時間來詳細解釋它,這將對我非常有幫助。在所有情況下,感謝您的先例答案! – nadir 2012-03-07 18:39:09

+0

非常感謝您的明確解釋,只有我有幾個問題,首先你是什麼意思的「紋理空間」? 和研究這些案件,並找到我認爲我將需要「正常」的觀看角度,但我的圓柱體只是冒名頂替,所以法線是在片段着色器中計算每個片段,這是它發生的球體冒名頂替者, 如何在沒有正常情況下找到這個可視角度! – nadir 2012-03-09 00:07:49

+0

簡而言之,紋理空間是(u,v)空間 - 您在片段着色器中使用的空間。在上面的解釋中,我根本沒有考慮光照 - 我只解釋瞭如何在視角發生變化時創建曲率(圍繞X軸)。我建議你在沒有照明的情況下先獲得基本的氣瓶冒充者渲染,然後在以後添加。 – Ani 2012-03-09 15:41:41

2

我知道這個問題已經超過一歲,但我仍然想給我2美分。

我能夠用另一種技術生產圓柱仿冒者,我從pymol的代碼中獲得靈感。這裏的基本策略:

1)你想繪製圓柱體的邊界框(一個cuboid)。要做到這一點,你需要6面,翻譯成18個三角形,在三角形頂點轉化。假設您無法訪問幾何着色器,則將其傳遞給圓柱起點的36倍頂點着色器,圓柱體方向的36倍,並且對於通過邊界框相應點的每個頂點。例如,與點(0,0,0)相關的頂點表示它將在邊界框的左下角轉換,(1,1,1)表示對角相對的點等。

2)在頂點着色器中,可以通過根據傳入的相應點位移每個頂點(您傳遞36個相等頂點)來構造圓柱體的點。 在此步驟結束時,您應該有一個邊界氣瓶盒。

3)在這裏你必須重建邊界框的可見表面上的點。從您獲得的觀點來看,您必須執行一個射線圓柱體相交。

4)從交點你可以重建深度和法線。您還必須丟棄在邊界框外發現的交點(當沿着其軸觀察圓柱體時,可能發生這種情況,交點將無限遠地發生)。

到這是一個非常艱鉅的任務,如果有人有興趣來這裏的路上的源代碼:

https://github.com/chemlab/chemlab/blob/master/chemlab/graphics/renderers/shaders/cylinderimp.frag

https://github.com/chemlab/chemlab/blob/master/chemlab/graphics/renderers/shaders/cylinderimp.vert

2

除了pygabriels回答我想用共享一個獨立的實現來自Blaine Bell(PyMOL,Schrödinger,Inc.)的上述着色器代碼。

pygabriel解釋的方法也可以改進。邊框可以以這樣的方式對齊,即它總是面向觀看者。最多隻能看到兩張臉。因此,只需要6個頂點(即由4個三角形組成的兩個面)。

見圖象在這裏,所述盒(其方向矢量)總是面向觀察者:
Image: Aligned bounding box

對於源代碼,下載:cylinder impostor source code

的代碼不包括圓形帽和正投影。它使用幾何着色器來生成頂點。您可以使用PyMOL許可協議下的着色器代碼。

1

圓柱體冒充者實際上可以像球體一樣完成,就像Nicol Bolas在他的教程中做的那樣。你可以製作一個正對着相機的方塊,並將它看起來像一個圓柱體,就像Nicol對球體做的一樣。而且它不是很難。

它的做法是光線追蹤當然。請注意,在攝像機空間中朝上的圓柱體很容易實現。例如與邊的交點可以投影到xz平面,這是與圓相交的線的二維問題。獲取頂部和底部並不困難,交點的z座標是給定的,所以你實際上知道射線和圓的平面的交點,所有你需要做的就是檢查它是否在圓內。基本上,就是這樣,你得到兩分,並返回更接近的一個(法線也很瑣碎)。

而當涉及到任意軸時,結果幾乎是相同的問題。當您在固定軸圓柱上求解方程時,您正在求解一個參數,該參數描述了從給定方向的給定點到達圓柱所需的時間。從它的「定義」中,你應該注意到,如果你旋轉這個世界,這個參數不會改變。因此,您可以旋轉任意軸成爲y軸,在方程式更簡單的空間中解決問題,獲得該空間中線方程的參數,但將結果返回到相機空間。

您可以從here下載着色器文件。只是一個在行動的它形象: screenshot http://oi40.tinypic.com/2h5tqhy.jpg

神奇在哪裏發生(這只是長「因爲它充滿了評論,但代碼本身是最多50行)代碼:

void CylinderImpostor(out vec3 cameraPos, out vec3 cameraNormal) 
{ 
    // First get the camera space direction of the ray. 
    vec3 cameraPlanePos = vec3(mapping * max(cylRadius, cylHeight), 0.0) + cameraCylCenter; 
    vec3 cameraRayDirection = normalize(cameraPlanePos); 

    // Now transform data into Cylinder space wherethe cyl's symetry axis is up. 
    vec3 cylCenter = cameraToCylinder * cameraCylCenter; 
    vec3 rayDirection = normalize(cameraToCylinder * cameraPlanePos); 


    // We will have to return the one from the intersection of the ray and circles, 
    // and the ray and the side, that is closer to the camera. For that, we need to 
    // store the results of the computations. 
    vec3 circlePos, sidePos; 
    vec3 circleNormal, sideNormal; 
    bool circleIntersection = false, sideIntersection = false; 

    // First check if the ray intersects with the top or bottom circle 
    // Note that if the ray is parallel with the circles then we 
    // definitely won't get any intersection (but we would divide with 0). 
    if(rayDirection.y != 0.0){ 
     // What we know here is that the distance of the point's y coord 
     // and the cylCenter is cylHeight, and the distance from the 
     // y axis is less than cylRadius. So we have to find a point 
     // which is on the line, and match these conditions. 

     // The equation for the y axis distances: 
     // rayDirection.y * t - cylCenter.y = +- cylHeight 
     // So t = (+-cylHeight + cylCenter.y)/rayDirection.y 
     // About selecting the one we need: 
     // - Both has to be positive, or no intersection is visible. 
     // - If both are positive, we need the smaller one. 
     float topT = (+cylHeight + cylCenter.y)/rayDirection.y; 
     float bottomT = (-cylHeight + cylCenter.y)/rayDirection.y; 
     if(topT > 0.0 && bottomT > 0.0){ 
      float t = min(topT,bottomT); 

      // Now check for the x and z axis: 
      // If the intersection is inside the circle (so the distance on the xz plain of the point, 
      // and the center of circle is less than the radius), then its a point of the cylinder. 
      // But we can't yet return because we might get a point from the the cylinder side 
      // intersection that is closer to the camera. 
      vec3 intersection = rayDirection * t; 
      if(length(intersection.xz - cylCenter.xz) <= cylRadius) { 
       // The value we will (optianally) return is in camera space. 
       circlePos = cameraRayDirection * t; 
       // This one is ugly, but i didn't have better idea. 
       circleNormal = length(circlePos - cameraCylCenter) < 
           length((circlePos - cameraCylCenter) + cylAxis) ? cylAxis : -cylAxis; 
       circleIntersection = true; 
      } 
     } 
    } 


    // Find the intersection of the ray and the cylinder's side 
    // The distance of the point and the y axis is sqrt(x^2 + z^2), which has to be equal to cylradius 
    // (rayDirection.x*t - cylCenter.x)^2 + (rayDirection.z*t - cylCenter.z)^2 = cylRadius^2 
    // So its a quadratic for t (A*t^2 + B*t + C = 0) where: 
    // A = rayDirection.x^2 + rayDirection.z^2 - if this is 0, we won't get any intersection 
    // B = -2*rayDirection.x*cylCenter.x - 2*rayDirection.z*cylCenter.z 
    // C = cylCenter.x^2 + cylCenter.z^2 - cylRadius^2 
    // It will give two results, we need the smaller one 

    float A = rayDirection.x*rayDirection.x + rayDirection.z*rayDirection.z; 
    if(A != 0.0) { 
     float B = -2*(rayDirection.x*cylCenter.x + rayDirection.z*cylCenter.z); 
     float C = cylCenter.x*cylCenter.x + cylCenter.z*cylCenter.z - cylRadius*cylRadius; 

     float det = (B * B) - (4 * A * C); 
     if(det >= 0.0){ 
      float sqrtDet = sqrt(det); 
      float posT = (-B + sqrtDet)/(2*A); 
      float negT = (-B - sqrtDet)/(2*A); 

      float IntersectionT = min(posT, negT); 
      vec3 Intersect = rayDirection * IntersectionT; 

      if(abs(Intersect.y - cylCenter.y) < cylHeight){ 
       // Again it's in camera space 
       sidePos = cameraRayDirection * IntersectionT; 
       sideNormal = normalize(sidePos - cameraCylCenter); 
       sideIntersection = true; 
      } 
     } 
    } 

    // Now get the results together: 
    if(sideIntersection && circleIntersection){ 
     bool circle = length(circlePos) < length(sidePos); 
     cameraPos = circle ? circlePos : sidePos; 
     cameraNormal = circle ? circleNormal : sideNormal; 
    } else if(sideIntersection){ 
     cameraPos = sidePos; 
     cameraNormal = sideNormal; 
    } else if(circleIntersection){ 
     cameraPos = circlePos; 
     cameraNormal = circleNormal; 
    } else 
     discard; 
}