在stage3D中动态创建贴图并绘制地形阴影

昨天生成地形后贴图
terrain

这张图,用肉眼很难分辨高海拔和低海拔区域,毕竟没有参考物.类似雪盲症的效果.
那么我们可以动态创建贴图,在海拔较高的地方填充白色,感觉像雪山.海拔较低的地方填充亮黄色,像沙地,其他部分就填充绿色.

还记得之前我们灰度图读取出来的值都存在 m_heightMap 中吗? 这个数组存储的长度和我们贴图的像素数量一样的.这样我们就可以遍历像素的同时,取出当前像素对应的高度值,然后根据高度值填充我们想要的颜色.

代码如下
[cc lang=”actionscript3″]
public function genTexture(light : Vector3D) : void
{
var texWidth : int = m_numVertsPerRow;
var texHeight : int = m_numVertsPerCol;

var emptyBmd : BitmapData = new BitmapData(texWidth, texHeight,false);
emptyBmd.lock();

for(var i : int = 0; i < texHeight; i ++) { for(var j : int = 0; j < texWidth; j++) { var c : uint; var height : Number = getHeightMapEntry(i,j) / m_heightScale; if(height < 42.5) c = Utils.BEACH_SAND; else if(height < 85) c = Utils.LIGHT_YELLOW_GREEN; else if(height < 127.5) c = Utils.PUREGREEN; else if(height < 170) c = Utils.DARK_YELLOW_GREEN; else if(height < 212.5) c = Utils.DARKBROWN; else c = Utils.WHITE; emptyBmd.setPixel32(j,i,c); } } emptyBmd.unlock(); setTexture(emptyBmd); } public function getHeightMapEntry(row : int, col : int) : int { return m_heightMap[row * m_numVertsPerRow + col]; } public function setTexture(data : BitmapData) : void { m_texture = Utils.getTextureByBmd(data, m_context3D); } [/cc] 当创建好这张动态贴图并贴上之后,就是下图的样子了. followHeight


像 Utils.BEACH_SAND; 就是事先定义好的颜色.这里也提供出来.

[cc lang=”actionscript3″]
public static const WHITE : uint = 0xffffffff;
public static const BLACK : uint = 0xff000000;
public static const RED : uint = 0xffff0000;
public static const GREEN : uint = 0xff00ff00;
public static const BLUE : uint = 0xff0000ff;
public static const YELLOW : uint = 0xffffff00;
public static const CYAN : uint = 0xff00ffff;
public static const MAGENTA : uint = 0xffff00ff;

public static const BEACH_SAND : uint = 0xfffff99d;
public static const DESERT_SAND : uint = 0xfffacd87;

public static const LIGHTGREEN : uint = 0xff3cb878;
public static const PUREGREEN : uint = 0xff00a651;
public static const DARKGREEN : uint = 0xff007236;

public static const LIGHT_YELLOW_GREEN : uint = 0xff7cc576;
public static const PURE_YELLOW_GREEN : uint = 0xff39b54a;
public static const DARK_YELLOW_GREEN : uint = 0xff197b30;

public static const LIGHTBROWN : uint = 0xffc69c6d;
public static const DARKBROWN : uint = 0xff736487;
[/cc]

现在我们已经能很轻松的分辨山顶,绿地和沙地了. 但是效果还是比较假,只是纯色块区分而已. 接下来我们要给这些地形添加上阴影.
使用AGAL来绘制阴影是很耗费性能呢,如果我们要求不高,不做会移动的太阳的话,就可以事先把阴影绘制到贴图上,就像3DMAX中烘培贴图一样.

我们使用一个向下照射的灯光向量 <0,-1,0>的相反值来确定灯光的位置 <0,1,0>. 之后我们要做的就是遍历整个地形所有的面来计算它门和光线的夹角,角度越小距离获得的光线就越充足,当超过90°后,就获取不到灯光了.

通过灯光向量和平面法线的关系我们能创建一个阴影标量, 0~1之间的范围. 贴图中的每个像素乘以趋于0的值时,颜色就越暗. 乘以趋于1的值时就越亮.
要计算平面法线我们需要2个共面非0且不平行的向量.

shade

[cc lang=”actionscript3″]
//创建2个顶点
var u : Vector3D = new Vector3D(m_cellSpacing, heightB – heightA,0);
var v : Vector3D = new Vector3D(0,heightC – heightA, -m_cellSpacing);
[/cc]

面法线 N = u x v – > Norm(N) 标准化
光线和面法线的夹角 Norm(L) * Norm(N); 求出的值是 -1~1之间的值,由于小于0的值就是大于90°的值,是接收不到光照的,因此我们只要去取 0~1之间的值即可.

[cc lang=”actionscript3″]
/**
* 计算地形阴影
*/
private function computeShade(cellRow : int, cellCol : int, light : Vector3D) : Number
{
var heightA : Number = getHeightMapEntry(cellRow, cellCol);
var heightB : Number = getHeightMapEntry(cellRow,cellCol + 1);
var heightC : Number = getHeightMapEntry(cellRow + 1, cellCol);

//创建2个顶点
var u : Vector3D = new Vector3D(m_cellSpacing, heightB – heightA,0);
var v : Vector3D = new Vector3D(0,heightC – heightA, -m_cellSpacing);

//用方格中的两个向量叉积找到面法线
var n : Vector3D = u.crossProduct(v);
n.normalize();

var cosine : Number = n.dotProduct(light);

if(cosine < 0) cosine = 0; return cosine; } [/cc] 现在我们已经取到了阴影的亮度值了,只需要遍历贴图的所有颜色,然后将颜色乘以亮度值即可. [cc lang="actionscript3"] private function lightTerrain(bmd : BitmapData, light : Vector3D) : void { var bmdData : Vector. = bmd.getVector(bmd.rect);
for(var i : int = 0; i < bmd.height - 1; i++) { for(var j : int = 0; j < bmd.width - 1; j++) { //从uint中分解出RGB,附带上亮度值后还原 var bright : Number = computeShade(i,j,light); var color : Number = bmdData[i * bmd.width + j]; var red : Number = ((color & 0xFF0000) >> 16) * bright;
var green : Number = ((color & 0x00FF00) >> 8) * bright;
var blue : Number = ((color & 0x0000FF)) * bright;
var endColor : uint = red << 16 | green << 8 | blue; bmdData[i * bmd.width + j] = endColor; } } bmd.setVector(bmd.rect,bmdData); } [/cc] 最后我们只需要把计算过亮度的像素制作成地形的贴图即可. terrainShadow

看~这下地形的高低起伏就逼真多了.

最后的最后,让我们可以在地形上,高低不平的行走吧~ 嘛~这个留到明天再写吧.~
如果你等不及,你可以看下面的Demo,它已经可以被行走了.
Demo is Here

发表评论

电子邮件地址不会被公开。 必填项已用*标注