- ในเกมที่สามารถขุดและถมภูมิประเทศได้ ทะเลสาบ แม่น้ำ และแอ่งน้ำต้องไหลออกตามขอบเขตใหม่ได้ จึงจำเป็นต้องมี การจำลองน้ำแบบกริด ที่เร็วและเสถียร
- เป้าหมายคือโมเดลความสูงแบบ 2D ที่ใช้กริดเดียวกับภูมิประเทศในสเกลประมาณ 1 เมตร พร้อมคุณสมบัติรักษาปริมาณน้ำ ควบคุมเสถียรภาพได้ และมีต้นทุนการอัปเดตแบบเชิงเส้น
- Smoothed Particle Hydrodynamics และ Stable Fluids ต่างก็เหมาะกับของไหลอนุภาคความละเอียดสูงและปริมาตรของไหลแบบปิดตามลำดับ จึงไม่ตรงกับโจทย์การจัดการผิวน้ำอิสระบนภูมิประเทศอย่างรวดเร็ว
- วิธี virtual pipes ที่เลือกใช้ จะเก็บความสูงของน้ำและการไหลระหว่างเซลล์ไว้บน staggered grid แล้วประมวลผลการเร่งการไหล การสเกลการไหลออก และการอัปเดตคอลัมน์น้ำด้วยลูปบนอาร์เรย์ 2D เพียงไม่กี่ชุด
- เมื่อใช้
dtและgที่เหมาะสม จะได้ผลลัพธ์ที่ดูเหมือนน้ำ แต่ยังมีข้อจำกัดคือไม่มี ความเฉื่อย และการแพร่ของความเร็ว ทำให้กระแสน้ำที่ไหลเร็วไม่ส่งต่อเข้าไปในทะเลสาบได้อย่างต่อเนื่อง
ทำไมน้ำจึงยากในเกมที่แก้ไขภูมิประเทศได้
- ในเกมวางแผนหรือเกมจำลองเมืองและหมู่บ้าน น้ำอาจทำหน้าที่เป็นขอบเขตตามธรรมชาติ รวมถึงรองรับการเดินเรือ การตกปลา การค้า สงครามทางทะเล น้ำดื่ม การขนส่ง และความสวยงาม
- หากผู้เล่นสามารถเปลี่ยนภูมิประเทศได้โดยตรง ความยากของการจัดการน้ำจะสูงขึ้นมาก
- ทรัพยากรอย่างดิน ทราย และดินเหนียว หากขุดออกจากพื้นแล้วภูมิประเทศหายไปด้วยก็ดูเป็นธรรมชาติกว่า
- หินและสินแร่โลหะก็ดูเหมาะกับการขุดเอาพื้นดินออกมากกว่าจะเป็นเพียงอ็อบเจ็กต์วางอยู่บนผิวดิน
- ถ้าสร้างอาคารบนทางลาดไม่ได้ ก็ต้องปรับพื้นให้เรียบก่อนก่อสร้าง
- สามารถให้ การแก้ไขภูมิประเทศ เองเป็นเครื่องมือสำหรับการแสดงออกอย่างสร้างสรรค์ได้
- สถานการณ์สำคัญคือเมื่อขุดเปิดขอบของทะเลสาบหรือแอ่งน้ำจนเกิดทางน้ำใหม่ น้ำจะไหลไปที่ไหนและมากแค่ไหน
จุดที่วิธีแก้ง่าย ๆ ยังไม่พอ
- ทางเลี่ยงที่เป็นไปได้ เช่น ตรึงน้ำไว้ที่ตำแหน่งเริ่มต้น มองว่าทุกอย่างที่ต่ำกว่าระดับหนึ่งคือน้ำ จำกัดไม่ให้ขุดลึกเกินไป หรือใช้โมเดลการไหลแบบง่ายสไตล์ Minecraft หรือ Dwarf Fortress
- วิธีเหล่านี้ใช้เป็นทางเลือกได้ แต่ถ้าจะใช้เป็นโมเดลหลักก็เรียบง่ายเกินไปหรือให้ความรู้สึกเป็นบล็อกมากเกินไป
- โมเดลของ Dwarf Fortress ใกล้เคียงกว่าวิธีอื่น แต่ถูกออกแบบโดยมี 3D เป็นพื้นฐาน ขณะที่ปัญหาที่ต้องการแก้ส่วนใหญ่คือ น้ำบนภูมิประเทศแบบ 2D
- Timberborn ใช้โมเดลประเภทเดียวกับที่กล่าวถึงที่นี่
เงื่อนไขของการจำลองที่ต้องการ
- โมเดลน้ำที่ตั้งเป้าไว้ต้องตอบเงื่อนไขต่อไปนี้
- ควรทำงานบน กริด เดียวกับภูมิประเทศ
- สเกลเฉลี่ยประมาณ 1 เมตร และไม่จำเป็นต้องจำลองรายละเอียดอย่างละอองน้ำกระเซ็นเล็ก ๆ
- มองน้ำเป็น height field บนภูมิประเทศ โดยไม่พิจารณาการไหลในแนวดิ่งหรือช่องว่างในหน้าตัดแนวดิ่ง
- น้ำต้องไหลได้ และต้องไม่หายไปเพราะความคลาดเคลื่อนของการจำลอง
- ต้องสามารถควบคุมเสถียรภาพได้
- ต้นทุนต่อหนึ่งสเต็ปต้องเป็นเชิงเส้นตามขนาดของการจำลอง และควรจบได้ในไม่กี่ลูป
ความไม่ตรงกันกับการจำลองของไหลแบบเดิม
- Smoothed Particle Hydrodynamics ให้ผลลัพธ์ของไหลความละเอียดสูงที่น่าทึ่ง แต่ต่างจากปัญหาที่ต้องการในที่นี้
- อนุภาคน้ำขนาด 1 เมตรอาจดูเหมือนลูกโป่งน้ำ
- หากทำให้อณูเล็กลง ต้นทุนด้านประสิทธิภาพจะสูงขึ้น
- เป้าหมายไม่ใช่ความสมจริงระดับสูง แต่เป็นโมเดลที่เร็วและดูน่าเชื่อ
- Stable Fluids ของ Jos Stam ใกล้เคียงกับโมเดลที่จัดการปริมาตรที่เต็มไปด้วยของไหล เช่น ถังปิด
- ต่างจากปัญหาการจัดการ ผิวน้ำอิสระ บนภูมิประเทศโดยตรง
- บางขั้นตอนต้องแก้ sparse linear system แบบวนซ้ำ ซึ่งมีต้นทุนสูง
- เป็นแนวทางที่แก้สมการ Navier-Stokes ทั้งชุด ขณะที่สิ่งที่ต้องการที่นี่คือ shallow water equations
Shallow water equations และการเลือกกริด
- Shallow water equations เป็นแนวทางที่เฉลี่ยชั้นน้ำนิ่งในแนวดิ่งบนภูมิประเทศ แล้วจัดการเป็นสมการ 2D
- คำว่า “Shallow” หมายถึงสมมติฐานว่าขนาดในแนวดิ่งของคอลัมน์น้ำเล็กกว่าระยะในแนวนอนที่สนใจมาก
- เช่น ความลึกของแม่น้ำมีตั้งแต่ไม่กี่เมตรถึงหลายสิบเมตร ขณะที่ระยะที่สนใจอยู่ในระดับกิโลเมตร
- collocated grid แบบทั่วไปจะเก็บความสูงของน้ำและความเร็วไว้ในเซลล์เดียวกัน แต่ในการจำลองพลศาสตร์ของไหลอาจเกิดปัญหาได้
- หากดิสครีไทซ์อนุพันธ์อันดับหนึ่งแบบตรงไปตรงมา อาจเกิดอคติของทิศทางหรือความไม่เสถียร
- ถ้ามีการไหลเข้าจากซ้ายขวาและไหลออกทางบนล่างภายในเซลล์เดียวกัน อาจเกิดความขัดแย้งที่ดูเหมือนความเร็วรวมเป็น 0
- Staggered grid จะเก็บค่าประเภทความสูงของน้ำหรือความหนาแน่นไว้ในเซลล์ ส่วนความเร็วหรือการไหลจะเก็บไว้ที่ขอบระหว่างเซลล์
- อาร์เรย์ความสูงของน้ำขนาด
N x N - อาร์เรย์การไหลแกน X ขนาด
(N+1) x N - อาร์เรย์การไหลแกน Y ขนาด
N x (N+1)
- อาร์เรย์ความสูงของน้ำขนาด
วิธี Virtual pipes
- Virtual pipes เป็นวิธีที่มองว่าเซลล์น้ำเชื่อมต่อกันด้วยท่อเสมือน แล้วคำนวณการไหลจากแนวคิดนั้น
- หนึ่งในงานวิจัยที่อ้างอิงยังรองรับคอลัมน์น้ำหลายระดับและการเชื่อมต่อแนวดิ่ง ส่วนอีกงานเน้น hydraulic erosion เป็นหลัก แต่ที่นี่ตัดเป้าหมายนั้นออกไป
- ค่าที่ต้องเก็บมี 3 ค่า
water: ความสูงของคอลัมน์น้ำในแต่ละเซลล์flowX: การไหลรวมของน้ำระหว่างเซลล์ที่ติดกันในแนวนอนflowY: การไหลรวมของน้ำระหว่างเซลล์ที่ติดกันในแนวตั้ง
- เก็บ การไหล (flow, flux) แทนความเร็ว
- การไหลอาจมองได้ว่าเป็นปริมาตรของน้ำที่ผ่านต่อหนึ่งหน่วยเวลา
- การไหลระหว่างเซลล์ว่างสามารถกำหนดเป็น 0 ได้อย่างเป็นธรรมชาติ
- ความเร็วคือการนำการไหลหารด้วยพื้นที่หน้าตัด ดังนั้นเมื่อแทบไม่มีน้ำอาจเกิดปัญหา 0/0 หรือปัญหาเรื่องค่าขีดขั้นต่ำได้
3 ขั้นตอนของหนึ่งสเต็ป
- การจำลองหนึ่งสเต็ปแบ่งเป็นสามขั้นตอน
- เร่งการไหล: เพิ่มการไหลระหว่างเซลล์ตามความต่างของระดับผิวน้ำของเซลล์ที่อยู่ติดกัน
- สเกลการไหลออก: หากน้ำที่ไหลออกจากเซลล์หนึ่งมากกว่าน้ำที่มีอยู่จริง ให้ลดการไหลออกลง
- อัปเดตคอลัมน์น้ำ: เพิ่มหรือลดความสูงของน้ำในแต่ละเซลล์ตามการไหลจากเซลล์ข้างเคียง
-
เร่งการไหล
- หากความสูงของน้ำในเซลล์ติดกันสองเซลล์ต่างกัน การไหลจะถูกเร่งจากฝั่งที่สูงไปยังฝั่งที่ต่ำ
- อัปเดตการไหลบนขอบภายในในทิศ X และ Y โดยใช้
g,dt,dx,dy - พื้นที่หน้าตัด
Aของท่อเสมือนถูกใช้ร่วมกับgเท่านั้น ดังนั้นในงานแบบง่ายสามารถมองว่าไปรวมอยู่ในgได้ - สามารถเพิ่มแรงเสียดทานได้ด้วยการลดการไหลลงทุกสเต็ป
- งานวิจัยแนะนำค่าสัมประสิทธิ์
pow(friction, dt) - เพื่อให้ใช้ค่าที่เข้าใจง่ายกว่า อาจใช้
pow(1-friction, dt)ได้ - มองว่า
friction=0คือแรงเสียดทานสูงสุดที่ลบการไหลเดิมออกทั้งหมด ส่วนfriction=1คือไม่มีแรงเสียดทาน - ยิ่ง
dtมาก การจำลองจะยิ่งเร็ว แต่ก็อาจไม่เสถียรขึ้น - ในการจำลองของไหล Courant-Friedrichs-Lewy condition มีความสำคัญ
- ในทางปฏิบัติต้องลด
dtลงจนกว่าจะเสถียร โดยค่าที่ใช้คือประมาณ0.001~0.01
-
อัปเดตคอลัมน์น้ำ
- แต่ละเซลล์จะดูการไหล 4 ทิศรอบตัวแล้วเพิ่มหรือลดน้ำ
flowX(x,y)และflowY(x,y)ที่ไหลเข้าจากซ้ายและด้านล่างจะถูกบวกเข้าไปflowX(x+1,y)และflowY(x,y+1)ที่ไหลออกทางขวาและด้านบนจะถูกลบออก- นี่คือขั้นตอนที่ย้ายน้ำจริงระหว่างเซลล์ตามค่าการไหลที่คำนวณไว้
-
สเกลการไหลออก
- หากการไหลมีค่ามากเกินไป หลังการอัปเดตความสูงของน้ำในบางเซลล์อาจกลายเป็น ค่าติดลบ
- จะรวมเฉพาะการไหลออกของแต่ละเซลล์ เพื่อตรวจสอบว่าน้ำที่จะถูกนำออกในหนึ่งสเต็ปมากกว่าปริมาณน้ำจริงหรือไม่
- หากปริมาณที่ต้องเอาออกมากเกินไป จะลดการไหลออกทั้งหมดด้วยสัดส่วนเดียวกันเพื่อให้ความสูงของน้ำไม่ต่ำกว่า 0
- ขั้นตอนนี้คือกลไกหลักที่ช่วยให้เสถียรและป้องกันปริมาณน้ำติดลบ
ภูมิประเทศ เงื่อนไขขอบเขต และการจัดการความหนืด
- ภูมิประเทศถูกสะท้อนเข้าไปในขั้นเร่งการไหล โดยใช้ ระดับผิวน้ำ แทนความสูงของคอลัมน์น้ำ
- ระดับผิวน้ำ =
terrain(x,y) + water(x,y) - เซลล์ที่ภูมิประเทศสูง แม้มีความสูงคอลัมน์น้ำเท่ากันก็จะมีผิวสูงกว่าและทำให้น้ำเคลื่อนได้
- ระดับผิวน้ำ =
- เงื่อนไขขอบเขตถูกกำหนดโดยนัยผ่านค่าการไหลที่ขอบ
flowX(0,y),flowX(N,y),flowY(x,0),flowY(x,N)คือค่าที่ขอบเขต- หากตั้งเป็น 0 จะทำงานเหมือนกำแพง
- ค่าไหลเข้าจะเติมน้ำ ส่วนค่าไหลออกจะนำน้ำออก
- สำหรับน้ำบนภูมิประเทศ ขอบเขตแบบ น้ำไหลออก ที่ปล่อยให้น้ำหายไปตรงขอบแผนที่อาจดูเป็นธรรมชาติ
- ส่วนที่แม่น้ำตัดผ่านขอบสามารถตั้งเป็นขอบเขตไหลเข้าเพื่อให้น้ำในแม่น้ำไหลต่อเนื่องได้
- ต้องตั้งค่าการไหลที่ขอบใหม่ทุกครั้งตอนเริ่มแต่ละสเต็ปของการจำลอง
- เพราะขั้นสเกลการไหลออกอาจเปลี่ยนค่าการไหลที่ขอบจนทำให้ขอบเขตไหลออกกลายเป็นเหมือนกำแพง
- ในงานวิจัยยังมีพจน์ ความหนืด ที่ลดการไหลตามความสูงของน้ำ
- แนวคิดคือชั้นน้ำบางจะเคลื่อนที่ได้ยากกว่าเพราะแรงภายใน ส่วนชั้นน้ำหนาจะเคลื่อนที่ได้อิสระกว่า
- อาจมีประโยชน์กับสิ่งอย่างการไหลของแมกมา
- ไม่ได้ใช้กับน้ำ และในสเกลภูมิประเทศใหญ่ ๆ ผลของความหนืดแทบไม่มีนัยสำคัญ
ลำดับการติดตั้งใช้งานและรูปแบบประสิทธิภาพ
- โค้ดทั้งหมดมีลำดับดังนี้
- เริ่มต้นค่าการไหลที่ขอบ
- คำนวณค่าสัมประสิทธิ์แรงเสียดทาน
pow(1-friction, dt)ล่วงหน้า - เร่งการไหลแกน X
- เร่งการไหลแกน Y
- สเกลการไหลออกเพื่อป้องกันปริมาณน้ำติดลบ
- อัปเดตคอลัมน์น้ำ
- การจำลองเกือบทั้งหมดจบลงด้วย 4 ลูป ที่วิ่งผ่านอาร์เรย์ 2D ไม่กี่ชุดและสมการง่าย ๆ
- โค้ดอัปเดต C++ แบบเต็มดูได้ที่ water_2d.cpp
- ตัวอย่างวิดีโอมาจาก WebGPU water simulator ที่เผยแพร่เมื่อไม่กี่วันก่อน โดยอนุภาคในวิดีโอมีไว้เพื่อการแสดงผลเท่านั้นและไม่ได้มีส่วนร่วมในการจำลอง
- หากหาค่า
dtและgที่เหมาะสมได้ ผลลัพธ์จะดูเสถียร ตรงตามเงื่อนไขที่ต้องการ และดูเหมือนน้ำ
ข้อจำกัดที่ยังเหลืออยู่
- โมเดลนี้ไม่มี ความเฉื่อย และการแพร่ของความเร็ว
- แม้กระแสน้ำที่ไหลเร็วจะไหลเข้าสู่ทะเลสาบ แต่จะไม่ส่งต่อเข้าไปลึกในทะเลสาบอย่างต่อเนื่องและกลับกระจายออกทุกทิศทาง
- หากระดับน้ำเท่ากัน สายน้ำขนานสองสายที่ไหลสวนทางกันสามารถอยู่ร่วมกันได้โดยไม่เกิดปฏิสัมพันธ์
- เมื่อน้ำไหลเข้าสู่พื้นที่หนึ่งเป็นครั้งแรก จะเกิดระลอกน้ำที่อาจดูแปลกอยู่บ้าง
การขยายไปยังกริดหกเหลี่ยมและสามเหลี่ยม
- เกมเป้าหมายไม่ได้ใช้กริดสี่เหลี่ยม แต่ใช้ กริดสามเหลี่ยม แบบสม่ำเสมอ
- กริดสามเหลี่ยมสามารถมองได้ว่าเป็น dual ของกริดหกเหลี่ยม
- หากเชื่อมจุดศูนย์กลางของหกเหลี่ยมที่อยู่ติดกัน จะได้กริดสามเหลี่ยมแบบสม่ำเสมอ
- คล้ายกับรูปแบบในบทความ hexagonal grids ของ Red Blob Games ที่ใช้ axial coordinate system กับกริดคู่ของทิศทาง hex แบบ pointy-top
- กริดสามเหลี่ยมก็สามารถเก็บในอาร์เรย์ 2D ทั่วไปที่เอียงเล็กน้อยได้
- ความสูงของคอลัมน์น้ำถูกเก็บไว้ที่จุดยอดของกริดเพื่อให้เรนเดอร์ผิวน้ำได้ง่าย
- การไหลแบ่งออกเป็น 3 ทิศ
- การไหลแกน X
- การไหลแกน Y
- การไหลแกน Z
- สำหรับกริดจุดยอดขนาด
N x Nจะใช้อาร์เรย์ดังนี้- อาร์เรย์การไหล X ขนาด
(N+1) x N - อาร์เรย์การไหล Y ขนาด
N x (N+1) - อาร์เรย์การไหล Z ขนาด
(N+1) x (N+1)โดยไม่ใช้ค่าที่มุมล่างซ้ายและมุมขวาบน
- อาร์เรย์การไหล X ขนาด
- เมื่อเทียบกับกริดสี่เหลี่ยม เพียงเพิ่มการไหลแกน Z เข้าไปในเงื่อนไขขอบเขต การเร่งการไหล การสเกลการไหลออก และการอัปเดตน้ำ
- ส่วนที่ยากที่สุดคืออย่าให้อินเด็กซ์ผิด
- โค้ด C++ สำหรับกริดสามเหลี่ยมและหกเหลี่ยมดูได้ที่ water_2d_hex.cpp
- วิธีนี้อาจมีความเป็น isotropic มากกว่ากริดสี่เหลี่ยมเล็กน้อย
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
มีวิดีโอ Coding Adventure ที่นำเสนออีกแนวทางหนึ่งในการจัดการกับ การจำลองของไหล
Rendering Fluids: https://www.youtube.com/watch?v=kOkfC5fLfgE
I Tried Putting my Fluid Simulation on a Planet: https://www.youtube.com/watch?v=8nIB7e_eds4&t=817s
GitHub: https://github.com/SebLague/Fluid-Sim?tab=readme-ov-file
[0] https://www.youtube.com/playlist?list=PLFt_AvWsXl0dT82XMtKAT...
[1] https://github.com/SebLague/Geographical-Adventures
หนึ่งในเหตุผลที่ การจำลองอุทกวิทยา ในเกมที่ใช้การสร้างแบบ procedural นั้นยาก คือเมื่อมีน้ำสะสม มันจะส่งผลต่อช่องรอบ ๆ และผลนั้นก็จะแพร่ต่อไปยังช่องรอบ ๆ อื่น ๆ อย่างต่อเนื่อง
การสร้างแบบ procedural มักเหมาะกับการทำแบบขนาน แต่ในพื้นที่ไร้ขอบเขตซึ่งดูเหมือนเป็นจุดที่ต้องการการทำแบบขนานมากที่สุด กลับทำให้การคำนวณแบบนี้ทำขนานได้อย่างถูกต้องได้ยาก
ไม่ค่อยเห็นหัวข้อนี้ถูกสำรวจมากนัก และในบรรดาคนที่ทำงานที่เกี่ยวข้อง ชอบ https://nickmcd.me เป็นพิเศษ ภูมิประเทศแบบ procedural ที่เคยเห็นมานั้นถือว่าดีเป็นอันดับต้น ๆ
อย่างไรก็ตาม งานนั้นก็ยังถูกจำกัดพื้นที่เพราะการออกแบบการจำลอง วิธีแก้ที่เป็นไปได้ซึ่งดูดีที่สุดคือสร้าง ขอบเขตลุ่มน้ำ ที่แตกไม่ได้แบบ procedural ก่อน แล้วจึงจำลองทั้งลุ่มน้ำพร้อมกันแบบขนาน
เป็นปัญหาที่น่าสนใจมาก แต่เกินขอบเขตความรู้ของตัวเอง จึงอยู่ในมุมผู้สังเกตเป็นหลัก
หมายถึงบริเวณที่สามารถส่งผลต่อค่าของจุดใดจุดหนึ่ง และบริเวณที่ค่าของจุดนั้นสามารถส่งผลต่อได้ ซึ่งเชื่อมโยงกับสิ่งที่กล่าวไว้ด้านบนโดยตรง ในบางกรณีสามารถรู้บริเวณเหล่านั้นล่วงหน้าได้
เช่น ถ้าต้องการจำลองช่วงเวลา 10 ชั่วโมง ก็วางขอบเขตกว้าง 10 ช่องกริด คำนวณ 10 ขั้นในแต่ละพื้นที่ จากนั้นซิงก์สถานะขอบเขตกับการจำลองขอบเขตอื่น ๆ ที่คำนวณแบบขนาน แล้วทำซ้ำ
ออกนอกประเด็นเล็กน้อย แต่นึกถึงส่วนในบทความที่บอกว่าต้องปรับแต่งภูมิประเทศเพื่อเก็บทรัพยากร
เคยคิดมาตลอดว่า Animal Crossing จัดการเรื่องนี้ได้ค่อนข้างฉลาดและมีประสิทธิภาพโดยไม่ต้องปรับแต่งภูมิประเทศ เมื่อตัดต้นไม้ก็ได้ท่อนไม้ออกมา แต่มีจำนวนจำกัดและโดยพื้นฐานแล้วจะมีช่วงคูลดาวน์
ให้ความรู้สึกของ feedback และทรัพยากรที่มีจำกัดได้โดยไม่ต้องใช้การปรับแต่งภูมิประเทศที่มีต้นทุนสูง แน่นอนว่าไม่ได้เหมาะกับทุกเกม และเหมาะกับแผนที่ขนาดเล็กมากกว่า แต่ก็ควรพิจารณา ถ้าเกมไม่ได้จำเป็นต้องใช้จริง ๆ หลายครั้งการไม่ปรับแต่งภูมิประเทศอาจดีกว่า
ในฐานะวิธีแจกทรัพยากรก็ถือเป็นมาตรฐาน แต่คูลดาวน์ไม่ได้แก้ปัญหาทรัพยากรไม่จำกัด แค่ทำให้ช้าลง และยังค่อนข้างน่าเบื่อกับมีอิทธิพลน้อย
เป็นบทความที่เจาะหัวข้อนี้ได้อย่างเรียบร้อย และดีใจที่พูดถึง Timberborn
ช่วงนี้ติดเกมนั้นมาก ถ้ายังไม่เคยลอง ขอแนะนำอย่างยิ่ง การไหลของน้ำที่อิงฟิสิกส์เหมือนเป็นตัวละครอีกตัวในเกม และการหาวิธีกักน้ำเพื่อใช้กับเครื่องยนต์และส่งไปยังแปลงเพาะปลูกคือแกนหลักของ gameplay loop
สนุกและทำออกมาได้ดีจริง ๆ ความเสี่ยงใหญ่ที่สุดตอนพัฒนาอะไรแบบนี้คือการเสียเวลาเป็นชั่วโมง ๆ ไปกับการปรับพารามิเตอร์เพราะมัวแต่มองผลลัพธ์สวย ๆ
นึกถึงตอนปี 2011 ที่เคยทำ พลศาสตร์ของไหลบน GPU เองสำหรับงานวิจัย เป็นของไหลที่ไหลบนพื้นผิว กล่าวคือเลือดที่ไหลบนเนื้อเยื่อ จำลองเป็น 2D ก่อน แล้วฉายขึ้นไปบน mesh โดยคำนึงถึงแรงโน้มถ่วงและความชันของพื้นผิว
อัปโหลดวิดีโอสั้น ๆ ไว้บน YouTube ด้วย: https://youtu.be/4vGrNc-GGW8
เจ๋งมาก
เมื่อเร็ว ๆ นี้ได้ทดลองไอเดียคล้าย ๆ กันด้วยความช่วยเหลือจาก o3-mini-high อธิบายไอเดียอัลกอริทึมให้มัน แล้วมันก็ implement และ render เป็น 3D โดยไม่ต้องแทรกแซงด้วยมือ แต่ใส่พรอมป์หลายครั้ง
https://3d-water-sim.netlify.app/
ยังไม่สมบูรณ์แบบ เพราะหยุดไปก่อนตอนกำลังลองปรับ แต่ทุกครั้งที่วนซ้ำก็ดีขึ้นค่อนข้างมาก สิ่งที่น่าสนใจคือ สำหรับการสร้างภูมิประเทศ มันไม่ได้ไปดึงจากที่อย่าง CDN แต่ implement เวอร์ชันที่ทำงานได้ของ Perlin noise ตั้งแต่ต้นอย่างถูกต้อง
เป็นคำถามเรื่องความต่างระหว่างการเดินทางกับจุดหมาย
ส่วนที่บทความบอกว่า “โมเดลนี้ไม่มีความเฉื่อยและการแพร่กระจายของความเร็ว แม้น้ำที่ไหลเร็วจะไหลเข้าสู่ทะเลสาบ ก็จะไม่แพร่ต่อเข้าไปด้านในทะเลสาบ แต่กระจายออกทุกทิศทางโดยละเลยความเฉื่อยที่สะสมไว้ หากระดับน้ำเท่ากัน กระแสน้ำขนานสองสายที่ไหลสวนทางกันก็อาจไม่โต้ตอบกันเลย” นั้น น่าจะแก้ได้ด้วยการเฉลี่ยกับลูกศรการไหลรอบข้าง 6 อันที่มีทิศทางเดียวกัน
คือให้น้ำหนักมากกับลูกศรด้านหน้าและด้านหลัง และให้น้ำหนักน้อยกับลูกศรด้านข้าง เช่น เมื่อมีลูกศรแบบนี้
-a-> -b->
-c-> -d-> -e->
-f-> -g->
New_d = d * (1 - 2*.1 - 4*.01) + (c+e).1 + (a+b+f+b).01
ในที่นี้ .1 และ .01 เป็นค่าน้ำหนักที่ตั้งขึ้นเอง จึงต้องปรับแต่ง และอาจใส่เลขยกกำลังเหมือนที่ใช้ลดการสั่นได้ด้วย หากรวมค่าสัมประสิทธิ์นั้นเข้าไป ก็อาจเป็นแบบนี้
New_d = d * (1 - 2*.1 - 4*.01 - .001) + (c+e).1 + (a+b+f+b).01
grid 0: ความสูงของน้ำในแต่ละช่อง
grid 1: การไหลของน้ำที่แต่ละขอบ หรือก็คืออนุพันธ์อันดับหนึ่ง
grid 2: ความเร่งของน้ำในแต่ละช่อง หรือก็คืออนุพันธ์อันดับสอง
เป็นโครงสร้างที่แต่ละกริดเป็นกริดคู่ของกริดก่อนหน้าและเก็บค่าอนุพันธ์ของมันไว้ จริง ๆ แล้วอาจไม่จำเป็นต้องจัดการข้อมูลขอบเป็นพิเศษ แค่วางข้อมูลที่จุดยอดแล้วประมวลผลเป็นกริดคู่อย่างเดียวก็น่าจะได้ การไหลตามขอบสามารถอนุมานได้จากผลรวมของการไหลที่จุดยอดทั้งสองด้านของขอบนั้น
ดังนั้นจึงอัปเดตความสูงของของไหลด้วยการไหล จากนั้นอัปเดตความเร่งตามว่ามวลของของไหลไหลเข้ามาในช่องมากเท่าใดด้วยความเร็วเท่าใด แล้วจึงอัปเดตการไหลจากความเร่งและความสูงของของไหลปัจจุบัน ผมไม่ค่อยรู้พลศาสตร์ของไหล แต่ในมุมมองของการจำลองเชิงตัวเลขก็ดูถูกต้อง และยังทำให้การไหลแนวทแยงเป็นไปได้ด้วย
อย่างที่บทความบอก ภาระการคำนวณจะเพิ่มขึ้นมาก ดังนั้นควรพิจารณาก่อนว่ากรณีใช้งานจริงต้องการความสมจริงระดับนั้นหรือไม่
เป็นผลงานหยาบ ๆ ที่ทำขึ้นเมื่อหลายปีก่อนเพราะสงสัย: https://aperocky.com/hydrosim/
ก่อนที่โปรเจกต์ส่วนตัวนี้จะถูกเก็บเข้าชั้นแช่เย็น ผมยังหาวิธีจัดการ การกัดเซาะ ไม่ได้ ชอบที่ผู้เขียนพูดถึงส่วนนี้และแนบสมการมาด้วย
เมื่อไม่นานมานี้ได้เปิดเผยสิ่งที่คล้ายกัน มีทั้งการสร้าง heightfield แบบสุ่ม การเคลื่อนย้ายตะกอน และการกัดเซาะ: https://github.com/Ono-Sendai/terraingen
คุณสามารถลองเล่น การจำลองน้ำท่วม เพื่อการศึกษาที่นักพัฒนาฝีมือเยี่ยมของบริษัทเราทำขึ้นเป็นส่วนหนึ่งของโปรเจกต์วิจัยได้
https://flood.concord.org/
ถ้าต้องการเห็นผลชัด ๆ ต้องเปลี่ยนค่าของโมเดลในแถบเครื่องมือด้านล่าง
เป็นการจำลองแบบเซลล์ที่คำนวณค่าของช่องใน WebGL โดยอิงจากช่องข้างเคียง เชดเดอร์ที่ทำการคำนวณนั้นอยู่ที่นี่
https://github.com/concord-consortium/flooding-model/blob/ma...