여러 노이즈 필터를 만들어보자.
처음 만들 노이즈 필터는 꼭대기가 뾰족한 산과 비슷한 노이즈 필터를 만들어보자. sin 함수를 아래와 같은 과정을 통해 꼭대기가 뾰족한 산과 같은 그래프 형태를 만들 수 있다.
이 때 고도가 낮은 지형을 좀 더 평평하게 만들기 위해 제곱을 취한다.
기존 NoiseFilter를 SimpleNoiseFilter로 바꾼 후에 진행한다. SimpleNoiseFilter의 내용을 그대로 복사해준 후에 공식 부분을 위에서 세운 함수로 수정해준다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RidgidNoiseFilter : INoiseFilter
{
NoiseSettings settings;
Noise noise = new Noise();
public RidgidNoiseFilter(NoiseSettings settings)
{
this.settings = settings;
}
public float Evaluate(Vector3 point)
{
float noiseValue = 0;
float frequency = settings.baseRoughness;
float amplitude = 1;
float weight = 1;
for (int i = 0; i < settings.numLayers; i++)
{
float v = 1 - Mathf.Abs(noise.Evaluate(point * frequency + settings.centre));
v *= v;
v *= weight;
weight = v;
noiseValue += v * amplitude;
frequency *= settings.roughness;
amplitude *= settings.persistence;
}
noiseValue = Mathf.Max(0, noiseValue - settings.minValue);
return noiseValue * settings.strength;
}
}
ShapeGenerator에서 SimpleNoiseFilter 말고도 다양한 NoiseFilter를 배열 형식으로 담을 수 있도록 하기 위해 INoiseFilter, NoiseFilterFactory 스크립트를 추가로 작성해준다.
using System.Collections.Generic;
using UnityEngine;
public interface INoiseFilter
{
float Evaluate(Vector3 point);
}
그 다음에 RidgidNoiseFilter, SimpleNoiseFilter의 Interface를 설정해준 후에 NoiseSettings에서 해당 타입을 유지할 수 있도록 한다.
public class SimpleNoiseFilter : INoiseFilter
public class RidgidNoiseFilter : INoiseFilter
public class NoiseSettings
{
public enum FilterType
{
Simple,
Ridgid
}
public FilterType filterType;
public float strength = 1;
[Range(1, 8)]
public int numLayers = 1;
public float baseRoughness = 1;
public float roughness = 2;
public float persistence = .5f;
public Vector3 centre;
public float minValue;
}
이제 NoiseSettings의 FilterType을 확인해서 해당 타입에 맞는 노이즈 필터를 만들도록 NoiseFilterFactory를 작성한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NoiseFilterFactory
{
public static INoiseFilter CreateNoiseFilter(NoiseSettings settings)
{
switch (settings.filterType)
{
case NoiseSettings.FilterType.Simple:
return new SimpleNoiseFilter(settings);
case NoiseSettings.FilterType.Ridgid:
return new RidgidNoiseFilter(settings);
}
return null;
}
}
이제 ShapeGenerator에서 SimpleNoiseFilter 배열이 아닌 INoiseFilter 배열을 사용한 후에 NoiseFilterFactory의 CreateNoiseFilter 메소드를 호출시켜서 NoiseFilter를 각각 타입에 맞도록 만들어준다.
ShapeSettings settings;
INoiseFilter[] noiseFilters;
public ShapeGenerator(ShapeSettings settings)
{
this.settings = settings;
noiseFilters = new INoiseFilter[settings.noiseLayers.Length];
for(int i = 0; i < noiseFilters.Length; i++)
{
noiseFilters[i] = NoiseFilterFactory.CreateNoiseFilter(settings.noiseLayers[i].noiseSettings);
}
}
이제 유니티로 돌아가 세 번째 노이즈 필터를 만들고 필터 타입을 Ridgid르 바꿔주면 뾰족한 산들이 만들어진다.
값을 잘 조절하면 산맥과 같은 모양처럼 만들 수 있다.
FirstLayerAsMask를 체크하면 실제 대륙에 산맥들이 있는 모양을 구현할 수 있다.
weightMultiplier를 설정해서 노이즈를 생성하면서 가중치를 점점 낮추도록 할 수 있다. 이 때 SimpleNoiseFilter가 아닐 때만 가중치 곱을 해주기 위해 아래와 같이 작성해준다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class NoiseSettings
{
public enum FilterType
{
Simple,
Ridgid
}
public FilterType filterType;
public SimpleNoiseSettings simpleNoiseSettings;
public RidgidNoiseSettings ridgidNoiseSettings;
[System.Serializable]
public class SimpleNoiseSettings
{
public float strength = 1;
[Range(1, 8)]
public int numLayers = 1;
public float baseRoughness = 1;
public float roughness = 2;
public float persistence = .5f;
public Vector3 centre;
public float minValue;
}
[System.Serializable]
public class RidgidNoiseSettings : SimpleNoiseSettings
{
public float weightMultiplier = .8f;
}
}
각각의 settings를 불러오는 곳에서 타입에 맞게 불러오도록 수정한다.
NoiseSettings.RidgidNoiseSettings settings;
Noise noise = new Noise();
public RidgidNoiseFilter(NoiseSettings.RidgidNoiseSettings settings)
{
this.settings = settings;
}
NoiseSettings.SimpleNoiseSettings settings;
Noise noise = new Noise();
public SimpleNoiseFilter(NoiseSettings.SimpleNoiseSettings settings)
{
this.settings = settings;
}
public class NoiseFilterFactory
{
public static INoiseFilter CreateNoiseFilter(NoiseSettings settings)
{
switch (settings.filterType)
{
case NoiseSettings.FilterType.Simple:
return new SimpleNoiseFilter(settings.simpleNoiseSettings);
case NoiseSettings.FilterType.Ridgid:
return new RidgidNoiseFilter(settings.ridgidNoiseSettings);
}
return null;
}
}
속성에 따라서 editor를 수정하기위해 Brecht Lecluyes의 editor scripts를 사용한다.
[ConditionalHide("filterType", 0)]
public SimpleNoiseSettings simpleNoiseSettings;
[ConditionalHide("filterType", 1)]
public RidgidNoiseSettings ridgidNoiseSettings;
이제 FilterType에 따라 인스펙터의 속 FilterType에 따라 Noise Settings가 하나만 나타나는 것을 확인할 수 있다.
이제 적절하게 값을 조절해서 산맥이 있는 대륙이 만들어진 행성을 아래와 같이 구현할 수 있다.
해상도가 높아지면 NoiseSettings의 값들을 조금만 바꿔도 렌더링하는데 매우 오랜 시간이 걸린다. 한 번에 하나의 면만 렌더링하는 옵션을 행성에 추가해보자.
public enum FaceRenderMask { All, Top, Bottom, Left, Right, Front, Back };
public FaceRenderMask faceRenderMask;
void Initialize()
{
shapeGenerator = new ShapeGenerator(shapeSettings);
if (meshFilters == null || meshFilters.Length == 0)
{
meshFilters = new MeshFilter[6];
}
terrainFaces = new TerrainFace[6];
Vector3[] directions = { Vector3.up, Vector3.down, Vector3.left, Vector3.right, Vector3.forward, Vector3.back };
for (int i = 0; i < 6; i++)
{
if (meshFilters[i] == null)
{
GameObject meshObj = new GameObject("mesh");
meshObj.transform.parent = transform;
meshObj.AddComponent<MeshRenderer>().sharedMaterial = new Material(Shader.Find("Standard"));
meshFilters[i] = meshObj.AddComponent<MeshFilter>();
meshFilters[i].sharedMesh = new Mesh();
}
terrainFaces[i] = new TerrainFace(shapeGenerator, meshFilters[i].sharedMesh, resolution, directions[i]);
bool renderFace = faceRenderMask == FaceRenderMask.All || (int)faceRenderMask - 1 == i;
meshFilters[i].gameObject.SetActive(renderFace);
}
}
void GenerateMesh()
{
for (int i = 0; i < 6;i++)
{
if (meshFilters[i].gameObject.activeSelf)
{
terrainFaces[i].ConstructMesh();
}
}
}
이제 유니티로 돌아가 컴파일 해준 후에 Planet의 FaceRenderMask의 값들을 수정해서 원하는 면만 렌더링할 수 있다. 이는 실제 인게임에서 유닛이 위치하고 보고있는 면만을 렌더링함으로써 resolution(해상도)가 높아져도 빠른 시간안에 렌더링이 가능하다는 장점이 있다.
해상도를 최대(256)으로 설정한 후에 Noise Settings의 Strength를 수정했을때 기존과 달리 빠르게 렌더링 되는 것을 확인할 수 있다.
다시 All로 바꿔주면 모든 면이 렌더링된다.