এক্সএনএ-তে 2D স্প্রিটকে মাস্ক করার সর্বোত্তম উপায়?


24

আমি বর্তমানে কিছু স্প্রিটকে মুখোশ দেওয়ার চেষ্টা করছি। এটি কথায় ব্যাখ্যা করার পরিবর্তে, আমি কয়েকটি উদাহরণ ছবি তৈরি করেছি:

অঞ্চলটি মাস্ক করুন মুখোশের অঞ্চল (সাদা রঙের)

মুখোশের চিত্রটি সহ মুখোশ করার ক্ষেত্র এখন, লাল স্প্রিট যা ক্রপ করা দরকার।

চূড়ান্ত ফলাফল চূড়ান্ত ফলাফল।

এখন, আমি জানি যে এক্সএনএ-তে আপনি এটি সম্পাদন করতে দুটি জিনিস করতে পারেন:

  1. স্টেনসিল বাফার ব্যবহার করুন।
  2. একটি পিক্সেল শেডার ব্যবহার করুন।

আমি একটি পিক্সেল শেডার করার চেষ্টা করেছি, যা মূলত এটি করেছে:

float4 main(float2 texCoord : TEXCOORD0) : COLOR0
{
    float4 tex = tex2D(BaseTexture, texCoord);
    float4 bitMask = tex2D(MaskTexture, texCoord);

    if (bitMask.a > 0)
    { 
        return float4(tex.r, tex.g, tex.b, tex.a);
    }
    else
    {
        return float4(0, 0, 0, 0);
    }
}

এটি চিত্রগুলি ক্রপ করে মনে হচ্ছে (তবে চিত্রটি একবারে চলতে শুরু করার পরে এটি সঠিক নয়) তবে আমার সমস্যাটি হ'ল চিত্রগুলি অবিচ্ছিন্নভাবে চলতে থাকে (সেগুলি অচল নয়), সুতরাং এই ক্রপিংটি গতিশীল হওয়া দরকার।

এর অবস্থানটি বিবেচনায় নেওয়ার জন্য আমি কীভাবে শেডার কোড পরিবর্তন করতে পারি?


বিকল্পভাবে, আমি স্টেনসিল বাফার ব্যবহার সম্পর্কে পড়েছি, তবে বেশিরভাগ নমুনাগুলি রেন্ডারেটেজ ব্যবহার করার উপর জড়িত বলে মনে হয়, যা আমি সত্যিই করতে চাই না। (আমি ইতিমধ্যে বাকি খেলাগুলির জন্য 3 বা 4 ব্যবহার করছি এবং এর উপরে আরও একটি যুক্ত করা অতিরিক্ত চাপ বলে মনে হচ্ছে)

আমি যে একমাত্র টিউটোরিয়ালটি পেয়েছি যে রেন্ডারটারেটস ব্যবহার করে না তা এখানে শন হারগ্রিভসের ব্লগ এটির সাথে সমস্যাটি যদিও এটি XNA 3.1 এর জন্য এবং এটি XNA 4.0 তে ভাল অনুবাদ করে বলে মনে হচ্ছে না।

আমার কাছে মনে হচ্ছে পিক্সেল শেডারটি যাওয়ার উপায়, তবে আমি কীভাবে অবস্থানটি সঠিকভাবে পেতে পারি সে সম্পর্কে আমি অনিশ্চিত। আমি বিশ্বাস করি শেডার স্থানাঙ্কের জন্য আমাকে আমার অনস্ক্রিন স্থানাঙ্কগুলি (500, 500 এর মতো কিছু) হতে হবে। আমার একমাত্র সমস্যাটি রূপান্তরকৃত স্থানাঙ্কগুলি কীভাবে সঠিকভাবে ব্যবহার করতে হবে তা নিয়ে কাজ করার চেষ্টা করছে।

কোনো সাহায্যের জন্য আগাম ধন্যবাদ!


স্টেনসিলকে যাওয়ার উপায় বলে মনে হচ্ছে, যদিও এগুলি কীভাবে সঠিকভাবে ব্যবহার করতে হবে সে সম্পর্কে আমারও জানতে হবে: পি আমি এই বিষয়ে একটি ভাল উত্তরের জন্য অপেক্ষা করব।
গুস্তাভো ম্যাকিয়েল

