พื้นผิวเสมือนจริงเป็นตรรกะที่รุนแรงของพื้นผิวแผนที่
Atlas พื้นผิวเป็นพื้นผิวขนาดยักษ์ที่มีพื้นผิวสำหรับแต่ละตาข่ายภายใน:
แผนที่พื้นผิวได้รับความนิยมเนื่องจากความจริงที่ว่าการเปลี่ยนพื้นผิวทำให้ขั้นตอนการทำงานเต็มไปด้วย GPU เมื่อสร้างตาข่าย UV จะถูกบีบอัด / เลื่อนเพื่อให้แสดงถึง 'ส่วน' ที่ถูกต้องของแผนที่พื้นผิวทั้งหมด
ดังที่ @ nathan-reed พูดถึงในความคิดเห็นหนึ่งในข้อเสียเปรียบหลักของ atlases พื้นผิวคือการสูญเสียโหมดการห่อเช่นการทำซ้ำตัวหนีบเส้นขอบ ฯลฯ นอกจากนี้หากพื้นผิวไม่มีเส้นขอบรอบพอคุณสามารถบังเอิญได้ ตัวอย่างจากพื้นผิวที่อยู่ติดกันเมื่อทำการกรอง สิ่งนี้สามารถนำไปสู่การตกเลือดสิ่งประดิษฐ์
พื้นผิว Atlases มีข้อ จำกัด ที่สำคัญอย่างหนึ่ง: ขนาด API กราฟิกส์วางข้อ จำกัด ที่อ่อนบนพื้นผิวที่สามารถมีขนาดใหญ่ ที่กล่าวว่าหน่วยความจำกราฟิกมีขนาดใหญ่มาก ดังนั้นจึงมีข้อ จำกัด อย่างหนักเกี่ยวกับขนาดพื้นผิวที่กำหนดโดยขนาดของ v-ram ของคุณ พื้นผิวเสมือนแก้ปัญหานี้โดยการยืมแนวคิดจากหน่วยความจำเสมือน
พื้นผิวเสมือนจริงใช้ประโยชน์จากความจริงที่ว่าในฉากส่วนใหญ่คุณจะเห็นเพียงส่วนเล็ก ๆ ของพื้นผิวทั้งหมด ดังนั้นเฉพาะเซตย่อยของพื้นผิวเท่านั้นที่ต้องอยู่ใน vram ส่วนที่เหลือสามารถอยู่ใน RAM หลักหรือบนดิสก์
มีไม่กี่วิธีที่จะใช้มันผมจะอธิบายการดำเนินการอธิบายโดยฌอนบาร์เร็ตต์ในของเขามี แต่การพูดคุย GDC (ซึ่งฉันขอแนะนำให้ดู)
เรามีองค์ประกอบหลักสามประการคือพื้นผิวเสมือนพื้นผิวจริงและตารางค้นหา
พื้นผิวเสมือนจริงแสดงให้เห็นถึงแผนที่ขนาดใหญ่ทางทฤษฎีที่เราจะมีถ้าเรามี vram เพียงพอที่จะพอดีกับทุกอย่าง มันไม่มีอยู่จริงในหน่วยความจำ พื้นผิวทางกายภาพแสดงให้เห็นถึงสิ่งที่ข้อมูลพิกเซลที่เรามีจริงใน vram ตารางการค้นหาคือการแมประหว่างทั้งสอง เพื่อความสะดวกเราแบ่งองค์ประกอบทั้งสามออกเป็นขนาดเท่ากันหรือหลายหน้า
ตารางการค้นหาจะจัดเก็บตำแหน่งของมุมบนซ้ายของแผ่นกระเบื้องในพื้นผิวทางกายภาพ ดังนั้นเมื่อให้รังสียูวีกับพื้นผิวเสมือนทั้งหมดเราจะได้รังสียูวีที่สอดคล้องกันสำหรับพื้นผิวทางกายภาพได้อย่างไร
ก่อนอื่นเราต้องค้นหาที่ตั้งของหน้าภายในพื้นผิวทางกายภาพ จากนั้นเราจำเป็นต้องคำนวณตำแหน่งของ UV ภายในหน้า ในที่สุดเราสามารถเพิ่มสองออฟเซ็ตเหล่านี้เข้าด้วยกันเพื่อให้ได้ตำแหน่งของรังสียูวีภายในพื้นผิวทางกายภาพ
float2 pageLocInPhysicalTex = ...
float2 inPageLocation = ...
float2 physicalTexUV = pageLocationInPhysicalTex + inPageLocation;
กำลังคำนวณ pageLocInPhysicalTex
หากเราทำให้ตารางการค้นหามีขนาดเท่ากับจำนวนของกระเบื้องในพื้นผิวเสมือนเราสามารถลองตารางการค้นหาที่มีการสุ่มตัวอย่างเพื่อนบ้านที่ใกล้ที่สุดและเราจะได้ตำแหน่งของมุมบนซ้ายของหน้าภายในพื้นผิวทางกายภาพ
float2 pageLocInPhysicalTex = lookupTable.Sample(virtTexUV, nearestNeighborSampler);
กำลังคำนวณ inPageLocation
inPageLocation เป็นพิกัด UV ที่สัมพันธ์กับมุมบนซ้ายของหน้าแทนที่จะเป็นมุมบนซ้ายของพื้นผิวทั้งหมด
วิธีหนึ่งในการคำนวณสิ่งนี้คือการลบ UV ที่ด้านซ้ายบนของหน้าออกจากนั้นปรับขนาดของหน้า อย่างไรก็ตามนี่เป็นคณิตศาสตร์นิดหน่อย แต่เราสามารถใช้ประโยชน์จากการแสดง IEEE floating point แทน จุดลอยตัว IEEE เก็บส่วนที่เป็นเศษส่วนของตัวเลขด้วยชุดของเศษส่วนฐาน 2
ในตัวอย่างนี้จำนวนคือ:
number = 0 + (1/2) + (1/8) + (1/16) = 0.6875
ทีนี้มาดูพื้นผิวเสมือนจริงที่ง่ายขึ้น:
1/2 บิตบอกเราว่าเราอยู่ในครึ่งซ้ายของพื้นผิวหรือด้านขวา 1/4 บิตบอกเราว่าเราอยู่ในไตรมาสใดในครึ่งนี้ในตัวอย่างนี้เนื่องจากพื้นผิวถูกแบ่งออกเป็น 16 หรือ 4 ต่อด้านสองบิตแรกเหล่านี้บอกเราว่าเราอยู่ในหน้าที่เหลืออยู่ บิตบอกตำแหน่งภายในหน้า
เราสามารถรับบิตที่เหลือได้โดยขยับลอยด้วย exp2 () และดึงออกด้วยเศษส่วน ()
float2 inPageLocation = virtTexUV * exp2(sqrt(numTiles));
inPageLocation = fract(inPageLocation);
โดยที่ numTiles คือ int2 ที่ให้จำนวนไพ่ต่อด้านของพื้นผิว ในตัวอย่างของเรานี่จะเป็น (4, 4)
ดังนั้นลองคำนวณ inPageLocation สำหรับจุดสีเขียว (x, y) = (0.6875, 0.375)
inPageLocation = float2(0.6875, 0.375) * exp2(sqrt(int2(4, 4));
= float2(0.6875, 0.375) * int2(2, 2);
= float2(1.375, 0.75);
inPageLocation = fract(float2(1.375, 0.75));
= float2(0.375, 0.75);
สิ่งสุดท้ายที่ต้องทำก่อนที่เราจะทำ ปัจจุบัน inPageLocation เป็นพิกัดรังสียูวีใน 'เนื้อที่' เสมือนจริง อย่างไรก็ตามเราต้องการพิกัดรังสียูวีใน 'เนื้อที่' ทางกายภาพ ในการทำเช่นนี้เราเพียงแค่ปรับขนาดในหน้าตำแหน่งโดยอัตราส่วนของขนาดพื้นผิวเสมือนต่อขนาดพื้นผิวทางกายภาพ
inPageLocation *= physicalTextureSize / virtualTextureSize;
ดังนั้นฟังก์ชั่นสำเร็จรูปคือ:
float2 CalculatePhysicalTexUV(float2 virtTexUV, Texture2D<float2> lookupTable, uint2 physicalTexSize, uint2 virtualTexSize, uint2 numTiles) {
float2 pageLocInPhysicalTex = lookupTable.Sample(virtTexUV, nearestNeighborSampler);
float2 inPageLocation = virtTexUV * exp2(sqrt(numTiles));
inPageLocation = fract(inPageLocation);
inPageLocation *= physicalTexSize / virtualTexSize;
return pageLocInPhysicalTex + inPageLocation;
}