-
ฮ่าๆ สองบทเรียนนี้แปลมานานแล้วแต่ยังไม่ได้ลงเลย ทุกคนรอมานาน (มีใครรออีกมั้ย?)
ความโปร่งใสที่เรียบง่าย
เอฟเฟ็กต์พิเศษส่วนใหญ่ใน OpenGL เกี่ยวข้องกับการผสม (สี) บางประเภท
การผสมสีหมายถึงการรวมสีของพิกเซลบางพิกเซลเข้ากับสีของพิกเซลที่เกี่ยวข้องที่วาดบนหน้าจอ
วิธีการรวมสีทั้งสองเข้าด้วยกันนั้นขึ้นอยู่กับค่าของส่วนประกอบช่องอัลฟ่าของสีและ/หรือฟังก์ชันการผสมสีที่ใช้
โดยปกติแล้วอัลฟ่าจะเป็นองค์ประกอบสีที่ 4 ที่ส่วนท้ายของค่าสี
ในบทเรียนก่อนหน้านี้ เราใช้ GL_RGB เพื่อระบุองค์ประกอบสามส่วนของสี
GL_RGBA ที่สอดคล้องกันสามารถระบุค่าขององค์ประกอบอัลฟาได้
ก้าวไปอีกขั้น เราสามารถใช้ glColor4f() แทน glColor3f() ได้
คนส่วนใหญ่ยอมรับว่าองค์ประกอบอัลฟ่าแสดงถึงความโปร่งใสของวัสดุ
ซึ่งหมายความว่าค่าอัลฟ่า 0.0 แสดงถึงวัสดุที่มีความโปร่งใสโดยสมบูรณ์
ค่าอัลฟ่า 1.0 แสดงถึงวัสดุที่มีความทึบแสงโดยสมบูรณ์
สูตรผสมสี
หากคุณไม่ได้สนใจวิชาคณิตศาสตร์และเพียงต้องการดูวิธีทำความโปร่งใส ให้ข้ามส่วนนี้ไป
หากคุณต้องการทำความเข้าใจให้ลึกซึ้งยิ่งขึ้นเกี่ยวกับวิธีการผสมสี (สี) ส่วนนี้น่าจะเหมาะกับคุณ
『การเพิ่มเติมของ CKER: จริงๆ แล้วมันก็ไม่ยากเลย^-^ สูตรในบทความต้นฉบับเป็นดังนี้ ให้ CKER กลับมาพูดถึงอีกครั้ง
ความจริงแล้วหลักการพื้นฐานของการผสมคือการแยกสีของแต่ละพิกเซลและสีพื้นหลังของภาพที่จะแยกตามกฎ RGB
ตาม - องค์ประกอบสี RGB ของภาพ * ค่าอัลฟ่า + องค์ประกอบสี RGB ของพื้นหลัง * (ค่า 1 อัลฟา)
-หลังจากผสมกับสูตรง่ายๆ ดังกล่าวแล้ว ส่วนประกอบ RGB ที่ได้จากการผสมก็จะถูกรวมเข้าด้วยกันอีกครั้งในที่สุด -
สูตรมีดังนี้:
(Rs Sr + Rd Dr, Gs Sg + Gd Dg, Bs Sb + Bd Db, As Sa + Ad Da)
OpenGL คำนวณผลลัพธ์การผสมสีของพิกเซลทั้งสองนี้ตามสูตรด้านบน
ตัวพิมพ์เล็ก s และ r แสดงถึงพิกเซลต้นทางและพิกเซลเป้าหมายตามลำดับ ตัวพิมพ์ใหญ่ S และ D เป็นปัจจัยการผสมสีที่สอดคล้องกัน
สิ่งเหล่านี้กำหนดวิธีผสมสีให้กับพิกเซลเหล่านั้น
ในกรณีส่วนใหญ่ค่าการผสมอัลฟ่าของแต่ละช่องสีจะเท่ากัน
ด้วยวิธีนี้ เรามี (As, As, As, As) สำหรับพิกเซลต้นทาง
พิกเซลเป้าหมายคือ 1, 1, 1, 1) - (As, As, As, As)
สูตรข้างต้นจะเป็นดังนี้:
(Rs As + Rd (1 - As), Gs As + Gd (1 - As), Bs As + Bs (1 - As), As + Ad (1 - As))
สูตรนี้ให้ผลลัพธ์ที่โปร่งใส/โปร่งแสง
การผสมสีใน OpenGL
ขั้นตอนในการใช้การผสมสีใน OpenGL นั้นคล้ายคลึงกับกระบวนการ OpenGL ที่เรากล่าวถึงก่อนหน้านี้
จากนั้นตั้งค่าสูตรและปิดแคชความลึกการเขียนเมื่อวาดวัตถุโปร่งใส
เพราะเราต้องการวาดวัตถุไว้ด้านหลังรูปร่างกึ่งโปร่งใส
นี่ไม่ใช่วิธีที่ถูกต้องในการผสมสี แต่โดยส่วนใหญ่แล้วจะใช้ได้ดีกับโปรเจ็กต์ง่ายๆ
นอกจากนี้ Rui Martins: กระบวนการผสมสีที่ถูกต้องควรวาดทั้งฉากก่อนแล้วจึงวาดกราฟิกโปร่งใส
และพวกมันจะถูกวาดในลำดับย้อนกลับของบัฟเฟอร์ความลึก (วัตถุที่ไกลที่สุดจะถูกวาดก่อน)
พิจารณาอัลฟ่าผสมรูปหลายเหลี่ยมสองรูป (1 และ 2) ลำดับการวาดที่แตกต่างกันจะให้ผลลัพธ์ที่แตกต่างกัน
(ในที่นี้สันนิษฐานว่ารูปหลายเหลี่ยม 1 อยู่ใกล้ผู้สังเกตมากที่สุด ดังนั้นกระบวนการที่ถูกต้องควรวาดรูปหลายเหลี่ยม 2 ก่อน แล้วจึงวาดรูปหลายเหลี่ยม 1
ดังที่คุณเห็นในความเป็นจริง
แสงที่มาจากด้านหลังรูปหลายเหลี่ยม <โปร่งใส> ทั้งสองนี้จะผ่านรูปหลายเหลี่ยม 2 ก่อนเสมอ
จากนั้นมันจะผ่านรูปหลายเหลี่ยม 1 และไปถึงดวงตาของผู้สังเกตในที่สุด -
เมื่อเปิดใช้งานการแคชเชิงลึก คุณควรเรียงลำดับกราฟิกแบบโปร่งใสตามความลึก
และวาดวัตถุโปร่งใสเหล่านี้หลังจากวาดฉากทั้งหมดแล้ว มิฉะนั้นคุณจะได้รับผลลัพธ์ที่ไม่ถูกต้อง
ฉันรู้ว่าการทำเช่นนี้บางครั้งอาจเจ็บปวด แต่เป็นวิธีที่ถูกต้อง
เราจะใช้โค้ดจากบทที่ 7
เริ่มต้นด้วยการเพิ่มตัวแปรใหม่สองตัวที่จุดเริ่มต้นของโค้ด ฉันเขียนโค้ดใหม่ทั้งหมดเพื่อความชัดเจน
-
วาร์
h_RC: HGLRC; // บริบทการแสดงผล (ตารางคำอธิบายการแรเงา)
h_DC: HDC; // บริบทอุปกรณ์ (ตารางคำอธิบายอุปกรณ์)
h_Wnd: HWND; // ที่จับหน้าต่าง
h_Instance: HINST; // อินสแตนซ์ของโปรแกรม (อินสแตนซ์)
ปุ่ม : Array[0..255] ของ Boolean; // Array สำหรับรูทีนของคีย์บอร์ด
แสง : บูลีน; // เปิด/ปิดแหล่งกำเนิดแสง
ผสมผสาน : บูลีน; // ปิด / เปิดการผสม (ใหม่)
lp : Boolean; // กดปุ่ม L หรือไม่?
fp : Boolean; // กดปุ่ม F หรือไม่?
bp : Boolean; // กดปุ่ม B หรือไม่ (ใหม่)
xrot : GLfloat; // การหมุน X
yrot : GLfloat; // การหมุน Y
xspeed : GLfloat; // X ความเร็วในการหมุน
yspeed : GLfloat; // Y ความเร็วในการหมุน
z : GLfloat = -5.0 f; // ระยะห่างลึกเข้าไปในหน้าจอ
LightAmbient: Array[0..3] ของ GLfloat = (0.5, 0.5, 0.5, 1.0); // พารามิเตอร์แสงโดยรอบ (ใหม่)
LightDiffuse: Array[0..3] ของ GLfloat = (1.0, 1.0, 1.0, 1.0); // พารามิเตอร์แสงกระจาย (ใหม่)
LightPosition: Array[0..3] ของ GLfloat = (0.0, 0.0, 2.0, 1.0); // ตำแหน่งแหล่งกำเนิดแสง (ใหม่)
ตัวกรอง : GLuint; // ประเภทตัวกรอง
texture : Array[0..2] ของ GLuint; // พื้นที่เก็บข้อมูลสำหรับ 3 textures
PROcedure glGenTextures (n: GLsizei; พื้นผิว Var: GLuint); stdcall ภายนอก
opengl32;
ขั้นตอน glBindTexture (เป้าหมาย: GLenum; texture: GLuint); stdcall ภายนอก
opengl32;
ฟังก์ชั่น gluBuild2DMipmaps (เป้าหมาย: GLenum; ส่วนประกอบ, ความกว้าง, ความสูง: GLint;
รูปแบบ atype: GLenum; ข้อมูล: ตัวชี้): จำนวนเต็ม; ชื่อ glu32 ภายนอก
'gluBuild2DMipmaps';
-
จากนั้นเลื่อนลงไปที่ LoadGLTextures()
ค้นหาว่า (TextureImage[0]=LoadBMP('Data/Crate.bmp'))
เส้นนี้. ตอนนี้เรากำลังใช้พื้นผิวกระจกสีแทนพื้นผิวกล่องไม้จากบทเรียนที่แล้ว
if (TextureImage[0]=LoadBMP("Data/glass.bmp")); // โหลดบิตแมปแก้ว (แก้ไขแล้ว)
-
ฟังก์ชั่น LoadTexture: boolean; // โหลดบิตแมปและแปลงเป็นพื้นผิว
วาร์
สถานะ : บูลีน; // ตัวบ่งชี้สถานะ
TextureImage : Array[0..1] ของ PTAUX_RGBImageRec; // สร้างพื้นที่เก็บข้อมูลพื้นผิว
เริ่ม
สถานะ := เท็จ;
ZeroMemory(@TextureImage, sizeof(TextureImage)); // ตั้งค่าตัวชี้เป็น NULL
TextureImage[0] := LoadBMP('Walls.bmp');
ถ้า TextureImage[0] <> ไม่มีเลย
เริ่ม
สถานะ := TRUE; // ตั้งค่าสถานะเป็น TRUE
glGenTextures(1, texture[0]); // สร้างพื้นผิว
// สร้างแผนผังตัวกรองที่ใกล้ที่สุด
glBindTexture(GL_TEXTURE_2D, พื้นผิว[0]);
// สร้างพื้นผิว
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, 3, พื้นผิวรูปภาพ[0].sizeX,
พื้นผิวรูปภาพ[0].sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
พื้นผิวรูปภาพ[0].ข้อมูล);
glBindTexture(GL_TEXTURE_2D, texture[1]); //ใช้พื้นผิวทั่วไปที่สร้างจากข้อมูลบิตแมป
// สร้างพื้นผิว
glTexImage2D(GL_TEXTURE_2D, 0, 3, พื้นผิวรูปภาพ[0].sizeX,
พื้นผิวรูปภาพ[0].sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
พื้นผิวรูปภาพ[0].ข้อมูล);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // การกรองเชิงเส้น
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // การกรองเชิงเส้น
//สร้างพื้นผิว MipMapped
glBindTexture(GL_TEXTURE_2D, พื้นผิว[2]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexพารามิเตอร์(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_NEAREST); // (ใหม่)
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[0].sizeX,
พื้นผิวรูปภาพ[0].ขนาด, GL_RGB, GL_UNSIGNED_BYTE,
TextureImage[0].data); //(ใหม่) }
จบ;
หากกำหนด (TextureImage[0]) จากนั้น // ไม่ว่าจะมีพื้นผิวอยู่หรือไม่
หากกำหนด (TextureImage[0].data) จากนั้น // ไม่ว่าจะมีรูปภาพพื้นผิวอยู่หรือไม่
TextureImage[0].data := Nil; // ปล่อยหน่วยความจำที่ครอบครองโดยภาพพื้นผิว
TextureImage[0] := Nil; // ปล่อยโครงสร้างภาพ
ผลลัพธ์ := สถานะ; // สถานะการกลับมา
จบ;
-
เพิ่มสองบรรทัดต่อไปนี้ในส่วนโค้ด glInit()
บรรทัดแรกวาดวัตถุนี้ด้วยความสว่างเต็มที่และให้การผสมอัลฟ่า 50% (กึ่งโปร่งใส)
เมื่อเปิดตัวเลือกการผสม วัตถุนี้จะโปร่งใส 50%
บรรทัดที่สองกำหนดประเภทของการผสมที่ใช้
เพิ่มโดย Rui Martins:
ค่าช่องอัลฟา 0.0 หมายความว่าวัสดุของวัตถุมีความโปร่งใสโดยสมบูรณ์
1.0 หมายถึงทึบแสงโดยสมบูรณ์
-
ขั้นตอน glInit(); // เริ่มการตั้งค่าทั้งหมดสำหรับ OpenGL ที่นี่
เริ่ม
ถ้า (ไม่ใช่ LoadTexture) จากนั้น // เรียกรูทีนย่อยการโหลดพื้นผิว
exit; // หากโหลดไม่สำเร็จ ให้ออก
glEnable(GL_TEXTURE_2D); // เปิดใช้งานการแมปพื้นผิว
glShadeModel(GL_SMOOTH); // เปิดใช้งานการปรับเงาให้เรียบ
glClearColor(0.0, 0.0, 0.0, 0.0); // พื้นหลังสีดำ
glClearDepth(1.0); //ตั้งค่าบัฟเฟอร์ความลึก
glEnable(GL_DEPTH_TEST); // เปิดใช้งานการทดสอบเชิงลึก
glDepthFunc(GL_LESS); // ประเภทของการทดสอบเชิงลึกเสร็จสิ้น
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // การคำนวณการฉายภาพเปอร์สเปคทีฟที่ปรับให้เหมาะสมที่สุด
glLightfv(GL_LIGHT1, GL_AMBIENT, @LightAmbient[0]); // ตั้งค่าแสงโดยรอบ
glLightfv(GL_LIGHT1, GL_DIFFUSE, @LightDiffuse[0]); // ตั้งค่าการกระจายแสง
glLightfv(GL_LIGHT1, GL_POSITION, @LightPosition); // ตำแหน่งแหล่งกำเนิดแสง
glEnable(GL_LIGHT1); // เปิดใช้งานแหล่งกำเนิดแสงหมายเลข 1
glColor4f (1.0, 1.0, 1.0, 0.5); // ความสว่างเต็ม, การผสมอัลฟ่า 50% (ใหม่)
glBlendFunc(GL_SRC_ALPHA, GL_ONE); // ฟังก์ชั่นการผสมแบบโปร่งแสงตามค่าช่องอัลฟาของพิกเซลต้นทาง (ใหม่)
จบ;
{ค้นหาข้อมูลโค้ดต่อไปนี้ใกล้กับส่วนท้ายของบทที่ 7
หากคีย์ [VK_LEFT] จากนั้น // กดปุ่มทิศทางซ้ายหรือไม่?
yspeed := yspeed - 0.01; // ถ้าใช่ ให้ลด yspeed
ตามโค้ดข้างต้น เราจะเพิ่มโค้ดต่อไปนี้
บรรทัดเหล่านี้จะตรวจสอบว่ามีการกดปุ่ม B หรือไม่
หากเป็นเช่นนั้น คอมพิวเตอร์จะตรวจสอบว่าตัวเลือกการผสมเปิดอยู่หรือไม่
แล้วตั้งเป็นสถานะตรงกันข้าม
-
ถ้า (keys[ord('B')] และไม่ใช่ bp) จากนั้น //กดปุ่ม B และ bp เป็น FALSE?
เริ่ม
bp := TRUE; // ถ้าใช่ bp จะถูกตั้งค่าเป็น TRUE
blend := ไม่ผสมผสาน // สลับตัวเลือกการผสมเป็น TRUE / FALSE
ถ้า (ผสมผสาน) แล้ว // กำลังปั่นอยู่เหรอ?
เริ่ม
glEnable(GL_BLEND); // เปิดการผสม
glDisable(GL_DEPTH_TEST); // ปิดการทดสอบเชิงลึก
จบ
อย่างอื่น // อย่างอื่น
เริ่ม
glDisable(GL_BLEND); // ปิดการผสม
glEnable(GL_DEPTH_TEST); // เปิดการทดสอบเชิงลึก
จบ;
จบ;
ถ้า (ไม่ใช่คีย์ [ord ('B')]) จากนั้น // คีย์ B ถูกปล่อยออกมาหรือไม่?
เริ่ม
bp := FALSE; // ถ้าใช่ ให้ตั้งค่า bp เป็น FALSE
จบ;
-
แต่คุณจะระบุสีของการผสมผสานได้อย่างไรเมื่อใช้ Texture Mapping?
เมื่อปรับโหมดพื้นผิว สีของแต่ละพิกเซลของพื้นผิวจะถูกกำหนดโดยพารามิเตอร์ช่องอัลฟ่า
ได้จากการคูณสีพิกเซลปัจจุบัน
เช่น สีที่วาดคือ (0.5, 0.6, 0.4)
เราจะนำสีมาคูณกันเพื่อให้ได้ (0.5, 0.6, 0.4, 0.2)
(เมื่อไม่ได้ระบุพารามิเตอร์ alpha จะมีค่าเริ่มต้นเป็นศูนย์)
แค่นั้นแหละ! การใช้การผสมอัลฟ่าใน OpenGL นั้นง่ายมาก!
-
-
บันทึกต้นฉบับ (11/13/99)
รหัสการผสมสีของฉัน (NeHe) ได้รับการแก้ไขเพื่อทำให้วัตถุที่แสดงดูสมจริงยิ่งขึ้น
การใช้พารามิเตอร์อัลฟ่าเพื่อผสมผสานพิกเซลต้นทางและพิกเซลปลายทางพร้อมกันจะทำให้ร่องรอยปลอมของวัตถุดูชัดเจน
ซึ่งจะทำให้ด้านหลังของวัตถุดูมืดลงตามด้านข้าง
โดยพื้นฐานแล้ววัตถุจะดูแปลก
วิธีการผสมสีที่ผมใช้อาจไม่ดีที่สุดแต่ก็ใช้ได้ผล
เมื่อเปิดไฟ วัตถุจะดูสมจริง
ขอขอบคุณทอมที่ให้รหัสต้นฉบับวิธีการผสมสีที่เขาใช้นั้นถูกต้อง
แต่วัตถุดูไม่น่าดึงดูดเท่าที่ควร :)
รหัสได้รับการแก้ไขอีกครั้งเนื่องจากแก้ไขปัญหาเกี่ยวกับฟังก์ชัน glDepthMask() บนกราฟิกการ์ดบางรุ่น
คำสั่งนี้ดูเหมือนจะไม่ค่อยมีประสิทธิภาพนักเมื่อเปิดใช้งานหรือปิดใช้งานการทดสอบบัฟเฟอร์เชิงลึกบนการ์ดบางรุ่น
ดังนั้นฉันจึงแปลงโค้ดเพื่อเปิดหรือปิดใช้งานการทดสอบบัฟเฟอร์เชิงลึกเป็น glEnable และ glDisable แบบเก่า
การผสมผสานแผนที่พื้นผิวแบบอัลฟ่า
พารามิเตอร์อัลฟ่าที่ใช้สำหรับแผนผังพื้นผิวสามารถอ่านได้จากแผนผังปัญหาเช่นเดียวกับสี
วิธีการดังต่อไปนี้ คุณต้องโหลดวัสดุที่ต้องการและรับพารามิเตอร์อัลฟ่าในเวลาเดียวกัน
จากนั้นใช้รูปแบบสีของ GL_RGBA เมื่อเรียก glTexImage2D()
-