ฉันจะนำแสง voxel มาใช้กับการบดเคี้ยวในเกมสไตล์ Minecraft ได้อย่างไร


13

ฉันใช้ C # และ XNA อัลกอริทึมปัจจุบันของฉันสำหรับให้แสงสว่างเป็นวิธีเรียกซ้ำ อย่างไรก็ตามมันมีราคาแพงจนถึงจุดที่ก้อน 8x128x8 หนึ่งก้อนคำนวณทุก 5 วินาที

  • มีวิธีการให้แสงแบบอื่นที่จะทำให้เกิดเงาแบบแปรผันได้หรือไม่?
  • หรือวิธีการเวียนเกิดเป็นวิธีที่ดีและบางทีฉันอาจทำผิด

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

รหัสการเรียกซ้ำปัจจุบันของฉันอยู่ด้านล่าง สิ่งนี้ถูกเรียกจากที่ใด ๆ ในก้อนที่ไม่มีระดับแสงเป็นศูนย์หลังจากการล้างและเพิ่มแสงแดดและคบไฟใหม่

world.get___atเป็นฟังก์ชั่นที่สามารถรับบล็อกด้านนอกของก้อนนี้ (นี่คือภายในชั้นเรียนชิ้น) Locationเป็นโครงสร้างของตัวเองที่คล้ายกับ a Vector3แต่ใช้จำนวนเต็มแทนค่าทศนิยม light[,,]เป็น lightmap สำหรับชิ้นข้อมูล

    private void recursiveLight(int x, int y, int z, byte lightLevel)
    {
        Location loc = new Location(x + chunkx * 8, y, z + chunky * 8);
        if (world.getBlockAt(loc).BlockData.isSolid)
            return;
        lightLevel--;
        if (world.getLightAt(loc) >= lightLevel || lightLevel <= 0)
            return;
        if (y < 0 || y > 127 || x < -8 || x > 16 || z < -8 || z > 16)
            return;
        if (x >= 0 && x < 8 && z >= 0 && z < 8)
            light[x, y, z] = lightLevel;

        recursiveLight(x + 1, y, z, lightLevel);
        recursiveLight(x - 1, y, z, lightLevel);
        recursiveLight(x, y + 1, z, lightLevel);
        recursiveLight(x, y - 1, z, lightLevel);
        recursiveLight(x, y, z + 1, lightLevel);
        recursiveLight(x, y, z - 1, lightLevel);
    }

1
มีบางอย่างผิดปกติอย่างมากหากคุณทำ 2 ล้านบล็อกต่อชิ้นโดยเฉพาะอย่างยิ่งเนื่องจากมีเพียง 8,192 บล็อกใน 8 * 128 * 8 อัน คุณจะทำอะไรได้บ้างที่คุณจะผ่านแต่ละบล็อค ~ 244 ครั้ง (นั่นอาจเป็น 255 ได้ไหม)
doppelgreener

1
ฉันทำผิดคณิตศาสตร์ ขออภัย: P. เปลี่ยนแปลง แต่เหตุผลที่คุณต้องไปแม้ว่าจะมีมากคือคุณต้อง "ฟองสบู่" ออกจากทุก ๆ บล็อกจนกว่าคุณจะได้ระดับแสงที่มากกว่าระดับที่คุณตั้งไว้ นั่นหมายความว่าทุกบล็อคอาจถูกเขียนทับ 5-10 ครั้งก่อนที่จะถึงระดับแสงจริง 8x8x128x5 = มาก

2
คุณเก็บ Voxels ของคุณอย่างไร? นั่นเป็นสิ่งสำคัญในการลดเวลาการสำรวจเส้นทาง
Samaursa

1
คุณช่วยโพสต์อัลกอริทึมการส่องสว่างของคุณได้ไหม? (คุณถามว่าคุณกำลังทำมันไม่ดีเราไม่มีความคิด)
doppelgreener

ฉันกำลังเก็บพวกมันไว้ในอาร์เรย์ "บล็อก" และบล็อกประกอบด้วย enum สำหรับเนื้อหารวมถึงเมตาดาต้าไบต์สำหรับใช้ในอนาคต

คำตอบ:


6
  1. แสงทุกดวงมีตำแหน่งที่แม่นยำ (จุดลอยตัว) และทรงกลมที่กำหนดขอบเขตโดยค่ารัศมีแสงสเกลาร์, LR.
  2. Voxel ทุกตัวมีตำแหน่งที่แม่นยำ (จุดลอยตัว) ที่จุดศูนย์กลางซึ่งคุณสามารถคำนวณจากตำแหน่งในตารางได้อย่างง่ายดาย
  3. วิ่งผ่านทุก ๆ 8192 voxels เพียงครั้งเดียวและดูว่ามันตกอยู่ในปริมาตรทรงกลมของแสง N ทรงกลมแต่ละอันด้วยการตรวจสอบหรือไม่โดย|VP - LP| < LRที่ VP เป็นเวกเตอร์ตำแหน่งของ voxel เทียบกับแหล่งกำเนิดและLPเป็นเวกเตอร์ตำแหน่งของแสงที่สัมพันธ์กับกำเนิด แสงแต่ละที่มีรัศมี voxel |VP - LP|ปัจจุบันพบว่าจะอยู่ในที่เพิ่มขึ้นจะเป็นปัจจัยแสงตามระยะทางจากศูนย์แสง หากคุณทำให้เวกเตอร์นั้นเป็นแนวปกติแล้วได้ขนาดมันจะอยู่ในช่วง 0.0-> 1.0 ระดับแสงสูงสุดที่ voxel สามารถเข้าถึงได้คือ 1.0

รันไทม์คือO(s^3 * n)โดยที่sความยาวด้าน (128) ของภูมิภาค voxel ของคุณและnเป็นจำนวนแหล่งกำเนิดแสง หากแหล่งกำเนิดแสงของคุณคงที่นี่จะไม่มีปัญหา หากแหล่งกำเนิดแสงของคุณเคลื่อนที่แบบเรียลไทม์คุณสามารถทำงานบนเดลต้าแทนการคำนวณใหม่ทั้งหมดในการอัปเดต

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


ถ้าฉันเข้าใจอัลกอริธึมของเขาอย่างถูกต้องเขาพยายามทำ pseudo-radioity บางชนิดโดยให้แสงส่องไปยังที่ไกล ๆ แม้ว่ามันจะหมายความว่ามันต้อง "รอบ" บางมุม หรือกล่าวอีกนัยหนึ่งอัลกอริธึมเติมน้ำท่วมของช่องว่าง "ว่างเปล่า" (ไม่แข็ง) ที่มีระยะทางสูงสุดล้อมรอบจากแหล่งกำเนิด (แหล่งกำเนิดแสง) และระยะทาง (และจากนั้นการลดทอนแสง) คำนวณตาม เส้นทางที่สั้นที่สุดไปยังจุดกำเนิด ดังนั้น - ไม่ใช่สิ่งที่คุณเสนอในปัจจุบัน
Martin Sojka

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

4

Minecraft เองไม่ได้ทำเรื่องแบบนี้

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

คุณต้องเพิ่มคบเพลิงและไฟที่ไม่ท่วมอื่น ๆ ในภายหลัง

มีวิธีอื่น ๆ อีกมากมายในการทำเช่นนี้รวมถึงการแพร่กระจายแสงทิศทางแฟนซี ฯลฯ แต่เห็นได้ชัดว่าช้าลงและคุณต้องคิดออกหากคุณต้องการลงทุนในความสมจริงเพิ่มเติมจากบทลงโทษเหล่านั้น


เดี๋ยวก่อน Minecraft จะทำยังไงกันแน่? ฉันไม่เข้าใจในสิ่งที่คุณพูด ... "ทุกชั้นกำลังรวบรวมแสงจากเพื่อนบ้าน voxels ในชั้นก่อนหน้าพร้อมการลดทอน" หมายความว่าอะไร?

2
เริ่มต้นด้วยเลเยอร์สูงสุด (ชิ้นส่วนของความสูงคงที่) เติมด้วยแสงแดด จากนั้นไปที่เลเยอร์ด้านล่างและ voxel ทุกอันจะได้รับแสงสว่างจาก voxels ที่ใกล้เคียงที่สุดในเลเยอร์ก่อนหน้า (เหนือ) ใส่แสงเป็นศูนย์ในของแข็ง voxels คุณมีสองวิธีในการตัดสินใจ "เคอร์เนล", น้ำหนักของการมีส่วนร่วมจาก voxels ด้านบน Minecraft ใช้ค่าสูงสุดที่พบ แต่ลดลง 1 หากการขยายพันธุ์ไม่ตรง นี่คือการลดทอนด้านข้างดังนั้นคอลัมน์แนวตั้งที่ไม่มีการปิดกั้นของ voxels จะได้รับการแพร่กระจายของแสงแดดอย่างเต็มที่และโค้งแสงรอบ ๆ มุม
Bjorn Wesen

1
โปรดทราบว่าวิธีการนี้ไม่ได้มีพื้นฐานมาจากฟิสิกส์จริงใด ๆ :) ปัญหาหลักคือคุณกำลังพยายามที่จะประมาณแสงที่ไม่ได้บอกทิศทาง มันดูดีทีเดียว
Bjorn Wesen

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

1
@ Felheart: มันเป็นเวลาที่ฉันได้ดูสิ่งนี้ แต่ในสาระสำคัญมีระดับแสงรอบข้างขั้นต่ำที่มักจะเพียงพอสำหรับใต้ยื่นเพื่อให้พวกเขาไม่ได้ดำอย่างสมบูรณ์ เมื่อฉันทำสิ่งนี้ด้วยตัวเองฉันได้เพิ่มการส่งผ่านครั้งที่สองจากขึ้น - ลง แต่ฉันไม่เห็นการปรับปรุงด้านสุนทรียภาพที่ยิ่งใหญ่เมื่อเทียบกับวิธีการรอบข้าง ต้องใช้ Torches / ปอตไลท์แยกกัน - ฉันคิดว่าคุณสามารถเห็นรูปแบบการแพร่กระจายที่ใช้ใน MC หากคุณวางไฟฉายไว้ตรงกลางกำแพงและทดลองสักเล็กน้อย ในการทดสอบของฉันฉันเผยแพร่พวกเขาใน lightfield ที่แยกต่างหากจากนั้นเพิ่ม
Bjorn Wesen

3

มีคนพูดว่าจะตอบคำถามของคุณเองถ้าคุณคิดออกว่าใช่ หาวิธี

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

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

ฉันรู้ว่ามันซับซ้อนฉันพยายามอธิบายให้ดีที่สุด แต่ความจริงที่สำคัญคือมันใช้งานได้และรวดเร็ว


2

ฉันขอแนะนำอัลกอริทึมที่เรียงลำดับการรวมโซลูชัน multi-pass ของคุณกับวิธีการเรียกซ้ำและเป็นไปได้ว่าเร็วกว่าวิธีใดวิธีหนึ่ง

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

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

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

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

คุณอาจทราบว่าฉันไม่ได้ระบุวิธีจัดเก็บรายการ จริง ๆ แล้วการใช้งานที่สมเหตุสมผลใด ๆ ควรจะทำตราบใดที่การแทรกบล็อกที่กำหนดและการแยกบล็อกโดยพลการนั้นเป็นการดำเนินการที่รวดเร็ว รายการที่เชื่อมโยงควรใช้งานได้ แต่จะมีการใช้งานอาร์เรย์ที่มีความยาวผันแปรได้เช่นกัน เพียงแค่ใช้สิ่งที่ดีที่สุดสำหรับคุณ


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

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

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

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