[Unity] Planet Generation [7] - Ocean Depth

suhan0304·2024년 3월 25일
post-thumbnail

지표면은 어느정도 완성됐는데 바다가 좀 어색한데 이 부분을 개선시켜보자.


바다 색을 지정할 Gradient를 따로 만들어준다.

ColorSettings.cs

public Gradient oceanColor;

텍스쳐의 해상도를 2배로 늘려준 다음에 그 절반을 바다를 위해 ColorSettings에 선언해준 oceanColor Gradient를 사용해서 구현한다. textureResolution을 2배 해줬기 때문에 Update Color에서 절반은 oceanColor를 절반은 기존의 biome Gradient Texture를 구현하도록 업데이트한다.

ColorGenerator.cs

public void UpdateSettings(ColorSettings settings)
{
    this.settings = settings;
    if (texture == null || texture.height != settings.biomeColorSettings.biomes.Length)
    {
        texture = new Texture2D(textureResolution*2, settings.biomeColorSettings.biomes.Length, TextureFormat.RGBA32, false);
    }
    biomeNoiseFilter = NoiseFilterFactory.CreateNoiseFilter(settings.biomeColorSettings.noise);
}

public void UpdateColors()
{
    Color[] colors = new Color[textureResolution * texture.height];
    int colorIndex = 0;
    foreach(var biome in  settings.biomeColorSettings.biomes)
    {
        for (int i = 0; i < textureResolution * 2; i++)
        {
            Color gradientCol;
            if (i < textureResolution)
            {
                gradientCol = settings.oceanColor.Evaluate(i / (textureResolution - 1f)); ;
            }
            else
            {
                gradientCol = biome.gradient.Evaluate((i-textureResolution) / (textureResolution - 1f)); ;
            }
            Color tintCol = biome.tint;
            colors[colorIndex] = gradientCol * (1 - biome.tintPercent) + tintCol * biome.tintPercent;
            colorIndex++;
        }
    }
    texture.SetPixels(colors);
    texture.Apply();
    settings.planetMaterial.SetTexture("_texture", texture);
}

기존에 우리는 매끄러운 바다 표면을 위해 정점의 최소 높이를 지정해서 해당 높이 보다 낮은 정점을 일정한 높이로 끌어올려주어서 바다 표면을 구현했기 때문에 바다의 깊이에 관한 정보는 가지고 있지 않다.

더 이상 최소 높이를 고정하지 않고 바다가 얼마나 깊은지를 알기 위해 UV에 해당 깊이 정보를 저장하도록 해보자. 일단 SimpleNoiseFilter의 Mathf.Max를 지워준다.

SimpleNoiseFilter.cs

// before : noiseValue = Mathf.Max(0, noiseValue - settings.minValue);
noiseValue = noiseValue - settings.minValue;

그 다음 ShapeGenerator의 CalcluationElevation을 아래와 같이 수정한다.

ShapeGenerator.cs

public float CalculateUnscaledElevation(Vector3 pointOnUnitSphere)
{
    float firstLayerValue = 0;
    float elevation = 0;

    if (noiseFilters.Length > 0)
    {
        firstLayerValue = noiseFilters[0].Evaluate(pointOnUnitSphere);
        if (settings.noiseLayers[0].enabled)
        {
            elevation = firstLayerValue;
        }
    }
    for (int i = 1; i < noiseFilters.Length; i++)
    {
        if (settings.noiseLayers[i].enabled)
        {
            float mask = (settings.noiseLayers[i].useFirstLayerAsMask) ? firstLayerValue : 1;
            elevation += noiseFilters[i].Evaluate(pointOnUnitSphere) * mask;
        }
    }
    elevationMinMax.AddValue(elevation);
    return elevation;
}

public float GetScaledElevation(float unscaledElevation)
{
    float elevation = Mathf.Max(0, unscaledElevation);
    elevation = settings.planetRadius * (1 + elevation);
    return elevation;
}

이렇게 코드를 업데이트하면 float 형식의 unscaledElevation을 구한 후에 scaled(Mathf.Max) 작업을 따로 해주기 때문에 높이를 조정하기 전의 표면의 높이, 즉 깊이 정보를 가질 수 있게된다.

즉, 기존에는 바로 Mathf.Max 작업으로 높이를 고정시켜서 바다를 만들었다면 이제 scaled 하지 않고 높이를 구한 후에 그 다음 scaled 하는 작업을 두 함수로 구분하면서 중간에 scaled 하지 않은 기존의 높이를 얻을 수 있게 된다.

TerrainFacs.cs

Vector2[] uv = (mesh.uv.Length == vertices.Length) ? mesh.uv : new Vector2[vertices.Length];

for (int y = 0; y < resolution; y++)
{
    for (int x = 0; x < resolution; x++)
    {
        int i = x + y * resolution;
        Vector2 percent = new Vector2(x, y) / (resolution - 1);
        Vector3 pointOnUnitCube = localUp + (percent.x - .5f) * 2 * axisA + (percent.y - .5f) * 2 * axisB;
        Vector3 pointOnUnitSphere = pointOnUnitCube.normalized;
        float unscaledElevation = shapeGenerator.CalculateUnscaledElevation(pointOnUnitSphere);
        vertices[i] = pointOnUnitSphere * shapeGenerator.GetScaledElevation(unscaledElevation);
        uv[i].y = unscaledElevation;

        if (x != resolution - 1 && y != resolution - 1)
        {
            triangles[triIndex] = i;
            triangles[triIndex + 1] = i + resolution + 1;
            triangles[triIndex + 2] = i + resolution;

            triangles[triIndex + 3] = i;
            triangles[triIndex + 4] = i + 1;
            triangles[triIndex + 5] = i + resolution + 1;
            triIndex += 6;
        }
    }
}

이제 Shader를 아래와 같이 수정해준다.

이제 이렇게 바뀌는데 oceanGradient를 따로 설정해줬기 때문에 기존 Color Gradient에서 ocean Color를 다 제거해준다.

이제 ocean Gradient를 적절히 조절해주면 깊이에 따른 바다 색을 이렇게 구현할 수 있어 실제 지구와 같은 모습으로 바뀌는 것을 확인할 수 있다.

profile
Be Honest, Be Harder, Be Stronger

0개의 댓글