深入解析Perlin Simplex噪声函数:在C++中构建现代、高效、免费的3D图形背景

2023-09-19 16:11:54

引言

在计算机图形中,噪声是一个经常被讨论的话题。无论是为了制造自然的纹理,还是为了模拟复杂的现实世界现象,噪声函数都在其中起着关键作用。而在众多噪声函数中,Perlin Simplex 噪声无疑是最受欢迎的一种。其原因不仅在于其干净、快速的特性,更因为其所提供的连续性和一致性非常适合图形渲染。本文将为你展示如何在C++中实现一个Perlin Simplex噪声函数。

1. Perlin Simplex 噪声:背后的原理

1.1 什么是Perlin噪声?

Perlin噪声是由Ken Perlin在1983年为电影《Tron》开发的。它是一种渐变噪声,不同于常规的随机噪声。渐变噪声的关键特性是它的连续性,这意味着相邻的值会有某种逻辑上的联系,而不是完全随机。

1.2 Simplex vs Classic Perlin噪声

尽管原始的Perlin噪声非常成功,但Ken Perlin后来发现了一些可以改进的地方。这就是Simplex噪声的由来。与经典的Perlin噪声相比,Simplex噪声提供了更少的视觉伪影,更快的计算速度,尤其在高维度的情况下。

2. C++实现:开始之前

为了实现Perlin Simplex噪声,我们首先需要准备一些基础工具和数据结构。

2.1 导入必要的库

我们将使用C++的标准库来完成大部分的工作:

#include <cmath>
#include <vector>
#include <algorithm>
2.2 定义基础数据结构

在进一步进行之前,我们需要一个表示3D点的结构:

struct Vector3 {
    float x, y, z;

    Vector3(float x = 0.0f, float y = 0.0f, float z = 0.0f)
        : x(x), y(y), z(z) {}
};

这样,我们就有了一个简单的3D点来表示空间中的位置。

3. 网格与渐变向量

要理解Perlin Simplex噪声,必须首先了解其背后的两个核心概念:网格和渐变向量。

3.1 网格

首先,我们假设空间被划分成了一个个的立方体。每个立方体都有一个整数坐标(i, j, k)。空间中的任何点都可以通过这三个坐标来定位。

3.2 渐变向量

为了生成噪声,我们需要为每个立方体的角分配一个随机的3D向量,这就是所谓的渐变向量。我们将使用一个预定义的数组来存储这些向量:

std::vector<Vector3> gradients;

我们会在稍后的部分为这个数组分配随机向量。

注意:为了简洁和清晰,本文中的代码可能不是最优的或最完整的实现。为了获得完整的项目和更多的优化技巧,请下载完整项目

4. 初始化渐变向量

为了生成连续的噪声,我们需要确保每个顶点上的渐变向量是一致的。为此,我们会使用一个预定义的渐变向量集,并随机地为每个顶点选择一个向量。

4.1 预定义渐变向量集

我们可以选择以下12个3D向量作为预定义的渐变向量集:

const std::vector<Vector3> predefined_gradients = {
    Vector3(1,1,0), Vector3(-1,1,0), Vector3(1,-1,0), Vector3(-1,-1,0),
    Vector3(1,0,1), Vector3(-1,0,1), Vector3(1,0,-1), Vector3(-1,0,-1),
    Vector3(0,1,1), Vector3(0,-1,1), Vector3(0,1,-1), Vector3(0,-1,-1)
};
4.2 为每个顶点分配渐变向量

接下来,我们需要使用一种随机化策略为每个顶点分配一个渐变向量。为了保持结果的连续性,我们使用一个hash函数来确保相同的输入总是产生相同的输出:

int hash(int x, int y, int z) {
    int result = x * 73856093 ^ y * 19349663 ^ z * 83492791;
    return result & (predefined_gradients.size() - 1);
}

Vector3 getGradient(int x, int y, int z) {
    int hashedValue = hash(x, y, z);
    return predefined_gradients[hashedValue];
}

通过这种方式,我们确保每个空间的顶点都被分配了一个固定的渐变向量。

5. 计算噪声值

有了渐变向量,我们就可以开始计算Perlin Simplex噪声了。

5.1 计算权重

我们首先需要为空间中的每个点计算一个权重。权重是根据点与顶点的距离计算的:

float weight(float distance) {
    float t = 3.0f - 2.0f * distance;
    return t * t * t * (distance * distance * (6.0f * t - 15.0f) + 10.0f);
}
5.2 为每个顶点计算贡献

接下来,我们将计算空间中每个点受其相邻顶点的影响:

float computeNoiseContribution(float x, float y, float z, int gridX, int gridY, int gridZ) {
    Vector3 gradient = getGradient(gridX, gridY, gridZ);
    float distanceX = x - (float)gridX;
    float distanceY = y - (float)gridY;
    float distanceZ = z - (float)gridZ;
    float dotProduct = gradient.x * distanceX + gradient.y * distanceY + gradient.z * distanceZ;
    float weightValue = weight(sqrt(distanceX * distanceX + distanceY * distanceY + distanceZ * distanceZ));
    return dotProduct * weightValue;
}

这个函数返回点(x, y, z)受顶点(gridX, gridY, gridZ)的影响。

5.3 计算总噪声值

现在,我们可以为任意点计算其Perlin Simplex噪声值了:

float computePerlinNoise(float x, float y, float z) {
    int intX = (int)x;
    int intY = (int)y;
    int intZ = (int)z;
    float result = 0.0f;

    for (int dx = 0; dx <= 1; dx++) {
        for (int dy = 0; dy <= 1; dy++) {
            for (int dz = 0; dz <= 1; dz++) {
                result += computeNoiseContribution(x, y, z, intX + dx, intY + dy, intZ + dz);
            }
        }
    }

    return result;
}

以上就是计算Perlin Simplex噪声值的过程。


这是第二部分的内容,描述了如何在C++中实现Perlin Simplex噪声。在接下来的部分,我们将探讨如何优化和使用这个函数,以及它在实际应用中的可能用途。

6. 优化和调整

虽然上述实现已经能够为我们生成Perlin Simplex噪声,但在实际应用中,我们通常需要进行一些优化和调整,以适应特定的需求。

6.1 多重Octave

为了获得更丰富的噪声纹理,我们通常会使用多个频率和振幅的噪声叠加。这种方法称为多重Octave。下面是如何实现它:

float computeMultiOctavePerlinNoise(float x, float y, float z, int octaves, float persistence) {
    float total = 0;
    float frequency = 1;
    float amplitude = 1;
    float maxValue = 0;

    for(int i=0; i<octaves; i++) {
        total += computePerlinNoise(x * frequency, y * frequency, z * frequency) * amplitude;
        
        maxValue += amplitude;
        amplitude *= persistence;
        frequency *= 2;
    }

    return total / maxValue;
}

在这里,octaves决定了噪声叠加的次数,而persistence决定了每一次叠加时振幅的衰减。

6.2 无缝平铺

在某些应用中,我们可能需要无缝地平铺噪声纹理。要实现这一点,可以通过周期性地包装噪声值来实现:

float computeTiledPerlinNoise(float x, float y, float z, float tileWidth) {
    float tiledX = fmod(x, tileWidth) / tileWidth;
    float tiledY = fmod(y, tileWidth) / tileWidth;
    float tiledZ = fmod(z, tileWidth) / tileWidth;

    return computeMultiOctavePerlinNoise(tiledX, tiledY, tiledZ, 4, 0.5);
}

7. Perlin Simplex噪声的应用

Perlin Simplex噪声具有广泛的应用价值。以下是一些常见的应用场景:

7.1 地形生成

可以使用Perlin Simplex噪声来创建自然且连续的地形高度图。

7.2 纹理生成

噪声可以帮助我们创建各种各样的自然纹理,如云、水、火等。

7.3 模拟

在物理模拟中,噪声可以为流体或火焰动画添加细节和随机性。

7.4 3D建模

在3D建模中,噪声可用于产生随机的表面细节,如岩石或树皮的纹理。

结论

Perlin Simplex噪声是图形学中的一个强大工具,尤其是在需要模拟自然现象或创建复杂纹理的场合。本文为您提供了一个在C++中实现该噪声的方法,希望您能够利用这一技术为您的项目带来更多创意和实用性。

注意:为了简洁和清晰,本文中的代码可能不是最优的或最完整的实现。为了获得完整的项目和更多的优化技巧,请下载完整项目

更多推荐

【C# 基础精讲】异步和同步的区别

异步(Asynchronous)和同步(Synchronous)是在编程中经常遇到的两种执行模式。它们涉及到程序中任务的执行方式以及对资源的管理方式。在本文中,我们将深入探讨异步和同步的区别、使用场景以及在C#中如何实现异步编程。1.同步执行同步执行是指程序按照严格的顺序依次执行每个任务,当前任务执行完成后再执行下一个

