บ้าน / หลังคา / แสง opengl บทเรียนและตัวอย่างการเขียนโปรแกรม Array() วิธีการทำงานของแสง

แสง opengl บทเรียนและตัวอย่างการเขียนโปรแกรม Array() วิธีการทำงานของแสง

อย่างที่คุณเห็น เราได้แนะนำเมทริกซ์เพิ่มเติม ปกติ มันเป็นสิ่งจำเป็นในการแปลค่าปกติของวัตถุจากระบบพิกัดโลคัลของวัตถุไปยังโลกหนึ่ง ซึ่งจำเป็นสำหรับการคำนวณแสงในระบบพิกัดโลก

ควรสังเกตว่าเมื่อใช้ FFP OpenGL แสงไม่ได้คำนวณในระบบพิกัดโลก แต่อยู่ในมุมมอง ในความคิดของฉันมันไม่สะดวกเพราะ ระบบพิกัดมุมมองเชื่อมต่อกับกล้องและสะดวกในการตั้งค่าแหล่งกำเนิดแสงในระบบพิกัดโลกและทำการคำนวณทั้งหมดที่นั่น

การคำนวณแสงสว่าง

การแรเงาแบบพงษ์ใช้สำหรับการคำนวณแสงในบทช่วยสอนนี้ ความหมายหลักของแบบจำลองคือการส่องสว่างสุดท้ายของวัตถุประกอบด้วยสามองค์ประกอบ:

  • แสงพื้นหลัง (บรรยากาศ)
  • แสงกระจาย (กระจาย)
  • แสงสะท้อน (สเปกตรัม)

นอกจากพารามิเตอร์เหล่านี้แล้ว เราจะเพิ่มการเรืองแสง (การเปล่งแสง) ของวัสดุของเราเอง พารามิเตอร์นี้ช่วยให้คุณสามารถเน้นวัตถุได้แม้ว่าจะไม่ได้รับแสงสว่างจากแหล่งกำเนิดแสงใดๆ ก็ตาม

ดังนั้น แต่ละองค์ประกอบจะถูกคำนวณโดยคำนึงถึงพารามิเตอร์ของแหล่งกำเนิดแสงและวัสดุของวัตถุ คุณสามารถรับข้อมูลเพิ่มเติมเกี่ยวกับรุ่นแสงไฟนี้ได้ในบันทึกนี้

การคำนวณแสงอาจเป็นได้ทั้งแสงต่อจุดยอดหรือแสงต่อพิกเซล ในบทเรียนนี้ เราจะพิจารณาการจัดแสงแบบพิกเซลต่อพิกเซล ซึ่งช่วยให้คุณปรับส่วนที่ขาดรายละเอียดในโมเดลหลายเหลี่ยมได้อย่างราบรื่น และคำนวณการส่องสว่างที่แต่ละจุดของวัตถุได้แม่นยำยิ่งขึ้น การคำนวณแสงหลักต่อพิกเซลเกิดขึ้นในแฟรกเมนต์เชเดอร์

ก่อนดำเนินการคำนวณแสง จำเป็นต้องคำนวณและส่งพารามิเตอร์จุดยอดบางส่วนจากจุดยอด Shader ไปยังส่วนแยกส่วน:

  • ปกติกับพื้นผิวของวัตถุที่จุดยอด (ปกติ)
  • ทิศทางของแสงตกกระทบจากจุดยอดถึงแหล่งกำเนิดแสง (ทิศทางแสง)
  • ดูทิศทาง จากจุดยอดถึงผู้สังเกต (ดูทิศทาง)
  • ระยะทางจากแหล่งกำเนิดแสงจุดสูงสุด (ระยะทาง)

ปกติพื้นผิวของวัตถุและทิศทางของแสงที่ตกกระทบใช้ในการคำนวณแสงกระจาย (กระจาย) และแสงสะท้อน (สเปกตรัม) อย่างไรก็ตาม ในการคำนวณแสงสะท้อน คุณต้องทราบทิศทางการจ้องมองของผู้สังเกตด้วย . ต้องใช้ระยะห่างจากด้านบนถึงแหล่งกำเนิดแสงเพื่อคำนวณปัจจัยการลดทอนโดยรวม จุดสุดยอด Shader จะมีลักษณะดังนี้:

#เวอร์ชัน 330 คอร์ #กำหนด VERT_POSITION 0 #กำหนด VERT_TEXCOORD 1 #กำหนด VERT_NORMAL 2 เค้าโครง (ตำแหน่ง = VERT_POSITION) ในตำแหน่ง vec3; เค้าโครง (ตำแหน่ง = VERT_TEXCOORD) ใน vec2 texcoord; เค้าโครง (ตำแหน่ง = VERT_NORMAL) ใน vec3 ปกติ // พารามิเตอร์การแปลงโครงสร้างเครื่องแบบการแปลง ( รุ่น mat4; mat4 viewProjection; mat3 ปกติ; vec3 viewPosition; ) แปลง; // ชี้พารามิเตอร์แหล่งกำเนิดแสงโครงสร้างเครื่องแบบ PointLight ( ตำแหน่ง vec4; vec4 ambient; vec4 diffuse; vec4 specular; vec3 attenuation; ) light; // พารามิเตอร์สำหรับตัวแบ่งส่วนออก Vertex ( vec2 texcoord; vec3 ปกติ vec3 lightDir; vec3 viewDir ระยะทางลอย ) Vert; โมฆะ main(โมฆะ ) ( // แปลพิกัดจุดยอดเป็นระบบพิกัดโลก vec4 จุดยอด =transform.model * vec4(ตำแหน่ง, 1 .0 ) ; // ทิศทางจากจุดยอดไปยังแหล่งกำเนิดแสงในระบบพิกัดโลก vec4 lightDir = light.position - จุดยอด; // ส่งพารามิเตอร์บางตัวไปยัง Fragment Shader // ส่งพิกัดพื้นผิว vert.texcoord = เท็กคอร์ด; // ผ่านค่าปกติในระบบพิกัดโลก vert.normal = แปลงร่างปกติ * ปกติ; // ส่งทิศทางไปยังแหล่งกำเนิดแสง Vert.lightDir = vec3(lightDir) ; // ส่งทิศทางจากจุดยอดไปยังผู้สังเกตในระบบพิกัดโลก Vert.viewDir = transform.viewPosition - vec3(จุดยอด) ; // ส่งระยะทางจากจุดยอดไปยังแหล่งกำเนิดแสง Vert.distance = ความยาว (lightDir) ; // แปลงพิกัดจุดยอดให้เป็นเนื้อเดียวกัน gl_Position = transform.viewProjection * จุดสุดยอด; )

หากไม่มีแหล่งกำเนิดแสง ภาพจะไม่สามารถมองเห็นได้ ในการเริ่มต้นแหล่งที่มาและเปิดใช้งานตัวจัดการสำหรับการคำนวณผลกระทบของแหล่งที่มาบนวัตถุ การดำเนินการคำสั่งต่อไปนี้ก็เพียงพอแล้ว: glEnable(gl_lighting);//เปิดใช้งานโหมดการวิเคราะห์แสง

GlEnable (gl_light0); // รวมแหล่งที่มา (null) เฉพาะในฉากพร้อมคุณลักษณะ

ฟังก์ชัน Disable() ใช้เพื่อปิดการใช้งานแหล่งที่มา แหล่งกำเนิดแสงเริ่มต้นอยู่ในอวกาศพร้อมพิกัด (0,0,∞) คุณสามารถสร้างแหล่งกำเนิดแสงที่จุดใดก็ได้ในพื้นที่ภาพ

รองรับแหล่งกำเนิดแสงสี่ประเภทในไลบรารี OpenGl:

  • แสงพื้นหลัง (แสงโดยรอบ),
  • แหล่งที่มาของจุด
  • ไฟค้นหา (ไฟสปอร์ตไลท์),
  • แหล่งกำเนิดแสงระยะไกล (แสงระยะไกล)
แหล่งกำเนิดแสงแต่ละชนิดมีลักษณะเฉพาะของตัวเอง
ลักษณะของแหล่งกำเนิดแสงสอดคล้องกับพารามิเตอร์ของแบบจำลองพงษ์
ในการตั้งค่าพารามิเตอร์เวกเตอร์ จะใช้ฟังก์ชัน glLightfv() ซึ่งมีรูปแบบดังนี้:

glLightfv(แหล่งที่มา พารามิเตอร์ pointer_to_array);

