ฉันจะทำให้น้ำดูมืดลงได้ลึกเหมือน Minecraft ได้อย่างไร


24

ใน Minecraft เมื่อคุณดูน้ำยิ่งคุณเห็นความมืดที่ได้รับ ไม่มีใครรู้วิธีการรหัสบางอย่างเช่นนั้น

Minecraft ที่มีเอฟเฟกต์ Minecraft ที่มีผลกระทบ

เกมที่คล้ายกันโดยไม่มีผลกระทบ เกมที่คล้ายกันโดยไม่มีผลกระทบ


18
สิ่งนี้ทำได้โดยอัตโนมัติหรือไม่เนื่องจากวัสดุของลูกบาศก์น้ำเป็นแบบกึ่งโปร่งใส?
pek

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

2
บางทีมันอาจเป็นเอฟเฟกต์การผสมแบบเติมที่ใช้กับลูกบาศก์น้ำเท่านั้น? อีกครั้งสิ่งนี้น่าจะง่ายเนื่องจากวัสดุกึ่งโปร่งใส
pek

1
คุณสามารถเปลี่ยนสีของกล่องตามความลึกได้
Ali1S232

คำตอบ:


51

โดยพื้นฐานแล้วมีสองวิธีที่แตกต่างกันสำหรับแสงน้ำตามความลึก:

voxel-Lighting

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

น้ำปิดกั้นแสงแดดและลดแสงลง 3 ระดับต่อบล็อก (แทนที่จะเป็น 1 ระดับเริ่มต้น) ซึ่งหมายถึงความสว่างในมหาสมุทรในแต่ละระยะทางจากพื้นผิวคือ:

0 (surface): 15 (direct sunlight)
1:           12
2:            9
3:            6
4:            3
5 and below:  0 (darkness)

ที่มา: Minecraft Wiki - Light

Shadowing ระยะทาง

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

การคำนวณโดยตรง

หากคุณมีพื้นผิวที่เรียบคุณสามารถคำนวณระยะทางที่แสงเดินทางผ่านน้ำได้ถ้าคุณผ่านพื้นผิวออกห่างจากแหล่งน้ำทั่วไป\ vec {n}และผลคูณของจุดของตำแหน่งนี้และตำแหน่งพื้นผิวsลงในรูปร่างเรขาคณิต

ระยะทางน้ำที่มีประสิทธิภาพคือ

\ max (\ left (s - \ vec {n} \ cdot \ vec {p} \ right), 0) \ cdot \ left (1 + \ tan (\ alpha) \ right)

โดยที่\ vec {p}เป็นตำแหน่งของจุดยอดและแอลฟาเป็นมุมระหว่างทิศทางแสงใต้พื้นผิวและพื้นผิวของน้ำไปทางร่างกายของน้ำ

เมื่อพระอาทิตย์ตกดินแอลฟาจะถึงน้อยกว่า 50 °เล็กน้อยเพราะแสงหักเหเมื่อเข้าสู่น้ำ
นี่คือโพสต์บล็อกพร้อมคำอธิบายที่ดี: กล้องดิจิตอล: การสะท้อนกลับหมดภายใน
โพสต์อีกรายละเอียดเพิ่มเติม: กล้องดิจิตอล: กฎการหักเหของ Snell

หากคุณกำลังใช้ heightmap บนขนานพื้นผิวลงไปในน้ำที่จะกลายเป็น\ left (s - \ vec {n} \ cdot \ vec {p} \ right) \ left (s - h \ ขวา)ปัจจัยที่ถูกต้องเท่ากับ 1 หากดวงอาทิตย์อยู่เหนือผิวน้ำโดยตรง
ด้วยแสงจุดคุณต้องคำนวณแอลฟาสำหรับแต่ละจุดยอดตามตำแหน่งสัมพันธ์กับแหล่งกำเนิดแสง

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

ข้อดี:

  • รวดเร็วและแม่นยำ

จุดด้อย:

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

การทำแผนที่เงา

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

หากพื้นผิวค่อนข้างเรียบคุณควรใช้แหล่งกำเนิดแสงแบบหักเหเพื่อผลลัพธ์ที่ดีกว่า

ข้อดี:

  • ใช้งานได้กับเรขาคณิตของน้ำที่ค่อนข้างซับซ้อนตราบใดที่มันไม่ได้เกิดขึ้นเอง *
  • สามารถนำกลับมาใช้ใหม่ได้สำหรับปริมาณที่โปร่งใสบางส่วนเกือบทุกชนิด

จุดด้อย:

  • ช้ากว่าการคำนวณโดยตรง
  • ต้องการ VRAM เพิ่มเติมสำหรับแผนที่ความลึก
  • ไม่แม่นยำ 100%

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

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

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

Ray-ติดตาม

การติดตามรังสีเป็นวิธีที่แม่นยำที่สุด แต่ก็เป็นวิธีที่แพงที่สุดในการแสดงปริมาณที่โปร่งใส มีสองวิธีในการทำสิ่งนี้: 1. การติดตามจากพื้นมหาสมุทรสู่พื้นผิวและ 2. การติดตามจากแหล่งกำเนิดแสงสู่น้ำ จำเป็นต้องมีรังสีหลายจุดสำหรับแต่ละจุดบนพื้นเพื่อคำนวณความสว่าง