Qt5开发及实例V2.0-第二章Qt模板库工具类及控件

Qt5开发及实例V2.0-第二章Qt模板库工具类及控件第2章Qt5模板库、工具类及控件2.1字符串类2.1.1操作字符串2.1.2查询字符串数据2.1.3字符串的转换2.2容器类2.2.1QList类、QLinkedList类和QVector类2.2.2QMap类和QHash类2.3QVariant类2.4算法及正则表

解密Java多线程中的锁机制:CAS与Synchronized的工作原理及优化策略

目录CAS什么是CASCAS的应用ABA问题异常举例Synchronized原理基本特征加锁过程偏向锁轻量级锁重量级锁其他优化操作锁消除锁粗化CAS什么是CASCAS:全称Compareandswap,字面意思:”比较并交换“,CAS涉及如下操作:假设内存中的原数据为A,旧的预期值为B,需要修改的值为C。首先把A与B进

安卓系统--翻译手机rom语言 添加多国语言 编译apk 反编译ODEX 工具步骤解析

很多小品牌机型不具备多语言设置。国内大都是中文。要想换为其他语言除非固件支持。例如国际版固件等等。大厂基本都有中文或者英文或者其他语言配置。而小品牌机型只能通过修改rom来达到多语言调用.工具步骤演示今天给友友介绍一款工具,可以用来翻译手机rom语言。添加多国语言和有些系统apk的编译与反编译等等。工具支持小米系列低版

NTPS/YCD80-1-400V终端电气综合治理保护系统 末端用电防护治理装置

NTPS/YCD80-1-400V终端电气综合治理保护系统赵经理:18O668722O7(同微信)QQ:3O824OO571公司是一家从事智能电网用户端的智能电力监控与电气安全系统的研发,生产和销售于一体的高新技术企业,自主研发了风机节能控制器,新风空调节能控制器,电梯节能控制器,水泵节能控制器,热交换系统节能控制器,

还没用熟 TypeScript 社区已经开始抛弃了

根据rich-harris-talks-sveltekit-and-whats-next-for-svelte这篇文章的报道,Svelte计划要把代码从TS换到JS了。TheteamisswitchingtheunderlyingcodefromTypeScripttoJavaScript.Thatandtheupda

【腾讯云Cloud Studio 实战训练营】深入解析Cloud Studio—React 快速构建点餐页面

序言运气不会凭空而来,只有当你足够努力的时候,才会足够幸运。文章标记颜色说明:黄色:重要标题红色:用来标记结论绿色:用来标记论点蓝色:用来标记论点腾讯云CloudStudio是一款基于云端的集成开发环境(IDE),它可以让开发人员在任何地方、任何时间使用互联网访问和编写代码,而无需安装任何软件或工具。在本文中,将详细介

Windows 上的 React(初学者)

🎬岸边的风:个人主页🔥个人专栏:《VUE》《javaScript》⛺️生活的理想,就是为了理想的生活!目录必备条件一些基本术语和概念尝试在VisualStudioCode中使用React尝试通过API使用React如果你不熟悉如何使用React,本指南介绍了一些基础知识,可帮助你入门。一些基本术语和概念尝试在Vis

【腾讯云 Cloud Studio 实战训练营】使用Cloud Studio快速构建React完成点餐H5页面还原

文章目录一、前言二、CloudStudio功能介绍三、实验介绍四、实操指导打开官网注册CloudStudio五、开发一个简版的点餐系统安装antd-mobile安装Less六、发布仓库七、开发空间八、总结一、前言IDE(集成开发环境)是一种软件工具,旨在为开发者提供一个集成的开发平台,其中包含了各种用于编写、调试和部署

Pandas 掉包侠刷题实战--条件筛选

本博文内容为力扣刷题过程的记录,所有题目来源于力扣。题目链接:https://leetcode.cn/studyplan/30-days-of-pandas/文章目录准备工作1.isin(values)和~2.df.drop_duplicates()3.df.sort_values()4.df.rename()5.pd

使用FileZilla连接本地和服务器进行文件传输

项目场景:使用FileZilla连接本地和服务器进行文件传输问题描述使用FileZilla连接本地和服务器进行文件传输出现150或425原因分析:1.防火墙是否开放端口2.公共ip或账号密码是否正确解决方案:从配置到解决问题的步骤1.下载安装FileZilla搭建步骤步骤一:安装FileZillaServer服务软件安

热文推荐