ฉันจะได้รับเอฟเฟกต์แสง 2D ที่ราบรื่นได้อย่างไร


18

ฉันกำลังสร้างเกมตัวต่อแบบสองมิติใน XNA

ปัจจุบันฟ้าผ่าของฉันดูเหมือนว่านี้

ฉันจะทำให้มันเป็นแบบนี้ได้อย่างไร

แทนที่จะบล็อกแต่ละบล็อกมีสีของตัวเอง แต่ก็มีการซ้อนทับที่ราบรื่น

ฉันสมมติว่ามี shader อยู่บ้างและส่งค่าแสงสำหรับกระเบื้องโดยรอบไปยัง shader แต่ฉันเป็นมือใหม่ที่มีร่มเงาดังนั้นฉันไม่แน่ใจ

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

นี่คือตัวอย่างเกี่ยวกับวิธีที่ฉันคำนวณแสงในปัจจุบัน (ฉันทำสิ่งนี้จากซ้ายขวาและล่างเช่นกัน แต่ฉันเหนื่อยมากที่ทำเฟรมนี้เป็นเฟรม ... )

ป้อนคำอธิบายรูปภาพที่นี่

ที่จริงแล้วการได้รับและวาดแสงจนถึงตอนนี้ก็ไม่มีปัญหา !

ฉันได้เห็นบทเรียนมากมายเกี่ยวกับหมอกแห่งสงครามและการใช้การซ้อนทับแบบวงกลมลาดเพื่อสร้างสายฟ้าที่ราบรื่น แต่ฉันมีวิธีที่ดีในการกำหนดค่าสายฟ้าผ่าให้กับกระเบื้องแต่ละอันแล้ว