มีพารามิเตอร์เวกเตอร์สี่ตัวที่กำหนดตำแหน่งและทิศทางของรังสีต้นกำเนิดและองค์ประกอบสีของส่วนประกอบ - พื้นหลัง การแพร่กระจาย และสเปกตรัม
ในการตั้งค่าพารามิเตอร์สเกลาร์ใน OpenGL ให้ใช้ฟังก์ชัน glLightf() ดังนี้

glLightf(แหล่งที่มา พารามิเตอร์ ค่า);

ตัวอย่างเช่น จำเป็นต้องรวมแหล่ง GL_LIGHT0 ไว้ในฉาก ซึ่งต้องอยู่ที่จุด (1.0, 2.0, 3.0) ตำแหน่งของแหล่งที่มาจะถูกจัดเก็บไว้ในโปรแกรมโดยเป็นจุดในพิกัดที่เป็นเนื้อเดียวกัน:

GLfloat light0_pos=(1.0, 2.0, 3.0, 1.0);

หากองค์ประกอบที่สี่ของจุดนี้มีค่าเท่ากับศูนย์ แหล่งกำเนิดของจุดจะกลายเป็นองค์ประกอบที่ห่างไกล ซึ่งมีเพียงทิศทางของรังสีเท่านั้นที่มีนัยสำคัญ:

GLfloat light0_dir=(1.0, 2.0, 3.0, 0.0);

ถัดไป กำหนดองค์ประกอบสีของพื้นหลัง การแพร่กระจาย และองค์ประกอบสเปกกูลาร์ของแหล่งที่มา หากในตัวอย่างนี้ แหล่งที่มามีองค์ประกอบสเปกกูลาร์สีขาว และพื้นหลังและส่วนประกอบการแพร่กระจายต้องเป็นสีแดง ดังนั้นส่วนของโปรแกรมที่สร้างแหล่งที่มาจะมีลักษณะดังนี้:

GLfloat diffise0= (1.0, 0.0, 0.0, 1.0);

GLfloat ambient0=(1.0, 0.0, 0.0, 1.0);

GLfloat specular0=(1.0, 1.0, 1.0, 1.0);

GlEnable(GL_LIGHTING);

GlEnable (GL_LIGHT0);

GlLightfv(GL_LIGHT0, GL_POSITION, light0_pos);

GlLightfv(GL_LIGHT0, GL_AMBIENT, ambient0);

GlLightfv(GL_LIGHT0, GL_DIFFUSE, กระจาย0);

GlLightfv(GL_LIGHT0, GL_SPECULAR, specular0);

คุณยังสามารถรวมแสงพื้นหลังโดยรวมไว้ในฉากได้ ซึ่งไม่เกี่ยวข้องกับแหล่งกำเนิดแสงใดโดยเฉพาะ ตัวอย่างเช่น ถ้าคุณต้องการเน้นวัตถุทั้งหมดในฉากด้วยสีขาวแบบสลัว คุณควรใส่ข้อมูลโค้ดต่อไปนี้ในโปรแกรมของคุณ:

GLfloat global_ambient=(0.1, 0.1, 0.1, 1.0);

GlLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient);

ในแบบจำลองแสง คำที่คำนึงถึงระยะทางไปยังแหล่งกำเนิดมีรูปแบบ:

K= 1/(a+ b*d+ c*d^2)

และส่วนประกอบคงที่ เชิงเส้น และกำลังสอง ค่าสัมประสิทธิ์ที่สอดคล้องกันสำหรับแต่ละแหล่งจะถูกตั้งค่าแยกกันโดยใช้ฟังก์ชันการตั้งค่าพารามิเตอร์สเกลาร์ ตัวอย่างเช่น:

GlLightf(GL_LIGHT0, GL_CONSTANT_ATTENATION, ก);

ในการแปลงแหล่งกำเนิดแสงเป็นสปอตไลท์ คุณต้องระบุทิศทางลำแสงสปอตไลท์ (GL_SPOT_DIRECTION) ดัชนีฟังก์ชันการกระจายความเข้ม (GL_SPOT_EXPONENT) และมุมกระจายแสง (GL_SPOT_CUTTOF) พารามิเตอร์เหล่านี้ตั้งค่าโดยใช้ฟังก์ชัน glLightf() และ glLightfv()

พารามิเตอร์ที่ตั้งค่าสำหรับแหล่งกำเนิดแสงเริ่มต้นแสดงในตารางที่ 3

การตั้งค่าเริ่มต้นสำหรับไฟ

ตารางที่ 3

ชื่อพารามิเตอร์ ค่าเริ่มต้น เนื้อหา
GL_AMBIENT (0.0, 0.0, 0.0, 1.0) ความเข้มของแสง RGBA โดยรอบ
GL_DIFFUSE (1.0, 1.0, 1.0, 1.0) กระจายความเข้มของแสง RGBA
GL_SPECULAR (1.0, 1.0, 1.0, 1.0) ความเข้มของแสง RGBA แบบสเปกตรัม
GL_POSITION (0.0, 0.0, 1.0, 0.0) (x,y,z,w) ตำแหน่งของแสง
GL_SPOT_DIRECTION (0.0, 0.0, -1.0) (x, y, z) ทิศทางของสปอตไลท์
GL_SPOT_EXPONENT 0.0 เลขชี้กำลังสปอตไลท์
GL_SPOT_CUTOFF 180.0 มุมตัดสปอตไลท์
GL_CONSTANT_ATTENUATION 1.0 ปัจจัยการลดทอนคงที่
GL_LINEAR_ATTENUATION 0.0 ปัจจัยการลดทอนเชิงเส้น
GL_QUADRATIC_ATTENUATION 0.0 ปัจจัยการลดทอนกำลังสอง

การจัดแสงใน OpenGL ES เป็นคุณสมบัติที่ดีที่ทำให้เกม 3 มิติมีสัมผัสที่ดี ในการใช้ฟังก์ชันนี้ เราต้องเข้าใจโมเดลแสง OpenGL ES ก่อน

วิธีการทำงานของแสง

ลองคิดดูว่าแสงทำงานอย่างไร ก่อนอื่นเราต้องมีแหล่งกำเนิดแสงที่เปล่งแสง คุณจะต้องมีวัตถุเรืองแสงด้วย ประการสุดท้าย เราจำเป็นต้องมีเซ็นเซอร์ เช่น ดวงตาหรือกล้อง ที่รับโฟตอนที่แหล่งกำเนิดแสงส่งออกไปและสะท้อนจากวัตถุ การจัดแสงจะเปลี่ยนสีที่รับรู้ของวัตถุขึ้นอยู่กับประเภทของแหล่งกำเนิดแสง สีหรือความเข้มของแหล่งกำเนิดแสง ตำแหน่งของแหล่งกำเนิดแสงและทิศทางที่สัมพันธ์กับวัตถุที่ส่องสว่าง วัสดุและพื้นผิวของวัตถุ

ความเข้มของแสงที่สะท้อนจากวัตถุขึ้นอยู่กับหลายปัจจัย ปัจจัยที่สำคัญที่สุดที่เราให้ความสำคัญคือมุมที่ลำแสงตกกระทบพื้นผิว ยิ่งมุมนี้เข้าใกล้มุมฉากมากเท่าใด ความเข้มของแสงที่สะท้อนจากวัตถุก็จะยิ่งมากขึ้นเท่านั้น นี่คือภาพประกอบในรูปที่ 11.1.

เมื่อลำแสงกระทบพื้นผิว แสงจะสะท้อนเป็นสองทิศทาง แสงส่วนใหญ่จะสะท้อนกระจาย ซึ่งหมายความว่ารังสีของแสงที่สะท้อนจะกระจายอย่างไม่สม่ำเสมอบนพื้นผิวของวัตถุ รังสีบางส่วนจะสะท้อนออกมาในลักษณะพิเศษ ซึ่งหมายความว่าลำแสงจะสะท้อนกลับมาราวกับว่ากำลังกระทบกับกระจกที่สมบูรณ์แบบ บนมะเดื่อ รูปที่ 11.2 แสดงความแตกต่างระหว่างการสะท้อนแสงแบบกระจายและแบบสเปกกูลาร์

ข้าว. 11.1. ยิ่งมุมใกล้เป็นมุมฉากเท่าใด ความเข้มของแสงสะท้อนก็จะยิ่งมากขึ้นเท่านั้น

ข้าว. 11.2. กระจายแสงและสะท้อนแสง

