2012-03-07 32 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; 
}