ฉันจะทำซ้ำข้อ จำกัด สีของ NES ด้วยส่วนขยาย HLSL พิกเซลได้อย่างไร


13

ดังนั้นเนื่องจากโหมดสี 256 ค่าเสื่อมราคาและไม่รองรับภายใต้โหมด Direct3D อีกต่อไปฉันจึงมีแนวคิดที่จะใช้พิกเซล shader แทนเพื่อจำลองจานสี NES ของสีที่เป็นไปได้ทั้งหมดเพื่อให้วัตถุที่ซีดจางและสิ่งที่ไม่จางหายไปอย่างราบรื่น . (ฉันรู้ว่าวัตถุไม่สามารถจางหายไปจาก NES ได้จริง ๆ แต่ฉันมีวัตถุทั้งหมดที่เลือนเข้าและออกบนพื้นหลังสีดำทึบซึ่งเป็นไปได้ด้วย palette swapping นอกจากนี้หน้าจอจะจางหายไปเมื่อคุณหยุด ซึ่งฉันรู้ว่ายังเป็นไปได้ด้วยการเปลี่ยนพาเล็ตในเกม Mega Man สองสามเกม) ปัญหาคือฉันรู้อะไรเกี่ยวกับ HLSL shaders

ฉันต้องทำอย่างไร?


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

คุณต้องการลดจานสีหรือยังต้องการทำสีด้วย (เช่นกันโดยใช้เฉดสีด้วย) มิฉะนั้นคำตอบ zezba9000 น่าจะเป็นคำตอบที่ดีที่สุดสำหรับฉัน
tigrou

ฉันต้องการลดสีที่เป็นไปได้ให้กับจาน NES ฉันต้องการมันเป็นหลักในการจับเอฟเฟกต์ของช่องอัลฟาและเอฟเฟกต์หมึกอื่น ๆ เพราะในโหมด 256 สีมันจะจับได้ แต่ Direct3D ไม่รองรับโหมดสี 256 อีกต่อไปดังนั้นเอฟเฟกต์ต่างๆ
Michael Allen Crain

คำตอบ:


4

ในตัวแบ่งพิกเซลคุณสามารถส่งผ่าน Texture2D ขนาด 256x256 สีพาเลททั้งหมดเรียงกันเป็นแนวนอนเป็นแถว จากนั้นพื้นผิว NES ของคุณจะถูกแปลงเป็น direct3D Texture2Ds ที่มีการแปลงพิกเซลเป็นค่าดัชนี 0-255 มีรูปแบบพื้นผิวที่ใช้ค่าสีแดงใน D3D9 เท่านั้น ดังนั้นพื้นผิวจะใช้เพียง 8 บิตต่อพิกเซล แต่ข้อมูลที่เข้ามาใน shader จะมาจาก 0-1

// Pixel Shader อาจมีลักษณะเช่นนี้:

