圓柱體冒充者實際上可以像球體一樣完成,就像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