হি, বেশিরভাগ ভাল স্টেনসিল টিউটোরিয়ালগুলি এক্সএনএ ৩.১ এর জন্য এবং 4.0.০ টির বেশিরভাগই রেন্ডার টার্গেট ব্যবহার করে (যা আমার কাছে অপব্যয় বলে মনে হয়)। আমি আমার প্রশ্নটি পুরানো ৩.১ টি টিউটোরিয়ালের লিঙ্কের সাথে আপডেট করেছি যা দেখে মনে হয়েছিল এটি কাজ করতে পারে তবে 4.0-এ নয়। হয়তো কেউ জানেন যে কীভাবে এটি 4.0 সালে সঠিকভাবে অনুবাদ করতে হয়?
ইলেক্ট্রোফ্লেমে

আপনি যদি এটি সম্পাদন করতে পিক্সেল শেডার ব্যবহার করেন তবে আমার ধারণা আপনার পিক্সেল স্ক্রিন / জগতের স্থানাঙ্ককে অপ্রজেক্ট করতে হবে (আপনি যা করতে চান তার উপর নির্ভর করে), এবং এটি ক্ষয়ক্ষতিহীন (স্টেনসিলের এই সমস্যাটি হবে না)
গুস্তাভো ম্যাকিয়েল

এই প্রশ্নটি একটি দুর্দান্ত প্রশ্ন।
ক্লাসিকথান্ডার

উত্তর:


11

আপনি আপনার পিক্সেল শেডার পদ্ধতির ব্যবহার করতে পারেন তবে এটি অসম্পূর্ণ।

আপনাকে এতে কিছু পরামিতি যুক্ত করতে হবে যা পিক্সেল শেডারকে জানিয়ে দেয় যে আপনি নিজের স্টেনসিলটি কোথায় অবস্থিত করতে চান।

sampler BaseTexture : register(s0);
sampler MaskTexture : register(s1) {
    addressU = Clamp;
    addressV = Clamp;
};

//All of these variables are pixel values
//Feel free to replace with float2 variables
float MaskLocationX;
float MaskLocationY;
float MaskWidth;
float MaskHeight;
float BaseTextureLocationX;  //This is where your texture is to be drawn
float BaseTextureLocationY;  //texCoord is different, it is the current pixel
float BaseTextureWidth;
float BaseTextureHeight;

float4 main(float2 texCoord : TEXCOORD0) : COLOR0
{
    //We need to calculate where in terms of percentage to sample from the MaskTexture
    float maskPixelX = texCoord.x * BaseTextureWidth + BaseTextureLocationX;
    float maskPixelY = texCoord.y * BaseTextureHeight + BaseTextureLocationY;
    float2 maskCoord = float2((maskPixelX - MaskLocationX) / MaskWidth, (maskPixelY - MaskLocationY) / MaskHeight);
    float4 bitMask = tex2D(MaskTexture, maskCoord);

    float4 tex = tex2D(BaseTexture, texCoord);

    //It is a good idea to avoid conditional statements in a pixel shader if you can use math instead.
    return tex * (bitMask.a);
    //Alternate calculation to invert the mask, you could make this a parameter too if you wanted
    //return tex * (1.0 - bitMask.a);
}

আপনার সি # কোডে আপনি SpriteBatch.Drawকলটির আগে পিক্সেল শেডারে ভেরিয়েবলগুলি পাস করেন :

maskEffect.Parameters["MaskWidth"].SetValue(800);
maskEffect.Parameters["MaskHeight"].SetValue(600);

উত্তরের জন্য ধন্যবাদ! দেখে মনে হচ্ছে এটি কার্যকর হবে তবে বর্তমানে আমি এটি নিয়ে একটি সমস্যা করছি। বর্তমানে, এটি চিত্রের বাম দিকে ক্রপ করা হচ্ছে (পাশাপাশি একটি সরল প্রান্তযুক্ত)। আমার কি পজিশনের জন্য স্ক্রিন স্থানাঙ্ক ব্যবহার করার কথা রয়েছে? বা আমার কি ইতিমধ্যে তাদের 0 - 1 এ রূপান্তরিত করার কথা?
ইলেক্ট্রোফ্লেমে

