voxel3d lizhi forked:1favorite:6lines:250license : MIT License modified : 2012-12-11 10:21:49 Embed Tweet // 地表渲染 // // OK.. 我知道 voxel 指的别的什么... 但是许多人使用 "voxel" 这个 // 名字来表示一种渲染技术.我写这个来解释 newvox4 渲染的基本思想; // newvox4 写的很糟糕 (名字里有个 4 是因为是继续到第四次的实验) // 而且它是用 pascal + asm 写的. 自从我得到了一些说明的请求, 我 // 便决定用 C 写一个渲染内核, 希望它能更容易被理解. 这段程序仅仅 // 是基本的地表 (没有天空等) 而且只支持键盘,但是我想你可以自己想 // 得到应该怎样写其它的部分 // // I'm releasing this code to the public domain for free... and as it's // probably really obvious there's no warranty of any kind on it. // You can do whatever you want with this source; however a credit in any // program that uses part of this code would be really appreciated :) // // 欢迎给予任何评价和建议 :) // // Andrea "6502" Griffini, programmer // agriff@ix.netcom.com // http://vv.val.net/~agriffini // // 译者 注: // 我在学习 3D 地表的生成算法时有幸拜读了这段程序,深受启发. // 原来的程序已经是很清晰了, 但作者还是加入了少许优化. 我将仅有的 // 优化也去掉了 ;) 想使程序更容易被理解. 大多数英文注解被我用汉语 // 从写了. 并在许多地方描述的更详细. 希望能对大家有更多的帮助 :-) // btw, 原来的程序是用 Watcom C 写的, 云风用 Djgpp 重写了 // 这个程序写的极为清晰, 所以它没有过多的效果修饰, 如果你能花上一定 // 时间 (云风花了 10 分钟 ;) 读懂它, 就可以进一步的增加边缘平滑, 背景 // 图案等等效果. 这些云风在自己的程序里都加上了, 并对程序做了许多优化, // 但是为了让大家更容易的读懂, 还是在这里保留了程序的原貌. // // 有任何问题, 欢迎和我讨论 :) // 云风 cloudwu@163.net // http://www.nease.net/~cloudwu // // //as3 code @author sliz http://game-develop.net/blog/ // // package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.events.Event; import flash.events.KeyboardEvent; import flash.filters.BlurFilter; import flash.utils.ByteArray; import net.hires.debug.Stats; [SWF(frameRate=60)] public class Newvoxc extends Sprite { private var W:int = 465; private var H:int = 465; private var HP1:int = H - 1; private var CMap:Vector.<Vector.<uint>> = createArray(256, 256); //[256][256]; // 色彩值数组 private var HMap:Vector.<Vector.<uint>> = createArray(256, 256); //=[256][256]; // 地表高度数组 private var Video:ByteArray=new ByteArray //[320*200]; // 屏幕缓冲区 private var lasty:Vector.<uint> = new Vector.<uint>(W); //[320], // 画在指定列上的最后一个点 private var lastc:Vector.<uint> = new Vector.<uint>(W); //[320]; // 最后一点的颜色 private var FOV:Number = Math.PI / 4; // 45 度宽的视角 private var view:Bitmap = new Bitmap(new BitmapData(W, H, false, 0xff0000)); private var ss:Number, sa:Number, a:Number, s:Number; private var x0:int, y0:int; private var input:Input; public function Newvoxc() { addChild(view); input = new Input(stage); var i:int, k:int; // // 计算地图高度 // ComputeMap(); // // 主循环 // // a = 角度 // x0,y0 = 当前坐标 // s = 固定速度 // ss = 当前向前/向后的速度 // sa = 旋转角速度 // a = 0; k = x0 = y0 = 0; s = 4096; ss = 8090; sa = 0.1; addEventListener(Event.ENTER_FRAME, update); addChild(new Stats); } private function update(e:Event):void { if (input.left) { sa -= 0.005; }else if(input.right){ sa += 0.005; }else { sa = 0; } if (input.up) { ss += s; }else if(input.down){ ss -= s; } a += sa; // // 刷新位置/角度 // x0 += ss * Math.cos(a); y0 += ss * Math.sin(a); // // 画一帧 // View(x0, y0, a+(mouseX-stage.stageWidth/2)*4/stage.stageWidth); } // // 将值限制在 0..255 之间 // private function Clamp(x:int):int { return Math.max(0, Math.min(x, 255)); } // // 取 x 的低字节位, 即对 255 取模 (HMap 和 CMap 都是 256 x 256 的数组) // private function L(x:int):int { return x & 0xff; } private function createArray(x:int, y:int):Vector.<Vector.<uint>> { var arr:Vector.<Vector.<uint>> = new Vector.<Vector.<uint>>(x); for (var i:int = 0; i < x; i++){ arr[i]=new Vector.<uint>(y); } return arr; } private function rand():int { return 2147483647 * Math.random(); } // // 地表高度和色彩表的计算 // private function ComputeMap():void { var p:int, i:int, j:int, k:int, k2:int, p2:int; // // 从一个平坦的地表开始 // HMap[0][0] = 128; for (p = 256; p > 1; p = p2){ p2 = p / 2; k = p * 8 + 20; k2 = k / 2; for (i = 0; i < 256; i += p){ for (j = 0; j < 256; j += p){ var a:int, b:int, c:int, d:int; a = HMap[i][j]; // a:::::::b b = HMap[L(i + p)][j]; // ::::::::: 以 a,b,c,d c = HMap[i][L(j + p)]; // ::::::::: 为角的区域 d = HMap[L(i + p)][L(j + p)]; // c:::::::d HMap[i][L(j + p2)] = // 在 a,c 中点,以a,c平均高度为基准 Clamp(((a + c) >> 1) + (rand() % k - k2)); // 产生一随机的高度 HMap[L(i + p2)][L(j + p2)] = // 在 a,b,c,d 区域中心,以平均高度 Clamp(((a + b + c + d) >> 2) + (rand() % k - k2)); // 为基准,产生一随机高度 HMap[L(i + p2)][j] = // 在 a,b 中点,以a,b平均高度为基准 Clamp(((a + b) >> 1) + (rand() % k - k2)); // 产生一随机的高度 } } } // // 平滑处理 // for (k = 0; k < 3; k++) for (i = 0; i < 256; i++) for (j = 0; j < 256; j++){ HMap[i][j] = (HMap[L(i + 1)][j] + HMap[i][L(j + 1)] + //将前后左右,四个点取 HMap[L(i - 1)][j] + HMap[i][L(j - 1)]) / 4; //平均值,这样做平滑 } // // 颜色计算 (地表高度的衍生物) // for (i = 0; i < 256; i++) for (j = 0; j < 256; j++){ k = 128 + (HMap[L(i + 1)][L(j + 1)] - HMap[i][j]) * 4; CMap[i][j] = Clamp(k); // 以坡度决定灰度 } } // // 画地表的一个"部分"; 它能画出距离视点一定远处的图象 // 使用 lasty 数组中保存的上次画过的位置, 保正了这个部分不会 // 覆盖掉以前画的部分. x0,y0 和 x1,y1 和 xy 坐标描述 // 地表的高度, hy 是视点的高度, s 是由距离决定的比例因子. // x0,y0,x1,y1 是 16.16 的定点数, // 比例因子是 16.8 的定点值. // private function Line(x0:int, y0:int, x1:int, y1:int, hy:int, s:int):void { var i:int, sx:int, sy:int; // 计算 xy 速度 sx = (x1 - x0) / W; sy = (y1 - y0) / W; for (i = 0; i < W; i++){ var c:int, y:int, h:int, u0:int, v0:int, u1:int, v1:int, a:int, b:int, h0:int, h1:int, h2:int, h3:int; // // 计算 xy 坐标; a 和 b 将被定位于 // 一个 (0..255)(0..255) 的区间里面. // u0 = (x0 >> 16)&0xff; a = (x0 >> 8)&0xff; v0 = (y0 >> 16)&0xff; b = (y0 >> 8)&0xff; u1 = (u0 + 1)&0xff; v1 = (v0 + 1)&0xff; // // 由周围 4 个点来决定里面的高度 // h0 = HMap[v0][u0]; h2 = HMap[v1][u0]; h1 = HMap[v0][u1]; h3 = HMap[v1][u1]; h0 = (h0 << 8) + a * (h1 - h0); h2 = (h2 << 8) + a * (h3 - h2); h = ((h0 << 8) + b * (h2 - h0)) >> 16; // // 由周围 4 个点来决定里面的颜色 (颜色值是 16.16 的定点数) // h0 = CMap[v0][u0]; h2 = CMap[v1][u0]; h1 = CMap[v0][u1]; h3 = CMap[v1][u1]; h0 = (h0 << 8) + a * (h1 - h0); h2 = (h2 << 8) + a * (h3 - h2); c = ((h0 << 8) + b * (h2 - h0)); // // 使用比例因子计算屏幕高度 // y = (((h - hy) * s) >> 11) + 100; // // 画一列 // if (y < (a = lasty[i])){ var sc:int, cc:int; if (lastc[i] == -1) lastc[i] = c; sc = (c - lastc[i]) / (a - y); cc = lastc[i]; if (a > HP1){ b -= (a - HP1) * W; cc += (a - HP1) * sc; a = HP1; } if (y < 0) y = 0; while (y < a){ var bc:uint = cc >>17; var bi:int = 4*(a * W + i); Video[bi] = bc; Video[++bi] = bc; Video[++bi] = bc; Video[bi] = bc; cc += sc; b -= W; a--; } lasty[i] = y; } lastc[i] = c; // // 进一步计算下一个 xy 坐标 // x0 += sx; y0 += sy; } } // // 画出从点 x0,y0 (16.16) 以 a 角 看到的图象 // private function View(x0:int, y0:int, aa:Number):void { var d:int; var a:int, b:int, h:int, u0:int, v0:int, u1:int, v1:int, h0:int, h1:int, h2:int, h3:int; // // 清除屏幕缓冲 // // memset(Video,0,320*200); Video.clear(); Video.length = W * H*4; // // 初始化 last-y 和 last-color 数组 // for (d = 0; d < W; d++){ lasty[d] = H; lastc[d] = -1; } // // 计算视点高度变量 // // 计算 xy 坐标; a 和 b 将被定位于 // 一个 (0..255)(0..255) 的区间里面. // u0 = (x0 >> 16) & 0xFF; a = (x0 >> 8) & 255; v0 = (y0 >> 16) & 0xFF; b = (y0 >> 8) & 255; u1 = (u0 + 1) & 0xFF; v1 = (v0 + 1) & 0xFF; // // 由周围 4 个点来决定里面的高度 // h0 = HMap[v0][u0]; h2 = HMap[v1][u0]; h1 = HMap[v0][u1]; h3 = HMap[v1][u1]; h0 = (h0 << 8) + a * (h1 - h0); h2 = (h2 << 8) + a * (h3 - h2); h = ((h0 << 8) + b * (h2 - h0)) >> 16; // // 无覆盖的由近及远画地表 // var sx0:int = 65536 * Math.cos(aa - FOV); var sy0:int = 65536 * Math.sin(aa - FOV); var sx1:int = 65536 * Math.cos(aa + FOV); var sy1:int = 65536 * Math.sin(aa + FOV); var hp30:uint = Math.min(60,h - mouseY); for (d = 0; d < 200; d +=1+(d>>6)){ Line(x0 + d * sx0, y0 + d * sy0, x0 + d * sx1, y0 + d * sy1, hp30, 25600 / (d+1)); } // // 将最终图象 blit 到屏幕 // //_movedatal(_my_ds(), (unsigned)Video, _dos_ds, 0xa0000, // 16000); //320*200/4 view.bitmapData.lock(); view.bitmapData.setPixels(view.bitmapData.rect, Video); view.bitmapData.unlock(); } } } import flash.display.Stage; import flash.events.KeyboardEvent; import flash.ui.Keyboard; class Input { private var stage:Stage; private var leftKeyCodes:Array = [Keyboard.LEFT,"A".charCodeAt(0)]; private var rightKeyCodes:Array = [Keyboard.RIGHT,"D".charCodeAt(0)]; private var downKeyCodes:Array = [Keyboard.DOWN,"S".charCodeAt(0)]; private var upKeyCodes:Array = [Keyboard.UP, "W".charCodeAt(0)]; public var left:Boolean = false; public var right:Boolean = false; public var down:Boolean = false; public var up:Boolean = false; public function Input(stage:Stage) { this.stage = stage; stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown); stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp); } private function onKeyUp(e:KeyboardEvent):void { if (leftKeyCodes.indexOf(e.keyCode)!=-1) { left = false; }else if (rightKeyCodes.indexOf(e.keyCode)!=-1) { right = false; }else if (downKeyCodes.indexOf(e.keyCode)!=-1) { down = false; }else if (upKeyCodes.indexOf(e.keyCode)!=-1) { up = false; } } private function onKeyDown(e:KeyboardEvent):void { if (leftKeyCodes.indexOf(e.keyCode)!=-1) { left = true; right = false; }else if (rightKeyCodes.indexOf(e.keyCode)!=-1) { right = true; left = false; }else if (downKeyCodes.indexOf(e.keyCode)!=-1) { down = true; up = false; }else if (upKeyCodes.indexOf(e.keyCode)!=-1) { up = true; left = false; } } } Code Fullscreen Preview Fullscreen fujimaru Albert tjoen Bruce_Jawn : terrain denis : voxel3dvoxel3d jian.wang79 : 3d 地表 terrain voxel3d keyCode stage charCodeAt addChild indexOf KeyboardEvent ByteArray clear mouseY mouseX KeyboardEvent.ENTER_FRAME Keyboard.DOWN Keyboard.UP Keyboard.RIGHT KeyboardEvent.KEY_UP Keyboard.LEFT stageWidth Math.cos KeyboardEvent.KEY_DOWN Vector sort new page view favorite forked pv212 forked from: voxel3d andyshang forked:0 favorite:0lines:233 (diff:1)