Shader (เชเดอร์) คือโปรแกรมที่ทำงานบน GPU เพื่อประมวลผลข้อมูลกราฟิก: จุดยอด เศษส่วน หรืองานคำนวณ ต่างจากโค้ดทั่วไปบน CPU ตรงที่ shader ทำงานแบบขนานบนหลายร้อยคอร์ โดยประมวลผลแต่ละองค์ประกอบอย่างอิสระ จากข้อมูลของ Khronos Group (2025) shader ถูกใช้ใน 100% ของแอปพลิเคชัน 3D สมัยใหม่ ตั้งแต่เกมมือถือไปจนถึงเอฟเฟกต์ UI ในแอปพลิเคชันระบบ นักพัฒนาเขียน shader ด้วยภาษาที่เชี่ยวชาญเฉพาะ: GLSL, HLSL, Metal Shading Language หรือ SPIR-V
ประเด็นสำคัญ
Shader คือโปรแกรมขนาดเล็กที่เขียนด้วยภาษาเฉพาะทางและทำงานบน GPU แต่ละ shader ประมวลผลองค์ประกอบข้อมูลหนึ่งรายการ — จุดยอด เศษส่วน (พิกเซล) หรือรายการงาน — โดยอิสระจากรายการอื่น ความสามารถในการทำงานขนานของ GPU ช่วยให้สามารถเรียกใช้ shader นับพันอินสแตนซ์พร้อมกันได้
คำนี้ถูกนำเสนอโดย Pixar ในช่วงทศวรรษ 1980 เพื่ออธิบายโปรแกรมที่ควบคุมลักษณะภายนอกของพื้นผิวใน RenderMan shader แบบเรียลไทม์ปรากฏขึ้นพร้อมกับ NVIDIA GeForce 3 (2001) — การ์ดจอตัวแรกที่มีไปป์ไลน์ที่ตั้งโปรแกรมได้ ก่อน shader กราฟิกถูกกำหนดค่าด้วยพารามิเตอร์คงที่: สี พื้นผิว โหมดการผสม
จากข้อมูลของ Unity Technologies (2025) เกมมือถือโดยเฉลี่ยใช้ 50–200 shader แต่ละ shader มีรูปแบบต่างๆ สำหรับแพลตฟอร์ม ระดับคุณภาพ และการกำหนดค่าแสงที่แตกต่างกัน จำนวนรูปแบบอาจสูงถึง 10,000 ต่อโปรเจกต์
สถาปัตยกรรมของ shaster แบ่งออกเป็น ข้อมูลนำเข้า (แอตทริบิวต์, ตัวแปร uniform, พื้นผิว), โค้ดโปรแกรม และ ข้อมูลส่งออก (สี, ตำแหน่ง) ตัวแปร uniform ถูกกำหนดโดย CPU ก่อนการทำงานของ shader และไม่เปลี่ยนแปลงสำหรับทุกองค์ประกอบของเฟรม แอตทริบิวต์คือข้อมูลต่อองค์ประกอบ (พิกัดจุดยอด, เส้นตั้งฉาก, พิกัด UV)
SIMT (Single Instruction, Multiple Threads) คือโมเดลการทำงานของ shader บน GPU คำสั่งหนึ่งถูกโหลดครั้งเดียวและทำงานโดยหลายร้อยเธรดบนข้อมูลที่แตกต่างกัน ตัวอย่างเช่น Fragment Shader สำหรับ 1,000 พิกเซลทำงาน 1,000 เธรดด้วยโค้ดเดียวกัน แต่ละเธรดได้รับพิกัด UV และแอตทริบิวต์ที่ถูกอินเทอร์โพเลตของตัวเอง
ผลลัพธ์ที่สำคัญของ SIMT: การแตกกิ่ง ใน shader มีต้นทุนสูง ถ้าภายในบล็อก if ครึ่งหนึ่งของเธรดไปที่กิ่งหนึ่งและอีกครึ่งหนึ่งไปอีกกิ่ง GPU จะทำงานทั้งสองกิ่งตามลำดับสำหรับทุกเธรด ประสิทธิภาพลดลงเหลือ 50% หลีกเลี่ยงการแตกกิ่งแบบไดนามิกใน shader หรือลดให้น้อยที่สุดด้วยการคำนวณล่วงหน้าบน CPU
GPU สมัยใหม่ รองรับ shader หลายประเภท แต่ละประเภทสำหรับขั้นตอนของไปป์ไลน์กราฟิกหรือการคำนวณ มาดูประเภทหลัก: vertex, fragment, compute และ mesh shader สมัยใหม่
| ประเภท shader | ขั้นตอนไปป์ไลน์ | ประมวลผล | ภาษา |
|---|---|---|---|
| Vertex Shader | จุดยอด | แต่ละจุดยอด | GLSL, HLSL |
| Fragment Shader | เศษส่วน | แต่ละพิกเซล | GLSL, HLSL |
| Compute Shader | การคำนวณ | ข้อมูลตามอำเภอใจ | GLSL, HLSL |
| Geometry Shader | เรขาคณิต | พื้นฐาน | GLSL, HLSL |
| Tessellation Shader | เทสเซลเลชัน | แพตช์ | GLSL, HLSL |
| Mesh Shader | ไปป์ไลน์ mesh | Task + Mesh | HLSL, MSL |
Vertex Shader ประมวลผลแต่ละจุดยอดของเรขาคณิต: แปลงพิกัด คำนวณแสง ส่งข้อมูลไปยัง fragment shader เป็นขั้นตอนบังคับของไปป์ไลน์กราฟิก — หากไม่มีมัน จุดยอดจะไม่ถึงหน้าจอ Vertex Shader ถูกเรียกสำหรับทุกจุดยอดของทุกเฟรม
Fragment Shader (หรือ Pixel Shader) เป็นขั้นตอนที่ใช้ทรัพยากรมากที่สุด คำนวณสีสุดท้ายของแต่ละเศษส่วนโดยใช้พื้นผิว แสง และวัสดุ Fragment Shader ทำงานสำหรับทุกพิกเซลที่ถูกปกคลุมด้วยเรขาคณิต โดยพิจารณาพื้นผิวและเอฟเฟกต์ทั้งหมดที่ใช้ การเพิ่มประสิทธิภาพ shader นี้ให้การปรับปรุงประสิทธิภาพมากที่สุด
Compute Shader คือ shader อเนกประสงค์สำหรับการคำนวณตามอำเภอใจบน GPU ต่างจาก shader กราฟิกตรงที่มันไม่ผูกติดกับเรขาคณิตหรือพิกเซล Compute Shader ทำงานกับบัฟเฟอร์ข้อมูลตามอำเภอใจผ่านกลุ่มงาน ใช้สำหรับฟิสิกส์ การจำลองอนุภาค เอฟเฟกต์หลังการประมวลผล และการอนุมานโครงข่ายประสาทเทียม
Mesh Shader เป็น shader ประเภทล่าสุด ที่ปรากฏพร้อมกับ NVIDIA Turing (2018) และ Metal 3 Mesh Shader แทนที่ shader vertex, geometry และ tessellation ด้วยไปป์ไลน์ mesh เดียว ทำงานร่วมกับ Task Shader: Task Shader ตัดสินใจว่ากลุ่ม mesh ใดจะถูกเรนเดอร์ และ Mesh Shader สร้างเรขาคณิตทันที
Shader ถูกเขียน ด้วยภาษาเฉพาะทางที่ถูกคอมไพล์เป็นรหัสเครื่อง GPU การเลือกภาษาขึ้นอยู่กับแพลตฟอร์มและ API มาดูภาษาหลัก: GLSL, HLSL, Metal Shading Language และรูปแบบกลาง SPIR-V
GLSL คือภาษา shader สำหรับ OpenGL, OpenGL ES และ WebGL ไวยากรณ์อิงจาก C โดยเพิ่มประเภทเวกเตอร์ (vec2, vec4, mat4) และฟังก์ชันในตัว (texture, normalize, reflect) GLSL ES เป็นเวอร์ชันสำหรับอุปกรณ์มือถือที่มีความแม่นยำจำกัด (lowp, mediump, highp) จากข้อมูลของ Khronos (2025) GLSL ยังคงเป็นภาษา shader ที่แพร่หลายที่สุดเนื่องจากความเป็นข้ามแพลตฟอร์ม
#version 300 es
precision mediump float;
in vec2 v_texCoord;
uniform sampler2D u_texture;
out vec4 fragColor;
void main() {
fragColor = texture(u_texture, v_texCoord);
}
fragment shader อย่างง่ายอ่านสีจากพื้นผิวตามพิกัด UV precision mediump สั่งให้ GPU ใช้ความแม่นยำครึ่งหนึ่งสำหรับ float — เร่งการทำงานบนอุปกรณ์มือถือ 25–40%
HLSL คือภาษา shader ของ Microsoft สำหรับ DirectX ไวยากรณ์ใกล้เคียงกับ C++ มากขึ้น รองรับคลาส โครงสร้าง และเทมเพลต HLSL ใช้ในแอปพลิเคชัน Windows และผ่าน Shader Model 6.7+ รองรับ Ray Tracing, Mesh Shaders และ Sampler Feedback สำหรับการพัฒนาโมบาย HLSL ไม่ได้ใช้โดยตรง แต่ถูกคอมไพล์เป็น SPIR-V สำหรับ Vulkan
MSL (Metal Shading Language) คือภาษา shader ของ Apple ซึ่งอิงจาก C++14 ต่างจาก GLSL และ HLSL ตรงที่ MSL ถูกคอมไพล์พร้อมกับแอปพลิเคชัน ทำให้ไม่ต้องคอมไพล์ JIT บนอุปกรณ์ MSL รองรับพอยน์เตอร์ เทมเพลต และไลบรารีมาตรฐาน C++ ใช้บนอุปกรณ์ Apple ทั้งหมด: iPhone, iPad, Mac, Apple TV
#include <metal_stdlib>
using namespace metal;
struct VertexOut {
float4 position [[position]];
float2 texCoord;
};
fragment float4
myFragment(VertexOut in [[stage_in]],
texture2d<float> tex [[texture(0)]]) {
constexpr sampler s = sampler(filter::linear);
return tex.sample(s, in.texCoord);
}
MSL ใช้แอตทริบิวต์ [[position]], [[stage_in]] และ [[texture(N)]] สำหรับการผูกทรัพยากร คอมไพเลอร์ของ Apple สร้างโค้ดที่ปรับให้เหมาะสมสำหรับ GPU ปัจจุบัน โดยพิจารณาจำนวนรีจิสเตอร์และแคช
SPIR-V คือรูปแบบไบนารีกลางของ shader มาตรฐานสำหรับ Vulkan shader ถูกเขียนด้วย GLSL หรือ HLSL คอมไพล์เป็น SPIR-V และโหลดเข้าสู่แอปพลิเคชัน Vulkan SPIR-V ไม่ผูกติดกับภาษาเฉพาะ — มีคอมไพเลอร์จาก Rust, Python, OpenCL C ไปยัง SPIR-V
shader บนแพลตฟอร์มมือถือ มีข้อจำกัดเมื่อเทียบกับเดสก์ท็อป: รีจิสเตอร์น้อยกว่า ความแม่นยำจำกัด และขาดการรองรับคำสั่งบางอย่าง มาดูลักษณะเฉพาะของ shader บน Android (Vulkan/OpenGL ES) และ iOS (Metal)
Android ใช้ GLSL ES สำหรับ OpenGL และ SPIR-V สำหรับ Vulkan OpenGL ES 3.2 รองรับ shader ด้วย mediump เป็นค่าเริ่มต้น Vulkan ต้องการการคอมไพล์ที่ชัดเจนจาก GLSL เป็น SPIR-V ผ่าน glslangValidator บน Android shader ถูกโหลดจากทรัพยากรสตริงหรือจากไฟล์ SPIR-V ที่คอมไพล์แล้ว
Qualcomm Adreno GPU ปรับ shader ให้เหมาะสมในระดับไดรเวอร์ คำแนะนำ: ใช้ mediump สำหรับสีและ UV, highp สำหรับตำแหน่งเท่านั้น หลีกเลี่ยงการสุ่มตัวอย่างพื้นผิวใน vertex shader จัดกลุ่มการคำนวณเป็นเวกเตอร์ (vec4 แทน 4x float) ตามข้อมูลของ Qualcomm (2025) การปรับแต่งเหล่านี้ให้การปรับปรุง 30–50%
iOS ใช้ Metal Shading Language เท่านั้น shader ถูกคอมไพล์เป็นรหัสเครื่องพร้อมกับแอปพลิเคชันผ่าน Xcode Metal มี Shader Debugger และ GPU Capture สำหรับการทำโปรไฟล์ shader GPU ของ Apple (TBDR) มีการปรับแต่งเฉพาะ: early fragment test, memoryless render targets และ programmable blending
จากข้อมูลของ Apple WWDC 2024 สำหรับ shader iOS สิ่งสำคัญคือการใช้ half แทน float จำกัดความดันรีจิสเตอร์ (สูงสุด 64 รีจิสเตอร์) และหลีกเลี่ยงการสุ่มตัวอย่างพื้นผิวที่พึ่งพากัน shader iOS ควรกระชับ — ขนาดที่เหมาะสม 50–100 คำสั่ง ALU
เอนจิ้นเกม Unity และ Unreal Engine มี ตัวแก้ไข shader แบบภาพ (Shader Graph, Material Editor) และภาษานามธรรม (ShaderLab, USF) นักพัฒนาเขียน shader ด้วยภาษาระดับสูง และเอนจิ้นคอมไพล์สำหรับแพลตฟอร์มเป้าหมาย Unity ใช้ HLSL เป็นภาษากลาง Unreal ใช้ USF (Unreal Shader Format)
การเพิ่มประสิทธิภาพ shader เป็นขั้นตอนสำคัญในการพัฒนากราฟิกสำหรับอุปกรณ์มือถือ shader ที่เขียนไม่ดีสามารถลด FPS จาก 60 เหลือ 20 มาดูกฎและเทคนิคการเพิ่มประสิทธิภาพหลัก
ใช้ความแม่นยำขั้นต่ำที่เพียงพอ: lowp สำหรับสีและ UV, mediump สำหรับเส้นตั้งฉากและการให้แสง, highp สำหรับตำแหน่งและเมทริกซ์เท่านั้น ตามข้อมูลของ ARM (2025) การดำเนินการแบบ mediump เร็วกว่า highp 2 เท่าบน Mali GPU ใน GLSL ES กำหนดผ่านตัวระบุความแม่นยำ
การสุ่มตัวอย่างพื้นผิว เป็นการดำเนินการที่แพงที่สุดใน shader (10–30 รอบ เทียบกับ 1–2 สำหรับคำสั่ง ALU) ลดจำนวนการสุ่มตัวอย่าง: รวมพื้นผิวเป็น atlas, เก็บข้อมูลในอาร์เรย์ uniform แทนพื้นผิว, แคชผลลัพธ์ของพิกเซลข้างเคียงผ่านคำสั่งอนุพันธ์
การแตกกิ่งแบบไดนามิก (if กับตัวแปร uniform) ลดประสิทธิภาพของไปป์ไลน์ SIMT แทนที่การแตกกิ่งด้วยฟังก์ชันทางคณิตศาสตร์: mix(), step(), smoothstep() และ clamp() ฟังก์ชันเหล่านี้ทำงานใน 1–2 คำสั่ง ในขณะที่การแตกกิ่งอาจใช้ 8–16 คำสั่งเนื่องจากการแยกออกของเธรด
// ไม่ดี: การแตกกิ่งแบบไดนามิกใน shader
if (u_enableLight) {
color *= computeLighting(normal);
}
// ดี: การแทนที่กิ่งด้วยคณิตศาสตร์
color *= mix(1.0, computeLighting(normal),
float(u_enableLight));
mix() ทำการประมาณค่าเชิงเส้นระหว่างสองค่า เมื่อ u_enableLight=0 จะคืนค่า 1.0 — สีไม่เปลี่ยน เมื่อ u_enableLight=1 จะคืนค่าผลลัพธ์ของการให้แสง ไม่มีการแตกกิ่ง — ทุกเธรดทำงานโค้ดเดียวกัน
มาดูตัวอย่าง shader ที่ใช้งานได้จริงสำหรับการพัฒนาโมบาย ตั้งแต่การแรเงาอย่างง่ายไปจนถึงการสร้างพื้นผิวแบบ procedural แต่ละตัวอย่างแสดงเทคนิคเฉพาะ
โมเดล Phong คือโมเดลการให้แสงแบบคลาสสิกที่มีองค์ประกอบแบบกระจายและแบบสะท้อน Vertex Shader คำนวณแสงสำหรับแต่ละจุดยอด Fragment Shader เพียงแค่ประมาณค่าผลลัพธ์ (Gouraud shading) เหมาะสำหรับอุปกรณ์มือถือเนื่องจากต้นทุนต่ำ
#version 300 es
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec3 a_normal;
uniform mat4 u_mvp;
uniform mat4 u_modelView;
out vec3 v_color;
void main() {
vec3 normal = normalize(mat3(u_modelView) * a_normal);
vec3 lightDir = normalize(vec3(0.0, 1.0, 1.0));
float diff = max(dot(normal, lightDir), 0.0);
v_color = vec3(0.8, 0.2, 0.2) * (0.3 + diff * 0.7);
gl_Position = u_mvp * a_position;
}
shader คำนวณแสงแบบกระจาย: ผลคูณจุดของเส้นตั้งฉากกับทิศทางแสง ผลลัพธ์ถูกผสมกับองค์ประกอบโดยรอบ (0.3) และแบบกระจาย (0.7) ทำงาน 10–15 คำสั่ง ALU ต่อจุดยอด — ยอมรับได้สำหรับ 100,000 รูปหลายเหลี่ยมที่ 60 FPS บน GPU มือถือ
พื้นผิวแบบ procedural ถูกสร้างใน Fragment Shader โดยไม่ต้องโหลดจากไฟล์ ประหยัดหน่วยความจำวิดีโอและทำให้แอนิเมชันง่ายขึ้น ตัวอย่าง — กระดานหมากรุกที่มีขนาดเซลล์ปรับได้ เพียงไม่กี่คำสั่ง ALU — โหลดบน GPU น้อยที่สุด
#version 300 es
precision mediump float;
in vec2 v_uv;
uniform float u_cells;
out vec4 fragColor;
void main() {
vec2 cell = floor(v_uv * u_cells);
float isWhite = mod(cell.x + cell.y, 2.0);
fragColor = mix(vec4(0.1, 0.1, 0.1, 1.0),
vec4(0.9, 0.9, 0.9, 1.0),
isWhite);
}
8 คำสั่ง ALU — shader ที่เหมาะสำหรับอุปกรณ์มือถือ u_cells กำหนดจำนวนเซลล์ต่อแกน floor และ mod เป็นการดำเนินการราคาถูก ทำงานใน 1 รอบบน GPU มือถือทั้งหมด
คำถามที่พบบ่อย
Shader ทำงานบน GPU ในโหมด SIMT — หนึ่งคำสั่งสำหรับหลายร้อยเธรด โปรแกรม C++ ทั่วไปทำงานบน CPU ในโหมด MIMD shader ไม่สามารถเข้าถึงระบบไฟล์ I/O หรือหน่วยความจำแบบไดนามิก มันรับข้อมูลผ่านบัฟเฟอร์และพื้นผิว และส่งคืนตำแหน่งหรือสี
GLSL — สำหรับ OpenGL ES และ WebGL, ข้ามแพลตฟอร์ม Metal Shading Language — สำหรับ iOS HLSL — พื้นฐานสำหรับ Unity และ Unreal Engine เริ่มด้วย GLSL — ความเข้าใจพื้นฐานสามารถนำไปใช้กับภาษา shader ใดก็ได้
สาเหตุหลัก: ความแม่นยำสูง (highp แทน mediump), การสุ่มตัวอย่างพื้นผิวมากเกินไป, การแตกกิ่งแบบไดนามิก และการเกินขีดจำกัดรีจิสเตอร์ ใช้ RenderDoc หรือ Xcode GPU Frame Debugger สำหรับการวิเคราะห์ shader
Compute Shader คือ shader สำหรับการคำนวณตามอำเภอใจบน GPU: ฟิสิกส์อนุภาค การประมวลผลภาพ การจำลองผ้า ต่างจาก Fragment Shader ตรงที่ไม่ผูกติดกับพิกเซลและสามารถเขียนไปยังบัฟเฟอร์ตามอำเภอใจ
ได้ ผ่าน ตัวแก้ไขแบบภาพ: Shader Graph ใน Unity, Material Editor ใน Unreal Engine หรือ Metal Shader Converter สร้างโค้ด shader จากแผนภาพโหนด สำหรับการปรับแต่งเชิงลึก จำเป็นต้องเข้าใจภาษา shader
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม