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

ชาห์ริอาร์ ชาห์ราบี

·

ติดตาม

อ่าน 33 นาที

·

5 ต.ค. 2564

--

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

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

ตามปกติ คุณสามารถค้นหาโค้ดสำหรับโปรเจ็กต์บน Github ของฉัน:https://github.com/IRCSS/Compute-Shaders-Fluid-Dynamic-

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

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

คุณสมบัติของของไหล

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

คุณอาจสังเกตเห็นสามจุดต่อไปนี้:

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

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

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

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

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

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

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

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

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

AmountOfSomethingInThisCell = สิ่งที่แพร่กระจายจากเซลล์ข้างเคียง + สิ่งที่ถูกพัดพามาจากที่อื่นเนื่องจากการเคลื่อนที่ของน้ำ + สิ่งที่ผู้ใช้เพิ่มในเฟรมนั้นไปยังเซลล์นั้น

กล่าวอีกนัยหนึ่ง

FluidStateForFieldX = Diffusion_of_X + Advection_of_X + X_Added_by_the_user

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

การดำเนินการที่ไร้เดียงสาการแพร่กระจาย

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

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

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

_X=d0_X + ปัจจัยการแพร่กระจาย * deltaTime (d0_01 +d0_02+d0_03+d0_04 -4*d0_X)

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

จนถึงตอนนี้ดีมาก สิ่งนี้ง่ายพอที่จะนำไปใช้กับ GPU หรือ CPU

การดำเนินการที่ไร้เดียงสาการชักชวน

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

Field_amount_for_target[ตำแหน่งเซลล์ + current_cell_velocity * ขั้นตอนเวลา] = field_amount_for_current_cell
ข้อมูลเบื้องต้นเกี่ยวกับการจำลองของไหลตามเวลาจริงสำหรับโปรแกรมเมอร์และศิลปินด้านเทคนิค (5)

การนำไปใช้ การป้อนข้อมูลของผู้ใช้

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

Amount_of_field_X += user_input_for_this_cell

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

ข้อบกพร่องประการที่ 1 ของไหลที่บีบอัดได้?

ลักษณะสำคัญอย่างหนึ่งของของไหลคือลอนที่สวยงามที่เด้งขึ้นมาเมื่อคุณเคลื่อนนิ้วผ่านน้ำ คำถามคือ ทำไมลอนเหล่านี้จึงปรากฏขึ้น และทำไมการจำลองของเราถึงไม่มี

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

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

หรือสถานการณ์อื่น ความเร็วจะชี้ออกจากเซลล์กลางอย่างต่อเนื่อง เมื่อเวลาผ่านไป โมเลกุลเหล่านี้มาจากไหน สสารถูกสร้างขึ้นจากความว่างเปล่าหรือไม่?

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

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

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

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

ข้อบกพร่องที่สอง: ไม่ดีสำหรับการติดตั้ง GPU

มาดูการคำนวณการโน้มน้าวของเราอีกครั้ง แต่ละเซลล์จะดูที่ความเร็วที่มีอยู่ จากนั้นนำปริมาณฟิลด์ใดก็ตามที่มีตามความเร็วนั้น ไปยังตำแหน่งใดก็ตามที่ความเร็วจะไปถึงตามขั้นตอนของเวลา กล่าวอีกนัยหนึ่ง แต่ละเซลล์ไม่ได้เขียนไปยังหน่วยความจำของตัวเอง แต่เป็นตำแหน่งที่ไม่รู้จัก รูปแบบการเข้าถึงประเภทนี้เรียกว่าการดำเนินการกระจาย.

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

ตามหลักการแล้ว เราต้องการการตั้งค่าที่เธรดถูกแมปกับเซลล์ และแต่ละเธรดจะเขียนไปยังหน่วยความจำของเซลล์ที่กำหนดให้เท่านั้น ในขณะที่อ่านจากเซลล์ข้างเคียงเท่านั้น นี่จะเป็นตัวอย่างของกรวบรวมการดำเนินการ. GPU นั้นยอดเยี่ยมสำหรับการดำเนินการรวบรวม

ข้อบกพร่องที่สาม: ความไม่แน่นอน

นี่เป็นเรื่องยากที่จะอธิบายและอยู่นอกเหนือขอบเขตของบทความนี้ความรุ่งเรืองเต็มที่

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

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

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

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

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

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

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

ความเร็ว_field+ความเร็ว_due_to_pressure_difference=new_velocity_with_no_pressure_differenceความเร็ว_field=new_velocity_with_no_pressure_difference-ความเร็ว_due_to_pressure_difference

ต่อไปในบทความ (รูปที่ 11) คุณจะเห็นความสัมพันธ์ระหว่างความแตกต่างของความดันและความเร็วที่เหนี่ยวนำโดย:

ความแตกต่าง_In_ความดัน=-velocity_due_to_pressure_difference

เมื่อพิจารณาจากข้อความข้างต้นแล้ว การกำหนดขั้นสุดท้ายของเราคือ:

ความเร็ว_field=new_velocity_with_no_pressure_difference+ความแตกต่าง_In_ความดัน

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

velocity_field = Difference_In_Pressure + divergence_free_velocitydivergence_free_velocity = ความเร็ว_ฟิลด์ -Difference_In_Pressure

แต่เราจะคำนวณความดันที่สร้างขึ้นในเซลล์ได้อย่างไร? พิจารณาแผนภาพต่อไปนี้

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

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

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

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

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

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

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

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

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

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

1/4 * (p[10] + p[01] + p[12] + p[21]) = p[11]พี[10] + พี[01] + พี[12] + พี[21] = 4 * พี[11]

จะเกิดอะไรขึ้นเมื่อสองคำนี้ไม่เท่ากัน? ลองคำนวณความแตกต่างระหว่างด้านซ้ายและด้านขวา:

(หน้า[10] + หน้า[01] + หน้า[12] + หน้า[21])-4 * พี[11]

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

(p[10] + p[01] + p[12] + p[21])- 4 * p[11] = ความแตกต่าง (velocity_field);

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

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

Difference_in_p => ความดัน_ผลต่าง_เหนี่ยวนำ_ความเร็ว

ในการเปลี่ยนความสัมพันธ์ข้างต้นกับสมการบางอย่าง มันอาจจะเกี่ยวข้องกับการวัดมวลหรือความหนาแน่น (จำ f=ma) หรือค่าคงที่บางอย่าง

คุณจะวัดการเปลี่ยนแปลงของแรงกดสำหรับสนามตามแกนหลักได้อย่างไร การดำเนินการค่อนข้างง่าย สำหรับเซลล์ [11] (รูปที่ 10) ให้ลบค่า p ของเซลล์ทางขวาออกจากเซลล์ทางซ้าย ซึ่งจะกลายเป็นองค์ประกอบ x ของความแตกต่างของแรงดัน และคุณทำเช่นเดียวกันใน ทิศทางแนวตั้ง (ขึ้นและลง) ซึ่งกลายเป็นองค์ประกอบ y ของคุณ ดูรูปที่ 11 เป็นตัวอย่าง

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

ความแตกต่าง_in_p =-ความดัน_ผลต่าง_เหนี่ยวนำ_ความเร็ว

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

สิ่งนี้เกี่ยวข้องกับความแตกต่างที่คุณอาจถามอย่างไร ในตอนต้นของบทความ เราได้กำหนดความสัมพันธ์นี้:

velocity_field = Difference_In_Pressure + divergence_free_velocity

ซึ่งตอนนี้เราสามารถเขียนเป็น:

velocity_field = การไล่ระดับสี (แรงกด) + divergence_free_velocity

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

Divergence(velocity_field) = Divergence(การไล่ระดับสี(ความดัน)) + Divergence(divergence_free_velocity)

Divergence(divergence_free_velocity) มีค่าต่อคำจำกัดความเป็นศูนย์ เนื่องจากมันตัดกัน เราจึงเหลือสมการเดียวและอีกสมการที่ไม่รู้จัก! ดังนั้นเราจึงเหลือ:

