网页功能: 加入收藏 设为首页 网站搜索  
3D地表生成及渲染 (VOXEL)
发表日期:2007-01-16作者:[转贴] 出处:  

想 跟 着 云 风 的 讲 解 来 慢 慢 体 会 吗? 那 么 就 先 看 看 右 边 的 效 果 图, 来 个 感 性 的 认 识 吧. yeah! 这 就 是 我 们 要 达 到 的 效 果 ;-) 是 不 是 和 某 些 游 戏 里 采 用 的 Engine 的 效 果 不 大 一 样? 是 的, 我 们 不 准 备 使 用 多 边 形. 这 个 算 法 产 生 的 3D 地 表比 用 多 边 形 产 生 出 来 的 更 平 滑, 在 英 文 里 我 们 称 其 为 voxel. 它 大 量 的 被 用 于 现 在 的 模 拟 飞 行 的 游 戏 中, 记 得 Commache I 就 是 因 为 采 用 这 个 技 术, 而 使 我 耳 目 一 新, 顿 时 爱 上 了 这 个 游 戏 :-)

闲 话 不 提 了, 我 在 学 习 3D 地 表 的 生 成 算 法 时, 有 幸 拜 读 了 一 段 程 序, 深 受 启 发. 原 本 早 就 应 该 将 其 介 绍 给 大 家, 一 直 没 有 时 间 写 这 篇 文 章. 这 两 天 没 有 更 新 主 页, 来 这 儿 访 问 的 朋 友 还 是 那 么 多, 真 有 点 不 好 意 思. 就 为 大 家 花 点 时 间, 再 写 篇 有 价 值 的 东 东 吧:-)

和 这 个 3D 地 表 有 关 的, 有 两 个 部 分:一 是 生 成 的 算 法; 二 是 显 示 的 算 法.地 表 和 其 它 的 物 体 不 同, 它 没 有 复 杂 的 3D 结 构, 所 以 可 以 不 用 多 边 形 去 描 述 它. 这 里 我 们 采 用 了 一 个 256 x 256 的 数 组 来 储 存 这 个 范 围 内 的 每 一 个 点 的 高 度. 而 地 表 的 光 泽, 也 是 预 先 算 好 的 ;) 同 样 储 存 在 一 个 256 x 256 的 数 组 了. 渲 染 的 方 法 是 利 用 坡 度 (即 和 周 围 点 的 高 度 差 来 决 定 的), 当 然 你 也 可 以 考 虑 点 的 绝 对 高 度, 比 如 在 绝 对 高 度 高 的 区 域 白 一 些, 以 造 成 一 种 山 顶 积 雪 的 感 觉, 这 就 是 你 自 己 的 发 挥 了. 在 生 成 地 表 时,第 一 步 是 决 定 外 形 的 概 况,这 里 采 用 的 是 随 机 的 方 式. 由 粗 到 细, 逐 步 细 化, 每 次 地 表 上 下 波 动 的 幅 度, 由 运 算 的 面 积 来 决 定. 而 在 实 际 运 用 时, 可 以 在 随 机 的 过 程 中, 加 入 一 些 限 制, 来 控 制 地 表 的 生 成. 第 二 步, 就 是 将 前 面 生 成 的 图 象 做 平 滑 处 理, 让 每 个 点 去 和 周 围 的 点 运 算, 取 平 均 值, 使 不 至 于 出 现 过 大 的 变 化, 这 个 过 程 多 重 复 几 次 (这 里 是 3 次), 就 可 以 得 到 上 佳 的 效 果 了 :-) 由 于 所 有 的 生 成 部 分 都 是 预 先 算 好, 所 以 可 以 不 考 虑 速 度 问 题.

显 示 时, 同 样 不 需 要 过 多 的 数 学 知 识. 我 们 由 近 及 远 的 画 出 地 表 就 可 以 了. 只 要 知 道, 距 离 视 点 越 远, 看 到 的 高 度 就 越 低, 利 用 实 际 高 度 和 距 离, 不 难 计 算 出 应 当 在 屏 幕 上 绘 制 的 高 度. 如 果 你 以 前 稍 微 研 究 过 3D Engine, 就 不 难 理 解 我 的 意 思. 而 出 于 地 表 3D 结 构 的 简 单, 远 处 的 部 分 是 不 会 遮 挡 住 近 处 的 部 分 的, 这 个 减 小 了 许 多 设 计 难 度. 只 是 在 处 理 视 线 和 我 们 生 成 的 地 图 的 x,y 轴 成 一 定 角 度 时, 需 要 使 用 一 点 三 角 知 识.

我 最 大 的 遗 憾 是, 目 前 还 没 有 搞 清 视 线 不 是 水 平 时 的 算 法. (如 果 使 用 多 边 形 产 生 地 表, 却 很 简 单, 这 个 是 另 一 篇 文 章 的 内 容 了)

也 许 解 说 的 太 简 单, 但 是 我 认 为 你如 果 能 欣 赏 一 下 源 代 码, 一 切 都 会 变 的 简 单. 原 来 的 程 序 已 经 是 很 清 晰 了, 但 作 者 还 是 加 入 了 少 许 优 化. 为 了 写 这 篇 教 学 性 质 的 文 章,云 风 又 将 程 序 重 写 了 一 遍 (使 用 的 Djgpp 编 译),更 是 添 加 了 非 常 详 细 的 中 文 注 解. 大 家 慢 慢 品 味 吧 :-)