আমি সমস্যা দেখতে পাচ্ছি। আমার পিক্সেল শেডার ভাগ করে নেওয়ার আগে এটি সংকলন করা উচিত ছিল। : পি maskCoordএক্স এর হিসাব এক একটি ওয়াই আমি ফিক্স সঙ্গে আমার প্রাথমিক উত্তর সম্পাদনা করেছেন হয়েছে ও UV বাতা সেটিংস আপনার জন্য প্রয়োজন হবে অন্তর্ভুক্ত করা উচিত ছিল MaskTexture sampler
জিম

1
এখন এটি পুরোপুরি কাজ করে, আপনাকে ধন্যবাদ! কেবলমাত্র আমি যা উল্লেখ করতে পারি তা হ'ল যদি আপনার স্প্রাইটগুলি কেন্দ্রিক হয় (উত্সের জন্য প্রস্থ / 2 এবং উচ্চতা / 2 ব্যবহার করে) আপনার এক্স এবং ওয়াই অবস্থানগুলির জন্য আপনার প্রস্থ বা উচ্চতা অর্ধেক বিয়োগ করতে হবে (আপনি যেটিতে প্রবেশ করবেন শেডার)। এর পরে এটি নিখুঁতভাবে কাজ করে, এবং অত্যন্ত দ্রুত! অসংখ্য ধন্যবাদ!
ইলেক্ট্রোফ্লেমে

18

উদাহরণ চিত্র

প্রথম পদক্ষেপটি গ্রাফিক্স কার্ডটি বলা আমাদের স্টেনসিল বাফারের প্রয়োজন। আপনি গ্রাফিকস ডিভাইস ম্যানেজার তৈরি করার সময় এটি করার জন্য আমরা PreferredDepthStencil Format Depthformat.Depth24Stencil8 এ সেট করি যাতে লিখতে আসলে একটি স্টেনসিল থাকে।

graphics = new GraphicsDeviceManager(this) {
    PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8
};

আলফাটেষ্টএফেক্টটি সমন্বিত সিস্টেম সেট করতে এবং পিক্সেলগুলিকে আলফা দিয়ে ফিল্টার করে যা আলফা পরীক্ষায় উত্তীর্ণ হয়। আমরা কোনও ফিল্টার সেট করতে যাব না এবং ভিউ পোর্টে স্থানাঙ্ক সিস্টেম সেট করব।

var m = Matrix.CreateOrthographicOffCenter(0,
    graphics.GraphicsDevice.PresentationParameters.BackBufferWidth,
    graphics.GraphicsDevice.PresentationParameters.BackBufferHeight,
    0, 0, 1
);
var a = new AlphaTestEffect(graphics.GraphicsDevice) {
    Projection = m
};

এর পরে আমাদের দুটি ডিপথস্টেনসিল স্টেট সেট আপ করতে হবে। যখন স্প্রিটব্যাচ স্টেনসিলকে রেন্ডার করে এবং যখন স্প্রিটব্যাচ ব্যাকবফারকে রেন্ডার করে তখন এই রাজ্যগুলি নির্দেশ দেয়। আমরা প্রাথমিকভাবে দুটি ভেরিয়েবল স্টেনসিলফিউশন এবং স্টেনসিলপাসে আগ্রহী।

  • স্টেনসিলফানশন নির্দেশ দেয় যখন স্প্রিটব্যাচ স্বতন্ত্র পিক্সেল আঁকবে এবং কখন সেগুলি উপেক্ষা করা হবে।
  • টানা পিক্সেল পিক্সেল স্টেনসিলকে প্রভাবিত করলে স্টেনসিলপাস নির্দেশ করে।

প্রথম ডিপথস্টেনসিল স্টেটের জন্য আমরা স্টেনসিলফংশনকে তুলনা ফাংশনে সেট করি। এটি স্টেনসিলটেষ্ট সফল হওয়ার কারণ এবং যখন স্টেনসিলটেষ্ট স্প্রিটব্যাচ সেই পিক্সেলটি সরবরাহ করে। স্টেনসিলপাস স্টেনসিলপেশনে সেট করা আছে। এর অর্থ প্রতিস্থাপন করুন যে স্টেনসিলটেষ্ট সফল হলে সেই পিক্সেল রেফারেন্সস্টেনসিলের মান সহ স্টেনসিলবফারকে লেখা হবে।

var s1 = new DepthStencilState {
    StencilEnable = true,
    StencilFunction = CompareFunction.Always,
    StencilPass = StencilOperation.Replace,
    ReferenceStencil = 1,
    DepthBufferEnable = false,
};

সংক্ষেপে স্টেনসিলটেষ্ট সর্বদা পাস হয়, চিত্রটি সাধারণত স্ক্রিনে টানা হয় এবং পিক্সেলের জন্য পিক্সেলগুলির জন্য 1 এর মান স্টেনসিলবফারে সংরক্ষণ করা হয়।

দ্বিতীয় ডিপথস্টেনসিল স্টেট কিছুটা জটিল। এবার আমরা কেবল তখনই স্ক্রিনে আঁকতে চাই যখন স্টেনসিলবাফারের মান হবে। এটি অর্জনের জন্য আমরা স্টেনসিলফঞ্চনটি তুলনা ফাংশন.লেসেকুয়াল এবং রেফারেন্স স্টেনসিলকে 1 এ সেট করেছিলাম This এর অর্থ হ'ল যখন স্টেনসিল বাফারের মান 1 হবে তখন স্টেনসিলটেষ্ট সফল হবে। স্টেনসিলপাসকে স্টেনসিলপেশনে সেট করা হচ্ছে। স্টেনসিলফার আপডেট না করার কারণে রাখুন। এটি আমাদের একই মুখোশ ব্যবহার করে একাধিকবার আঁকতে সহায়তা করে।

var s2 = new DepthStencilState {
    StencilEnable = true,
    StencilFunction = CompareFunction.LessEqual,
    StencilPass = StencilOperation.Keep,
    ReferenceStencil = 1,
    DepthBufferEnable = false,
};

সংক্ষেপে স্টেনসিলটেষ্ট কেবল তখন পাস হয় যখন স্টেনসিলবফার 1 এর চেয়ে কম (মুখোশ থেকে আলফা পিক্সেল) থাকে এবং স্টেনসিলবফারকে প্রভাবিত করে না।

এখন আমাদের ডিপথস্টেনসিল স্টেটস সেট আপ হয়েছে। আমরা আসলে একটি মুখোশ ব্যবহার করে আঁকতে পারি। প্রথম ডিপথস্টেনসিল স্টেট ব্যবহার করে কেবল মুখোশ আঁকুন। এটি ব্যাকবফার এবং স্টেনসিলফার উভয়কেই প্রভাবিত করবে। এখন যে স্টেনসিল বাফারটির মান 0 রয়েছে যেখানে আপনার মুখোশের স্বচ্ছতা ছিল এবং 1 যেখানে এটি রঙ ধারণ করে আমরা পরবর্তী চিত্রগুলি মাস্ক করার জন্য স্টেনসিলবফার ব্যবহার করতে পারি।

spriteBatch.Begin(SpriteSortMode.Immediate, null, null, s1, null, a);
spriteBatch.Draw(huh, Vector2.Zero, Color.White); //The mask                                   
spriteBatch.End();

দ্বিতীয় স্প্রাইটব্যাচ দ্বিতীয় ডিপথস্টেনসিল স্টেট ব্যবহার করে। আপনি যা আঁকেন তা বিবেচনা না করেই, যেখানে স্টেনসিলফার 1 সেট করা হয়েছে কেবলমাত্র পিক্সেলগুলি স্টেনসিল পরীক্ষায় উত্তীর্ণ হবে এবং স্ক্রিনে টানা হবে।

spriteBatch.Begin(SpriteSortMode.Immediate, null, null, s2, null, a);            
spriteBatch.Draw(color, Vector2.Zero, Color.White); //The background
spriteBatch.End();

নীচের অঙ্কন পদ্ধতিতে কোডটির সম্পূর্ণতা, গেম কনস্ট্রাক্টরে PreferredDepthStencil Format = DepthFormat.Depth24Stencil8 সেট করতে ভুলবেন না।

GraphicsDevice.Clear(ClearOptions.Target 
    | ClearOptions.Stencil, Color.Transparent, 0, 0);

