forked from: voxel3d
forked from voxel3d (diff: 1)
地表渲染 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.neas
ActionScript3 source code
/**
* Copyright andyshang ( http://wonderfl.net/user/andyshang )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/6UaD
*/
// forked from lizhi's voxel3d
// 地表渲染
//
// 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;
public class Newvoxc extends Sprite {
private var HMap:Array = createArray(256, 256); //=[256][256]; // 地表高度数组
private var CMap:Array = createArray(256, 256); //[256][256]; // 色彩值数组
private var Video:Array = []; //[320*200]; // 屏幕缓冲区
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 = 4090;
sa = 0.1;
addEventListener(Event.ENTER_FRAME, update);
}
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;
trace(ss);
//
// 刷新位置/角度
//
x0 += ss * Math.cos(a);
y0 += ss * Math.sin(a);
//
// 画一帧
//
View(x0, y0, a);
}
private function onKeyDown(e:KeyboardEvent):void {
//
// 处理用户输入
//
// if ( kbhit() )
// {
// if ( (k=getch())==0 ) k=-getch();
//}
}
//
// 将值限制在 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):Array {
var arr:Array = [];
for (var i:int = 0; i < x; i++){
arr[i] = [];
}
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); // 以坡度决定灰度
}
}
private var lasty:Array = [] //[320], // 画在指定列上的最后一个点
private var lastc:Array = [] //[320]; // 最后一点的颜色
//
// 画地表的一个"部分"; 它能画出距离视点一定远处的图象
// 使用 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) / 320;
sy = (y1 - y0) / 320;
for (i = 0; i < 320; 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 = L(x0 >> 16);
a = L(x0 >> 8);
v0 = L(y0 >> 16);
b = L(y0 >> 8);
u1 = L(u0 + 1);
v1 = L(v0 + 1);
//
// 由周围 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 > 199){
b -= (a - 199) * 320;
cc += (a - 199) * sc;
a = 199;
}
if (y < 0)
y = 0;
while (y < a){
Video[a * 320 + i] = cc >> 18;
cc += sc;
b -= 320;
a--;
}
lasty[i] = y;
}
lastc[i] = c;
//
// 进一步计算下一个 xy 坐标
//
x0 += sx;
y0 += sy;
}
}
private var FOV:Number = 3.141592654 / 4; // 45 度宽的视角
private var view:Bitmap = new Bitmap(new BitmapData(320, 200, false, 0xff0000));
//
// 画出从点 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 = [];
//
// 初始化 last-y 和 last-color 数组
//
for (d = 0; d < 320; d++){
lasty[d] = 200;
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;
//
// 无覆盖的由近及远画地表
//
for (d = 0; d < 100; d += 1 + (d >> 6)){
Line(x0 + d * 65536 * Math.cos(aa - FOV), y0 + d * 65536 * Math.sin(aa - FOV), x0 + d * 65536 * Math.cos(aa + FOV), y0 + d * 65536 * Math.sin(aa + FOV), h - 30, 100 * 256 / (d + 1));
}
//
// 将最终图象 blit 到屏幕
//
//_movedatal(_my_ds(), (unsigned)Video, _dos_ds, 0xa0000,
// 16000); //320*200/4
for (var i:int = 0; i < Video.length; i++){
view.bitmapData.setPixel(i % 320, i / 320, 0xffffff*Video[i]/0xff);
}
}
}
}
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;
}
}
}