아일랜드 맵 마스크를 만드는 간단한 방법


21


C #으로 아일랜드 맵에 대한 마스크를 생성하는 쉽고 쉬운 방법을 찾고 있습니다.


기본적으로 지형이 물로 둘러싸여 있지 않은 펄린 노이즈로 생성 된 임의의 하이트 맵을 사용하고 있습니다.

여기에 이미지 설명을 입력하십시오

다음 단계는 모서리와 경계가 물이되도록 마스크를 생성하는 것입니다.

여기에 이미지 설명을 입력하십시오

그런 다음 펄린 노이즈 이미지에서 마스크를 빼서 섬을 얻을 수 있습니다.

여기에 이미지 설명을 입력하십시오

명암을 가지고 놀면서

여기에 이미지 설명을 입력하십시오

그래디언트 커브를 사용하면 원하는대로 아일랜드 하이트 맵을 얻을 수 있습니다.

여기에 이미지 설명을 입력하십시오

(이것은 물론 예일뿐입니다)

보시다시피, 섬의 "가장자리"는 잘려 나옵니다. 색상 값이 너무 흰색이 아닌 경우 큰 문제는 아닙니다. 왜냐하면 회색조를 4 층 (물, 모래, 잔디 및 록).

제 질문은 두 번째 이미지와 같이 잘 보이는 마스크를 어떻게 생성 할 수 있습니까?


최신 정보

이 기술을 찾았습니다. 좋은 출발점이 될 것 같지만 원하는 출력을 얻기 위해 얼마나 정확하게 구현할 수 있는지 잘 모르겠습니다. http://mrl.nyu.edu/~perlin/experiments/puff/


업데이트 2

이것이 나의 최종 해결책입니다.

makeMask()정규화 루프 내에서 다음과 같이 함수를 구현 했습니다.

        //normalisation
        for( int i = 0; i < width; i++ ) {
            for( int j = 0; j < height; j++ ) {
                perlinNoise[ i ][ j ] /= totalAmplitude;
                perlinNoise[ i ][ j ] = makeMask( width, height, i, j, perlinNoise[ i ][ j ] );
            }
        }

그리고 이것이 최종 기능입니다 :

    public static float makeMask( int width, int height, int posX, int posY, float oldValue ) {
        int minVal = ( ( ( height + width ) / 2 ) / 100 * 2 );
        int maxVal = ( ( ( height + width ) / 2 ) / 100 * 10 );
        if( getDistanceToEdge( posX, posY, width, height ) <= minVal ) {
            return 0;
        } else if( getDistanceToEdge( posX, posY, width, height ) >= maxVal ) {
            return oldValue;
        } else {
            float factor = getFactor( getDistanceToEdge( posX, posY, width, height ), minVal, maxVal );
            return oldValue * factor;
        }
    }

    private static float getFactor( int val, int min, int max ) {
        int full = max - min;
        int part = val - min;
        float factor = (float)part / (float)full;
        return factor;
    }

    public static int getDistanceToEdge( int x, int y, int width, int height ) {
        int[] distances = new int[]{ y, x, ( width - x ), ( height - y ) };
        int min = distances[ 0 ];
        foreach( var val in distances ) {
            if( val < min ) {
                min = val;
            }
        }
        return min;
    }

이것은 이미지 # 3과 같은 출력을 줄 것입니다.

코드가 약간 변경되어 이미지 # 2->와 같이 원래 원하는 출력을 얻을 수 있습니다.

    public static float makeMask( int width, int height, int posX, int posY, float oldValue ) {
        int minVal = ( ( ( height + width ) / 2 ) / 100 * 2 );
        int maxVal = ( ( ( height + width ) / 2 ) / 100 * 20 );
        if( getDistanceToEdge( posX, posY, width, height ) <= minVal ) {
            return 0;
        } else if( getDistanceToEdge( posX, posY, width, height ) >= maxVal ) {
            return 1;
        } else {
            float factor = getFactor( getDistanceToEdge( posX, posY, width, height ), minVal, maxVal );
            return ( oldValue + oldValue ) * factor;
        }
    }

당신은 당신이 당신의 전체 소스에 연결할 수 있습니까?
금욕

답변:


12

중심을 향한 더 높은 값에 대한 바이어스로 규칙적인 노이즈를 생성합니다. 귀하의 예와 같이 사각형 모양의 섬 모양을 원한다면 가장 가까운 가장자리까지의 거리를 요인으로 사용합니다.

여기에 이미지 설명을 입력하십시오

이 요소를 사용하면 마스크 노이즈를 생성 할 때 다음과 같은 것을 사용할 수 있습니다.