ความแตกต่าง(ความลาดเอียง(ความดัน)) = ความแตกต่าง(ความเร็ว_ฟิลด์)(p[10] + p[01] + p[12] + p[21])- 4 * p[11] = ความแตกต่าง (velocity_field);

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

จนถึงตอนนี้ดีมาก ตอนนี้เรามีสมการที่ต้องแก้ โดยที่ไม่ทราบค่า p (ความดัน) ทั้งหมด มาดูกันว่าเราจะทำได้อย่างไร!

(p[10] + p[01] + p[12] + p[21])- 4 * p[11] = ความแตกต่าง (velocity_field);

นักแก้ปัญหา

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

สรุปได้ว่า ระบบสมการอาจมีลักษณะดังนี้:

สมการ 1) y = x + 1สมการ 2) y = 5x - 2

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

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

สมการ 2) y = (x + 1) = 5x - 2 <= เนื่องจาก y คือ x +1 ด้วย
=> x - 5x = -2 -1 = -3
=> x = -3/-4 = 3/4
แทรกกลับในสมการ 1)
y = 3/4 + 1 = 7/4
X = 3/4 = 0.75, y = 7/4 = 1.75

ระบบของเราไม่เหมือนกับสมการนี้ ด้วยตาราง 128 ใน 128 เรามีสิ่งที่ไม่รู้จักและสมการ 16,000 รายการ ไม่มีทางที่คุณต้องการตั้งโปรแกรมข้างต้นใน CPU หรือ GPU นอกจากนี้ ยังมีบางสิ่งที่ไม่เหมือนใครเกี่ยวกับระบบของเรา ค่าของความดันสำหรับแต่ละเซลล์นั้นขึ้นอยู่กับเพื่อนบ้าน 4 ตัวเท่านั้น และไม่มีส่วนเกี่ยวข้องกับส่วนที่เหลือของ 16,000 เซลล์ที่ไม่รู้จัก ดังนั้นหากคุณจดสมการหนึ่งใน 16,000 สมการเหล่านี้ คุณจะเห็นว่า 99 เปอร์เซ็นต์ของค่าสัมประสิทธิ์ในแต่ละบรรทัดมีค่าเป็นศูนย์ คุณจึงไม่ต้องการเขียนมันเป็นเมทริกซ์แล้วกลับด้าน เนื่องจากเทอมส่วนใหญ่จะเป็นศูนย์ มีวิธีที่เร็วกว่านี้!

ก่อนที่ฉันจะอธิบายวิธีที่เร็วกว่านั้น เรามาแสดงมายากลในระบบง่ายๆ ของเรากันก่อนดีกว่า ให้จัดเรียงสองสมการใหม่เพื่อให้มี X และหนึ่ง Y ทางด้านซ้ายมือ

สมการ 1) y = x + 1สมการ 2) x = y/5 + 2/5

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

สมการ 1) y = 0 + 1 = 1สมการ 2) x = 0/5 + 2/5 = 2/5 = 0.4

ตอนนี้คุณมีค่าใหม่สำหรับ X และ Y, 0.4 และ 1 ให้ทำซ้ำขั้นตอนด้านบน แต่แทนที่จะเป็นศูนย์ เราจะใส่ค่าใหม่ของเรา

สมการ 1) y = 0.4 + 1 = 1.4สมการ 2) x = 1/5 + 2/5 = 3/5 = 0.6

เราสามารถทำซ้ำขั้นตอนได้หลายครั้งโดยในแต่ละขั้นตอนเราจะนำค่าก่อนหน้าของ X และ Y มาแทนในสมการอีกครั้ง มาดูกันว่าจะเกิดอะไรขึ้น!

ขั้นตอนที่ 0) อินพุต (X, y) = (0, 0), เอาต์พุต = (0.4, 1)
ขั้นตอนที่ 1) เข้า (0.4, 1), ออก (0.6, 1.4)
ขั้นตอนที่ 2) เข้า(0.6, 1.4), ออก((1.4 + 2)/5 , 0.6 + 1 ) = (0.68, 1.6)
ขั้นตอนที่ 3) เข้า(0.68, 1.6), ออก((1.6 + 2)/5 , 0.68 + 1 ) = (0.72, 1.68)
ขั้นตอนที่ 4) เข้า(0.72, 1.68), ออก((1.68+ 2)/5 , 0.72 + 1 ) = (0.736, 1.72)

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

มีตัวแก้ปัญหาที่แตกต่างกันมากมาย บางตัวต้องการใช้หน่วยความจำน้อยกว่า บางตัวต้องการขั้นตอนน้อยกว่าเพื่อให้เข้าใกล้วิธีแก้ปัญหามากพอ และบางตัวให้ความแม่นยำมากกว่า (โปรดจำไว้ว่าเนื่องจากข้อผิดพลาดของทศนิยม ไม่สำคัญว่าตัวเลขใดจะคูณด้วยอะไรและลำดับใด) . ฉันกำลังใช้ Jacobi ที่นี่ เพราะส่วนที่เหลือเป็นเรื่องยากที่จะใช้กับ GPU ด้วยลำดับความยากนี้ Jacobi — Gauss Seidel — Successive Overrelaxation — Multigrid มีเครื่องมือแก้ปัญหาที่ซับซ้อนกว่าที่คุณจะเจอในวิศวกรรมทางวิทยาศาสตร์ แต่ไม่เกี่ยวข้องกับกรณีการใช้งานของเรา

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

สำหรับคำอธิบายของฉัน ฉันต้องการใช้ Gauss Seidel แทนตัวแก้ Jacobi ซึ่งเรากำลังใช้ในการนำไปใช้งานจริง เนื่องจากจะได้ผลลัพธ์เร็วขึ้นสองเท่า ความแตกต่างระหว่างทั้งสองนั้นง่ายมาก ใน Gauss Seidel คุณจะใช้ค่าที่คุณคำนวณสำหรับสิ่งที่ไม่รู้ทันทีในการคำนวณสิ่งที่ไม่รู้ถัดไปโดยไม่ต้องรอขั้นตอนทั้งหมด ดังนั้นในตัวอย่างข้างต้น แทนที่จะดึง 0 และ 0 สำหรับ X และ Y คุณต้องใส่ 0 เป็น X แก้ Y เป็น 1 และแทนที่จะใส่ 0 สำหรับ Y ในสมการ 2 คุณจะใช้ค่า 1 ทันที คำนวณด้วย X ซึ่งจะให้ 0.6 จากนั้นคุณไปที่ขั้นตอนที่ 2 และเสียบ 0.6 สำหรับ X เพื่อค้นหา Y ใหม่ (1.6) และอื่นๆ สังเกตวิธีที่คุณได้รับโซลูชันเดียวกันอย่างมีประสิทธิภาพด้วยขั้นตอนเพียงครึ่งเดียว ยอดเยี่ยมใช่มั้ย มีสิ่งที่จับต้องได้สำหรับ GPU และมัลติเธรด เนื่องจากเรากำลังแก้ไขเซลล์ทั้งหมดพร้อมกัน เราจึงไม่สามารถใช้เทคนิคนี้โดยไม่ทำสิ่งต่อไปนี้รูปแบบการเข้าถึงสีแดงดำซึ่งในขณะที่การลดจำนวนขั้นตอนลงครึ่งหนึ่งจะทำให้การโทรของเราเพิ่มขึ้นเป็นสองเท่า ดังนั้นมันจึงเป็นกำไรเล็กน้อย

อีกครั้งกับระบบง่ายๆ ของเรา ด้านล่างคือพล็อตสำหรับสมการทั้งสอง จำไว้ว่าที่ทั้งสองพบกันคือทางออกของเรา เราทราบค่านี้แล้วจากการแก้ค่านี้โดยการวิเคราะห์ มันคือ X= 0.75 และ Y=1.75

สมการ 1) y = x + 1สมการ 2) y = 5x - 2

ตัวแก้ปัญหาของเราทำอะไรจริง ๆ ? สำหรับค่าเริ่มต้นของเรา ให้เอา 3 แทน x (ก่อนหน้านี้เป็นค่าเริ่มต้นเป็นศูนย์ แต่เราสามารถใส่ค่าใดก็ได้) ในขั้นตอนที่ 1 เราพบอะไรวายค่าที่กราฟของเรามีสำหรับ X = 3 จากนั้นเราจะดูว่ากราฟ 2 มีค่าเท่าใดเอ็กซ์มีสำหรับค่า Y ที่เราเพิ่งพบ เมื่อเราทำซ้ำ คุณอาจสังเกตเห็นว่าเรากำลังฉายกราฟบนกราฟซึ่งกันและกัน กลับไปกลับมา และเนื่องจากกราฟทั้งสองนี้บรรจบกันที่จุดเดียว เราจึงเข้าใกล้จุดตัดกันมากขึ้นเรื่อย ๆ เหมือนลูกบอลกลิ้งตกหน้าผา .

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

ดังนั้นในขั้นตอนที่ N คุณมีสิ่งนี้ การเล่นปิงปองที่สวยงามกลับไปกลับมาซึ่งจะเข้าใกล้วิธีแก้ปัญหาจริงเสมอ แต่ไม่เคยเข้าถึงหรือยิงเกินเลย

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

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

ในการใช้ตัวแก้โจทย์ของเรา เราเขียนสมการใหม่โดยให้ฟิลด์ตรงกลางอยู่ทางด้านซ้ายมือ และสำหรับเซลล์ใดก็ตามที่มีพิกัด i และ j เรามี:

แก้ปัญหา (Velocity_Divergence)
{
p_new[i, j] =((p[i+1,j] + p[i,j+1] + p[i-1,j] + p[i,j-1]) - Velocity_Divergence[i, ญ])/4
}

เมื่อดำเนินการนี้ เราสามารถทำได้:

คัดลอกเก่าไปใหม่()
{
p[i, j] = p_new[ผม, j]
}

และคำนวณซ้ำอีกครั้งด้วยค่าใหม่ของ p เราทำซ้ำประมาณ 30 ขั้นตอนจนกว่าเราจะได้คำตอบสำหรับเฟรมนั้น สำหรับขั้นตอนแรก เราจะถือว่า p เป็นศูนย์

ดังนั้นการฉายภาพของเราจะมีลักษณะดังนี้:

Velocity_Div_field = ความแตกต่าง (velocity_field);
สำหรับ (int i = 0; i < 30; i++)
{
แก้ไข (Velocity_Div_field);
CopyOldToNew();
}
deltaP = การไล่ระดับสี (p);
new_divergent_free_velocity = ความเร็วสนาม - deltaP;

แน่นอนว่าข้างต้นเป็นรหัสเทียม คุณจะไม่เขียนไปป์ไลน์ Compute Shader ของคุณแบบนั้น แต่ควรสร้างบัฟเฟอร์สำหรับสิ่งเหล่านี้ และตั้งค่าต้นทาง/ปลายทาง และเรียกเมล็ด

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

นี่คือทั้งหมดที่มีในการฉายภาพ ในโค้ด ฉันได้รวมทั้งหมดข้างต้นไว้ในฟังก์ชันที่เรียกว่า Project() ซึ่งคุณสามารถเรียกใช้ได้ทุกเมื่อที่คุณต้องการ

ปัญหาแรกเกี่ยวกับความสามารถในการบีบอัดได้รับการแก้ไขผ่านการฉายภาพ ตอนนี้เราสามารถจัดการกับความไม่เสถียรของฟังก์ชันการคำนวณการแพร่กระจายของเราซึ่งถูกนำมาใช้โดยการกำหนดสูตรอย่างชัดเจน

หากคุณจำได้จากด้านบน เมื่อกำหนดฟิลด์ d ใดๆ โดยที่ d0 คือค่าของฟิลด์สำหรับแต่ละเซลล์ในเฟรมก่อนหน้า การกำหนดที่ชัดเจนของเราคือ:

_X=d0_X + ปัจจัยการแพร่กระจาย * deltaTime *(d0_01 +d0_02+d0_03+d0_04 -4*d0_X)

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

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

d0_X =_X - ปัจจัยการแพร่กระจาย * deltaTime * (_01 +_02+_03+_04 -4*_X)

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

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

_X = (d0_X + ปัจจัยการแพร่กระจาย * deltaTime * (_01 +_02+_03+_04)) / (1 + 4 * ปัจจัยการแพร่ * เวลาเดลต้า)

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

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

เช่นเดียวกับการฉายภาพดูเหมือนว่าไม่สามารถแก้ได้ทันที (หนึ่งสมการและ 5 ไม่ทราบค่า d_x และ d_01 ถึง d_04) แต่นี่ก็เป็นระบบสมการเชิงเส้นด้วย หนึ่งสมการสำหรับแต่ละเซลล์ รวมเป็น N*N ข่าวดีก็คือ เราสามารถแก้ปัญหาได้ด้วยตัวแก้ปัญหาที่เรามีอยู่แล้ว

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

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

สุดท้าย แต่ไม่ท้ายสุด เราจำเป็นต้องแก้ไขการโน้มน้าวใจของเราเพื่อให้ใช้การดำเนินการรวบรวมแทนการดำเนินการกระจาย

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

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

field_amount_new_frame[ตำแหน่งเซลล์] += field_amount_old_frame[cellPosion-current_cell_velocity * ขั้นเวลา ]

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

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

สำหรับการจำลองอย่างง่าย การตั้งค่าของคุณจะมีลักษณะดังนี้ (ฉันทำโครงการหลายครั้ง เพราะวิธีนี้ดูดีกว่า):

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

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

ขอบเขตและขอบเขตโดยพลการ

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

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

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

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

ขอบเขตโดยพลการนั้นยากกว่าที่จะจัดการ

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

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

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

ข้อมูลเบื้องต้นเกี่ยวกับการจำลองของไหลตามเวลาจริงสำหรับโปรแกรมเมอร์และศิลปินด้านเทคนิค (22)
ข้อมูลเบื้องต้นเกี่ยวกับการจำลองของไหลตามเวลาจริงสำหรับโปรแกรมเมอร์และศิลปินด้านเทคนิค (23)
ข้อมูลเบื้องต้นเกี่ยวกับการจำลองของไหลตามเวลาจริงสำหรับโปรแกรมเมอร์และศิลปินด้านเทคนิค (24)
ข้อมูลเบื้องต้นเกี่ยวกับการจำลองของไหลตามเวลาจริงสำหรับโปรแกรมเมอร์และศิลปินด้านเทคนิค (25)

การจำลอง 3 มิติปลอม

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

แสงสว่าง

ฉันมีชุดไฟสำหรับวัตถุทั้งหมดในฉากสาธิต เป็นการเปลี่ยนแปลงของ BRDF Cook-Torrance มาตรฐานซึ่งคุณสามารถอ่านเพิ่มเติมได้ที่นี่. น้ำประกอบด้วย:

  1. แสงพิเศษจากแสงรอบทิศทาง
  2. การหักเห (ซึ่งฉันเพียงแค่จับพื้นผิวด้านหลังน้ำและแสดงด้วยการบิดเบือน/การกระจัดตามการเคลื่อนไหวบนผิวน้ำ)
  3. การสะท้อน

ตัวอย่างเช่น คุณจะเห็นส่วนพิเศษของแสงจากไฟทิศทาง

ฉันยังมีแสงกระจายอยู่เล็กน้อยสำหรับในกรณีที่น้ำเป็นโคลน อย่างไรก็ตาม เอฟเฟ็กต์นี้ยังไม่เสร็จสิ้น เนื่องจากยังต้องใช้ SSS และเงาเชิงปริมาตร/ลำแสง โครงการอีกครั้ง :)

