圆柱体冒充者实际上可以像球体一样完成,就像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;
}
你为什么会使用冒充者为此?为什么不画一个圆柱?另外,当我编写该教程时,我选择球体而不是圆柱体是有原因的。球体是对称的;它们由位置和半径来定义。光线追踪数学很简单。气缸远远更复杂。如果您只是将我在教程中使用的圆柱体模型拉出并渲染该模型,将会容易得多。 – 2012-03-07 03:25:45
正如我所说,我开发了一个用于学校项目3D分子可视化的小工具,所以我决定首先根据Brad Larson在其应用程序Brad Larson应用程序中使用的技术执行3D球体冒名顶替者和圆柱体冒名顶替者,是假冒伪造者比绘制一个包含百多个多边形的真正圆柱体更光明,而这对于分子的三维可视化来说非常重要,因为将要计算大量的分子! 但如果你说我太难了,我开始有些害怕了? – nadir 2012-03-07 12:59:54
我无法回答您的问题,但您链接到Larsson的论文非常有趣,所以非常感谢。如果我要提供建议,我会说只是把它留在领域而忽略了圆柱体:p。 – Robinson 2012-03-07 13:29:23