แสงสะท้อนจะปรากฏเป็นไฮไลท์บนวัตถุ แสงจะสะท้อนวัตถุในลักษณะที่เป็นเงาหรือไม่นั้นขึ้นอยู่กับวัสดุที่ทำ วัตถุที่มีพื้นผิวคล้ายผิวไม่เรียบหรือขรุขระมักจะไม่มีส่วนที่เป็นประกาย วัตถุที่มีพื้นผิวเรียบเช่นแก้วหรือหินอ่อนจะแสดงสิ่งประดิษฐ์ที่มีแสงเหล่านี้ แน่นอนว่าแก้วหรือหินอ่อนนั้นไม่ได้เรียบอย่างสมบูรณ์แบบ แต่เมื่อเปรียบเทียบกับไม้หรือผิวหนังมนุษย์แล้ว

เมื่อแสงตกกระทบพื้นผิว แสงสะท้อนจะเปลี่ยนสีตามองค์ประกอบทางเคมีของวัตถุที่ส่องด้วย วัตถุที่ปรากฏเป็นสีแดงให้เราสะท้อนเฉพาะส่วนของแสงสีแดงและดูดซับความถี่อื่นทั้งหมด วัตถุสีดำคือวัตถุที่ดูดซับแสงเกือบทั้งหมดที่ตกกระทบ

OpenGL ES ช่วยให้คุณจำลองพฤติกรรมในชีวิตจริงโดยการกำหนดแหล่งกำเนิดแสงและวัสดุวัตถุ

แหล่งกำเนิดแสง

เราถูกล้อมรอบด้วยแหล่งกำเนิดแสงที่หลากหลาย ดวงอาทิตย์ส่งโฟตอนออกมาตลอดเวลา จอมอนิเตอร์เปล่งแสงรอบตัวเราด้วยแสงระยิบระยับที่น่าพึงพอใจในยามค่ำคืน หลอดไฟและไฟหน้าช่วยให้เราหลีกเลี่ยงการชนกับวัตถุต่างๆ ในความมืด OpenGL ES ให้คุณสร้างแหล่งกำเนิดแสงได้สี่ประเภท

แบ็คไลท์ ไม่ใช่แหล่งกำเนิดแสงในตัวเอง แต่เป็นผลจากการปรากฏตัวของโฟตอนจากแหล่งกำเนิดแสงอื่น เมื่อรวมกันแล้ว โฟตอนแบบสุ่มเหล่านี้จะสร้างระดับการส่องสว่างที่คงที่ซึ่งไม่มีทิศทางและส่องสว่างวัตถุทั้งหมดอย่างเท่าเทียมกัน

แหล่งกำเนิดแสงแบบจุด พวกมันมีตำแหน่งอยู่ในอวกาศและเปล่งแสงออกมาทุกทิศทาง ตัวอย่างเช่น หลอดไฟเป็นจุดกำเนิดแสง

แหล่งกำเนิดแสงทิศทาง แสดงเป็นคำแนะนำใน OpenGL ES พวกเขาถือว่าอยู่ไกลออกไปอย่างไม่มีที่สิ้นสุด ตามหลักการแล้ว ดวงอาทิตย์สามารถเป็นแหล่งดังกล่าวได้ เราสามารถสันนิษฐานได้ว่ารังสีแสงทั้งหมดที่มาจากดวงอาทิตย์กระทบโลกในมุมเดียวกันเนื่องจากระยะห่างระหว่างโลกกับดวงอาทิตย์

โอ้โคมไฟ ไฟเหล่านี้คล้ายกับไฟจุดตรงที่มีตำแหน่งคงที่ในอวกาศ นอกจากนี้พวกมันยังมีทิศทางที่พวกมันจะเปล่งแสงออกมา พวกเขาสร้างกรวยแสงที่จำกัดด้วยรัศมีที่แน่นอน ตัวอย่างของแหล่งกำเนิดแสงดังกล่าวคือโคมไฟถนน

เราจะพิจารณาเฉพาะแสงพื้นหลังรวมถึงแหล่งกำเนิดแสงแบบจุดและทิศทาง แสงไฟมักใช้งานยากบน GPU ที่จำกัดของอุปกรณ์ Android เนื่องจากวิธีคำนวณแสงใน OpenGL ES ในไม่ช้าคุณจะเข้าใจว่าทำไมจึงเป็นเช่นนั้น

นอกจากตำแหน่งและทิศทางของแหล่งกำเนิดแสงแล้ว OpenGL ES ยังให้คุณกำหนดสีหรือความเข้มของแสงได้อีกด้วย คุณลักษณะเหล่านี้แสดงออกมาโดยใช้สี RGBA อย่างไรก็ตาม OpenGL ES กำหนดให้กำหนดสีที่แตกต่างกันสี่สีสำหรับแหล่งเดียวกันแทนที่จะเป็นสีเดียว

ไฮไลท์ - ความเข้ม/สีที่ก่อให้เกิดการแรเงาของวัตถุ วัตถุจะสว่างเท่ากันจากทุกทิศทาง โดยไม่คำนึงถึงตำแหน่งหรือทิศทางของวัตถุที่สัมพันธ์กับแหล่งกำเนิดแสง

กระจาย - ความเข้ม/สีของแสงที่จะส่องสว่างวัตถุหลังจากคำนวณการสะท้อนกระจาย ขอบของวัตถุที่ไม่หันเข้าหาแหล่งกำเนิดแสงจะไม่สว่างเหมือนในชีวิตจริง

Specular - ความเข้ม/สีคล้ายกับสีพร่า อย่างไรก็ตาม จะมีผลเฉพาะกับจุดต่างๆ ของวัตถุที่มีการวางแนวที่แน่นอนซึ่งสัมพันธ์กับแหล่งกำเนิดแสงและเซ็นเซอร์

Emissive เป็นการคำนวณสีที่ซับซ้อนมากซึ่งมีการใช้งานอย่างจำกัดอย่างมากในแอปพลิเคชันฟิสิกส์ในโลกแห่งความเป็นจริง ดังนั้นเราจะไม่กล่าวถึงในที่นี้

ส่วนใหญ่แล้วเราจะใช้ความเข้มแบบกระจายและแบบสเปกตรัมของแหล่งกำเนิดแสง และเราจะระบุค่าเริ่มต้นสำหรับอีกสองค่า นอกจากนี้ ส่วนใหญ่แล้วเราจะใช้สี RGBA เดียวกันสำหรับทั้งความเข้มแบบกระจายและแบบสเปกตรัม

วัสดุ

วัตถุทั้งหมดในโลกของเราทำจากวัสดุบางอย่าง วัสดุแต่ละชนิดจะกำหนดวิธีการสะท้อนแสงที่ตกกระทบวัตถุและเปลี่ยนสีของแสงที่สะท้อน OpenGL ES ช่วยให้คุณสามารถกำหนดสี RGBA สี่สีสำหรับวัสดุเช่นเดียวกับแหล่งกำเนิดแสง

ไฮไลท์ - สีที่กลมกลืนกับสีพื้นหลังของแหล่งกำเนิดแสงใดๆ ในฉาก

กระจาย - สีที่รวมกับสีกระจายของแหล่งกำเนิดแสงใด ๆ

Specular - สีที่ผสมผสานกับสี Specular ของแหล่งกำเนิดแสงใดๆ ใช้เพื่อสร้างไฮไลท์บนพื้นผิวของวัตถุ

Emissive - เรายังคงเพิกเฉยต่อสีประเภทนี้เนื่องจากไม่ได้ใช้จริงในบริบทของเรา

รูปที่ 11.3 แสดงคุณสมบัติของวัสดุ/แสงสามประเภทแรก ได้แก่ แสงพื้นหลัง แสงกระจาย และแสงแวววาว

ข้าว. 11.3. วัสดุ/ไฟประเภทต่างๆ: แบ็คไลท์เท่านั้น (ซ้าย), กระจายแสงเท่านั้น (กลาง), แบ็คไลท์และสีกระจายพร้อมไฮไลท์แบบ specular (ขวา)

บนมะเดื่อ 11.3 แสดงผลของคุณสมบัติต่างๆ ของวัสดุและแหล่งกำเนิดแสงที่มีต่อสี แสงไฟส่องสว่างขนาดเท่ากัน แสงที่กระจัดกระจายจะสะท้อนออกมาขึ้นอยู่กับมุมที่แสงตกกระทบวัตถุ บริเวณที่หันเข้าหาแหล่งกำเนิดแสงโดยตรงจะสว่างขึ้น บริเวณที่แสงส่องไม่ถึงจะมืด ในภาพด้านขวา คุณจะเห็นการผสมผสานระหว่างแสงพื้นหลัง แสงโดยรอบ และแสงแบบสเปกตรัม แสงแวววาวปรากฏให้เห็นเป็นไฮไลท์สีขาวบนทรงกลม

