• 上世纪的地形渲染方式的实现


    众所周知,上世纪的计算机在性能上都没法跟现在的计算机比,可那时的CPU极慢,浮点性能极低,那时候的程序员一谈到除法就眉头紧皱(因为那会CPU算除法的开销很大),可人们却又想玩游戏,怎么办?

    于是那时的程序员们想到了各种聪明的方法去实现各种图形学算法,这些算法的特点在于:很符合那时的计算机硬件特点(内存小,主频低,IO慢)。我前几个月无意在github上发现了一个渲染地形的算法,原文链接https://github.com/s-macke/VoxelSpace,觉得挺巧妙的,所以就实现了一下。原作者使用python实现的,20行代码,我觉得执行速度慢,用的C++,上百行(......)

    这个算法的原理可以用如下这个动图描述,一个colormap,RGB,用于表示每个地形位置的颜色,一个heightmap,Grayscale,用于表示高度信息。感兴趣的人看明白主要思想后,也可以直接实现一个,所以这里直接贴代码好像没有意义:

     

     

    我用的是SDL图形库(这个库入门很简单,最初都是国外人用,但最近越来越多的国内人也在用,负责处理在屏幕上绘制像素点级的操作)

    贴整个程序意义不大,在这里就只贴上最主要的部分:

    采样结构对象定义,每次对colormap和heightmap采样后返回一个这样的结构对象:

    1 struct TerrainSample
    2 {
    3     double h;           // height
    4     uint8_t r, g, b, a; // color
    5 };

    然后是用于插值的lerp函数系列:

     1 inline double lerp(double xmin, double xmax, double weight)
     2 {
     3     return xmin * (1 - weight) + xmax * weight;
     4 }
     5 
     6 // uint8_t 2d lerp
     7 template <typename Ty>
     8 inline Ty lerp2d_scalar(Ty a0, Ty a1, Ty a2, Ty a3, double xw, double yw)
     9 {
    10     double t1 = lerp(double(a0), double(a1), xw);
    11     double t2 = lerp(double(a2), double(a3), xw);
    12     double t3 = lerp(t1, t2, yw);
    13     return (Ty)t3;
    14 }

    执行采样的时候,由一个类实例对纹理采样,返回一个TerrainSample结构,以下是实现该功能的一个成员函数:

     1 TerrainSample sample(double x, double y)
     2 {
     3     TerrainSample ts;
     4     x = x - floor(x);
     5     y = y - floor(y);
     6     if (tsq == Point)
     7     {
     8         int px = int(x * colormap_w);
     9         int py = int(y * colormap_h);
    10         ts.r = colormap[4 * (py * colormap_w + px)];
    11         ts.g = colormap[4 * (py * colormap_w + px) + 1];
    12         ts.b = colormap[4 * (py * colormap_w + px) + 2];
    13         ts.a = colormap[4 * (py * colormap_w + px) + 3];
    14         px = int(x * heightmap_w);
    15         py = int(y * heightmap_h);
    16         ts.h = heightmap[py * colormap_w + px] / 2048.0;
    17     }
    18     else if (tsq == Linear)
    19     {
    20         x *= colormap_w;
    21         y *= colormap_h;
    22         double xl = x - 0.5;
    23         double xr = x + 0.5;
    24         double yu = y - 0.5;
    25         double yd = y + 0.5;
    26         double xw, yw;
    27         xw = (x - floor(xl)) - 0.5;
    28         yw = (y - floor(yu)) - 0.5;
    29         xl = xl - floor(xl);
    30         xr = xr - floor(xr);
    31         yu = yu - floor(yu);
    32         yd = yd - floor(yd);
    33         uint8_t p0[4], p1[4], p2[4], p3[4];
    34         double h[4];
    35         uint32_t *pixel = (uint32_t *)colormap;
    36         split32(pixel[int(yu) * colormap_w + int(xl)], p0, p0 + 1, p0 + 2, p0 + 3); // xl,yu
    37         split32(pixel[int(yu) * colormap_w + int(xr)], p0, p0 + 1, p0 + 2, p0 + 3); // xr,yu
    38         split32(pixel[int(yd) * colormap_w + int(xl)], p0, p0 + 1, p0 + 2, p0 + 3); // xl,yd
    39         split32(pixel[int(yd) * colormap_w + int(xr)], p0, p0 + 1, p0 + 2, p0 + 3); // xr,yd
    40         h[0] = heightmap[int(yu) * heightmap_w + int(xl)];
    41         h[1] = heightmap[int(yu) * heightmap_w + int(xr)];
    42         h[2] = heightmap[int(yd) * heightmap_w + int(xl)];
    43         h[3] = heightmap[int(yd) * heightmap_w + int(xr)];
    44         uint8_t color[4];
    45         double height;
    46         color[0] = lerp2d_scalar(p0[0], p1[0], p2[0], p3[0], xw, yw);
    47         color[1] = lerp2d_scalar(p0[1], p1[1], p2[1], p3[1], xw, yw);
    48         color[2] = lerp2d_scalar(p0[2], p1[2], p2[2], p3[2], xw, yw);
    49         color[3] = lerp2d_scalar(p0[3], p1[3], p2[3], p3[3], xw, yw);
    50         height = lerp2d_scalar(h[0], h[1], h[2], h[3], xw, yw);
    51         ts.r = color[0];
    52         ts.g = color[1];
    53         ts.b = color[2];
    54         ts.a = color[3];
    55         ts.h = height / 2048.0;
    56     }
    57     return ts;
    58 }

    程序跑出来的几个结果图如下,渲染结果里可以大致看出山脉的起伏,640x480分辨率。

    算法的局限性在于它只能渲染平视地形的情况,虽说可以加入俯仰角,但是本质上是个hack,不能上仰/下俯太多角度,否则会出现视图拉伸,整个图像会有平行四边形的那种切变特点,失真较大,而且对于近距物体的表现不佳,颗粒感较为明显,读者可以把对高度图的nearest filter改为bilinear filter试一下效果,也许会创造出其它的一些有趣效果。但是对于一个上世纪的地形渲染来说,这个小算法背后的想法还是挺cute的。

  • 相关阅读:
    poj 2584 T-Shirt Gumbo (二分匹配)
    hdu 1757 A Simple Math Problem (乘法矩阵)
    矩阵之矩阵乘法(转载)
    poj 2239 Selecting Courses (二分匹配)
    hdu 3661 Assignments (贪心)
    hdu 1348 Wall (凸包)
    poj 2060 Taxi Cab Scheme (二分匹配)
    hdu 2202 最大三角形 (凸包)
    hdu 1577 WisKey的眼神 (数学几何)
    poj 1719 Shooting Contest (二分匹配)
  • 原文地址:https://www.cnblogs.com/time-flow1024/p/10061722.html
Copyright © 2020-2023  润新知