8

分子の 3D 可視化用の小さなツールを開発しています。私のプロジェクトでは、"Brad Larson" 氏が Apple ソフトウェア " Molecules " で行った方法で何かを作ることにしました。使用されたテクニックの小さなプレゼンテーションを見つけることができるリンク: Brad Larsson ソフトウェア プレゼンテーション

私の仕事をするために、球の偽者円柱の偽者を計算する必要があります。

今のところ、別のチュートリアルLies and Impostorsの助けを借りて、「Sphere Impostor」を実行することに成功しました。

球詐欺師の計算を要約します。最初に、「球の位置」と「球の半径」を「頂点シェーダー」に送信します。これにより、カメラ空間に常にカメラに面する正方形が作成されます。そこで単純なレイ トレーシングを使用して、正方形のどのフラグメントが球体に含まれているかを見つけ、最後にフラグメントの法線と位置を計算して照明を計算します。(別のこととして、詐欺師の球体に適切な深さを与えるために gl_fragdepth も記述します!)

しかし、今、私は円柱詐欺師の計算でブロックされています。球詐欺師と円柱詐欺師の間で並列処理を試みましたが、何も見つかりませんでした。私の問題は、球がどのように見ても常に同じです。「円」と、もう 1 つのことは、球が数学によって完全に定義されていることです。そうすれば、照明を計算するための位置と法線を簡単に見つけて、詐欺師。

円柱も同じではなく、見る角度によって様々な形を見せてくれる「円柱ごっこ」のような形をモデリングするヒントが見つからなかったのです!

そこで、「シリンダー詐欺師」という私の問題の解決策または兆候についてお聞きしたいと思います。

4

4 に答える 4

3

この質問が 1 年以上前のものであることは承知していますが、それでも 2 セントを差し上げたいと思います。

pymol のコードからインスピレーションを得て、別の手法でシリンダーの詐欺師を作成することができました。基本的な戦略は次のとおりです。

1)円柱の境界ボックス (直方体) を描画します。これを行うには、6 つの面が必要です。これは 18 個の三角形に変換され、36 個の三角形の頂点に変換されます。ジオメトリ シェーダーにアクセスできないと仮定すると、円柱の開始点の 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

于 2013-05-08T19:51:55.033 に答える
3

ニコル・ボーラスがチュートリアルで行ったように、円柱の詐欺師は実際には球体とまったく同じ方法で作成できます。カメラに面する正方形を作成し、Nicol が球に対して行ったのと同じように、円柱のように見えるように色を付けることができます。そして、それはそれほど難しいことではありません。

その方法はもちろんレイトレーシングです。カメラ空間で上向きの円柱は実装が簡単であることに注意してください。たとえば、辺との交点は xz 平面に投影できます。これは、円と交差する線の 2D 問題です。上部と下部を取得することも難しくありません。交点の z 座標が与えられているため、光線と円の平面の交点が実際にわかります。円の内側にあるかどうかを確認するだけです。基本的には、それだけです。2 つのポイントを取得し、近い方のポイントを返します (法線も非常に簡単です)。

そして、任意の軸になると、ほぼ同じ問題であることがわかります。固定軸の円柱で方程式を解く場合、円柱に到達するために、特定の点から特定の方向に移動する必要がある時間を表すパラメーターについて、それらを解くことになります。その「定義」から、ワールドを回転させてもこのパラメータは変化しないことに気付くはずです。したがって、任意の軸を回転させて y 軸にし、方程式がより簡単な空間で問題を解き、その空間で直線方程式のパラメーターを取得しますが、結果をカメラ空間で返すことができます。

シェーダーファイルはここからダウンロードできます。実際の動作のイメージ: スクリーンショット

魔法が起こるコード (コメントでいっぱいなので長いですが、コード自体は最大 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;
}   
于 2013-05-28T00:48:03.183 に答える
2

私がこの論文について理解できることから、私はそれを次のように解釈します。

どの角度から見ても、偽物の円柱には次のような特徴があります。

  1. 上から、丸です。したがって、円柱を上から下に表示する必要がないことを考えると、何もレンダリングする必要はありません。
  2. 横から見ると長方形です。ピクセル シェーダーは、通常どおりイルミネーションを計算するだけで済みます。
  3. 他の角度から見ると、曲線は長方形 (手順 2 で計算したものと同じ) です。その曲率は、上部の楕円の曲率としてピクセル シェーダー内でモデル化できます。この曲率は、表示角度に応じて、テクスチャ空間内の各「列」のオフセットと見なすことができます。この楕円の短軸は、長軸 (円柱の厚さ) に現在の視角 (角度 / 90) を掛けることで計算できます。0 は円柱を横から見ていることを意味すると仮定します。

図 1。 視野角。以下の計算では 0-90 のケースのみを考慮しましたが、他のケースは自明に異なります。

図 2。 表示角度 (phi) と円柱の直径 (a) が与えられると、シェーダーがテクスチャ空間 Y = b' sin(phi) で Y 軸をワープする必要がある方法は次のようになります。また、b' = a * (ファイ / 90) です。ケース phi = 0 および phi = 90 は決してレンダリングされるべきではありません。

もちろん、この円柱の長さは考慮していません。これは、特定の投影に依存し、画像空間の問題ではありません。

于 2012-03-07T14:43:31.723 に答える
2

pygabriels の回答に加えて、Blaine Bell (PyMOL、Schrödinger, Inc.) の前述のシェーダー コードを使用したスタンドアロンの実装を共有したいと思います。

pygabriel によって説明されたアプローチも改善することができます。境界ボックスは、常に視聴者の方を向くように配置できます。多くて 2 つの顔だけが表示されます。したがって、必要な頂点は 6 つだけです (つまり、4 つの三角形で構成される 2 つの面)。

ここの図を参照してください。ボックス (その方向ベクトル) は常に視聴者の方を向いています:
画像: 整列されたバウンディング ボックス

ソース コードについては、ダウンロード:シリンダー インポスター ソース コード

このコードは、丸いキャップと正投影をカバーしていません。頂点生成にはジオメトリ シェーダーを使用します。PyMOL ライセンス契約の下でシェーダー コードを使用できます。

于 2013-05-24T19:55:56.020 に答える