OpenGL ES คำนวณแสงสว่างอย่างไร: Vertex Normals

คุณทราบดีว่าความเข้มของแสงที่สะท้อนจากวัตถุขึ้นอยู่กับมุมตกกระทบของวัตถุนั้น OpenGL ES ใช้ข้อเท็จจริงนี้ในการคำนวณแสง มันใช้บรรทัดฐานจุดยอดสำหรับสิ่งนี้ ซึ่งต้องกำหนดในโค้ดในลักษณะเดียวกับพิกัดพื้นผิวหรือสีจุดยอด บนมะเดื่อ รูปที่ 11.4 แสดงทรงกลมและจุดยอดปกติ

ข้าว. 11.4. ทรงกลมและจุดยอดปกติ

บรรทัดฐานคือเวกเตอร์หน่วยที่ระบุทิศทางที่พื้นผิวหันไป ในกรณีของเรา พื้นผิวเป็นรูปสามเหลี่ยม แทนที่จะกำหนดพื้นผิวปกติ เรากำหนดจุดยอดปกติ ความแตกต่างระหว่างเส้นปกติเหล่านี้คือจุดยอดปกติอาจไม่ชี้ไปในทิศทางเดียวกับเส้นปกติ จะเห็นได้ชัดเจนในรูปที่ 11.4 โดยที่แต่ละจุดปกติของจุดยอดคือค่าปกติเฉลี่ยของสามเหลี่ยมทั้งหมดที่มีจุดยอดอยู่ การหาค่าเฉลี่ยนี้ทำเพื่อสร้างการแรเงาที่ราบรื่นให้กับวัตถุ

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

ฟังดูน่ากลัวทีเดียว แต่จริงๆ แล้วก็ไม่ได้แย่ไปซะทั้งหมด เราจำเป็นต้องเปิดใช้งานการใช้แสงและกำหนดแหล่งกำเนิดแสง วัสดุของวัตถุที่เรนเดอร์ และค่าปกติจุดยอด (นอกเหนือจากพารามิเตอร์จุดยอดที่เรากำหนดตามปกติ เช่น ตำแหน่งหรือพิกัดพื้นผิว) มาดูกันว่าสามารถทำได้ด้วย OpenGL ES อย่างไร

ในการปฏิบัติ

ตอนนี้มาทำตามขั้นตอนที่จำเป็นทั้งหมดเพื่อทำงานกับการจัดแสงโดยใช้ OpenGL ES มาสร้างคลาสตัวช่วยเล็กๆ ที่จะทำให้การทำงานกับไฟง่ายขึ้น และใส่ไว้ในแพ็คเกจ com.badlogi ด้วย .androi dgames.framework.gl

การอนุญาตและข้อห้ามในการให้แสงสว่าง

เช่นเดียวกับสถานะ OpenGL ES อื่นๆ ต้องเปิดใช้งานฟังก์ชันที่มีชื่อก่อน สามารถทำได้ดังนี้:

หลังจากนั้น แสงจะถูกนำไปใช้กับวัตถุที่เรนเดอร์ทั้งหมด เพื่อให้ได้ผลลัพธ์ คุณต้องกำหนดแหล่งกำเนิดแสงและวัสดุ รวมถึงจุดยอดปกติ ทันทีที่เราวาดวัตถุที่จำเป็นทั้งหมดเสร็จแล้ว แสงสามารถปิดได้:

การกำหนดแหล่งกำเนิดแสง

OpenGL ES มีแหล่งกำเนิดแสง 4 ประเภท ได้แก่ แสงพื้นหลัง จุด ทิศทาง และดวงโคม มาดูวิธีระบุสามตัวแรกกัน เพื่อให้การติดตั้งมีประสิทธิภาพและดูดีแต่ละรุ่นจะต้องประกอบด้วยสามเหลี่ยมจำนวนมาก สำหรับอุปกรณ์เคลื่อนที่ในปัจจุบันนี้ไม่สามารถทำได้

OpenGL ES ช่วยให้คุณกำหนดไฟได้สูงสุด 8 ดวงพร้อมกัน รวมทั้งแหล่งกำเนิดแสงส่วนกลางหนึ่งดวง ไฟทั้ง 8 ดวงแต่ละดวงมี ID ตั้งแต่ GL10.GL LIGHT0 ถึง GL10.GL LIGHT7 หากคุณต้องการเปลี่ยนคุณสมบัติของไฟเหล่านี้ คุณสามารถทำได้โดยการกำหนด ID ที่สอดคล้องกัน

คุณสามารถเปิดใช้งานแหล่งกำเนิดแสงได้โดยใช้ไวยากรณ์ต่อไปนี้:

ถัดไป OpenGL ES จะได้รับคุณสมบัติของแหล่งกำเนิดแสงนี้และนำไปใช้กับวัตถุที่เรนเดอร์ทั้งหมด หากเราจำเป็นต้องปิดใช้งานแหล่งกำเนิดแสง เราสามารถทำได้โดยใช้ข้อความต่อไปนี้:

การไฮไลต์เป็นกรณีพิเศษเนื่องจากไม่มี ID สามารถมีไฮไลท์ได้เพียงหนึ่งรายการต่อฉาก OpenGL ES ลองพิจารณาแหล่งกำเนิดแสงนี้โดยละเอียด

แบ็คไลท์

แสงพื้นหลังเป็นแสงชนิดพิเศษ ไม่มีตำแหน่งหรือทิศทาง มีเพียงสีที่ใช้กับวัตถุที่ส่องสว่างทั้งหมดในลักษณะเดียวกัน OpenGL ES ให้คุณกำหนดไฮไลท์ส่วนกลางได้ดังนี้:

ambientCol หรืออาร์เรย์มีค่า RGBA ของสีอ่อนซึ่งแสดงเป็นตัวเลขทศนิยมตั้งแต่ 0 ถึง 1 เมธอด gl LightModel fv ใช้ค่าคงที่เป็นพารามิเตอร์ตัวแรกซึ่งระบุว่าเราต้องการตั้งค่าสีของพื้นหลัง แหล่งกำเนิดแสง อาร์เรย์ของเลขทศนิยม An ที่มีสีของแหล่งกำเนิดแสง และออฟเซ็ตสำหรับอาร์เรย์ของทศนิยม ซึ่งเมธอดจะเริ่มอ่านค่า RGBA ลองวางรหัสที่แก้ปัญหานี้ในชั้นเรียนขนาดเล็ก รหัสของมันแสดงอยู่ในรายการ 11.2

รายการ 11.2. คลาส AmbientLight.java OdenGL ES แบบง่าย ๆ เน้นสิ่งที่เป็นนามธรรมทั่วโลก

สิ่งที่เราทำคือจัดเก็บสีไฮไลท์ไว้ในอาร์เรย์ของโฟลตและจัดเตรียมสองวิธี วิธีหนึ่งใช้เพื่อตั้งค่าสี และอีกวิธีหนึ่งเพื่อบอก OpenGL ES ให้ใช้สีเฉพาะนั้น สีเริ่มต้นคือสีเทา

จุดกำเนิดแสง

ไฟส่องเฉพาะจุดมีตำแหน่งและพื้นหลัง กระจายแสง และสี/ความเข้มของแสง (เราไม่พิจารณาสี/ความเข้มที่เปล่งแสง) คุณสามารถกำหนดสีประเภทต่างๆ ได้ดังนี้

พารามิเตอร์แรกคือตัวระบุแหล่งกำเนิดแสง ในกรณีนี้ เราใช้แหล่งที่สี่ พารามิเตอร์ถัดไประบุแอตทริบิวต์ที่เราต้องการเปลี่ยนแปลง พารามิเตอร์ที่สามคืออาร์เรย์ของโฟลตที่มีค่า RGBA และพารามิเตอร์สุดท้ายคือออฟเซ็ตในอาร์เรย์นี้ การกำหนดตำแหน่งแหล่งที่มานั้นง่ายพอๆ กัน:

เรากำหนดแอตทริบิวต์ที่เราต้องการเปลี่ยนอีกครั้ง (ในกรณีนี้คือตำแหน่ง) ซึ่งเป็นอาร์เรย์ขององค์ประกอบสี่องค์ประกอบที่มีพิกัด x-, y- และ z ของแหล่งกำเนิดแสงในโลกที่ถูกสร้างขึ้น โปรดทราบว่าองค์ประกอบที่สี่ของอาร์เรย์ต้องเท่ากับหนึ่งหากแหล่งกำเนิดแสงมีตำแหน่ง ลองใส่สิ่งนี้ในคลาสตัวช่วย รหัสอยู่ในรายการ 11.3