#include <stdio.h>
#include <dos.h>
#include <go32.h>
#include <conio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <sys/movedata.h>
#include <sys/segments.h>
// 将值限制在 0..255 之间
#define Clamp(x) ((x)<0 ? 0 : ((x)>255 ? 255 : (x)))
// 取 x 的低字节位, 即对 255 取模 (HMap 和 CMap 都是 256 x 256 的数组)
#define L(x) ((x)&0xff)

typedef unsigned char byte;
byte HMap[256][256];    // 地表高度数组
byte CMap[256][256];    // 色彩值数组
byte Video[320*200];    // 屏幕缓冲区

// 地表高度和色彩表的计算

void ComputeMap(void)
{
    int p,i,j,k,k2,p2;

    // 从一个平坦的地表开始

    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 )
            {
                int a,b,c,d;
                a=HMap[i][j];
                b=HMap[ L(i+p) ][j];
                c=HMap[i][ L(j+p) ];
                d=HMap[ L(i+p)][ L(j+p) ];
                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);     // 以坡度决定灰度
        }
}

int lasty[320],         // 画在指定列上的最后一个点
lastc[320];             // 最后一点的颜色

// 画地表的一个"部分"; 它能画出距离视点一定远处的图象
// 使用 lasty 数组中保存的上次画过的位置, 保正了这个部分不会
// 覆盖掉以前画的部分. x0,y0 和 x1,y1 和 xy 坐标描述
// 地表的高度, hy 是视点的高度, s 是由距离决定的比例因子.
// x0,y0,x1,y1 是 16.16 的定点数,
// 比例因子是 16.8 的定点值.

void Line(int x0,int y0,int x1,int y1,int hy,int s)
{
    int i,sx,sy;
    // 计算 xy 速度
    sx=(x1-x0)/320; sy=(y1-y0)/320;
    for ( i=0; i<320; i++ )
    {
        int c,y,h,u0,v0,u1,v1,a,b,h0,h1,h2,h3;

        // 计算 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]) )
        {
            unsigned char *b=Video+a*320+i;
            int sc,cc;
            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>18; cc+=sc;
            b-=320; a--;
        }
        lasty[i]=y;
    }
    lastc[i]=c;

    // 进一步计算下一个 xy 坐标

    x0+=sx; y0+=sy;
    }
}

float FOV=3.141592654/4;   // 45 度宽的视角

// 画出从点 x0,y0 (16.16) 以 a 角 看到的图象

void View(int x0,int y0,float aa)
{
    int d;
    int a,b,h,u0,v0,u1,v1,h0,h1,h2,h3;

    // 清除屏幕缓冲

    memset(Video,0,320*200);

    // 初始化 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*cos(aa-FOV),y0+d*65536*sin(aa-FOV),
             x0+d*65536*cos(aa+FOV),y0+d*65536*sin(aa+FOV),
             h-30,100*256/(d+1));
    }

    // 将最终图象 blit 到屏幕

    _movedatal(_my_ds(), (unsigned)Video, _dos_ds, 0xa0000, 16000); //320*200/4
}

void main(void)
{
    union REGS r;
    int i,k;
    float ss,sa,a,s;
    int x0,y0;

    // 进入 320x200x256 模式

    r.w.ax=0x13; int386(0x10,&r,&r);

    // 设置前 64 个颜色为 64 级灰度

    for ( i=0; i<64; i++ )
    {
        outp(0x3C8,i);
        outp(0x3C9,i);
        outp(0x3C9,i);
        outp(0x3C9,i);
    }

    // 计算地图高度

    ComputeMap();

    // 主循环
    //   a     = 角度
    //   x0,y0 = 当前坐标
    //   s     = 固定速度
    //   ss    = 当前向前/向后的速度
    //   sa    = 旋转角速度

    a=0; k=x0=y0=0;
    s=4096;
    ss=0; sa=0;
    while(k!=27)
    {

        // 画一帧

        View(x0,y0,a);

        // 刷新位置/角度

        x0+=ss*cos(a); y0+=ss*sin(a);
        a+=sa;

        // 处理用户输入

        if ( kbhit() )
        {
            if ( (k=getch())==0 ) k=-getch();
            switch(k)
            {
            case -75: sa-=0.005; break;           // 左
            case -77: sa+=0.005; break;           // 右
            case -72: ss+=s; break;               // 前
            case -80: ss-=s; break;               // 后
            }
        }
    }

    // 退回到文本模式

    r.w.ax=0x03; int386(0x10,&r,&r);
}

我来说两句】 【加入收藏】 【返加顶部】 【打印本页】 【关闭窗口
中搜索 3D地表生成及渲染 (VOXEL)
本类热点文章
  DDraw和D3D立即模式编程手册
  矩阵求逆的快速算法
  本地化支持:OGRE+CEGUI中文输入:OGRE方..
  Direct3D中实现图元的鼠标拾取
  3D场景中的圆形天空顶
  OpenGL显卡编程
  一种高效的基于大规模地形场景的OCCLUS..
  一个完善的读取3DS文件例子
  如何制作一个可控制的人体骨骼模型
  Direct3D 入门之我见
  Slope(斜坡) 法线生成算法,在地形渲染..
  新手翻译的FAQ--可视和转换(学opengl两..
最新分类信息我要发布 
最新招聘信息

关于我们 / 合作推广 / 给我留言 / 版权举报 / 意见建议 / 广告投放  
Copyright ©2003-2024 Lihuasoft.net webmaster(at)lihuasoft.net
网站编程QQ群   京ICP备05001064号 页面生成时间:0.00556