zoukankan      html  css  js  c++  java
  • 上世纪的地形渲染方式的实现

    众所周知,上世纪的计算机在性能上都没法跟现在的计算机比,可那时的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的。

  • 相关阅读:
    NHibernate初学者指南系列文章导航
    c# 类一般在哪里实例化,是在类内、方法内还是其他地方?
    日期和时间的正则表达式
    virtual和abstract区别
    VS2010和选中代码相同的代码的颜色设置,修改高亮颜色
    SqlServer表和EXCEL数据互相复制方法
    C#操作XML的方法
    1、Spring Boot 2.x 简介
    C语言学习系列(六)基本语法
    C语言学习系列(六)存储类
  • 原文地址:https://www.cnblogs.com/time-flow1024/p/10061722.html
Copyright © 2011-2022 走看看