float4 mainPS() : COLOR0
{
    float4 colorIndex = tex2D(MainTexture, uv);
    float4 palletColor = tex2D(PalletTexture, float2(colorIndex.x, 0);
    return palletColor;
}

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

float4 mainPS() : COLOR0
{
    float4 colorIndex = tex2D(MainTexture, uv);
    float4 palletColor = tex2D(PalletTexture, float2(colorIndex.x, colorIndex.a);
    return palletColor;
}

วิธีที่สามคือการลอกเลียนแบบคุณภาพจาง ๆ ต่ำ ๆ ของ NES โดย toon การแรเงาสีอัลฟา:

float4 mainPS() : COLOR0
{
    float4 colorIndex = tex2D(MainTexture, uv);
    float4 palletColor = tex2D(PalletTexture, float2(colorIndex.x, 0);
    palletColor.a = floor(palletColor.a * fadeQuality) / fadeQuality;
    //NOTE: If fadeQuality where to equal say '3' there would be only 3 levels of fade.
    return palletColor;
}

1
คุณหมายถึง 256x1 ไม่ใช่ 256x256 ฉันเดาใช่ไหม นอกจากนี้อย่าลืมปิดการใช้งานการกรอง bilinear บนพื้นผิวทั้งสองมิฉะนั้นคุณจะได้รับการผสมระหว่างพาเล็ต "รายการ"
นาธานรีด

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

@Nathan Reed คุณสามารถทำได้เพื่อให้แสงสว่าง คุณเพียงแค่คำนวณแสงบน "palletColor" ก่อนที่คุณจะคืนค่าสี นอกจากนี้คุณสามารถสร้างพื้นผิว 256x1 ได้ แต่ฮาร์ดแวร์อาจอยู่ภายใต้ประทุนใช้ 256x256 อยู่ดีและ 256x256 เป็นขนาดที่เร็วที่สุดที่จะใช้กับฮาร์ดแวร์ส่วนใหญ่ เว้นแต่ว่าจะเปลี่ยน IDK
zezba9000

หากคุณให้แสงหลังจากปรับเปลี่ยนสีแล้วมันอาจจะไม่เป็นหนึ่งในสี NES อีกต่อไป ถ้านั่นคือสิ่งที่คุณต้องการมันไม่เป็นไร - แต่ก็ไม่เหมือนคำถามที่ถาม สำหรับ 256 สิ่งนั้นเป็นไปได้ถ้าคุณมี GPU อายุมากกว่า 10 ปี ... แต่สิ่งที่ใหม่กว่านั้นจะรองรับพื้นผิวรูปทรงพลังของสี่เหลี่ยม 2 เช่น 256x1
Nathan Reed

ถ้าเขาไม่ต้องการให้สีจางลงอย่างราบรื่นเขาสามารถทำได้: "palletColor.a = (float) floor (palletColor.a * fadeQuality) / fadeQuality;" เขาสามารถทำสิ่งเดียวกันกับวิธี 3D texture แต่ด้วย texture 2D โดยเปลี่ยน line 4 เป็น: "float4 palletColor = tex2D (PalletTexture, float2 (colorIndex.x, colorIndex.a);" ช่องอัลฟาเพียงแค่ทำดัชนีพาเลทอื่น เลเยอร์บนพื้นผิว 2D เดียว
zezba9000

3

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

return tex3d (paletteMap, color.rgb);

อาจไม่จำเป็นต้องไปถึง 256x256x256 - เช่น 64x64x64 อาจเพียงพอ - และคุณสามารถเปลี่ยนการแมปพาเล็ตได้ทันทีโดยใช้วิธีนี้ (แต่มีค่าใช้จ่ายมากเนื่องจากมีการอัปเดตพื้นผิวแบบไดนามิกขนาดใหญ่)


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

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

1
ขึ้นอยู่กับอายุที่คุณต้องการไป ฉันเชื่อว่าการรองรับพื้นผิว 3 มิติกลับไปเป็น GeForce 1 ดั้งเดิม - อายุ 14 ปีเพียงพอหรือไม่
Maximus Minimus

ฮ่า ๆ งั้นพวกเขาอาจจะทำไม่เคยใช้บัตรเหล่านั้นเดาว่าฉันกำลังคิดที่จะสอดคล้องกับ Phone GPU มากขึ้น วันนี้มีเป้าหมายมากมายที่ไม่รองรับพื้นผิว 3 มิติ แต่เนื่องจากเขาใช้ D3D และไม่คาดเดา OpenGL แม้แต่ WP8 ก็สนับสนุนสิ่งนี้ ยังคงเป็นพื้นผิว 3 มิติจะใช้แบนด์วิดธ์มากขึ้นจากนั้นเป็น 2D
zezba9000

1

(โซลูชันทั้งสองของฉันใช้งานได้เฉพาะในกรณีที่คุณไม่สนใจเกี่ยวกับการเปลี่ยน palletes ในทันทีโดยใช้เฉดสี)

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

สี 8bit อยู่ใน formar RRRGGGBB ซึ่งให้สีแดงและเขียว 8 เฉดกับสีน้ำเงิน 4 เฉด

วิธีนี้จะใช้ได้กับพื้นผิวรูปแบบสี RGB (A) ใด ๆ

float4 mainPS() : COLOR0
{
    const float oneOver7 = 1.0 / 8.0;
    const float oneOver3 = 1.0 / 3.0;

    float4 color = tex2D(YourTexture, uvCoord);
    float R = floor(color.r * 7.99) * oneOver7;
    float G = floor(color.g * 7.99) * oneOver7;
    float B = floor(color.b * 3.99) * oneOver3;

    return float4(R, G, B, 1);
}

หมายเหตุ: ฉันเขียนมาจากหัวของฉัน แต่ฉันแน่ใจว่ามันจะรวบรวมและทำงานให้คุณ


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

tex[index] = (R & 8) << 5 + ((G & 8) << 2) + (B & 4);

เป็นตัวอย่างที่ดี มีเพียงฉันเท่านั้นที่คิดว่าเขาต้องสามารถเปลี่ยนพาเลทสีได้ ในกรณีนั้นเขาจะต้องใช้บางอย่างเช่นตัวอย่างของฉัน
zezba9000

นี่ไม่ใช่จานสีของ NES เลย NES ไม่ได้ใช้ RGB 8 บิตมันใช้จานสีคงที่ประมาณ 50 ถึง 60 สีในพื้นที่ YPbPr
sam hocevar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.