รายการ 11.3 คลาส Point.Light.java ซึ่งเป็นนามธรรมของ OpenGL ES point light อย่างง่าย

คลาสผู้ช่วยของเราประกอบด้วยส่วนประกอบพื้นหลัง สีกระจาย และสีเฉพาะของแสง ตลอดจนตำแหน่ง (องค์ประกอบที่สี่คือหนึ่ง) นอกจากนี้ เราจัดเก็บตัวระบุสุดท้ายที่ใช้สำหรับแหล่งที่มาที่กำหนด ดังนั้นจึงเป็นไปได้ที่จะสร้างวิธีปิดการใช้งาน O ที่จะปิดไฟหากจำเป็น เรายังมีเมธอด enableO ซึ่งใช้อินสแตนซ์ของคลาส GL10 และ light ID (เช่น GL10.GL LIGHT6) เปิดใช้งานการใช้แสง ตั้งค่าแอตทริบิวต์ และจัดเก็บตัวระบุที่ใช้ วิธีการปิดการใช้งาน O เพียงแค่ปิดการใช้แสงโดยใช้สมาชิกคลาส 1ast.Ligh.tId ตั้งค่าในวิธีการเปิดใช้งาน

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

แหล่งกำเนิดแสงทิศทาง

แหล่งกำเนิดแสงแบบทิศทางเกือบจะเหมือนกันกับแหล่งกำเนิดแสงแบบจุด ข้อแตกต่างเพียงอย่างเดียวคือพวกมันมีทิศทางแทนที่จะเป็นตำแหน่ง วิธีการแสดงออกค่อนข้างสับสน แทนที่จะใช้เวกเตอร์เพื่อระบุทิศทาง OpenGL ES คาดหวังให้เรากำหนดจุดเดียว ทิศทางจะถูกกำหนดโดยใช้เวกเตอร์เชื่อมจุดนี้กับจุดกำเนิด ตัวอย่างต่อไปนี้ช่วยให้คุณสร้างทิศทางแสงที่มาจากด้านขวาของโลก:

เราสามารถแปลงเป็นเวกเตอร์ได้ดังนี้

คุณลักษณะที่เหลือ เช่น พื้นหลังหรือสีรอบข้าง จะเหมือนกันกับคุณลักษณะของไฟจุด รายการ 11.4 แสดงรหัสสำหรับคลาสผู้ช่วยขนาดเล็กที่ใช้สร้างไฟบอกทิศทาง

รายการ 11.4 คลาส Directi onLi ght.java ซึ่งเป็นนามธรรมง่ายๆ ของไฟบอกทิศทางใน OpenGL ES

คลาสตัวช่วยนี้เกือบจะเหมือนกับคลาส PointLight ข้อแตกต่างเพียงอย่างเดียวคือใน directi บนอาร์เรย์ องค์ประกอบที่สี่คือหนึ่ง นอกจากนี้ แทนที่เมธอด setPosition เมธอด setDirecti on จะปรากฏขึ้น ช่วยให้คุณระบุทิศทางได้ เช่น (-1; 0; 0) ในกรณีนี้ แสงจะมาจากด้านขวา ภายในเมธอด ส่วนประกอบเวกเตอร์ทั้งหมดจะเปลี่ยนเครื่องหมาย ดังนั้นเราจึงแปลงทิศทางเป็นรูปแบบที่ OpenGL ES คาดหวัง

เรากำหนดวัสดุ

วัสดุถูกกำหนดโดยแอตทริบิวต์หลายรายการ เช่นเดียวกับออบเจกต์ OpenGL ES อื่นๆ วัสดุคือสถานะที่จะทำงานจนกว่าเราจะเปลี่ยนแปลงอีกครั้งหรือจนกว่าบริบท OpenGL ES จะสูญหายไป ในการตั้งค่าแอตทริบิวต์ของวัสดุในปัจจุบัน เราสามารถทำได้ดังนี้:

ตามปกติ เราจำเป็นต้องกำหนดพื้นหลัง RGBA สีกระจาย และสีแบบสเปกตรัม ซึ่งสามารถทำได้ในลักษณะเดียวกับก่อนหน้านี้ โดยใช้อาร์เรย์ของตัวเลขทศนิยมซึ่งประกอบด้วยสี่องค์ประกอบ

การรวมการกระทำเหล่านี้เป็นคลาสตัวช่วยเดียวนั้นง่ายมาก คุณสามารถดูผลลัพธ์ในรายการ 11.5

รายการ 11.5 คลาส Material Java ซึ่งเป็นนามธรรมอย่างง่ายของวัสดุ OpenGL ES

ไม่มีอะไรน่าแปลกใจที่นี่เช่นกัน เราเพียงแค่เก็บสามองค์ประกอบที่อธิบายถึงเนื้อหาและจัดเตรียมฟังก์ชันเพื่อตั้งค่าและวิธีการเปิดใช้งานที่ส่งผ่านไปยัง OpenGL ES

OpenGL ES มีเคล็ดลับอีกอย่างเมื่อพูดถึงวัสดุ โดยปกติจะใช้สิ่งที่เรียกว่าสีวัสดุแทนเมธอด glMaterialfvO ซึ่งหมายความว่าแทนที่จะใช้สีพื้นหลังและสีกระจายที่กำหนดโดยวิธี glMaterial al fv OpenGL ES จะใช้สีจุดสุดยอดของโมเดลของเราเป็นสีพื้นหลังและสีกระจายของวัสดุ ในการเปิดใช้คุณลักษณะนี้ คุณเพียงแค่เรียกมันว่า:

นี่คือสิ่งที่ฉันทำเพราะพื้นหลังและสีกระจายมักจะเหมือนกัน เนื่องจากฉันไม่ได้ใช้ไฮไลท์แบบพิเศษในเกมและการสาธิตส่วนใหญ่ของฉัน ฉันจึงใช้วิธีนี้ได้ง่ายๆ และไม่เรียก glMaterial fv วิธีที่คุณใช้ขึ้นอยู่กับคุณ

การกำหนดบรรทัดฐาน

เพื่อให้แสงทำงานใน OpenGL ES คุณต้องกำหนดจุดยอดปกติสำหรับแต่ละจุดยอดของโมเดล จุดยอดปกติต้องเป็นเวกเตอร์หน่วยที่ชี้ (ปกติ) ในทิศทางที่พื้นผิวที่เป็นจุดยอดอยู่ บนมะเดื่อ รูปที่ 11.5 แสดงจุดยอดปกติสำหรับลูกบาศก์ของเรา

ข้าว. 11.5. จุดยอดปกติสำหรับแต่ละจุดยอดของลูกบาศก์ของเรา

จุดยอดปกติเป็นคุณลักษณะอื่นของจุดยอด เช่นเดียวกับตำแหน่งหรือสี เพื่อใช้ประโยชน์จากจุดยอดปกติ เราจำเป็นต้องแก้ไขคลาส Vertices3 อีกครั้ง เพื่อที่จะบอก OpenGL ES ว่าสามารถหาบรรทัดฐานสำหรับจุดยอดแต่ละจุดได้ที่ไหน เราจะใช้วิธี gl Normal PointerO เช่นเดียวกับที่เราใช้วิธี gl VertexPointer หรือ gl Col หรือ Pointer ก่อนหน้านี้ รายการ 11-6 แสดงเวอร์ชันสุดท้ายของคลาส Vertices3

รายการ 11.6. คลาส Vertices3.Java เวอร์ชันสุดท้ายที่รองรับบรรทัดฐาน

ชั้นเรียนมีสมาชิกใหม่ hasNormal.s ที่คอยติดตามว่าจุดยอดมีจุดปกติหรือไม่

ขณะนี้ตัวสร้างยังยอมรับพารามิเตอร์ hasNormals เรายังคงต้องแก้ไขการคำนวณของสมาชิก vertexSize โดยเพิ่มทศนิยมสามตัวต่อจุดยอดหากเป็นไปได้

อย่างที่คุณเห็น เมธอด setVertices และ setIndices ยังคงไม่เปลี่ยนแปลง

ในวิธีการผูกที่เราเพิ่งสาธิต เราใช้เทคนิคเดียวกันกับบัฟเฟอร์ ByteBuffer เหมือนเดิม แต่คราวนี้เราเพิ่มบรรทัดฐานโดยใช้วิธีการ gl Normal Pointer ในการคำนวณออฟเซ็ตของตัวชี้ปกติ จำเป็นต้องพิจารณาว่ามีการระบุพิกัดพื้นผิวและสีหรือไม่

อย่างที่คุณเห็น วิธีการวาดไม่ได้เปลี่ยนแปลงเช่นกัน การกระทำทั้งหมดเกิดขึ้นในวิธีการผูก O

สุดท้าย เราปรับเปลี่ยนวิธีการ unbindO เล็กน้อย เราปิดการใช้งานตัวชี้ปกติ (ถ้ามี) เพื่อล้างสถานะ OpenGL ES ตามนั้น

การใช้คลาส Verti ces3 ที่แก้ไขนั้นง่ายเหมือนเมื่อก่อน พิจารณาตัวอย่างเล็กน้อย:

เราสร้างอาร์เรย์ของทศนิยมเพื่อเก็บจุดยอดสามจุด แต่ละจุดมีตำแหน่ง (ตัวเลขสามตัวแรกในแต่ละบรรทัด) และปกติ (ตัวเลขสามตัวสุดท้ายในแต่ละบรรทัด) ในกรณีนี้ เรากำหนดรูปสามเหลี่ยมในระนาบ xy โดยที่ค่าปกติของมันชี้ไปในทิศทางของแกน z ที่เป็นบวก

สิ่งที่เราต้องทำคือสร้างอินสแตนซ์ของคลาส Vertices3 และตั้งค่าจุดยอด ค่อนข้างง่ายใช่มั้ย

การรวม การวาด และการเลิกผูกทั้งหมดจะทำในลักษณะเดียวกับในคลาสรุ่นก่อนหน้าทุกประการ เช่นเดียวกับก่อนหน้านี้ เราสามารถเพิ่มสีจุดยอดและพิกัดพื้นผิวได้

วางมันทั้งหมดเข้าด้วยกัน

เอามารวมกันให้หมด เราจำเป็นต้องวาดฉากที่มีแหล่งกำเนิดแสง จุด และทิศทางของแสงทั่วโลก พวกเขาจะส่องแสงลูกบาศก์ที่จุดกำเนิด เราต้องเรียกใช้เมธอด gl uLookAt เพื่อวางตำแหน่งกล้อง บนมะเดื่อ 11.6 แสดงลักษณะของโลกของเรา

สำหรับตัวอย่างอื่นๆ เราจะสร้างคลาสที่จะเรียกว่า LightTest ซึ่งขยายคลาส GLGame ตามปกติ มันจะส่งคืนอินสแตนซ์ของคลาส LightScreen โดยใช้วิธี getStartScreenO คลาส LightScreen สืบทอดมาจากคลาส GLScreen (รายการ 11-7)

ข้าว. 11.6. เวทีเรืองแสงแห่งแรกของเรา

รายการ 11.7 ส่วนของคลาส LightTest.java สร้างแสงด้วย OpenGL ES

เริ่มต้นด้วยการอธิบายสมาชิกสองสามคนของชั้นเรียน สมาชิกมุมเก็บข้อมูลเกี่ยวกับการหมุนปัจจุบันของลูกบาศก์รอบแกน y สมาชิก Vertices3 เก็บจุดยอดของแบบจำลองคิวบ์ ซึ่งเราจะกำหนดในไม่ช้า นอกจากนี้ เรายังมีอินสแตนซ์ของคลาส AmbientLight, PointLight และ Directional Light รวมถึงอินสแตนซ์ของคลาส Material

ถัดไปเป็นตัวสร้าง นี่คือจุดที่สร้างจุดยอดโมเดลคิวบ์และโหลดพื้นผิวกล่องด้วย เรายังเริ่มต้นแสงและวัสดุ สีส่องสว่างเป็นสีเขียวอ่อน แหล่งกำเนิดทิศทางเป็นสีแดงและอยู่ที่จุด (3; 3; 0) ของโลกของเรา แหล่งกำเนิดแสงทิศทางมีสีกระจายสีน้ำเงินแสงตกจากด้านซ้าย สำหรับวัสดุ ให้ใช้ค่าเริ่มต้น (พื้นหลังค่อนข้างขาวสำหรับส่วนประกอบกระจายแสง และสีดำสำหรับส่วนประกอบสเปกกูลาร์)

ในวิธีการดำเนินการต่อ เราตรวจสอบให้แน่ใจว่าพื้นผิวของเราถูกโหลด (ใหม่) หากบริบทหายไป

เมธอด createCube ไม่ได้เปลี่ยนแปลงจากตัวอย่างก่อนหน้านี้มากนัก อย่างไรก็ตาม คราวนี้เราเพิ่มบรรทัดฐานสำหรับจุดยอดแต่ละจุด ดังแสดงในรูป 11.5. นอกนั้นทุกอย่างยังเหมือนเดิม

ในวิธีการอัปเดต เราเพียงเพิ่มมุมการหมุนของลูกบาศก์

ที่นี่น่าสนใจกว่า สองสามบรรทัดแรกเป็นแบบสำเร็จรูปเพื่อล้างบัฟเฟอร์สีและความลึก เปิดใช้งานการทดสอบเชิงลึก และตั้งค่าขอบเขต

ต่อไป เราตั้งค่าเมทริกซ์การฉายภาพเท่ากับเมทริกซ์การฉายภาพเปอร์สเปคทีฟโดยใช้เมธอด gl uPerspective และเรายังใช้เมทริกซ์ gl uLookAt ในเมทริกซ์มุมมองแบบจำลอง เพื่อให้กล้องทำงานเหมือนกับในรูป 11.6.

จากนั้นเราอนุญาตให้ใช้แสง ณ จุดนี้ ยังไม่มีการกำหนดไฟใดๆ ดังนั้นเราจึงกำหนดไฟเหล่านี้ในอีกไม่กี่บรรทัดถัดไปด้วยวิธีการเรียกไฟและวัสดุ

ตามปกติ เรายังเปิดใช้การสร้างพื้นผิวและผูกพื้นผิวของกล่อง สุดท้าย เราเรียกเมธอด gl RotatefC) เพื่อหมุนลูกบาศก์แล้ววาดจุดยอดด้วยการเรียกอินสแตนซ์ที่จัดวางอย่างดีไปยังคลาส Vertices3

ในตอนท้ายของวิธีการ เราปิดไฟจุดและทิศทาง (โปรดจำไว้ว่าการไฮไลท์คือสถานะโดยรวม) รวมถึงการทดสอบพื้นผิวและความลึก นั่นคือการให้แสงสว่างใน OpenGL ES

ส่วนที่เหลือของชั้นเรียนว่างเปล่า เราไม่ต้องทำอะไรเลยในกรณีที่หยุดชั่วคราว บนมะเดื่อ 11.7 แสดงผลของโปรแกรม

ข้าว. 11.7. ฉากที่แสดงในรูป 11.6 เรนเดอร์ด้วย OpenGL ES

ข้อควรทราบเล็กน้อยเกี่ยวกับการจัดแสงใน OpenGL ES

แม้ว่าการใช้แสงจะช่วยเพิ่มลูกเล่นให้กับเกมของคุณได้ แต่ก็มีข้อจำกัดและข้อผิดพลาด มีบางสิ่งที่คุณควรทราบ

การใช้ไฟส่องสว่างใช้ทรัพยากรมากเกินไป โดยเฉพาะอย่างยิ่งในอุปกรณ์ที่ทำงานช้า ใช้แสงอย่างระมัดระวัง ยิ่งคุณอธิบายแสงมากเท่าใด ก็ยิ่งต้องใช้การคำนวณมากขึ้นในการแสดงฉาก

ควรกำหนดตำแหน่ง/ทิศทางของแหล่งกำเนิดแสงแบบจุด/ทิศทางหลังจากโหลดเมทริกซ์ของกล้องและก่อนที่เมทริกซ์มุมมองแบบจำลองจะถูกคูณด้วยเมทริกซ์อื่นๆ เพื่อย้ายและหมุนวัตถุ นี่เป็นสิ่งสำคัญ การไม่ปฏิบัติตามคำแนะนำเหล่านี้อาจส่งผลให้เกิดแสงประดิษฐ์ที่ไม่สามารถอธิบายได้