ข้อดี:

  • ทำงานอย่างถูกต้องกับทุกรูปทรงเรขาคณิต
  • แก้ไขการกัดกร่อน!

จุดด้อย:

  • ช้า!

ผลเพิ่มเติม

มีอีกสองสิ่งที่ต้องคำนึงถึงเมื่อเรนเดอร์น้ำ:

หมอก

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

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

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

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

แกลอรี่แกล้งทำ

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

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

caustics ควรจางหายเร็วกว่าแสงกระจายที่เพิ่มความลึก

การไล่ระดับสี

สีกระจัดกระจายแตกต่างกันดังนั้นสีของแสงควรเปลี่ยนตามความลึกที่เพิ่มขึ้น นอกจากนี้ยังป้องกันขอบฉับพลันเช่นที่ชายหาดตัดกับผิวน้ำ

มุมของอุบัติการณ์

เนื่องจากการหักเหของแสงแสงกระทบพื้นมหาสมุทรสูงชันกว่าปกติ บทความวิกิพีเดียเกี่ยวกับกฎหมายปราดเปรื่องของมีสูตรสำหรับมุมและเวกเตอร์


6

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

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

เงื่อนหนึ่งที่เป็นวิธีการใช้คือน้ำไม่ได้เข้มขึ้นเมื่ออยู่ห่างจากผู้ชม - เฉพาะเมื่อคุณลงไป ใช่เอฟเฟกต์หมอกจะเตะในระยะไกล แต่ไม่ใช่เอฟเฟกต์น้ำ

ดังนั้นสูตรพื้นฐานสำหรับการคำนวณแสงจะเป็นแบบนี้ในรหัสหลอก ...

light_on_cube = 1.0
for each cube above target cube, from lowest to highest {
   if cube being examined is tree foliage
      light_on_cube = 0.5
   else if cube being examined is water
      light_on_cube = light_on_cube - 0.1
   else if cube being examined is solid 
      light_on_cube = 0
}

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

  1. คำนวณแสงจากดวงอาทิตย์จากด้านบนโดยตรง (ผ่านรหัสหลอกด้านบน) สำหรับแต่ละลูกบาศก์
  2. หากคิวบ์มีแหล่งกำเนิดแสงติดกับมันให้พิจารณาว่ามันสว่างเต็มที่ (1.0)
  3. หากลูกบาศก์ไม่ได้รับแสงแดดจากด้านบนโดยตรงให้แสงสว่างบางส่วนโดยพิจารณาว่าห่างไกลจากก้อนที่มีแสงสว่างเพียงพอ ใกล้ชิดหมายถึงแสงมากขึ้นห่างออกไปหมายถึงน้อยลง (จนเป็นศูนย์)

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


1
ใช่ฉันค่อนข้างแน่ใจว่าเป็นตั๋ว ฉันทำอะไรที่คล้ายกันในเกมของฉัน
MichaelHouse

โดยวิธีการที่ฉันเพิ่งเพิ่มบล็อกของคุณไปยังรายชื่อผู้อ่าน Google ของฉัน Byte56 - นักพัฒนาบล็อก FTW!
ทิมโฮลท์

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

3

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

หากคุณมีความลึกd (เป็นบล็อกเริ่มต้นที่ 0) สมการที่เหมาะสมสำหรับความสว่างจะเป็น:

L = (1- m ) e - kd + m

รหัส: L = (1.0 - m) * exp(-k * d) + m;

kควบคุมความเร็วที่ได้รับเข้มขึ้น (สูงกว่า = เร็วกว่า) ค่าที่เหมาะสมจะเป็น 0.5
mคือความสว่างขั้นต่ำที่คุณต้องการ
Lแตกต่างกันไปตั้งแต่ 0 ถึง 1

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


ฉันไม่คิดว่าจะทำอย่างนั้น แค่อยากรู้อยากเห็นคุณได้สมการนั้นมาจากไหน?
ซาเวียร์

1
@ ซาเวียร์: ฉันเพิ่งทำมันขึ้นมา e^-kdบิตเป็นเพียงชี้แจงสลายซึ่งเป็นฟังก์ชั่นมาตรฐานสำหรับสิ่งที่ค่อยๆมีแนวโน้มไปศูนย์กว่ามูลค่าบางคน (ลึก) คูณโดย(1-m)และนอกเหนือจากmนี้เป็นเพียงขนาดและชดเชยการสลายตัวเพื่อที่จะจบลงที่ต่ำสุดของแต่ยังคงเริ่มต้นที่ m en.wikipedia.org/wiki/Exponential_decay1
Peter Alexander

สิ่งนั้นคือบล็อกของเฉดสีที่ลึกกว่าจะมองเห็นได้เฉพาะเมื่อบล็อกมีสีอัลฟา ในกรณีที่ไม่จำเป็นต้องเปลี่ยนสีของบล็อกอัลฟาจะสร้างเอฟเฟกต์แบบอัตโนมัติ
Jonathan Connell

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

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