四分钟内四十亿年——在 GPU 上模拟世界
Four billion years in four minutes – Simulating worlds on the GPU

原始链接: https://davidar.io/post/sim-glsl

本文讨论在 GLSL 片段着色器中创建程序地球模拟。 模拟从一块熔岩开始,代表早期地球,大约有 45 亿年的历史,因小行星撞击而坑坑洼洼。 地形是按程序生成的,没有使用预渲染纹理。 为了创建地形,根据纬度和经度计算三维笛卡尔坐标,并使用不同大小的陨石坑层为每个点分配高度值。 陨石坑是通过迭代处理与分数布朗运动噪声和缩放的混合来建模的,以反映更大的影响。 然后模拟随着时间向前推进,通过使用构造板块模型来模拟形成山脉、海沟和大陆地貌的地质过程。 随机生成的板块种子位置具有初始速度,随着时间的推移演变形成移动的陆地。 板块碰撞导致山脉和海沟的形成,而俯冲板块则导致海拔略有上升。 接下来,引入水力侵蚀模型来重建河流流域的形成,从而能够模拟窄至一个像素的河流。 模仿水流和侵蚀自然行为的简化方程推动了河流流域的发展,连接的水道被着色以进行视觉上吸引人的描绘。 气候建模随后会生成平均海平面压力地图,同时考虑地形布局和纬度影响等因素。 为了接近现实的气候条件,地图经过高斯模糊并进行调整以反映季节变化。 随着大气压力和风计算的加入,风环流模式出现,包括在某些地区(例如印度季风季节)观察到的风向逆转。 最后,使用 Lotka-Volterra 扩散方程对植物、食草动物和捕食者的种群动态进行建模,捕捉生态系统组成部分之间的相互作用。 该模拟最后简要概述了现代人类对地球气候的影响,提到燃烧化石燃料释放大量二氧化碳以及随后导致的地球变暖。

历史上,人类住区是在提供水、良好气候、肥沃土地和适当降雨等资源的有利环境附近发展起来的。 随着气候变化的发生,这些地区的可用资源通常会减少,导致承载能力下降。 另外,西伯利亚等不太适宜居住的地区可能会变得更适合人类生活。 然而,这些新的定居点可能与当前人口稠密的地区不一致,需要全球搬迁,并可能因现有的边界问题而引发冲突。 所提供的摘录并不是在描述一种神奇的设备,而是一种具有无与伦比的计算能力的高度先进的量子计算机。 虽然这项技术可能不存在于我们的现实中,但它以虚构科学的形式作为推动情节的关键要素。 此外,“软科幻小说”一词通常指注重情感方面而不是科学准确性或合理性的作品,而“硬科幻小说”则坚持既定的科学知识。 因此,将摘录中对量子计算机的描述称为纯粹的麦高芬,缺乏科学依据,是不正确的。 相反,它准确地描绘了量子计算技术的现实进步,并展现出硬科幻的特征。
相关文章

原文
Abstract

This post delves into the implementation of my procedural earth simulation, written entirely in GLSL fragment shaders. It simulates the complete history of an earth-like planet in a few minutes, with the simulation updating at 60 frames per second.

A video recording of the final shader.

Protoplanet

This story begins four and a half billion years ago, with a lump of molten rock...

The early earth was a protoplanet, red hot and heavily cratered by asteroid impacts. As my earth simulation is entirely procedurally generated, with no pre-rendered textures, the first task is to generate a map of this terrain. To calculate the height of the terrain at a given latitude and longitude, first translate to 3D cartesian coordinates:


vec3 p = 1.5 * vec3(
    sin(lon*PI/180.) * cos(lat*PI/180.),
    sin(lat*PI/180.),
    cos(lon*PI/180.) * cos(lat*PI/180.));

Now, as asteroids come in a variety of sizes, so do the resulting craters. To accommodate this, the shader iterates over five levels of detail, layering craters of decreasing size over each other. To make the craters have a realistic rugged appearance, this is mixed with some fractional Brownian motion noise, and scaled so that the largest craters have the most impact on the terrain.


float height = 0.;
for (float i = 0.; i < 5.; i++) {
    float c = craters(0.4 * pow(2.2, i) * p);
    float noise = 0.4 * exp(-3. * c) * FBM(10. * p);
    float w = clamp(3. * pow(0.4, i), 0., 1.);
    height += w * (c + noise);
}
height = pow(height, 3.);

