Offscreen Rendering (การเรนเดอร์นอกหน้าจอ) เป็นเทคนิคการเรนเดอร์ฉาก 3D ไม่ใช่ไปที่หน้าจอ แต่ไปยังบัฟเฟอร์เท็กซ์เจอร์ (FBO — Framebuffer Object) ซึ่งสามารถนำไปใช้เป็นเท็กซ์เจอร์ได้ในภายหลัง ตาม Apple Metal Documentation, 2025 การเรนเดอร์นอกหน้าจอใช้สำหรับสร้างเงา การสะท้อน เอฟเฟกต์หลังการประมวลผล และการประมวลผลกราฟิกเบื้องต้น Offscreen Rendering ช่วยให้สามารถแบ่งฉากที่ซับซ้อนออกเป็นรอบ (passes) ได้โดยไม่สูญเสียคุณภาพ
ประเด็นสำคัญ
Offscreen Rendering เป็นวิธีการเรนเดอร์เนื้อหากราฟิกลงในบัฟเฟอร์กลางในหน่วยความจำวิดีโอ แทนที่จะเป็น framebuffer หลักที่แสดงบนหน้าจอ ผลลัพธ์ของการเรนเดอร์นอกหน้าจอจะถูกบันทึกลงในเท็กซ์เจอร์หรือ renderbuffer
วัตถุประสงค์หลักของการเรนเดอร์นอกหน้าจอคือ การเรนเดอร์แบบหลายรอบ (multi-pass rendering) ฉากที่ซับซ้อนจะถูกแบ่งออกเป็นหลายรอบ: ขั้นแรกฉากจะถูกเรนเดอร์ลงในเท็กซ์เจอร์ (offscreen) จากนั้นเท็กซ์เจอร์นี้จะถูกใช้เป็นอินพุตสำหรับรอบถัดไป และดำเนินต่อไปจนกว่าจะได้ภาพสุดท้าย วิธีการนี้ช่วยให้สามารถสร้างเอฟเฟกต์ที่เป็นไปไม่ได้ในรอบเดียว
กลไกหลักของการเรนเดอร์นอกหน้าจอใน OpenGL ES คือ Framebuffer Object (FBO) FBO เป็นคอนเทนเนอร์ที่สามารถแนบภาพเท็กซ์เจอร์หรือออบเจกต์ renderbuffer ได้ หลังจากผูก FBO แล้ว คำสั่งการเรนเดอร์ที่ตามมาทั้งหมดจะถูกส่งไปยังเท็กซ์เจอร์ที่แนบไว้ แทนที่จะเป็นหน้าจอ
Offscreen Rendering ถูกนำไปใช้โดยการสร้างออบเจกต์ framebuffer แยกต่างหาก แนบเท็กซ์เจอร์หรือ renderbuffer เข้ากับมัน สลับบริบทการเรนเดอร์ไปยัง FBO นี้ และดำเนินการคำสั่งวาด เมื่อรอบเสร็จสิ้น บริบทจะกลับไปยัง framebuffer หลัก
กระบวนการประกอบด้วยสามขั้นตอน: การสร้าง FBO และการแนบเท็กซ์เจอร์ — การเรนเดอร์ฉากลงในเท็กซ์เจอร์นอกหน้าจอ — การใช้เท็กซ์เจอร์ผลลัพธ์ในรอบถัดไป GL_COLOR_ATTACHMENT0 กำหนดว่าเท็กซ์เจอร์สีใดที่จะถูกเรนเดอร์
// การสร้าง FBO สำหรับการเรนเดอร์นอกหน้าจอ (OpenGL ES 3.0)
GLuint fbo, offscreenTex;
glGenFramebuffers(1, &fbo);
glGenTextures(1, &offscreenTex);
glBindTexture(GL_TEXTURE_2D, offscreenTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
width, height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER),
GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
offscreenTex, 0);
Offscreen Rendering ใช้สำหรับเอฟเฟกต์กราฟิกที่หลากหลาย แต่ละเทคนิคต้องการหนึ่งรอบหรือมากกว่านอกหน้าจอและการกำหนดค่า FBO เฉพาะ
| เทคนิค | จำนวนรอบ | วัตถุประสงค์ |
|---|---|---|
| Shadow Mapping | 2+ | สร้างแผนที่เงาจากแหล่งกำเนิดแสง |
| Reflection Mapping | 1-2 | เรนเดอร์การสะท้อนลงใน cube map |
| Bloom Effect | 3-5 | ทำให้บริเวณสว่างของภาพเปล่งประกาย |
| HDR Rendering | 2 | การปรับโทนสีสำหรับภาพ HDR |
| Depth Prepass | 1 | การเติม depth buffer ล่วงหน้า |
เทคนิคพื้นฐานที่ผลลัพธ์การเรนเดอร์ถูกบันทึกลงในเท็กซ์เจอร์เพื่อใช้ในภายหลัง Render-to-Texture ใช้สำหรับสร้างแผนที่ขนาดเล็ก เท็กซ์เจอร์ของออบเจกต์ไดนามิก และการแสดงภาพล่วงหน้าของวัสดุที่ซับซ้อน
เอฟเฟกต์ที่ซับซ้อน (HDR, bloom, depth of field) ต้องการหลายรอบตามลำดับ แต่ละรอบจะเรนเดอร์ฉากลงในเท็กซ์เจอร์นอกหน้าจอ ใช้ฟิลเตอร์ และส่งผลลัพธ์ไปยังรอบถัดไป วิธีการนี้ช่วยให้ สะสมเอฟเฟกต์ โดยไม่สูญเสียคุณภาพของภาพสุดท้าย
Shadow Mapping เป็นตัวอย่างคลาสสิกของการใช้การเรนเดอร์นอกหน้าจอ เงาถูกสร้างขึ้นในสองรอบ: ขั้นแรกฉากจะถูกเรนเดอร์จากมุมมองของแหล่งกำเนิดแสงลงในเท็กซ์เจอร์ความลึก (offscreen) จากนั้นในระหว่างการเรนเดอร์หลัก แต่ละส่วน (fragment) จะถูกเปรียบเทียบกับแผนที่ความลึกนี้เพื่อพิจารณาว่าอยู่ในเงาหรือไม่
รอบแรก: กล้องถูกวางไว้ที่ตำแหน่งของแหล่งกำเนิดแสง ฉากจะถูกเรนเดอร์ลงในบัฟเฟอร์นอกหน้าจอ โดยบันทึกเฉพาะความลึกลงในเท็กซ์เจอร์ รอบที่สอง: การเรนเดอร์หลักพร้อมการตรวจสอบแต่ละส่วน — หากความลึกของมันมากกว่าค่าในแผนที่ความลึก ส่วนนั้นจะ ถูกแรเงา
// Fragment Shader สำหรับการทำแผนที่เงา
in vec4 vShadowCoord;
uniform sampler2D uShadowMap;
float calcShadow(vec4 coord) {
vec3 uvw = coord.xyz / coord.w;
float depth = texture(uShadowMap, uvw.xy).r;
return (uvw.z > depth + 0.005) ? 0.3 : 1.0;
}
Offscreen Rendering เพิ่มรอบ GPU พิเศษ ซึ่งเพิ่มภาระการคำนวณโดยรวมและการใช้พลังงาน ในการพัฒนามือถือ สิ่งนี้สำคัญอย่างยิ่ง: การสลับ FBO แต่ละครั้งจำเป็นต้องมีการล้างไปป์ไลน์ (pipeline flush) และเวลาเพิ่มเติม
สำหรับอุปกรณ์มือถือ แนะนำให้: ใช้ renderbuffer แทนเท็กซ์เจอร์หากไม่จำเป็นต้องอ่านผลลัพธ์ (เฉพาะรอบความลึก); เลือกความละเอียดขั้นต่ำที่จำเป็นสำหรับเท็กซ์เจอร์นอกหน้าจอ; รวมรอบเมื่อเป็นไปได้ผ่าน MRT (Multiple Render Targets)
Offscreen Rendering บนแพลตฟอร์มต่าง ๆ ถูกนำไปใช้ผ่าน API ของตนเอง มาดูตัวอย่างสำหรับ OpenGL ES และ Apple Metal
// การตั้งค่า FBO และการเรนเดอร์ฉากไปยังเท็กซ์เจอร์
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glViewport(0, 0, texWidth, texHeight);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(offscreenProgram);
glBindVertexArray(sceneVAO);
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, 0);
// สลับกลับไปยัง framebuffer เริ่มต้น
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Metal: รอบการเรนเดอร์นอกหน้าจอไปยังเท็กซ์เจอร์
MTLRenderPassDescriptor passDesc = [MTLRenderPassDescriptor renderPassDescriptor];
passDesc.colorAttachments[0].texture = offscreenTexture;
passDesc.colorAttachments[0].loadAction = MTLLoadActionClear;
passDesc.colorAttachments[0].clearColor =
MTLClearColorMake(0.0, 0.0, 0.0, 0.0);
id<MTLCommandBuffer> cmdBuffer = [commandQueue commandBuffer];
id<MTLRenderCommandEncoder> enc =
[cmdBuffer renderCommandEncoderWithDescriptor:passDesc];
[enc drawIndexedPrimitives:MTLPrimitiveTypeTriangle
indexCount:indexCount
indexType:MTLIndexTypeUInt16
indexBuffer:indexBuffer
indexBufferOffset:0];
[enc endEncoding];
คำถามที่พบบ่อย
Offscreen Rendering คือเมื่อกราฟิกถูกวาดไม่ใช่บนหน้าจอ แต่ลงในบัฟเฟอร์ที่มองไม่เห็น (เท็กซ์เจอร์) บัฟเฟอร์นี้จะถูกใช้เป็นเท็กซ์เจอร์สำหรับเอฟเฟกต์อื่น ๆ ลองนึกภาพว่าคุณกำลัง วาดบนแผ่นฟิล์มใส ที่คุณวางซ้อนบนภาพสุดท้าย
การเรนเดอร์นอกหน้าจอช่วยให้สามารถสร้างเอฟเฟกต์ที่เป็นไปไม่ได้ในรอบเดียว: เงา (การเรนเดอร์จากมุมมองของแสง) การสะท้อน การเบลอ การเรืองแสง (bloom) และการปรับโทนสี HDR แต่ละเอฟเฟกต์ต้องการรอบแยกต่างหากไปยังเท็กซ์เจอร์นอกหน้าจอ
ใช่ แต่ละรอบนอกหน้าจอจะเพิ่มปริมาณงานของ GPU เป็นสองเท่า เนื่องจากฉากถูกเรนเดอร์อีกครั้ง การปรับให้เหมาะสม รวมถึงการลดความละเอียดของเท็กซ์เจอร์นอกหน้าจอ การใช้ renderbuffer และการรวมรอบผ่าน MRT (Multiple Render Targets)
FBO (Framebuffer Object) เป็นคอนเทนเนอร์ที่แนบเท็กซ์เจอร์หรือ renderbuffer Texture อนุญาตให้อ่านผลลัพธ์การเรนเดอร์ (จำเป็นสำหรับ shadow mapping, เอฟเฟกต์หลังการประมวลผล) Renderbuffer เร็วกว่า แต่ไม่สามารถใช้เนื้อหาเป็นเท็กซ์เจอร์ได้
API กราฟิกสมัยใหม่ทั้งหมด: OpenGL ES (ผ่าน FBO), Apple Metal (ผ่าน MTLRenderPassDescriptor), Vulkan (ผ่าน VkFramebuffer), Direct3D (ผ่าน Render Target View) ตามแนวคิดแล้วกลไกเหมือนกัน — การเรนเดอร์ลงในเท็กซ์เจอร์แทนที่จะเป็นหน้าจอ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม