วิธีง่าย ๆ ในการสร้างหน้ากากแผนที่เกาะ


21


ฉันกำลังค้นหาวิธีที่ดีและง่ายในการสร้างมาสก์สำหรับแผนที่เกาะด้วย C #


โดยทั่วไปฉันใช้กับแผนที่ความสูงแบบสุ่มที่สร้างด้วยเสียงเพอร์ลินซึ่งภูมิประเทศไม่ได้ล้อมรอบด้วยน้ำ

ป้อนคำอธิบายรูปภาพที่นี่

ขั้นตอนต่อไปคือการสร้างหน้ากากเพื่อให้แน่ใจว่ามุมและเส้นขอบเป็นเพียงน้ำ

ป้อนคำอธิบายรูปภาพที่นี่

จากนั้นฉันก็สามารถลบหน้ากากออกจากภาพเสียงเพอร์ลินเพื่อเกาะได้

ป้อนคำอธิบายรูปภาพที่นี่

และเล่นกับความคมชัด ..

ป้อนคำอธิบายรูปภาพที่นี่

และเส้นโค้งการไล่ระดับสีฉันสามารถรับความสูงของเกาะได้เช่นเดียวกับที่ฉันต้องการ ..

ป้อนคำอธิบายรูปภาพที่นี่

(นี่เป็นเพียงตัวอย่างของหลักสูตร)

อย่างที่คุณเห็น "ขอบ" ของเกาะเพิ่งถูกตัดออกซึ่งไม่ใช่ปัญหาใหญ่ถ้าค่าสีไม่ขาวเกินไปเพราะฉันจะแบ่งสีเทาออกเป็น 4 ชั้น (น้ำทรายหญ้าและ ร็อค)

คำถามของฉันคือฉันจะสร้างมาสก์หน้าตาดีได้อย่างไรในภาพที่สอง?


UPDATE

ฉันได้พบเทคนิคนี้ดูเหมือนว่าจะเป็นจุดเริ่มต้นที่ดีสำหรับฉัน แต่ฉันไม่แน่ใจว่าฉันจะสามารถนำไปใช้เพื่อให้ได้ผลลัพธ์ที่ต้องการได้อย่างยอดเยี่ยมเพียงใด 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ผลตอบแทนระยะทางที่ใกล้ที่สุดขอบของแผนที่จากตำแหน่งว่า และisSolidตัดสินใจว่าค่าระหว่าง 0 ถึง 1 นั้นจะเป็นของแข็งหรือไม่ มันเป็นฟังก์ชั่นที่เรียบง่ายมากมันอาจเป็นแบบนี้:

isSolid (ค่าทศนิยม) ส่งคืนค่า <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 คุณเพียงแค่ค้นหาว่าค่าสูงสุดสามารถและหารค่าปัจจุบันของคุณด้วยอะไร จากนั้นฉันก็ลบมันออกจากอันหนึ่งเพราะฉันอยากให้ศูนย์อยู่บนขอบด้านนอกและ 1 อยู่บนขอบด้านใน
MichaelHouse

14

หากคุณยินดีที่จะใช้พลังในการคำนวณสำหรับเรื่องนี้คุณสามารถใช้เทคนิคที่คล้ายกันกับสิ่งที่ผู้เขียนบล็อกนี้ทำ ( หมายเหตุ:หากคุณต้องการคัดลอกรหัสโดยตรงจะอยู่ใน ActionScript) โดยทั่วไปเขาจะสร้างจุดกึ่งสุ่ม (เช่นลักษณะค่อนข้างสม่ำเสมอ) แล้วใช้เหล่านี้เพื่อสร้างรูปหลายเหลี่ยม Voronoi

รูปหลายเหลี่ยม Voronoi

จากนั้นเขาก็กำหนดรูปหลายเหลี่ยมนอกกับน้ำและ iterates ผ่านส่วนที่เหลือของรูปหลายเหลี่ยมที่ทำให้น้ำพวกเขาถ้าร้อยละหนึ่งของรูปหลายเหลี่ยมที่อยู่ติดกันมีน้ำ จากนั้นคุณจะเหลือหน้ากากรูปหลายเหลี่ยมที่เป็นตัวแทนของเกาะ

แผนที่รูปหลายเหลี่ยม

จากนี้คุณสามารถใช้จุดรบกวนกับขอบทำให้เกิดสิ่งคล้ายนี้ (สีมาจากอีกขั้นตอนที่ไม่เกี่ยวข้อง):

แผนที่รูปหลายเหลี่ยมที่มีขอบมีเสียงดัง

จากนั้นคุณจะถูกทิ้งให้อยู่กับหน้ากากรูปเกาะที่ดูสมจริงซึ่งจะตอบสนองวัตถุประสงค์ของคุณ คุณสามารถเลือกที่จะใช้เป็นหน้ากากสำหรับเสียงเพอร์ลินของคุณหรือคุณสามารถสร้างค่าความสูงตามระยะทางทะเลและเพิ่มเสียงรบกวน (แม้ว่าจะดูไม่จำเป็น)


ขอบคุณสำหรับการตอบกลับของคุณ แต่นี่เป็นโซลูชันแรก (ที่สวยมากเท่านั้น) ที่ฉันได้จากการค้นหาเว็บ วิธีนี้ดูเหมือนจะดีมาก แต่ฉันต้องการลองวิธี "ง่าย"
Ace

@ Ace ยุติธรรมพออาจเป็น overkill เล็กน้อยสำหรับสิ่งที่คุณกำลังจะทำ: P ยังคงเป็นมูลค่าการเก็บในใจถ้าคุณต้องการมัน
ขั้วโลก

ดีใจที่มีคนเชื่อมโยงกับหน้านี้ - หน้านั้นอยู่ในรายการ "โพสต์ที่ยอดเยี่ยมจริงๆเกี่ยวกับวิธีการที่บางคนใช้"
ทิมโฮลท์

+1 มันเจ๋งมาก ขอบคุณสำหรับสิ่งนี้มันจะเป็นประโยชน์สำหรับฉันอย่างแน่นอน!
อังเดร

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;
    }

อย่าลืมที่จะไต่ระดับความสูงของคุณไปที่ระดับความสูงเดียวกันตามแผนที่ความสูงของคุณและคุณยังจำเป็นต้องคำนึงถึงปัจจัยอื่น ๆ

ปัญหาของวิธีนี้คือฟิลด์ความสูงของคุณจะอยู่กึ่งกลางกึ่งกลางของแผนที่ วิธีนี้ แต่ควรจะได้รับคุณเริ่มต้นด้วยการเพิ่มคุณสมบัติและทำให้ภูมิทัศน์ของคุณที่มีความหลากหลายมากขึ้นในขณะที่คุณสามารถใช้นอกจากนี้เพื่อเพิ่มคุณสมบัติในแผนที่ความสูงของคุณ


ขอบคุณสำหรับการตอบกลับ ฉันไม่แน่ใจว่าฉันเข้าใจคุณถูกต้องหรือไม่ แต่คุณสังเกตเห็นว่ารูปแบบดังกล่าวไม่ส่งผลกระทบต่อข้อมูลแผนที่ความสูงดั้งเดิมเลยเพียงแค่ส่งผลกระทบในเชิงลบดังนั้นมันจึงกำหนดพิกเซลที่แสดง (เป็น%) หรือไม่ . แต่ฉันก็ลองมันด้วยการไล่ระดับสีง่ายๆและฉันก็ไม่พอใจกับผลลัพธ์
Ace

1

ฉันแนะนำที่สองของ ollipekka: สิ่งที่คุณต้องการทำคือลบฟังก์ชัน bias ที่เหมาะสมออกจาก heightmap ของคุณ

มีฟังก์ชั่นไบอัสที่เหมาะสมมากมาย แต่ฟังก์ชันหนึ่งที่ค่อนข้างง่ายคือ:

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

โดยที่xและyเป็นค่าพิกัดปรับให้อยู่ระหว่าง 0 และ 1 ฟังก์ชันนี้ใช้ค่า 0 ที่กึ่งกลางของแผนที่ (ที่x = y = 0.5) และมีแนวโน้มที่ไม่มีที่สิ้นสุดที่ขอบ ดังนั้นการลบ (ปรับสัดส่วนด้วยค่าคงที่ที่เหมาะสม) จากแผนที่ความสูงของคุณเพื่อให้แน่ใจว่าค่าความสูงจะมีค่าลบอนันต์ใกล้กับขอบแผนที่ เพียงแค่เลือกระดับความสูงที่คุณต้องการและเรียกว่าระดับน้ำทะเล

ในฐานะที่เป็น ollipekka บันทึกวิธีนี้จะไม่รับประกันว่าเกาะจะต่อเนื่องกัน อย่างไรก็ตามการปรับฟังก์ชั่นไบอัสด้วยสเกลแฟคเตอร์ที่ค่อนข้างเล็กน่าจะทำให้แบนส่วนใหญ่อยู่ในพื้นที่กึ่งกลางของแผนที่ ดังนั้นการทำเช่นนี้ควรให้เกาะที่เป็นเกาะที่อยู่ติดกันส่วนใหญ่จะเป็นเกาะเล็ก ๆ สองสามเกาะที่อยู่ใกล้กับขอบ

แน่นอนถ้าคุณไม่คำนึงถึงความเป็นไปได้ของภูมิประเทศที่ถูกตัดการเชื่อมต่อปัจจัยที่มีขนาดใหญ่ขึ้นควรให้น้ำมากขึ้นและรูปร่างเกาะที่ดูเป็นธรรมชาติยิ่งขึ้น การปรับระดับน้ำทะเลและ / หรือมาตราส่วนความสูงดั้งเดิมของคุณสามารถใช้เพื่อปรับขนาดและรูปร่างของเกาะที่เกิดขึ้นได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.