The craters themselves are generated on a 3D grid, from which a sphere is carved out for the surface terrain. To avoid visible regularity, the crater centres are given a pseudo-random offset from the grid points, using a hash function. To calculate influence of a crater at a given location, take a weighted average of the craters belonging to the nearby grid points, with weights exponentially decreasing with distance from the centre. The crater rims are generated by a simple sine curve.


float craters(vec3 x) {
    vec3 p = floor(x);
    vec3 f = fract(x);
    float va = 0.;
    float wt = 0.;
    for (int i = -2; i <= 2; i++)
     for (int j = -2; j <= 2; j++)
      for (int k = -2; k <= 2; k++) {
        vec3 g = vec3(i,j,k);
        vec3 o = 0.8 * hash33(p + g);
        float d = distance(f - g, o);
        float w = exp(-4. * d);
        va += w * sin(2.*PI * sqrt(d));
        wt += w;
    }
    return abs(va / wt);
}

The final procedurally generated heightmap looks like this:

Although relatively simple, after filling the low-lying regions with water, this procedural terrain resembles what scientists believe the early earth actually looked like:

Artistic impression of the early earth, by NASA.

Water contained within was vaporised by the heat, which escaped and began circulating through the early atmosphere forming around the planet. As time progressed and the rock cooled, the water vapour began to condense into oceans. The flow of liquid water across the surface carved valleys in the terrain, leaving an accumulation of sediment in its wake.

Tectonic plates

The formation of mountains, ocean trenches, and familiar continental landforms requires a model of tectonic movement. The simulation randomly generates seed locations for plates, with an initial velocity. These plates grow in size over time with a simple aggregation model, which randomly selects neighbouring points and adds them to a plate if they have not already been assigned to another plate. All of the pixels within a plate store the velocity of the plate's movement. The aggregation model is similar to that of a diffusion-limited aggregation (but without the diffusion):

Continuous movement of the plates is difficult, as it would require plate boundaries to account for movements measured in fractions of a pixel. To avoid this, the plates are instead moved at discrete time-steps, by a whole pixel either horizontally or vertically. These times are randomised for each plate such that the average velocity is maintained at the set speed and direction, and also so that it is unlikely that neighbouring plates will move simultaneously.

Plate collisions occur when some boundary pixels of one plate move onto a location previously occupied by pixels belonging to another plate. This causes subduction, which is modelled by simply slightly increasing the elevation of the terrain at the locations of the collision. Although this only occurs at the pixels along the boundary of a plate, the impact is gradually spread to neighbouring pixels through a simple thermal erosion model, which pushes the elevation of a pixel in the direction of the average of its neighbours.

Altogether this provides a decent simulation of the formation of continents with mountain ranges (which will be further improved with the introduction of hydraulic erosion in the next section):

Hydraulic erosion

The rugged appearance of natural terrain is largely driven by the formation of river basins, which erode landscapes in a familiar branching pattern. A variety of water flow simulations are readily available for this task, but a difficulty here is that the resolution of the terrain map is quite low for an entire planet. Therefore, the model will have to be able to simulate rivers which are no more than a single pixel wide. Barnes (2018) proposes a simple model which achieves just this.

Simply put, each pixel examines its eight neighbours, to determine which direction has the greatest decrease in elevation (adjusted for the fact that the diagonal neighbours are further away). This direction of greatest slope is where water flowing out of this pixel will travel. Water is initially distributed amongst cells by rainfall, which is then transported between neighbouring pixels at each time-step.

Erosion is driven by a stream power law:


elevation -= 0.05 * pow(water, 0.8) * pow(slope, 2.);

Here we have the elevation and amount of water located at the current cell, along with the slope in the direction the water is travelling. The decrease in elevation is capped so that it doesn't become lower than the location the water is flowing to.

The interaction between the water flow and erosion results in the natural formation of river basins in the terrain:

By colouring connected waterways (with the colour determined by the location of the river's mouth), it's possible to produce striking visualisations reminiscent of real river basin maps:

Simulated river basins. Original shader.
River basins of USA, by Grasshopper Geography.

Global climate

Simulating the climate system of an entire planet is a daunting task, but luckily it turns out that it can be approximated relatively easily. The driving force behind everything in my climate simulation is a procedurally generated map of the mean sea-level pressure (MSLP).

According to the Climate Cookbook, the main ingredients in creating a MSLP map are where the landforms are located amidst the ocean, and the impact of latitude. In fact, if you take data from a real MSLP map of the Earth, separate out locations according to whether they are land or ocean, and plot the MSLP against latitude, you end up with two sinusoidal curves for the land and ocean with slightly different shapes.

By fitting the parameters appropriately, I came up with a crude model of the annual mean pressure (here the latitude is measured in degrees):


if (land) {
    mslp = 1012.5 - 6. * cos(lat*PI/45.);
} else { // ocean
    mslp = 1014.5 - 20. * cos(lat*PI/30.);
}

Of course, this isn't quite enough to generate a realistic MSLP map, as generating values for the land and ocean separately results in sharp discontinuities at the boundaries between them. In reality, MSLP smoothly varies across the transition from ocean to land, due to the local diffusion of gas pressure. This diffusion process can be approximated quite well by simply applying a Gaussian blur to the MSLP map (with a standard deviation of 10--15 degrees).

To allow for the climate to change along with the seasons, it's necessary to also model the difference in MSLP between January and July. Once again, terrestrial data suggests this follows a sinusoidal pattern. By fitting parameters and applying a Gaussian blur, this can be combined with the annual MSLP map to generate dynamic climate patterns which vary throughout the year.


if (land) {
    delta = 15. * sin(lat*PI/90.);
} else { // ocean
    delta = 20. * sin(lat*PI/35.) * abs(lat)/90.;
}

Now, with the MSLP in hand, it is possible to generate wind currents and temperatures. In reality it's the temperate which generates the pressure, but correlation is correlation. This requires a little more fiddling to generate realistic values (season oscillates between -1 and 1 throughout the year):


float temp = 40. * tanh(2.2 * exp(-0.5 * pow((lat + 5. * season)/30., 2.)))
             - 15. - (mslp - 1012.) / 1.8 + 1.5 * land - 4. * elevation;

Wind tends to move from high-pressure to low, but at a global scale we also need to account for the Coriolis force, which is responsible for causing winds to circulate around pressure zones (grad is the MSLP gradient vector):


vec2 coriolis = 15. * sin(lat*PI/180.) * vec2(-grad.y, grad.x);
vec2 velocity = coriolis - grad;

Although a relatively crude simulation, this generates remarkably realistic wind circulation patterns. If you look closely, you may notice a number of natural phenomena being replicated, including the reversal of winds over India during the monsoon season:

As a final detail, precipitation can be simulated by advecting water vapour from the ocean, through the wind vector field, and onto the land:

The advection is implemented in a similar manner to fluid simulations:

Life

The climate influences the distribution of life on a planet. Rainfall patterns and temperature variation dictate rates of plant growth. As the seasons change, herbivores migrate to regions with enough vegetation to sustain them. And, as they follow the vegetation, predators follow them. All of these dynamics can be captured by a Lotka--Volterra diffusion model:


float dx = plant_growth - c.y;
float dy = reproduction * c.x - predation * c.z - 1.;
float dz = predation * c.y - 1.;
float dt = 0.1;
c.xyz += dt * c.xyz * vec3(dx, dy, dz);

The xyz elements of c represent the populations of vegetation, herbivores, and predators respectively. On a large scale, the dynamics of animal populations generate interesting patterns:

In real life, these kinds of patterns are most easily seen with microbe populations in a petri dish, but the same laws govern large animal populations across the globe.

Spiral waves in colonies of mold.

Humanity

Concluding the prelude on the early earth, the pace slows to a cycle between day and night, terrain becoming fixed as tectonic movements become imperceptible. Soon the night reveals unprecedented patterns of light, as humanity proceeds to colonise the surface of the planet.

This rapid expansion brings its own set of changes, as humans begin to burn large amounts of fossil fuels to power their settlements. Carbon that had lain dormant for millions of years is released into the atmosphere, and dispersed around the planet.

Over several hundred years, humans burn through all available fossil fuel resources, releasing five trillion tonnes of carbon into the atmosphere. This strengthens the greenhouse effect, raising the global average temperature by almost 10 degrees Celsius. Large regions of land around the equator are rendered uninhabitable by extreme temperatures, resulting in the disappearance of humanity from a significant portion of the planet.

联系我们 contact @ memedata.com