เมื่อใช้วิธี gl Seal ef เพื่อปรับขนาดโมเดล ค่าปกติจะถูกปรับขนาดด้วย สิ่งนี้ไม่ดีเพราะ OpenGL ES คาดหวังว่าบรรทัดฐานจะอยู่ในหน่วยที่กำหนด เมื่อต้องการแก้ไขปัญหานี้ คุณสามารถใช้คำสั่ง glEnable(GL10.GL NORMALIZE) หรือในบางกรณี gl Enable(GL10 .GL RESCALE N0RMAL) ฉันคิดว่าควรใช้คำสั่งแรกเพราะคำสั่งที่สองมีข้อ จำกัด และข้อผิดพลาด ปัญหาคือการทำให้นอร์มัลไลซ์หรือปรับขนาดนอร์มอลต้องใช้พลังการประมวลผลจำนวนมาก ทางออกที่ดีที่สุดในแง่ของประสิทธิภาพคือไม่ต้องปรับขนาดวัตถุที่ส่องสว่าง

ในบทนี้ เราจะเรียนรู้วิธีจัดแสงและแรเงาโมเดล 3 มิติของเรา นี่คือรายการของสิ่งที่เราจะเรียนรู้:

  • วิธีทำให้วัตถุสว่างขึ้นเมื่ออยู่ใกล้แหล่งกำเนิดแสง
  • วิธีทำแสงสะท้อนเมื่อเราเห็นแสงสะท้อนบนวัตถุ (แสงแบบสเปกตรัม)
  • วิธีทำให้วัตถุดูมีเงาเล็กน้อยเมื่อแสงไม่ได้ตกกระทบวัตถุโดยตรง (แสงกระจาย)
  • ฉากส่องสว่าง (แสงโดยรอบ)
  • เงา หัวข้อนี้สมควรได้รับบทเรียนแยกต่างหาก (หรือบทเรียน หากไม่ใช่แม้แต่หนังสือ)
  • กระจกเงา การสะท้อน (เช่นน้ำ)
  • ใต้ผิวดินกระจาย (เช่นเหมือนขี้ผึ้ง)
  • แอนไอโซโทรปิก วัสดุ(ทาสีโลหะ เป็นต้น)
  • การแรเงาตามกระบวนการทางกายภาพเพื่อเลียนแบบความเป็นจริงให้ดียิ่งขึ้น
  • การบังแสง (Ambient Occlusion ถ้ามีอะไรบังแสง แสงจะมืดลง)
  • การสะท้อนสี (พรมแดงจะทำให้เพดานสีขาวมีสีแดงเล็กน้อย)
  • ความโปร่งใส
  • Global Illumination (โดยหลักการแล้วทุกสิ่งที่เราระบุไว้ข้างต้นสามารถเรียกคำนี้ได้)

กล่าวอีกนัยหนึ่งแสงและเงาที่ง่ายที่สุด

ปกติ

ในบทเรียนที่แล้ว เราทำงานกับคนปกติ แต่ไม่เข้าใจมากนักว่าทำไมจึงจำเป็น

บรรทัดฐานสามเหลี่ยม

ปกติของระนาบคือเวกเตอร์หน่วยที่ตั้งฉากกับระนาบนั้น

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

นี่คือรหัสจำลองการคำนวณปกติ:

สามเหลี่ยม (v1, v2, v3)
ด้าน1=v2-v1
side2=v3-v1
สามเหลี่ยม.ปกติ = vectProduct(side1, side2).ทำให้เป็นมาตรฐาน()

จุดสุดยอดปกติ

นี่เป็นวิธีปกติที่แนะนำเพื่อความสะดวกในการคำนวณ นี่คือค่าปกติรวมจากค่าปกติของสามเหลี่ยมรอบจุดยอดที่กำหนด วิธีนี้สะดวกมาก เพราะในจุดยอด เรากำลังจัดการกับจุดยอด ไม่ใช่สามเหลี่ยม ไม่ว่าในกรณีใดใน ใน OpenGL เราแทบไม่เคยจัดการกับรูปสามเหลี่ยมเลย

ด้านบน v1, v2, v3, ....
สามเหลี่ยม tr1, tr2, tr3 // พวกมันทั้งหมดใช้จุดยอด v1
v1.ปกติ = ทำให้เป็นมาตรฐาน (tr1.ปกติ + tr2.ปกติ + tr3.ปกติ)

การใช้จุดยอดปกติใน OpenGL

การใช้บรรทัดฐานใน OpenGL นั้นง่ายมาก ค่าปกติเป็นเพียงแอตทริบิวต์ของจุดยอด เช่นเดียวกับตำแหน่ง สี หรือพิกัด UV... ดังนั้นจึงไม่มีอะไรใหม่ให้เรียนรู้... แม้แต่ฟังก์ชัน loadOBJ แบบธรรมดาของเราก็โหลดค่าปกติแล้ว

GLint บัฟเฟอร์ปกติ;
glGenBuffers(1, &บัฟเฟอร์ปกติ);

glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), &ค่าปกติ, GL_STATIC_DRAW);

// บัฟเฟอร์แอตทริบิวต์ที่สาม: Normals
glEnableVertexAttribArray(2);

glBindBuffer(GL_ARRAY_BUFFER, บัฟเฟอร์ปกติ);
glVertexAttribPointer(
2, // คุณลักษณะ
3, // ขนาด
GL_FLOAT, //พิมพ์
GL_FALSE, // ปรับเป็นมาตรฐาน?
0, // ขั้นตอน
(โมฆะ*)0 // ชดเชยบัฟเฟอร์
);

และนั่นก็เพียงพอแล้วสำหรับการเริ่มต้น:


แสงกระจาย

ความสำคัญของพื้นผิวปกติ

เมื่อลำแสงตกกระทบพื้นผิว แสงส่วนใหญ่จะสะท้อนกลับในทุกทิศทาง สิ่งนี้เรียกว่า "ส่วนประกอบกระจาย" เราจะดูส่วนประกอบที่เหลือในภายหลัง

หลังจากที่ลำแสงตกลงมา พื้นผิวจะสะท้อนแสงในรูปแบบต่างๆ ขึ้นอยู่กับมุมที่ลำแสงตกกระทบพื้นผิว หากลำแสงตกลงในแนวตั้งฉากกับพื้นผิว แสดงว่ามีความเข้มข้นในพื้นที่ขนาดเล็ก หากเป็นเส้นสัมผัส ลำแสงจะกระจายไปทั่วพื้นผิวที่ใหญ่กว่ามาก:


จากมุมมองของคอมพิวเตอร์กราฟิก สีของพิกเซลจะขึ้นอยู่กับความแตกต่างของมุมของทิศทางแสงและพื้นผิวปกติ


//
//
โฟลต cosTheta = dot(n,l);

ในโค้ดนี้ "n" คือค่าปกติ และ "l" คือเวกเตอร์หน่วยที่เปลี่ยนจากพื้นผิวไปยังแสง (ไม่ใช่ในทางกลับกัน แม้ว่าสิ่งนี้อาจดูสับสน)

ป้ายระวังด้วยนะครับ

บางครั้งสูตรของเราจะไม่ทำงาน ตัวอย่างเช่น เมื่อแสงอยู่ด้านหลังสามเหลี่ยม n และ l จะตรงกันข้าม ดังนั้น n.l จะเป็นลบ และในท้ายที่สุด เราจะมีสีเชิงลบบางอย่าง และเป็นผลให้เป็นเรื่องไร้สาระบางอย่าง ดังนั้นเราจะแปลงจำนวนลบทั้งหมดเป็น 0 โดยใช้ฟังก์ชันแคลมป์

// โคไซน์ของมุมระหว่างทิศทางปกติและทิศทางแสง
// 1 - ถ้าแสงตั้งฉากกับสามเหลี่ยม
// 0 - ถ้าแสงขนานกับสามเหลี่ยม
// 0 - ถ้าแสงอยู่หลังสามเหลี่ยม
float cosTheta = ที่หนีบ(จุด(n, l), 0.1);
color=LightColor*cosTheta;

สีวัสดุ

แน่นอนว่าสีของวัตถุควรขึ้นอยู่กับสีของวัสดุเป็นสำคัญ แสงสีขาวมีองค์ประกอบสามส่วนคือ สีแดง สีน้ำเงิน และสีเขียว เมื่อแสงตกกระทบพื้นผิวสีแดง ส่วนประกอบสีเขียวและสีน้ำเงินจะถูกดูดซับและสีแดงจะสะท้อนออกมา



เราสามารถจำลองสิ่งนี้ได้ด้วยการคูณอย่างง่าย:

