這是一個簡單的問題。在對renderDisk()的調用中,您提供的參數很糟糕。看起來你從一些教程中複製了這個功能,卻不知道它是如何工作的。前三個參數控制中心位置,其他三個參數使用磁盤始終面對的第二個位置控制旋轉。如果兩個位置相等(這是您的情況下),這條線被執行:
//handle the degenerate case of z1 == z2 with an approximation
if(vz == 0.0f)
vz = .0001f;
和設置Z到非零使盤垂直於XZ平面,這也是爲您的地形水平面。所以......使其沒關係,你需要修改你的函數是這樣的:
void renderDisk_convenient(float x, float y, float z, float radius, int subdivisions)
{
// Mouse opacity
glColor4f(0.0f, 7.5f, 0.0f, 0.5f);
GLUquadricObj* quadric = gluNewQuadric();
gluQuadricDrawStyle(quadric, GLU_LINE);
gluQuadricNormals(quadric, GLU_SMOOTH);
gluQuadricTexture(quadric, GL_TRUE);
float upX = 0, upY = 1, upZ = 0; // up vector (does not need to be normalized)
renderDisk(x, y, z, x + upX, y + upY, z + upZ, radius, subdivisions, quadric);
gluDeleteQuadric(quadric);
}
這應該把光盤放入XZ平面所以這將是好的,如果地勢平坦。但在其他地方,您實際上需要修改法線方向((upX,upY,upZ)矢量)。如果您的地形是從高度圖生成,則可以正常使用代碼來計算,如本:
const char *p_s_heightmap16 = "ps_height_1k.png";
const float f_terrain_height = 50; // terrain is 50 units high
const float f_terrain_scale = 1000; // the longer edge of terrain is 1000 units long
TBmp *p_heightmap;
if(!(p_heightmap = p_LoadHeightmap_HiLo(p_s_heightmap16))) {
fprintf(stderr, "error: failed to load heightmap (%s)\n", p_s_heightmap16);
return false;
}
// load heightmap
TBmp *p_normalmap = TBmp::p_Alloc(p_heightmap->n_width, p_heightmap->n_height);
// alloc normalmap
const float f_width_scale = f_terrain_scale/max(p_heightmap->n_width, p_heightmap->n_height);
// calculate the scaling factor
for(int y = 0, hl = p_normalmap->n_height, hh = p_heightmap->n_height; y < hl; ++ y) {
for(int x = 0, wl = p_normalmap->n_width, wh = p_heightmap->n_width; x < wl; ++ x) {
Vector3f v_normal(0, 0, 0);
{
Vector3f v_pos[9];
for(int yy = -1; yy < 2; ++ yy) {
for(int xx = -1; xx < 2; ++ xx) {
int sx = xx + x;
int sy = yy + y;
float f_height;
if(sx >= 0 && sy >= 0 && sx < wh && sy < hh)
f_height = ((const uint16_t*)p_heightmap->p_buffer)[sx + sy * wh]/65535.0f * f_terrain_height;
else
f_height = 0;
v_pos[(xx + 1) + 3 * (yy + 1)] = Vector3f(xx * f_width_scale, f_height, yy * f_width_scale);
}
}
// read nine-neighbourhood
/*
0 1 2
+----------+----------+
|\ | /|
| \ | /|
| \ | / |
| \ | / |
3|_________\|/_________|5
| 4/|\ |
| /| \ |
| / | \ |
|/ | \ |
|/ | \|
+----------+----------+
6 7 8
*/
const int p_indices[] = {
0, 1, //4,
1, 2, //4,
2, 5, //4,
5, 8, //4,
8, 7, //4,
7, 6, //4,
6, 3, //4,
3, 0 //, 4
};
for(int i = 0; i < 8; ++ i) {
Vector3f a = v_pos[p_indices[i * 2]];
Vector3f b = v_pos[p_indices[i * 2 + 1]];
Vector3f c = v_pos[4];
// triangle
Vector3f v_tri_normal = (a - c).v_Cross(b - c);
v_tri_normal.Normalize();
// calculate normals
v_normal += v_tri_normal;
}
v_normal.Normalize();
}
// calculate normal from the heightmap (by averaging the normals of eight triangles that share the current point)
uint32_t n_normalmap =
0xff000000U |
(max(0, min(255, int(v_normal.z * 127 + 128))) << 16) |
(max(0, min(255, int(v_normal.y * 127 + 128))) << 8) |
max(0, min(255, int(-v_normal.x * 127 + 128)));
// calculate normalmap color
p_normalmap->p_buffer[x + wl * y] = n_normalmap;
// use the lightmap bitmap to store the results
}
}
(注意,這包含一些結構和功能未包括在內,所以你將無法使用這個代碼直接,但基本概念是存在的)
一旦你有了法線,你需要在位置(x,z)下取樣並在你的函數中使用它。這仍然會使圓盤與平坦表面(二階導數高的地方)之間存在陡坡的地形相交。爲了解決這個問題,你可以將光標向上擡起(沿着法線),或者禁用深度測試。
如果你的地形是多邊形的,你也可以使用頂點法線,只需要在(x,y,z)下面的三角形並插入它的頂點法線就可以得到光盤的法線。
我希望這可以幫助,隨時評論,如果您需要進一步的建議...