简体   繁体   中英

Perlin Noise wave effect on a 3d sphere

I have been trying to figure out how to create wave effect on a 3d sphere using Perlin Noise

I have found some tutorials on how to do it on a plane, however, none on a 3d object, This code works just fine on a plane, does anyone know how to adapt it on a 3d sphere ? Thank you in advance for your help

using UnityEngine;
using System.Collections;
public class PerlinTerrain : MonoBehaviour {

    public float perlinScale;
    public float waveSpeed;
    public float waveHeight;
    public float offset;

    void Update () {
        CalcNoise();
    }

    void CalcNoise() {
        MeshFilter mF = GetComponent<MeshFilter>();
        MeshCollider mC = GetComponent<MeshCollider>();

        mC.sharedMesh = mF.mesh;

        Vector3[] verts = mF.mesh.vertices;

        for (int i=0; i< verts.Length; i++) {
            float pX = (verts[i].x * perlinScale) + (Time.timeSinceLevelLoad * waveSpeed) + offset;
            float pZ = (verts[i].z * perlinScale) + (Time.timeSinceLevelLoad * waveSpeed) + offset;
            verts[i].y = Mathf.PerlinNoise(pX, pZ) * waveHeight;
        }

        mF.mesh.vertices = verts;
        mF.mesh.RecalculateNormals();
        mF.mesh.RecalculateBounds();
    }

}

The easiest way would be to apply the 2D noise to the surface. You could do this by using the vertex normal to know which way you should move the position to.

In your code you are only moving the Y position. For a plane this means you moved the vertex in its normal direction by the amount that the noise function gives you. For a spheere you could do something like this:

verts[i] = verts[i] + (Mathf.PerlinNoise(pX, pZ) * waveHeight * mF.mesh.normals[i].normalized);

This will work for any mesh because it takes in account its original position and just moves the vertex in the direction of its normal.

This will move the vertex up which means it will always be higher than original position, you can apply a simple offset to the noise function to make it go lower as well

verts[i] = verts[i] + ((Mathf.PerlinNoise(pX, pZ) - 0.5f) * waveHeight * mF.mesh.normals[i].normalized);

I noticed you are animating this noise, its important to take the original mesh each update and not the new one that had noise applied to it. So in the end it would look something like this

using UnityEngine;
using System.Collections;

public class PerlinTerrain : MonoBehaviour
{

    public float perlinScale;
    public float waveSpeed;
    public float waveHeight;
    public float offset;

    Vector3[] baseVertices;

    private void OnEnable()
    {
        MeshFilter mF = GetComponent<MeshFilter>();

        baseVertices = mF.mesh.vertices;
    }

    void Update()
    {
        CalcNoise();
    }

    void CalcNoise()
    {
        MeshFilter mF = GetComponent<MeshFilter>();

        mF.sharedMesh.vertices = baseVertices;
        mF.sharedMesh.RecalculateNormals();

        Vector3[] verts = mF.sharedMesh.vertices;

        for (int i = 0; i < verts.Length; i++)
        {
            float pX = (verts[i].x * perlinScale) + (Time.timeSinceLevelLoad * waveSpeed) + offset;
            float pZ = (verts[i].z * perlinScale) + (Time.timeSinceLevelLoad * waveSpeed) + offset;
            verts[i] = verts[i] + ((Mathf.PerlinNoise(pX, pZ)) * waveHeight * mF.sharedMesh.normals[i].normalized);
        }

        mF.sharedMesh.vertices = verts;
    }
}

But you still have 1 problem. There are multiple vertices that have the same position but have different normal. You will have to figure out which vertices are shared and calculate its normal.

Group the vertices by its position and take the average normal and only iterate over each unique vertex once.

Because it would be called simplex noise

https://en.wikipedia.org/wiki/Simplex_noise

Perlin is the specific name of 2D version

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM