มีวิธีใช้จำนวนไฟโดยพลการในชิ้นส่วน shader?


19

มีวิธีการส่งผ่านจำนวนสถานที่แสง (และสี) โดยพลการสำหรับชิ้นส่วน shader และวนรอบพวกเขาใน Shader?

ถ้าไม่เช่นนั้นไฟหลายดวงควรถูกจำลองอย่างไร? ตัวอย่างเช่นในแง่ของการกระจายแสงทิศทางคุณไม่สามารถส่งผ่านน้ำหนักรวมสำหรับ shader ได้


ฉันไม่ได้ทำงานกับ WebGL แต่ใน OpenGL คุณมีแหล่งกำเนิดแสงสูงสุด 8 แหล่ง ในความคิดของฉันถ้าคุณต้องการผ่านมากกว่านั้นคุณต้องใช้ตัวอย่างตัวแปร
zacharmarz

วิธีการแบบเก่าคือการส่งผ่านแสงทุกครั้งไฟที่ไม่ได้ใช้ตั้งค่าเป็น 0 ความสว่างและดังนั้นจะไม่ส่งผลกระทบต่อฉาก อาจไม่ได้ใช้งานอีกต่อไป ;-)
Patrick Hughes

7
เมื่อคุณเป็นอย่างนี้กับ Google อย่าใช้คำว่า 'WebGL' - เทคโนโลยีนั้นยังเด็กเกินไปที่คนจะมีปัญหาถึงกับเข้าใกล้ปัญหาเหล่านี้ ใช้การค้นหานี้เช่น 'ดีใจจังค้นแล้วเจอเลย' จะได้ผล โปรดจำไว้ว่าปัญหา WebGL ควรแปลเป็นปัญหา OpenGL เดียวกันอย่างแน่นอน
Jonathan Dickinson

สำหรับการแสดงผลไปข้างหน้ามากกว่า 8 ครั้งโดยทั่วไปฉันมักใช้ตัวประมวลผลแบบหลายช่องทางและให้แต่ละกลุ่มในการประมวลผลไฟ 8 อันโดยใช้การผสมแบบเติมแต่ง
ChrisC

คำตอบ:


29

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

การส่งต่อการแสดงผล

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

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

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

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

การเรนเดอร์ที่ถูกเลื่อน

การเรนเดอร์ที่ถูกเลื่อนมีความซับซ้อนกว่าเล็กน้อย

สมการแสงที่คุณใช้ในการคำนวณแสงสำหรับจุดบนพื้นผิวใช้พารามิเตอร์พื้นผิวต่อไปนี้:

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

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

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

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

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

HDR อีกครั้งเป็นขั้นตอนหลังกระบวนการ

ข้อเสียที่ใหญ่ที่สุดในการเรนเดอร์คือการลดรอยหยัก มันต้องใช้การทำงานกับ antialias อีกเล็กน้อย

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

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

Light Pre-Pass

นี่เป็นความแตกต่างของการเรนเดอร์ที่มีการแลกเปลี่ยนที่น่าสนใจ

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

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

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

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

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

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


-1: ความล้มเหลวในการพูดถึง LPP / PPL -1 รอการตัดบัญชี: การแสดงผลเป็นการชนะทันทีบนฮาร์ดแวร์ DX9.0 ใด ๆ (ใช่แม้บนแล็ปท็อป 'ธุรกิจ' ของฉัน) - ซึ่งเป็นข้อกำหนดพื้นฐานในปี 2009 เว้นแต่ว่าคุณกำหนดเป้าหมายเป็น DX8.0 (ซึ่งไม่สามารถทำได้แบบเลื่อนเวลา / LPP) รอการตัดบัญชี / LPP เป็นค่าเริ่มต้น ในที่สุด 'แบนด์วิดธ์หน่วยความจำจำนวนมาก' ก็บ้า - โดยทั่วไปเรายังไม่อิ่มตัวแม้แต่ PCI-X x4 แต่ LPP จะลดแบนด์วิดท์หน่วยความจำลงอย่างมาก สุดท้าย -1 สำหรับความคิดเห็นของคุณ; ลูปเช่นนี้ตกลง คุณรู้หรือไม่ว่าลูปเหล่านี้เกิดขึ้น 2073600 ครั้งต่อเฟรมใช่ไหม แม้จะมีความทะเยอทะยานของกราฟิกการ์ด แต่ก็ไม่ดี
Jonathan Dickinson

1
@ JonathanDickinson ฉันคิดว่าประเด็นของเขาคือแบนด์วิดท์หน่วยความจำสำหรับการรอการตัดบัญชี / แสง pre-pass นั้นใหญ่กว่าการเรนเดอร์ไปข้างหน้าหลายเท่า สิ่งนี้ไม่ได้ทำให้วิธีการรอการตัดบัญชีเป็นโมฆะ เป็นเพียงสิ่งที่ต้องพิจารณาเมื่อเลือก BTW: บัฟเฟอร์ที่เลื่อนออกไปของคุณควรอยู่ในหน่วยความจำวิดีโอดังนั้นแบนด์วิดท์ PCI-X จึงไม่เกี่ยวข้อง มันเป็นแบนด์วิดธ์ภายในของ GPU ที่สำคัญ พิกเซลที่มีเฉดสียาวเช่นกับลูปที่ไม่ได้หมุนจะไม่มีอะไรน่าประหลาดใจหากพวกเขาทำงานที่มีประโยชน์ และไม่มีอะไรผิดปกติกับเคล็ดลับ prepass z-buffer; มันใช้งานได้ดี
นาธานรีด