ดังนั้นเพื่อทบทวน

  • คำนวณแสงสว่าง (เสร็จสิ้น)
  • วาดไทล์ (เสร็จแล้วฉันรู้ว่าฉันจะต้องแก้ไขมันสำหรับ shader)
  • Shade tiles (วิธีการผ่านค่าและใช้ "การไล่ระดับสี)

คำตอบ:


6

แล้วเรื่องแบบนี้ล่ะ?

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

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

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

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

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

UPDATE

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

ตามที่ฉันแนะนำคุณสามารถบรรลุผลเช่นเดียวกันโดยใช้ BlendState ที่กำหนดเอง โดยเฉพาะ BlendState ที่กำหนดเองนี้:

BlendState Multiply = new BlendState()
{
    AlphaSourceBlend = Blend.DestinationAlpha,
    AlphaDestinationBlend = Blend.Zero,
    AlphaBlendFunction = BlendFunction.Add,
    ColorSourceBlend = Blend.DestinationColor,
    ColorDestinationBlend = Blend.Zero,
    ColorBlendFunction = BlendFunction.Add
}; 

สมการผสมคือ

result = (source * sourceBlendFactor) blendFunction (dest * destBlendFactor)

ดังนั้นด้วย BlendState ที่กำหนดเองของเรานั่นจะกลายเป็น

result = (lightmapColor * destinationColor) + (0)

ซึ่งหมายความว่าสีแหล่งที่มาของสีขาวบริสุทธิ์ (1, 1, 1, 1) จะรักษาสีปลายทางสีแหล่งที่มาของสีดำบริสุทธิ์ (0, 0, 0, 1) จะทำให้สีปลายทางมืดลงเป็นสีดำบริสุทธิ์และอื่น ๆ สีเทาในระหว่างนั้นจะทำให้สีปลายทางมืดลงด้วยปริมาณที่ปานกลาง

หากต้องการนำสิ่งนี้ไปใช้ในทางปฏิบัติอันดับแรกให้ทำสิ่งที่คุณต้องทำเพื่อสร้าง Lightmap ของคุณ:

var lightmap = GetLightmapRenderTarget();

จากนั้นเพียงแค่วาดฉากที่ไม่ส่องแสงของคุณโดยตรงไปที่ backbuffer ตามปกติ:

spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
/* draw the world here */
spriteBatch.End();

จากนั้นวาด Lightmap โดยใช้ BlendState ที่กำหนดเอง:

var offsetX = 0; // you'll need to set these values to whatever offset is necessary
var offsetY = 0; // to align the lightmap with the map tiles currently being drawn
var width = lightmapWidthInTiles * tileWidth;
var height = lightmapHeightInTiles * tileHeight;

spriteBatch.Begin(SpriteSortMode.Immediate, Multiply);
spriteBatch.Draw(lightmap, new Rectangle(offsetX, offsetY, width, height), Color.White);
spriteBatch.End();

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


ดูเหมือนง่ายมากฉันจะเห็นว่าฉันจะทำอะไรในภายหลัง ฉันลองใช้ simlar ก่อนหน้านี้ (ฉันใช้ lightmap เหนือทุกสิ่งและใช้เบลอเบลอ แต่ฉันไม่สามารถทำให้ renderTarget "โปร่งใส" มีการซ้อนทับสีม่วงได้ แต่ฉันต้องการให้มันผ่านเลเยอร์ของกระเบื้อง actuall)
Cyral

หลังจากตั้งค่าเป้าหมายการเรนเดอร์ของคุณบนอุปกรณ์ให้โทรหาอุปกรณ์ล้าง (Color.Transparent);
Cole Campbell

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

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

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

10

โอเคที่นี่เป็นวิธีง่าย ๆ วิธีหนึ่งในการสร้างฟ้าผ่าแบบ 2D ที่เรียบง่ายและราบรื่นแสดงผลในสามรอบ:

  • ผ่านที่ 1: โลกของเกมที่ไม่มีสายฟ้า
  • ผ่าน 2: สายฟ้าที่ไม่มีโลก
  • การรวมกันของ 1 และ 2 ผ่านซึ่งจะปรากฏบนหน้าจอ

ในตอนที่ 1 คุณวาดสไปรต์และภูมิประเทศทั้งหมด

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

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


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

2
นั่นเป็นเรื่องยุ่งยากมากขึ้น คุณจะต้องใช้ ray-cast เพื่อให้ได้เงา Teraria ใช้บล็อกก้อนใหญ่สำหรับภูมิประเทศของพวกเขาดังนั้นจึงไม่มีปัญหา คุณสามารถใช้การหล่อเรย์ที่ไม่แม่นยำ แต่นั่นจะดูไม่ดีไปกว่า Terraria และอาจมีขนาดเล็กลงถ้าคุณยังไม่บล็อกกราฟิก สำหรับเทคนิคขั้นสูงและดูดีมีบทความนี้: gamedev.net/page/resources/_/technical/ …
API-Beast

2

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

ดูเหมือนว่าฉันจะเป็นพื้นผิวที่คุณ "ลบ" บิตของการซ้อนทับสีดำ (โดยการตั้งค่าอัลฟาของพิกเซลเหล่านั้นเป็น 0) เมื่อผู้เล่นเลื่อนไปข้างหน้า

ฉันจะบอกว่าคนนั้นต้องใช้แปรงชนิด "ลบ" ที่ผู้เล่นสำรวจ


1
มันไม่ใช่หมอกแห่งสงครามมันคำนวณสายฟ้าในแต่ละเฟรม เกมที่ฉันชี้ให้เห็นนั้นคล้ายกับ Terraria
Cyral

สิ่งที่ฉันหมายถึงคือมันอาจถูกนำไปใช้คล้ายกับหมอกแห่งสงคราม
bobobobo

2

จุดยอดแสง (มุมระหว่างแผ่นกระเบื้อง) แทนกระเบื้อง ผสมผสานแสงให้ทั่วแต่ละกระเบื้องโดยยึดตามจุดยอดทั้งสี่

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

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

texture t_Sprite
vec2 in_UV
vec4 in_Light // coudl be one float; assuming you might want colored lights though

fragment shader() {
  output = sample2d(t_Sprite, in_UV) * in_Light;
}

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

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


1
line-of sight tests to each vertex? นั่นจะมีราคาแพง
ashes999

คุณสามารถเพิ่มประสิทธิภาพด้วยความฉลาดบางอย่าง สำหรับเกม 2D คุณสามารถทำการทดสอบจำนวนรังสีกับตารางที่เข้มงวดได้อย่างน่าประหลาดใจ แทนที่จะเป็นการทดสอบ LOS แบบตรงคุณสามารถ "ไหล" (ฉันไม่สามารถคิดได้ถึงคำศัพท์ที่เหมาะสมในตอนนี้; คืนเบียร์) ค่าแสงเหนือจุดยอดแทนที่จะทำการทดสอบด้วยเรย์
Sean Middleditch

เมื่อใช้การทดสอบสายตาเพื่อเพิ่มความซับซ้อนมากขึ้นฉันก็สามารถเข้าใจได้แล้ว ตอนนี้เมื่อฉันส่ง 4 vertensions ไปยัง shader ฉันจะทำอย่างไร ฉันรู้ว่าคุณไม่รู้สึกสะดวกสบายที่จะให้ตัวอย่างโค้ดจริง แต่ถ้าฉันได้รับข้อมูลเพิ่มเติมอีกนิดหน่อยก็คงจะดี ฉันยังแก้ไขคำถามด้วยข้อมูลเพิ่มเติม
Cyral
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.