maxDVal = (minIslandSolid - maxIslandSolid)

getMaskValueAt(x, y)
    d = distanceToNearestEdge(x,y)
    if(d < maxIslandSolid)
        return isSolid(NonSolidValue) //always non-solid for outside edges
    else if(d < minIslandSolid)
        //noisy edges
        return isSolid((1 - (d/maxDVal)) * noiseAt(x,y))
    else
        return isSolid(SolidValue) //always return solid for center of island

여기서 distanceToNearestEdge해당 위치에서지도의 가장 가까운 가장자리까지의 거리를 반환합니다. 그리고 isSolid0과 1 사이의 값이 견고한 지 여부를 결정합니다 (잘라낸 값이 무엇이든). 매우 간단한 함수이며 다음과 같이 보일 수 있습니다.

isSolid (float value) 반환 값 <solidCutOffValue

solidCutOffValue솔리드를 결정하기 위해 사용하는 값은 어디에 있습니까 ? 그것은 될 수 .5도 분할에 대한, 또는 .75더 단단한 또는 .25더 적은 고체 위해.

마지막으로이 작은 (1 - (d/maxDVal)) * noiseAt(x,y). 먼저 다음과 같이 0과 1 사이의 인수를 얻습니다.

(1 - (d/maxDVal))

여기에 이미지 설명을 입력하십시오

어디는 0외부 가장자리에 있고 1안쪽 가장자리에 있습니다. 이것은 우리의 소음이 내부에서 단단하고 외부에서 비 고체 일 가능성이 더 크다는 것을 의미합니다. 이것이 우리가 얻는 소음에 적용되는 요소입니다 noiseAt(x,y).

다음은 이름이 실제 값으로 오도 될 수 있으므로 값이 무엇인지에 대한보다 시각적 인 표현입니다.

여기에 이미지 설명을 입력하십시오


빠른 답변을 위해이 기술을 구현하려고 노력할 것입니다. 이것으로 원하는 결과를 얻을 수 있기를 바랍니다.
Ace

시끄러운 가장자리를 원하는 방식으로 조정하려면 약간의 조정이 필요할 수 있습니다. 그러나 이것은 당신에게 확실한 기초가되어야합니다. 행운을 빕니다!
MichaelHouse

지금까지 기본 구현이 작동했습니다. IsSolid 함수의 예를 보여 주시겠습니까? 가장자리에서 최소 및 최대 거리를 기반으로 0과 1 사이의 값을 얻는 방법을 모릅니다. 지금까지 내 코드 업데이트를 참조하십시오.
Ace

나는 거기에 약간의 논리가 섞여있었습니다. 더 이해하기 쉽게 고쳤습니다. 예를 들어isSolid
MichaelHouse

0과 1 사이의 값을 얻으려면 최대 값이 무엇인지 알아 내고 현재 값을 그 값으로 나눕니다. 그런 다음 0을 바깥 쪽 가장자리에 놓고 1을 안쪽 가장자리에 있기를 원했기 때문에 하나에서 빼기를했습니다.
MichaelHouse

14

이를 위해 약간의 계산 능력을 기꺼이 사용하려는 경우이 블로그 작성자와 유사한 기술을 사용할 수 있습니다 . ( NB : 코드를 직접 복사하려면 ActionScript를 사용하십시오). 기본적으로 그는 임의의 임의의 점을 생성 한 다음 (예 : 상대적으로 균일하게 보임)이를 사용하여 보로 노이 다각형 을 만듭니다 .

보로 노이 다각형

그런 다음 외부 다각형을 물로 설정하고 나머지 다각형을 반복 하여 인접 다각형의 특정 비율이 물인 경우 물을 만듭니다. 그런 다음 대략 섬을 나타내는 다각형 마스크가 남습니다.

다각형지도

이것으로 가장자리에 노이즈를 적용하여 이와 비슷한 결과를 얻을 수 있습니다 (색상은 관련이없는 다른 단계의 것입니다).

시끄러운 모서리가있는 다각형 맵

그런 다음 실제와 같은 섬 모양의 마스크가 남게되어 목적에 도움이됩니다. Perlin 노이즈 마스크로 사용하도록 선택하거나 바다까지의 거리를 기반으로 높이 값을 생성하고 노이즈를 추가 할 수 있습니다 (필요하지 않은 것 같음).


귀하의 답변에 대한 thx, 그러나 이것은 웹 검색에서 얻은 첫 번째 (거의 유일한) 솔루션입니다. 이 솔루션은 매우 훌륭하다고 생각하지만 "간단한"방법을 시도하고 싶습니다.
Ace