color = MaterialDiffuseColor * LightColor * cosTheta;

การสร้างแบบจำลองแสง

สมมติว่าเรามีแหล่งกำเนิดแสงแบบจุดที่เปล่งแสงไปทุกทิศทาง เช่น เทียนไข

ด้วยแหล่งกำเนิดแสงระดับความสว่างของพื้นผิวจะขึ้นอยู่กับระยะห่างจากแหล่งกำเนิดแสง: ยิ่งไกลออกไปก็ยิ่งมืดลง การพึ่งพานี้คำนวณดังนี้:

color = MaterialDiffuseColor * LightColor * cosTheta / (ระยะทาง*ระยะทาง);

ในไม่ช้าเราจะต้องใช้พารามิเตอร์อีกหนึ่งตัวเพื่อควบคุมระดับความเข้มของแสง - สีของแสง แต่ตอนนี้สมมติว่าเรามีหลอดไฟสีขาวที่มีกำลังไฟ (เช่น 60 วัตต์)

color = MaterialDiffuseColor * LightColor * LightPower * cosTheta / (ระยะทาง*ระยะทาง);

วางมันทั้งหมดเข้าด้วยกัน

เพื่อให้รหัสนี้ใช้งานได้ เราจำเป็นต้องมีชุดพารามิเตอร์ (สีและกำลังไฟ) และรหัสเพิ่มเติมบางอย่าง

MaterialDiffuseColor - เราสามารถนำมาจากพื้นผิวได้โดยตรง

จะต้องตั้งค่า LightColor และ LightPower ใน Shader โดยใช้เครื่องแบบ GLSL

CosTheta จะขึ้นอยู่กับเวกเตอร์ n และ l สามารถคำนวณได้สำหรับช่องว่างใด ๆ มุมจะเหมือนกัน เราจะใช้พื้นที่ของกล้องเนื่องจากการคำนวณตำแหน่งของแหล่งกำเนิดแสงเป็นเรื่องง่ายมากที่นี่:

// แฟรกเมนต์ปกติในพื้นที่กล้อง
vec3 n = ทำให้เป็นมาตรฐาน (Normal_cameraspace);
// ทิศทางของแสง (จากชิ้นส่วนไปยังแหล่งกำเนิดแสง
vec3 l = ทำให้เป็นมาตรฐาน (LightDirection_cameraspace);

_cameraspace ปกติและ LightDirection _cameraspace คำนวณใน Vertex Shader และส่งผ่านไปยัง Fragment Shader เพื่อการประมวลผลเพิ่มเติม:

// ตำแหน่งของจุดสุดยอดในพื้นที่กล้อง: ตำแหน่ง MVP *
gl_Position= MVP * vec4(vertexPosition_modelspace,1);
// ตำแหน่งของจุดยอดในปริภูมิโลก: * ตำแหน่ง
Position_worldspace = (M * vec4(vertexPosition_modelspace,1)).xyz;
// เวกเตอร์ที่มาจากด้านบนกล้องในพื้นที่กล้อง
// ในพื้นที่กล้อง กล้องอยู่ที่ตำแหน่ง (0,0,0)
ผัก 3 จุดสุดยอดตำแหน่ง _ พื้นที่กล้อง = ( วี * * ผัก 4( จุดสุดยอดตำแหน่ง _ โมเดลสเปซ ,1)). xyz ;
EyeDirection_cameraspace = vec3(0,0,0) - vertexPosition_cameraspace;
// เวกเตอร์ที่ลากจากจุดยอดไปยังแหล่งกำเนิดแสงในพื้นที่กล้อง
//เมทริกซ์ ละไว้เนื่องจากเป็นเอกพจน์ในช่องว่างนี้
vec3 LightPosition_cameraspace = (V * vec4(LightPosition_worldspace,1)).xyz;
LightDirection_cameraspace = LightPosition_cameraspace +
EyeDirection_cameraspace;
// ปกติ ยอดเขา วี ช่องว่าง กล้อง
Normal_cameraspace = (V * M * vec4(vertexNormal_modelspace,0)).xyz; // จะ งาน เท่านั้น วี ปริมาณ กรณี , เมื่อไร เมทริกซ์ โมเดล ไม่ การเปลี่ยนแปลง ของเธอ ขนาด .

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

ท้าให้ลอง!!!

M และ V เป็นเมทริกซ์ Model และ View ที่ส่งผ่านไปยัง shader เช่นเดียวกับ MVP เก่าของเรา

เวลาทดสอบ

ฉันได้บอกคุณทุกสิ่งที่คุณจำเป็นต้องรู้เพื่อสร้างแสงแบบกระจายแสง เอาเลยลองดู

ผลลัพธ์

ด้วยส่วนประกอบกระจายแสงเพียงชิ้นเดียว เราจึงได้ภาพดังกล่าวที่นี่ (ยกโทษให้ฉันสำหรับพื้นผิวที่น่าเกลียด)



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

แสงโดยรอบ

แสงโดยรอบคือการโกงอย่างแท้จริง

ด้านหลังศีรษะของ Suzanne ไม่ควรเป็นสีดำสนิท เพราะในชีวิตจริง แสงจากหลอดไฟควรตกกระทบผนัง พื้น เพดาน สะท้อนบางส่วน และส่องส่วนที่เป็นเงาของวัตถุ

อย่างไรก็ตาม การดำเนินการนี้มีราคาแพงเกินกว่าจะทำแบบเรียลไทม์ และนั่นคือเหตุผลที่เราจะเพิ่มองค์ประกอบคงที่ ราวกับว่าตัววัตถุกำลังเปล่งแสงออกมาเพื่อไม่ให้เป็นสีดำสนิท

vec3 MaterialAmbientColor = vec3(0.1,0.1,0.1) * MaterialDiffuseColor;
สี=
// สภาพแวดล้อม แสงสว่าง : จำลอง ทางอ้อม แสงสว่าง
วัสดุ AmbientColor +
// กระจาย : " สี " วัตถุนั่นเอง
วัสดุDiffuseColor * LightColor * LightPower * cosTheta /
(ระยะทาง*ระยะทาง);

ผลลัพธ์

นี่คือที่ที่มันดีขึ้นเล็กน้อย คุณสามารถเล่นกับค่าสัมประสิทธิ์ (0.1, 0.1, 0.1) เพื่อลองและรับผลลัพธ์ที่ดีที่สุด



แสงสะท้อน

ส่วนของแสงที่สะท้อนจะสะท้อนไปทางลำแสงที่สะท้อนไปยังพื้นผิวเป็นหลัก



ดังที่เราเห็นในภาพ แสงสะท้อนจะก่อตัวเป็นจุดแสง ในบางกรณี เมื่อองค์ประกอบกระจายแสงเป็นศูนย์ จุดแสงนี้จะแคบมาก (แสงทั้งหมดจะสะท้อนไปในทิศทางเดียวกันหมด) และเราได้กระจกเงา

(อย่างไรก็ตาม แม้ว่าคุณจะปรับพารามิเตอร์เพื่อให้ได้กระจกเงาได้ แต่ในกรณีของเรา มันจะคำนึงถึงการสะท้อนของแหล่งกำเนิดแสงของเราเท่านั้น ดังนั้นมันจะกลายเป็นกระจกแปลก ๆ )


// จ้องมองเวกเตอร์ (ไปทางกล้อง)
vec3 E = ทำให้เป็นปกติ (EyeDirection_cameraspace);
// ทิศทางที่สามเหลี่ยมสะท้อนแสง
ผัก 3 = สะท้อน (- , );
// โคไซน์ของมุมระหว่างเวกเตอร์มุมมองและเวกเตอร์การสะท้อนที่ตัดเป็น เป็นศูนย์หากจำเป็น
// - มองตรงไปที่เงาสะท้อน -> 1
// - เรามองไปที่อื่นในทิศทางอื่น -\u003e< 1
float cosAlpha = แคลมป์(จุด(E,R), 0.1);
สี=
// แสงโดยรอบ: จำลองแสงทางอ้อม
วัสดุ AmbientColor +
// กระจาย : " สี " วัตถุนั่นเอง
วัสดุDiffuseColor * LightColor * LightPower * cosTheta /
(ระยะทาง*ระยะทาง) ;
// สะท้อน: การสะท้อนที่สะท้อนเหมือนกระจก
MaterialSpecularColor * LightColor * LightPower * pow(cosAlpha,5) /

ในบทเรียนถัดไป เราจะวิเคราะห์วิธีเพิ่มความเร็วในการเรนเดอร์ VBO ของเรา