การสะท้อน

ภาพสะท้อนในตัวอย่างมีสองส่วน ส่วนแรกเป็นการสะท้อนระนาบ:

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

ข้อมูลเบื้องต้นเกี่ยวกับการจำลองของไหลตามเวลาจริงสำหรับโปรแกรมเมอร์และศิลปินด้านเทคนิค (26)
ข้อมูลเบื้องต้นเกี่ยวกับการจำลองของไหลตามเวลาจริงสำหรับโปรแกรมเมอร์และศิลปินด้านเทคนิค (27)
ข้อมูลเบื้องต้นเกี่ยวกับการจำลองของไหลตามเวลาจริงสำหรับโปรแกรมเมอร์และศิลปินด้านเทคนิค (28)

โซดาไฟ

แนวทางของฉันเกี่ยวกับโซดาไฟค่อนข้างมาตรฐาน โซดาไฟมีสองประเภท: 1) ใต้น้ำ 2) สะท้อนออกจากน้ำ

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

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

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

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

แอนิเมชั่นปลา

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

รายละเอียดไมโคร

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

การพัฒนาต่อไป?

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

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

ข้อมูลเบื้องต้นเกี่ยวกับการจำลองของไหลตามเวลาจริงสำหรับโปรแกรมเมอร์และศิลปินด้านเทคนิค (31)
ข้อมูลเบื้องต้นเกี่ยวกับการจำลองของไหลตามเวลาจริงสำหรับโปรแกรมเมอร์และศิลปินด้านเทคนิค (32)
ข้อมูลเบื้องต้นเกี่ยวกับการจำลองของไหลตามเวลาจริงสำหรับโปรแกรมเมอร์และศิลปินด้านเทคนิค (33)
ข้อมูลเบื้องต้นเกี่ยวกับการจำลองของไหลตามเวลาจริงสำหรับโปรแกรมเมอร์และศิลปินด้านเทคนิค (34)
ข้อมูลเบื้องต้นเกี่ยวกับการจำลองของไหลตามเวลาจริงสำหรับโปรแกรมเมอร์และศิลปินด้านเทคนิค (35)
ข้อมูลเบื้องต้นเกี่ยวกับการจำลองของไหลตามเวลาจริงสำหรับโปรแกรมเมอร์และศิลปินด้านเทคนิค (36)
ข้อมูลเบื้องต้นเกี่ยวกับการจำลองของไหลตามเวลาจริงสำหรับโปรแกรมเมอร์และศิลปินด้านเทคนิค (37)
ข้อมูลเบื้องต้นเกี่ยวกับการจำลองของไหลตามเวลาจริงสำหรับโปรแกรมเมอร์และศิลปินด้านเทคนิค (38)
ข้อมูลเบื้องต้นเกี่ยวกับการจำลองของไหลตามเวลาจริงสำหรับโปรแกรมเมอร์และศิลปินด้านเทคนิค (39)
  1. Stam 1999 Paper on Stable Fluid Dynamic นี่เป็นตัวพ่อของวิธีการนี้ในการคำนวณกราฟิก:https://www.researchgate.net/publication/2486965_Stable_Fluids
  2. รายการ GPU Gems ในการจำลองของไหล:https://developer.download.nvidia.com/books/HTML/gpugems/gpugems_ch38.html
  3. Harris, Fast Fluid Dynamics Simulation 2004 ใน GPU gems 3, บทที่ 38, เป็นการนำกระดาษ Stam 1999 มาใช้กับโปรแกรมแก้ปัญหา Jacobi บน GPU:https://developer.download.nvidia.com/books/HTML/gpugems/gpugems_ch38.html
  4. Stam 2003 Real Time Fluid Dynamics สำหรับเกม ไม่แน่ใจว่ากระดาษชิ้นนี้กับกระดาษปี 1999 ต่างกันอย่างไรนอกจากตัวแก้ที่เป็นตัวแก้การผ่อนคลายของ Gauss Seidel ชนิดของ 2003 ปัดเงาเหนือวิธีการฉายภาพ แต่ดูเหมือนว่าจะใช้สมการปัวซองเดียวกันกับแรงกดดัน:https://www.researchgate.net/publication/2560062_Real-Time_Fluid_Dynamics_for_Games
  5. การใช้ Stam 2003 สำหรับ 3D และสำรวจวิธีการเรนเดอร์โดย Mike Ash:https://mikeash.com/pyblog/fluid-simulation-for-dummies.html
  6. Amador et al 2012, Linear Solvers for Stable Fluids: GPU vs CPU, การอภิปรายเกี่ยวกับตัวแก้ปัญหาเชิงเส้นสำหรับสถาปัตยกรรม GPU:https://www.it.ubi.pt/17epcg/Actas/artigos/17epcg_submission_39.pdf
  7. เฟดคิว และคณะ 2544 Visual Simulation of Smoke วิธีการนำ vortices กลับมาผ่านการกักเก็บ vortices ซึ่งหายไปในการจำลอง:https://www.researchgate.net/publication/2390581_Visual_Simulation_of_Smoke
  8. Ďurikovič 2017, การจำลองสีน้ำแบบเรียลไทม์พร้อมกระแสน้ำไหลวนภายในจังหวะพู่กัน:https://www.researchgate.net/publication/321112340_Real-Time_Watercolor_Simulation_with_Fluid_Vorticity_Within_Brush_Stroke
  9. เคอร์ติสและคณะ 2540 สีน้ำที่สร้างโดยคอมพิวเตอร์:https://www.researchgate.net/publication/2917328_Computer-Generated_Watercolor
  10. Enhua Wu และคณะ พ.ศ. 2547 การศึกษาปรับปรุงการจำลองของไหลตามเวลาจริงบน GPU:http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.99.7898&rep=rep1&type=pdf
  11. กระทู้ถกแก้:https://twitter.com/vassvik/status/1422115054980370438?s=20
  12. การเรนเดอร์น้ำในขั้นตอนหลังทำ:https://www.gamedev.net/articles/programming/graphics/rendering-water-as-a-post-process-effect-r2642/
  13. ทรัพยากรที่ใช้ในการให้แสงสว่างแก่น้ำ เรียนรู้ open gl:https://learnopengl.com/PBR/Lighting
  14. การเขียนโค้ดแบบ Catlike บนการแรเงาของน้ำ:https://catlikecoding.com/unity/tutorials/flow/Looking-through-water/
  15. แสงมหาสมุทรเรียลไทม์แบบเรียลไทม์https://hal.inria.fr/inria-00443630/file/article-1.pdf
  16. บทความ Wikipedia เกี่ยวกับวิธีการและความเสถียรของตัวเลขที่ชัดเจนและโดยปริยาย:https://en.wikipedia.org/wiki/Explicit_and_implicit_methods
  17. ขอบเขตโดยพลการ การศึกษาแบบเรียลไทม์ที่ได้รับการปรับปรุง
    การจำลองของไหลบน GPU, Enhua Wu และคณะ :http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.99.7898&rep=rep1&type=pdf
ข้อมูลเบื้องต้นเกี่ยวกับการจำลองของไหลตามเวลาจริงสำหรับโปรแกรมเมอร์และศิลปินด้านเทคนิค (2024)

References

Top Articles
Latest Posts
Article information

Author: Jonah Leffler

Last Updated:

Views: 5809

Rating: 4.4 / 5 (45 voted)

Reviews: 84% of readers found this page helpful

Author information

Name: Jonah Leffler

Birthday: 1997-10-27

Address: 8987 Kieth Ports, Luettgenland, CT 54657-9808

Phone: +2611128251586

Job: Mining Supervisor

Hobby: Worldbuilding, Electronics, Amateur radio, Skiing, Cycling, Jogging, Taxidermy

Introduction: My name is Jonah Leffler, I am a determined, faithful, outstanding, inexpensive, cheerful, determined, smiling person who loves writing and wants to share my knowledge and understanding with you.