翻译: 星球生成 I
翻译: 星球生成 I
本文翻译自Planet Generation - Part I
- 译者: FreeBlues
以下为译文:
概述
我一直是一个过程内容生成的爱好者, 它允许你创建一个甚至不断改变的潜在内容无限的游戏. 它也弥补了我在艺术天赋方面的欠缺.
过程景观生成对很多游戏都很有用, 从策略游戏中的侦察模式(Skirmish mode)到单人地牢游戏(dungeon in a Roguelike), 现在我将要探索如何生成星球.
星球截图:
在过去进行平面和三维像素地形生成时, 使我熟悉了相当一些渲染生成算法, 但是在这之前我从未制造过星球, 我决定现在是时候改变这一事实了.
计划
现实世界中的星球包含了大量的信息. 地球额表面区域大约有 5.1
亿平方公里, 几乎 2/3
的地方是水, 1/3
的地方是陆地. 这些是大量的你想要生成的地形数据.
穿越陆地, 我们有河流, 湖泊和不同类型的生物群落, 描述沙漠, 丛林, 冰原以及其他更多的. 生物群落的信息能告诉你在不同的区域生活着什么类型的动物.
这也跟光照有关. 我们的光线来自太阳, 它可以被想做一种简单的直射光, 但是事实上光线射入大气层, 被称作光子的光粒子撞击地球大气颗粒, 并且反射出更多的蓝色和黄色. 这被称作大气散射.
在本文中, 我讲从制造简单的几何体开始, 作为星球的基础. 在下一篇文章中, 我将会加入更多的细节, 包括高度数据, 光线, 大气散射和细节级别(level of detail), 它将会允许戏剧性地增加星球包含的数据的总数量.
几何体
我干的第一件事是用简单的经纬度算法创建一个球体:
截图:
使用这种方法的问题是顶点会汇聚到极点, 导致顶点分布不均匀, 并且使得视觉扭曲, 特别是当你试着增加一个纹理贴图时(译者注: 就是越到两极处, 纹理贴图越小, 我们就看不到南极和北极的地形了).
因此我去寻找了一种不同的算法并且找到一些很有用的, 包括测地线和立方体映射(Geodesic and Cube Mapping).
测地线网格看起来提供了确实不错的结果, 它在给出一个很好的球体形状的同时保持了网格的均匀一致没有变形扭曲.
测地线网格能通过再分割一个 二十面体(icosahedron) 来创建, 增加额外的三角形并且推到向着球体半径的位置. 足够的镶嵌将会创建一个网格均匀没有变形的平滑的球体.
截图:
出于简单和速度的原因, 我决定从一个立方体映射算法开始. 照着我在网上找到的 Philip Nowell
写的一个公式, 我能用下面这些代码轻易地把一个立方体网格折成一个球体:
// For every vertex in the mesh
// Where vertices form 6 grids making a cube
// With bounds of [-1, -1, -1] to [1, 1, 1]
void MapCubeToSphere( Vector3& vPosition )
{
float x2 = vPosition.x * vPosition.x;
float y2 = vPosition.y * vPosition.y;
float z2 = vPosition.z * vPosition.z;
vPosition.x = vPosition.x * sqrt( 1.0f - ( y2 * 0.5f ) - ( z2 * 0.5f ) + ( (y2 * z2) / 3.0f ) );
vPosition.y = vPosition.y * sqrt( 1.0f - ( z2 * 0.5f ) - ( x2 * 0.5f ) + ( (z2 * x2) / 3.0f ) );
vPosition.z = vPosition.z * sqrt( 1.0f - ( x2 * 0.5f ) - ( y2 * 0.5f ) + ( (x2 * y2) / 3.0f ) );
}
在创建立方体每一面的网格并且对每个顶点应用上面那些代码后, 立方体折成了一个球体, 如下图所示:
动态截图:
示例代码演示了创建立方体网格的一个面然后把它折成一个球体:
// Declared Variables
// width - number of vertices across the x axis
// height - number of vertices across the y axis
// radius - sphere's radius
// pVertexBuffer - vertex buffer array
// Grid facing negative z
Vector3 vMinPosition( -1.0f, -1.0f, -1.0f );
for ( int y = 0; y < height; ++y )
{
for ( int x = 0; x < width; ++x )
{
Vector3 vPosition = vMinPosition;
vPosition.x += (float)x / (float)(width-1) * 2.0f; // Multiply by 2.0f to map position from -1 to +1
vPosition.y +- (float)y / (float)(height-1) * 2.0f; // Multiply by 2.0f to map position from -1 to +1
// Map the grid position into a sphere position
MapCubeToSphere( vPosition );
// The normal is just the vector from the center of the sphere.
Vector3 vNormal = vPosition.Normal();
// Extrude the sphere by the radius
vPosition *= radius;
// Assign to vertex buffer
pVertexBuffer[ y * width + x ].Position = vPosition;
pVertexBuffer[ y * width + x ].Normal = vNormal;
}
}
立方体映射的优势
这里有一些我希望使用立方体映射的好处:
- 网格是均匀的, 类似于一个二维平面网格地形, 使它容易处理;
- 把地形当做一个网格来处理允许我们用类似 Chunked LOD 和 Geometry Clipmaps 这样的细节级别(LOD)算法渲染每一个面.
- 允许对纹理贴图使用立方体映射, 可能会提升性能.
更多的好处在下一篇文章里.
后续
下一阶段的工作是为星球增加更多的细节, 现在它只是一个简单的球体. 在平面网格中使用很普遍的一种方法是创建一个高度图来置换顶点的 Y
坐标值.
在高度图被应用到一个球体之前, 需要做一些额外的工作, 这样图才会很好地在不同面之间包起来.
在下一篇文章中, 我会详细描述如何创建一个高度图以及一些提升细节的技巧.
下一篇文章在此 Planet Generation - Part II, 译文: 星球生成 II
参考
原文: Planet Generation - Part I
Planet Generation - Part II
Mapping a Cube to a Sphere
Rendering Massive Terrains using Chunked Level of Detail Control: DRAFT
GPU Gems 2: Chapter 2. Terrain Rendering Using GPU-Based Geometry Clipmaps