การแก้ไขความลึกสำหรับบัฟเฟอร์ z กับ scanline


10

ฉันต้องเขียนซอฟต์แวร์ 3d rasterizer ของตัวเองและจนถึงตอนนี้ฉันสามารถฉายโมเดล 3 มิติของฉันที่สร้างจากรูปสามเหลี่ยมลงในพื้นที่ 2d:

ฉันหมุนแปลและฉายจุดเพื่อให้ได้พื้นที่ 2 มิติของสามเหลี่ยมแต่ละอัน จากนั้นฉันใช้จุดสามเหลี่ยม 3 จุดและใช้อัลกอริธึม scanline (โดยใช้การแก้ไขเชิงเส้น) เพื่อค้นหาทุกจุด [x] [y] ตามขอบ (ซ้ายและขวา) ของรูปสามเหลี่ยมเพื่อให้ฉันสามารถสแกนสามเหลี่ยมในแนวนอนได้ เรียงต่อกันแล้วเติมด้วยพิกเซล

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

แนวคิดดูเหมือนชัดเจนพอแรกพบ Za และ Zb ด้วยการคำนวณเหล่านี้:

var Z_Slope = (bottom_point_z - top_point_z) / (bottom_point_y - top_point_y);
var Za = top_point_z + ((current_point_y - top_point_y) * Z_Slope);

จากนั้นสำหรับ Zp แต่ละอันฉันทำการแก้ไขแบบเดียวกันในแนวนอน:

var Z_Slope = (right_z - left_z) / (right_x - left_x);
var Zp = left_z + ((current_point_x - left_x) * Z_Slope);

ป้อนคำอธิบายรูปภาพที่นี่

และถ้า z ปัจจุบันใกล้ชิดกับวิวเวอร์มากกว่า z ก่อนหน้านี้ที่ดัชนีนั้นจากนั้นเขียนสีไปยังบัฟเฟอร์สีและเขียน z ใหม่ไปยังบัฟเฟอร์ z (ระบบพิกัดของฉันคือ x: left -> right; y: top -> bottom; z: หน้าคุณ -> หน้าจอคอมพิวเตอร์;)

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

PS: ฉันได้อ่านที่นี่ว่าคุณจะต้องเปลี่ยน z เป็นส่วนกลับ (ความหมายz = 1/z) ของพวกเขาก่อนที่คุณจะแก้ไข ฉันลองมันและดูเหมือนว่าจะไม่มีการเปลี่ยนแปลง ฉันกำลังคิดถึงอะไร (ทุกคนสามารถชี้แจงได้อย่างแม่นยำว่าคุณต้องเปลี่ยน z เป็น 1 / z และที่ไหน (ถ้า) เพื่อเปลี่ยนกลับได้หรือไม่)

[แก้ไข]นี่คือข้อมูลบางส่วนเกี่ยวกับค่า z และค่าต่ำสุดที่ฉันได้รับ:

    max z: 1;                  min z: -1;                 //<-- obvious, original z of the vertices of the triangles
    max z: 7.197753398761272;  min z: 3.791703256899924;   //<-- z of the points that were drawn to screen (you know, after rotation, translation), by the scanline with zbuffer, gotten with interpolation but not 1/z.
    max z: 0.2649908532179404; min z: 0.13849507306889008;//<-- same as above except I interpolated 1/z instead of z.
//yes, I am aware that changing z to 1/z means flipping the comparison in the zBuffer check. otherwise nothing gets drawn.

ก่อนที่ฉันจะทำการดีบักอย่างระมัดระวังใครบางคนสามารถยืนยันได้ว่าแนวคิดของฉันถูกต้องแล้วหรือยัง?

[EDIT2]

ฉันแก้ไข z-buffering แล้ว ตามที่ปรากฎคำสั่งวาดไม่ได้ยุ่งเลย คำนวณพิกัด z อย่างถูกต้อง

ปัญหาคือในความพยายามที่จะเพิ่มอัตราเฟรมของฉันฉันกำลังวาดกล่อง 4px / 4px ทุกพิกเซลที่ 4 แทนพิกเซลจริงบนหน้าจอ ดังนั้นฉันกำลังวาด 16px ต่อพิกเซล แต่ตรวจสอบบัฟเฟอร์ z สำหรับหนึ่งในนั้น ฉันเป็นคนโง่

TL / DR:คำถามยังคงอยู่: คุณจะต้องใช้ส่วนกลับของ Z (ดังใน 1 / z) เป็นอย่างไร / ทำไม / เมื่อใดจึงต้องใช้แทน Z เพราะตอนนี้ทุกอย่างทำงานเหมือนกัน (ไม่มีความแตกต่างที่เห็นได้ชัดเจน)


Re: "และแน่นอนฉันเพิ่ม zBuffer หาก z ปัจจุบันใกล้ชิดกับวิวเวอร์มากกว่าค่าก่อนหน้าในดัชนีนั้น" ไม่ชัดเจนสำหรับฉันที่คุณพูดว่าคุณหมายถึงอะไร แต่ต้องการให้แน่ใจว่าสิ่งที่คุณหมายถึงคือ "ถ้า z ปัจจุบันใกล้ชิดกับผู้ชมมากกว่า z ก่อนหน้านี้ที่ดัชนีนั้นแล้วเขียนสีลงในบัฟเฟอร์สีและเขียน new z to the buffer z "วัตถุประสงค์ของบัฟเฟอร์ z คือบล็อกสีเขียนถ้าสีที่พิกเซลนั้นถูกเขียนใกล้กับดวงตาของกล้องแล้ว
Alturis

ถูกต้อง. ขออภัยมันสายเมื่อฉันพูดคำถามของฉัน ฉันจะแก้ไข
Spectraljump

คำตอบ:


5

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

คุณไม่สังเกตเห็นเพราะตราบใดที่การเปรียบเทียบระหว่าง Z1 และ Z2 นั้นถูกต้อง zbuffer จะทำสิ่งที่ถูกต้องแม้ว่าค่าทั้งสองจะผิด คุณจะสังเกตเห็นได้อย่างชัดเจนเมื่อคุณเพิ่มการจับคู่พื้นผิว (และเพื่อตอบคำถามที่คุณมี: สอดแทรก 1 / Z, U / Z และ V / Z และสร้าง U และ V ใหม่จากค่าเหล่านี้: U = (U / Z) / (1 / Z), V = (V / Z) / (1 / Z) คุณจะขอบคุณฉันทีหลัง)

ตัวอย่าง. รับกระดาษชิ้นหนึ่ง มุมมองจากบนลงล่างดังนั้นลืมพิกัด Y X คือแกนนอน, Z คือแกนตั้ง, กล้องอยู่ที่ (0, 0), ระนาบการฉายคือ z = 1

พิจารณาคะแนน A (-2, 2) และ B (2, 4) จุดกึ่งกลาง M ของส่วน AB คือ (0, 3) จนถึงตอนนี้ดีมาก

คุณฉายภาพ A เป็น A ': X' = X / Z = -1 ดังนั้น A 'คือ (-1, 1) เช่นเดียวกัน B 'คือ (0.5, 1) แต่โปรดทราบว่าการฉายภาพของ M คือ (0, 1) ซึ่งไม่ใช่จุดกึ่งกลางของ A'B ' ทำไม? เนื่องจากครึ่งขวาของเซ็กเมนต์อยู่ห่างจากกล้องมากกว่าครึ่งซ้ายจึงดูเล็กลง

แล้วจะเกิดอะไรขึ้นถ้าคุณพยายามคำนวณ Z of M โดยใช้การประมาณเชิงเส้น? dx = (0.5 - -1) = 1.5, dz = (4 - 2) = 2 ดังนั้นสำหรับ M 'โดยที่ X' = 0 โพลาไรซ์เชิงเส้น Z คือ zA + (dz / dx) (x - xA) = 2 + (2 / 1.5) (0 - -1) = 2 + 1.333 = 3.3333 - ไม่ใช่ 3!

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

ในที่สุดจะเกิดอะไรขึ้นถ้าคุณแก้ไข 1 / Z แทน? ก่อนอื่นคุณคำนวณ 1 / Z ที่ A และ B: 0.5 และ 0.25 ตามลำดับ จากนั้นคุณแก้ไข: dx = (0.5 - -1) = 1.5, dz = (0.25 - 0.5) = -0.25 ดังนั้นที่ X '= 0 คุณคำนวณ 1 / Z = 0.5 + (-0.25 / 1.5) * (0 - -1) = 0.3333 แต่นั่นคือ 1 / Z ดังนั้นค่าของ Z คือ ... แน่นอน 3 อย่างที่ควรจะเป็น

ใช่คณิตศาสตร์นั้นยอดเยี่ยม


1
โอ้และเกี่ยวกับ "เมื่อ": คำนวณค่า 1 / Z ก่อนที่จะเริ่ม rasterize รูปสามเหลี่ยม (เช่นก่อนหน้าลูปแนวตั้ง) ดังนั้นคุณจะได้รับการประมาณ 1 / Z ที่ด้านซ้ายและขวาของสแกนไลน์ สอดแทรกสิ่งเหล่านี้เป็นเส้นตรง (อย่าทำ 1 / Z อีกครั้ง - ค่าที่ถูกแก้ไขแล้วคือ 1 / Z!) และยกเลิกการแปลงก่อนที่จะตรวจสอบ zbuffer
ggambett

1
และในที่สุดทำไม ระนาบ (ที่ฝังรูปสามเหลี่ยม) คือ Ax + By + Cz + D = 0 z เป็นฟังก์ชันเชิงเส้นที่ชัดเจนของ (x, y) คุณคาดหวังดังนั้น x '= x / z และ y' = y / z จากนั้น x = x'z และ y = y'z หากคุณแทนที่สิ่งเหล่านี้ในสมการดั้งเดิมคุณจะได้ Ax'z + By'x + Cz + D = 0 ตอนนี้ z = -D / (Axe '+ โดย' + C) โดยที่ชัดเจนว่า z ไม่ใช่ฟังก์ชันเชิงเส้น จาก (x ', y') แต่ 1 / z จึงเป็น (Axe + โดย '+ C) / -D ซึ่งเป็นฟังก์ชันเชิงเส้นของ (x', y ')
ggambett

1
คุณรู้ไหมฉันอ่านบทความและหลักสูตรไม่กี่ข้อและไม่มีบทความใดที่ชัดเจนพอสำหรับคำตอบของคุณ สำหรับลูกหลานฉันจะทราบด้วยว่า "ตัวอักษร" U "และ" V "หมายถึงแกนของพื้นผิว 2D เพราะ" X "," Y "และ" Z "ถูกใช้เพื่อแสดงถึงแกนของวัตถุ 3 มิติในแบบจำลอง การปูพื้นผิวด้วย UV ช่วยให้รูปหลายเหลี่ยมที่ประกอบขึ้นเป็นวัตถุ 3 มิติที่จะวาดด้วยสีจากภาพ " - Wikipedia - การทำแผนที่ UV
Spectraljump

ดีใจที่ได้ยินเช่นนั้น. ฉันไม่ได้ในความเป็นจริงสอนคอมพิวเตอร์กราฟิกในชีวิตก่อนหน้า :)
ggambett

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