3
@JonathanDickinson: นี่เป็นการพูดคุยเกี่ยวกับ WebGL ดังนั้นการอภิปรายเกี่ยวกับ "shader models" ใด ๆ นั้นไม่เกี่ยวข้อง และการแสดงผลแบบใดที่จะใช้ไม่ใช่ "หัวข้อทางศาสนา": มันเป็นเพียงเรื่องของฮาร์ดแวร์ที่คุณใช้งานอยู่ GPU ในตัวที่ซึ่ง "หน่วยความจำวิดีโอ" เป็นเพียง CPU RAM ปกติจะทำงานได้แย่มากเมื่อใช้การเรนเดอร์ที่เลื่อนออกไป สำหรับตัวแสดงภาพที่เรียงตามมือถือจะยิ่งแย่ลงไปอีก การเรนเดอร์ที่เลื่อนออกไปไม่ใช่ "การชนะทันที" โดยไม่คำนึงถึงฮาร์ดแวร์ มันมีการแลกเปลี่ยนเหมือนฮาร์ดแวร์ใด ๆ
Nicol Bolas

2
@ JonathanDickinson: "ด้วยเคล็ดลับ z-buffer pre-pass คุณจะต้องดิ้นรนเพื่อกำจัด z-fighting ด้วยวัตถุที่ควรวาด" นั่นเป็นเรื่องไร้สาระทั้งหมด คุณกำลังแสดงวัตถุเดียวกันด้วยเมทริกซ์การแปลงและจุดยอดเดียวกัน การเรนเดอร์มัลติพอยต์ได้กระทำในVoodoo 1วัน นี่เป็นปัญหาที่แก้ไขแล้ว แสงสะสมไม่ทำอะไรเลยที่จะเปลี่ยนแปลงสิ่งนั้น
Nicol Bolas

8
@ JonathanDickinson: แต่เราไม่ได้พูดถึงการแสดงโครงร่างใช่ไหม? เรากำลังพูดถึงการเรนเดสามเหลี่ยมเดียวกันก่อน OpenGL รับประกันความไม่แน่นอนสำหรับวัตถุเดียวกันที่ถูกเรนเดอร์ (ตราบใดที่คุณใช้เวอร์เท็กซ์เชดเดอร์ที่แน่นอนแน่นอนและถึงตอนนั้นก็มีinvariantคำสำคัญสำหรับรับประกันในกรณีอื่น ๆ )
Nicol Bolas

4

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

เห็นได้ชัดว่า WebGL รองรับ MRT ซึ่งเป็นสิ่งจำเป็นอย่างยิ่งสำหรับการเรนเดอร์รูปแบบใด ๆ - ดังนั้นจึงอาจทำได้ ฉันแค่ไม่แน่ใจว่ามันเป็นไปได้จริง

หรือคุณสามารถตรวจสอบความสามัคคี 5 - ซึ่งมีการเรนเดอร์ที่ถูกต้องอยู่นอกกรอบ

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

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

หมายเหตุด้านข้าง:คุณไม่ต้องการที่จะวนซ้ำพวกเขาใน Shader หรือไม่? บอกลาการแสดงของคุณ GPU ไม่ใช่ซีพียูและไม่ได้รับการออกแบบมาให้ทำงานเช่นเดียวกับที่ใช้ใน JavaScript โปรดจำไว้ว่าแต่ละพิกเซลที่คุณเรนเดอร์ (แม้ว่าจะถูกเขียนทับ) ต้องทำการวนซ้ำ - ดังนั้นถ้าคุณใช้ที่ 1920x1080 และลูปแบบธรรมดาที่รัน 16 ครั้งคุณจะทำงานทุกอย่างภายในลูป 33177600 ครั้งอย่างมีประสิทธิภาพ การ์ดกราฟิกของคุณจะทำงานชิ้นส่วนเหล่านั้นในแบบคู่ขนานจำนวนมากแต่ลูปเหล่านั้นยังคงกินฮาร์ดแวร์รุ่นเก่า


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

1
โปรดอ่านย่อหน้าที่ 4
Jonathan Dickinson

2

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

นั่นเป็นแนวคิดพื้นฐาน แน่นอนว่าต้องมีการปรับแต่งมากมายเพื่อให้เร็วพอสำหรับฉากที่มีขนาดพอสมควร อย่าวาดแสงทั้งหมดเพียง แต่แสงที่มองเห็นได้ (frustum และการบดเคี้ยว) อย่าวาดฉากทั้งหมดใหม่ในแต่ละรอบเพียงแค่วัตถุที่อยู่ในระยะของแสงในบัตรนั้น มี shader หลายรุ่นที่รองรับจำนวนไฟที่แตกต่างกัน (1, 2, 3, ... ) ดังนั้นคุณไม่ต้องเสียเวลาประเมินแสงมากกว่าที่คุณต้องการ

การเรนเดอร์ตามที่กล่าวไว้ในคำตอบอื่นเป็นทางเลือกที่ดีเมื่อคุณมีไฟเล็ก ๆ มากมาย แต่ไม่ใช่วิธีเดียว

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