Introduction
标题地形渲染是一个非常大的标题,本文将集中介绍对地形的光照和阴影的渲染。在下图中你将看 到这种技术的效果。
使用本技术产生的地形阴影
法则描述
本法则实际上非常简单,对于每一个栅格点,我们选择从灯光坐标点发出的光线到地图的交点。 由于只选择射线L下面的点因此非常快。如下图:
让我们了解下面的符号:
A = 目标点
B = 灯光位置的投影坐标( vector B = vector(sun_pos.x, 0, sun_pos.z) );
C = 灯光位置坐标;
L = 光线矢量 ( L = A - C );
P = 光线经过路线的所有投影点;
X(P) = 投影点P在光线矢量上的坐标(用于单独计算LERP)。
点P用来参与从点A到点B之间的2D线计算。对于任意点P,如果在高度图中它的值大于点X(P) 的Y值,那么就选择他。这样我们就知道光线L与高度图相交,那么点A的光照值就等于'ambient_color_value', 接着我们就可以处理下一个目标点。
如果所有的P类型的点都测试完没有发现交点,那么点A的光照值就使用下面的公式
Illum(A) = ambient_color_value + (L dot N)
值的范围为[0,1]。
执行运算
如果上面的运算你还不明白,下面的代码将帮助你。最重要的函数是intersect_map。这个函数 检查目标点是否被遮挡。测试光线是否与高度图有相交。这个函数调用高度图中每一个点。当发现一 个交点时,就停止测试,接着测试下一个(如右图中的点)。就象我在运算法则描述中的那样,如果 目标点没有被遮挡,它的光照值使用上面的公式。
函数genLightmap 用于产生光照图。它测试和照亮高度图中所有点。
注意:如果你使用下面的代码,你应当知道法线被从每个部件是FLOAT类型压缩为一个字节。
int intersect_map(const vector3& iv,const ray& r,Image* hm,float fHeightScale){
int w,hits;
float d,h,D;
vector3 v,dir;
v = iv + r.direction;
w = hm-w;
hits = 0;
while (!(( v.x = w-1 ) || ( v.x = w-1 ) || ( v.z
// length of lightdir's projection
D = Magnitude(vector3(v.x,0,v.z)-vector3(r.origin.x,0,r.origin.z));
d = Magnitude(iv-v);
// light direction
h = iv.y + (d*r.origin.y) / D;
// X(P) point
// check if height in point P is bigger than point X's height
if (hm-data[ifloor(v.z)* w + ifloor(v.x)] * fHeightScale h){
hits++;
// if so, mark as hit, and skip this work point.
break;
};
dir = r.direction;
dir.y = 0;
v += Normalize(dir);
// fetch new working point
};
return hits;
};
Image* genLightmap(char* normal,Image* hm,vector3 fSunDir,int w,float fAmbient){
int i,j,hits;
float f,dot;
vector3 n,fVertex;
Image* lmap;
ray r;
float fHeightScale = 10.0f / 255.0f;
lmap = new Image(w,w,1);
if (!lmap){printf("(!) Error: cannot alloc lightmap!\n");return 0;};
for (j=0; jdata[j*w+i] * fHeightScale;
fVertex.z = j;
f = fAmbient ;
r.origin = fVertex + fSunDir * 2000.0f;
r.direction = fSunDir;
// checks current working point for intersection
if (!intersect_map(fVertex,r,hm,fHeightScale)){
// compute the lighting equation
n.x = (float)(normal[3*(j*w+i)+0]);
n.y = (float)(normal[3*(j*w+i)+1]);
n.z = (float)(normal[3*(j*w+i)+2]);
f += 0.5f*(1.0f+DotProduct(Normalize(n),Normalize(fSunDir)));
if (f1.0f) f = 1.0f;
};
dot = f * 255.0f;
lmap-data[j*w+i] = (unsigned char)dot;
};
};
return lmap;
};
在你成功的建立你自己的光照图后,你或许将要使用它。我将告诉你如何把他添加到你的引擎中, 这里有两个方法:一是把他当作光照图使用,另外一个方法是设置地形坐标的每一个点的颜色为它相 应阴影图中的颜色。
Terrain color map
Terrain shadow map
Final terrain texture
Lightmapping
如果你打算把阴影图当作光照图使用,你应当把他当作纹理部件的一部分,如果你准备使用这项技 术,你应该设置纹理的环境光模式为modulate。在OPENGL中,代码如下
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
颜色部件
当使用阴影图象素作为地形图元的颜色部件时,你应当为每一个顶点部件使用单独的缓冲。 By doing this, you will be able to send the data returned by genLightmap directly to the renderer, by setting the color array pointer to the address of the returned data. In OpenGL, this is done with the glColorPointer function.