@Ace 공정의 충분한, 그것은 아마 당신이 할거야 무엇 이건 조금 잔인한 : P 아직도, 그것은 마음에 가치를 유지입니다 해야 당신이 이제까지 필요.
Polar

이 페이지에 연결된 누군가 다행입니다. 해당 페이지는 항상 "누군가 무언가를 구현 한 방법에 대한 정말 환상적인 게시물"목록에 있습니다.
Tim Holt

+1. 이거 대박. 이것에 감사합니다, 그것은 확실히 나에게 도움이 될 것입니다!
Andre

1

매우 간단한 방법 중 하나는 너비 / 2와 높이 / 2에 중심이있는 역 방사형 또는 구형 그래디언트를 만드는 것입니다. 마스킹의 경우 그라디언트를 곱하기보다는 노이즈에서 빼기 를 원합니다 . 이것은 섬들이 반드시 연결되어 있지 않다는 결점을 가지고보다 사실적으로 보이는 해안을 제공합니다.

노이즈를 빼고 그라디언트로 곱하는 것의 차이점은 다음과 같습니다. http://www.vfxpedia.com/index.php?title=Tips_and_Techniques/Natural_Phenomena/Smoke

방사형 그래디언트를 만드는 방법을 잘 모르는 경우이를 시작점으로 사용할 수 있습니다.

    public static float[] CreateInverseRadialGradient(int size, float heightScale = 1)
    {
        float radius = size / 2;

        float[] heightMap = new float[size * size];

        for (int iy = 0; iy < size; iy++)
        {
            int stride = iy * size;
            for (int ix = 0; ix < size; ix++)
            {
                float centerToX = ix - radius;
                float centerToY = iy - radius;

                float distanceToCenter = (float)Math.Sqrt(centerToX * centerToX + centerToY * centerToY);
                heightMap[iy * size + ix] = distanceToCenter / radius * heightScale;
            }
        }

        return heightMap;
    }

그라디언트를 높이 맵과 동일한 높이로 조정하는 것을 잊지 마십시오. 여전히 워터 라인을 고려해야합니다.

이 방법의 문제점은 높이 필드가지도 중앙을 중심으로한다는 것입니다. 그러나이 방법을 사용 하면 지형지 물을 높이 맵에 지형지 물을 추가하는 데 사용할 수 있으므로 지형지 물을 추가 하고 지형을 더욱 다양하게 만들 수 있습니다 .


응답을위한 thx. 잘 이해하고 있는지 잘 모르겠지만 마스크가 원래 하이트 맵 데이터에 전혀 영향을 미치지 않는다는 것을 알았습니까? 네거티브에만 영향을 미치므로 표시되는 픽스 (%)를 정의합니다. . 그러나 나는 간단한 gradiants로 시도했지만 결과에 만족하지 못했습니다.
Ace

1

두 번째 ollipekka의 제안 : 당신이하고 싶은 것은 하이트 맵에서 적절한 바이어스 기능을 빼서 가장자리가 수중이되도록 보장하는 것입니다.

적절한 바이어스 기능이 많이 있지만 상당히 간단한 기능은 다음과 같습니다.

f(x, y) = 1 / (x * (1-x) * y * (1-y)) - 16

여기서 xy 는 좌표 값이며 0과 1 사이에 놓 이도록 크기가 조정됩니다.이 함수는지도 중심에서 값 0을 가져오고 ( x = y = 0.5에서) 가장자리에서 무한대 인 경향이 있습니다. 따라서 높이 맵에서 높이를 빼면 (적절한 상수로 스케일링 됨) 높이 값도 맵 가장자리 근처에서 무한대를 뺀 경향이 있습니다. 원하는 임의의 높이를 골라 해수면이라고 부릅니다.

ollipekka가 지적했듯이,이 접근법은 섬이 인접 해 있다고 보장하지는 않습니다. 그러나 상당히 작은 배율로 바이어스 기능을 스케일링하면 맵의 중간 영역에서 대부분 평평 해 지므로 지형에 큰 영향을 미치지 않으며 가장자리 근처에만 큰 바이어스가 나타납니다. 따라서 그렇게하면 가장자리 근처에 작은 섬이있을 수 있습니다.

물론, 지형이 단절 될 가능성을 염두에 두지 않으면 다소 큰 스케일링 계수로 인해 더 많은 물과보다 자연스러운 섬 모양을 얻을 수 있습니다. 해수면 및 / 또는 원래 하이트 맵의 스케일을 조정하여 결과 섬의 크기와 모양을 변경할 수도 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.