GLSL中的气缸冒名顶替者

时间:2012-03-07 03:12:00

标签: opengl glsl shader vertex-shader cylindrical

我正在开发一种用于分子三维可视化的小工具。 对于我的项目,我选择用“Brad Larson”先生用他的Apple软件“ Molecules ”做的事情。您可以在其中找到所使用技术的小型演示文稿的链接:Brad Larsson software presentation

为了完成我的工作,我必须计算球体冒名顶替者球体冒名顶替者

目前我已经在另一个教程的帮助下成功完成了“Sphere Impostor”Lies and Impostors

总结球体冒名顶替者的计算:首先我们将“球体位置”和“球体半径”发送到“顶点着色器”,这将在相机空间中创建一个始终面向相机的正方形,之后我们将广场发送到片段着色器,在那里我们使用简单的光线追踪来找到正方形的哪个片段包含在球体中,最后我们计算片段的法线和位置来计算光照。 (另外我们还写了gl_fragdepth来给我们的冒名顶替球体提供一个很好的深度!)

但是现在我被阻挡在气缸冒名顶替者的计算中,我试图在球体冒充者和气缸冒名顶替者之间做一个平行但是我找不到任何东西,我的问题是,对于球体来说它很容易因为无论我们如何看待它,球体总是一样的,我们总会看到同样的东西:“一个圆圈”,另一个是球体是由数学完美定义的,那么我们可以很容易地找到计算照明的位置和法线并创造我们的冒名顶替者。

对于气缸而言,它不是一回事,我没有找到建模可以用作“气缸冒名顶替者”的形状的提示,因为气缸根据我们看到的角度显示许多不同的形状!

所以我的要求就是问你关于“气缸冒名顶替者”问题的解决方案或指示。

4 个答案:

答案 0 :(得分:3)

我知道这个问题已超过一年了,但我还是想给我2美分。

我能够用另一种技术制作圆柱冒名顶替者,我从pymol的代码中获取灵感。这是基本策略:

1)你想为圆柱体绘制一个边界框({​​{3}})。要做到这一点,你需要6个面,它们可以翻译成18个三角形,这些三角形可以转换成36个三角形顶点。假设您无法访问几何着色器,则将圆顶着色器传递给圆柱的起点36倍,圆柱方向的36倍,并且对于每个顶点,您都会传递边界框的对应点。例如,与点(0,0,0)相关联的顶点意味着它将在边界框的左下角转换,(1,1,1)表示对角线相对的点等。

2)在顶点着色器中,您可以根据传入的相应点移动每个顶点(您通过36个相等的顶点)来构造圆柱的点。 在此步骤结束时,您应该有一个圆柱的边界框。

3)在这里,您必须重建边界框可见表面上的点。从您获得的角度来看,您必须执行光线圆柱交叉。

4)从交点可以重建深度和法线。您还必须丢弃在边界框外部找到的交叉点(当您沿其轴查看圆柱体时会发生这种情况,交点将无限远)。

顺便说一句,这是一项非常艰巨的任务,如果有人对此感兴趣,请参阅源代码:

cuboid

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

答案 1 :(得分:2)

根据我对该论文的理解,我会将其解释如下。

从任何角度观察的冒名顶限油缸都具有以下特征。

  1. 从顶部开始,它是一个圆圈。所以考虑到你永远不需要自上而下查看圆柱体,你不需要渲染任何东西。
  2. 从侧面看,它是一个矩形。像素着色器只需要正常计算照明。
  3. 从任何其他角度来看,它是一个曲线的矩形(在步骤2中计算的矩形)。其曲率可以在像素着色器内建模为顶部椭圆的曲率。根据视角,该曲率可以简单地视为纹理空间中每个“列”的偏移。这个椭圆的短轴可以通过将长轴(圆柱的厚度)乘以当前视角(角度/ 90)的系数来计算,假设0意味着你正在侧面观察圆柱体。
  4. Fig 1. 视角。我在下面的数学计算中只考虑了0-90的情况,但其他情况则完全不同。

    Fig 2. 给定视角(φ)和圆柱体的直径(a),这里着色器需要如何在纹理空间Y = b'sin(phi)中扭曲Y轴。并且b'= a *(phi / 90)。不应该渲染phi = 0和phi = 90的情况。

    当然,我没有考虑这个圆柱体的长度 - 这取决于你的特定投影,而不是图像空间问题。

答案 2 :(得分:2)

除了pygabriels的回答,我想使用Blaine Bell(PyMOL,Schrödinger,Inc。)中提到的着色器代码共享一个独立的实现。

pygabriel解释的方法也可以改进。边界框可以以这样的方式对齐,使其始终面向观看者。最多只能看到两个面。因此,仅需要6个顶点(即,由4个三角形组成的两个面)。

见图片,方框(方向矢量)始终面向观众:
Image: Aligned bounding box

有关源代码,请下载:cylinder impostor source code

该代码不包括圆帽和正交投影。它使用几何着色器生成顶点。您可以使用PyMOL许可协议下的着色器代码。

答案 3 :(得分:2)

圆柱体冒充者实际上可以像球体一样完成,就像Nicol Bolas在他的教程中所做的那样。您可以制作一个面向相机的正方形,并将其颜色看起来像一个圆柱体,就像Nicol为球体所做的一样。并且 很难。

它的完成方式当然是光线跟踪。请注意,相机空间中朝上的圆柱体很容易实现。例如,与侧面的交点可以投影到xz平面,这是与圆相交的线的2D问题。获得顶部和底部也不难,交叉点的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;
}   
相关问题