var m = Matrix.CreateOrthographicOffCenter(0,
    graphics.GraphicsDevice.PresentationParameters.BackBufferWidth,
    graphics.GraphicsDevice.PresentationParameters.BackBufferHeight,
    0, 0, 1
);

var a = new AlphaTestEffect(graphics.GraphicsDevice) {
    Projection = m
};

var s1 = new DepthStencilState {
    StencilEnable = true,
    StencilFunction = CompareFunction.Always,
    StencilPass = StencilOperation.Replace,
    ReferenceStencil = 1,
    DepthBufferEnable = false,
};

var s2 = new DepthStencilState {
    StencilEnable = true,
    StencilFunction = CompareFunction.LessEqual,
    StencilPass = StencilOperation.Keep,
    ReferenceStencil = 1,
    DepthBufferEnable = false,
};

spriteBatch.Begin(SpriteSortMode.Immediate, null, null, s1, null, a);
spriteBatch.Draw(huh, Vector2.Zero, Color.White); //The mask                                   
spriteBatch.End();

spriteBatch.Begin(SpriteSortMode.Immediate, null, null, s2, null, a);            
spriteBatch.Draw(color, Vector2.Zero, Color.White); //The background
spriteBatch.End();

1
+1 এটি এক্সএনএ ৪.০-এর জন্য আমি দেখেছি এমন একটি সম্পূর্ণ সম্পূর্ণ স্টেনসিলবাফার উদাহরণ। এর জন্য ধন্যবাদ! আমি পিক্সেল শেডার উত্তরটি বেছে নেওয়ার একমাত্র কারণ হ'ল এটি সেটআপ করা সহজ ছিল (এবং আসলে বুট করা খুব দ্রুত) তবে এটি একটি যথাযথ বৈধ উত্তর এবং আপনার যদি ইতিমধ্যে বেশ কয়েকটি ছায়াময় স্থানে থাকে তবে এটি আরও সহজ হতে পারে। ধন্যবাদ! এখন আমরা উভয় রুটের একটি সম্পূর্ণ উত্তর পেয়েছি!
ইলেক্ট্রোফ্লেমে

এটি আমার জন্য সেরা আসল সমাধান এবং প্রথমটির চেয়ে আরও বেশি
মেহেদি বাগনার্ড

আমি যদি 3 ডি মডেল রাখি তবে আমি কীভাবে এটি ব্যবহার করতে পারি? আমি মনে করি এটি আমার প্রশ্নে গেমদেব.স্ট্যাকেক্সেক্সঞ্জ / প্রশ্নগুলি / ৯৩৩৩৩/২ সমাধান করতে পারে তবে আমি কীভাবে এটি 3D মডেলগুলিতে প্রয়োগ করতে পারি তা আমি জানি না। আপনি কি জানেন যে আমি এটি করতে পারি কিনা?
dimitris93

0

উপরের উত্তরে , কিছু অদ্ভুত ঘটনা ঘটেছিল যখন আমি ঠিক যেমনটি ব্যাখ্যা করেছি ঠিক তেমনটি করেছিলাম।

আমার দু'জনের দরকার ছিল কেবলমাত্র DepthStencilStates

var s1 = new DepthStencilState {
    StencilEnable = true,
    StencilFunction = CompareFunction.Always,
    StencilPass = StencilOperation.Replace,
    ReferenceStencil = 1,
    DepthBufferEnable = false,
};

var s2 = new DepthStencilState {
    StencilEnable = true,
    StencilFunction = CompareFunction.LessEqual,
    StencilPass = StencilOperation.Keep,
    ReferenceStencil = 1,
    DepthBufferEnable = false,
};

এবং ছাড়া ড্র AlphaTestEffect

spriteBatch.Begin(SpriteSortMode.Immediate, null, null, s1, null, null);
spriteBatch.Draw(huh, Vector2.Zero, Color.White); //The mask                                   
spriteBatch.End();

spriteBatch.Begin(SpriteSortMode.Immediate, null, null, s2, null, null);            
spriteBatch.Draw(color, Vector2.Zero, Color.White); //The background
spriteBatch.End();

এবং সবকিছু প্রত্যাশার মতো ঠিক ছিল।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.