الشبكة العربية لمطوري الألعاب

موهوب  16mofed84 مشاركة 1

السلام عليكم☺ في البدايه اشكر كل من ساعدني في هذا المنتدى الرائع
انا احاول ان ابرمج على الdirectX مباشره و دون وجود وسيط مثل الXNA و واجهت صعوبه في تحويل الامر التالي:

Device.SetRenderTarget(0,null);
Texture2D tex = RT.GetTexture();
Eff.SetValue("...",tex);
Device.SetRenderTarget(0,RT);
.....

الامر RT.GetTexture ماذا يقابله في الDirectX سواء (c# او c++) لا يهم اللغه المهم الامر؟ علما بأني احاول ان اعمل تجميع لعده اضاءات بنفس الbuffer و لدي استفسار اخر لماذا يتم عمل Release or dispose لل surface بعد كل استخدام؟

خبير مدير وسام البهنسي مشاركة 2

وعليكم السلام مفيد،

لتنفيذ هذه العملية يجب أن تقوم بإنشاء كائن من نوع IDirect3DTexture9 مع تمرير القيمة D3DUSAGE_RENDERTARGET أثناء إنشائه.

الآن تستطيع استخدام الإكساء كسطح رسم وكإكساء. لاستخدامه كسطح رسم، قم بنداء الإجراء GetSurfaceLevel على الإكساء لتحصل على كائن من نوع IDirect3DSurface9. هذا الكائن يمكنك تمريره مباشرة للإجراء SetRenderTarget في جهاز الرسم.

لاستخدام الإكساء كإكساء فقط مرره للإجراء SetTexture أو أي إجراء آخر يستقبل كائن من نوع إكساء.

بالنسبة لنداء Release (لا يوجد Dispose في ++C)، فهو مرتبط بحقيقة أن كافة كائنات دايركت إكس مبنية باستخدام ما يدعى بإطار COM، وهو دون إطالة، نظام إدارة حياة الكائنات باستخدام عداد المرجعية (Reference Counter). هذا الأسلوب منتشر لإدارة الكائنات التي قد يتم استخدامها من عدة أماكن في البرنامج ويصعب معرفة الوقت المناسب لتحريرها. وفقاً لهذا الأسلوب، فكل من يحمل مؤشراً للكائن يقوم بزيادة عداد المرجعية الخاص بالكائن بمقدار 1. وعند عدم الحاجة لاستخدام المؤشر عليك بإنقاص عداد المرجعية بمقدار واحد أيضاً. عندما يصل هذا العداد للصفر سيتم تحرير الكائن من الذاكرة.

فلنأخذ كائن من نوع IDirect3DTexture9 مثلاً. برنامجك قام بإنشاء كائن من هذا النوع، فيبدأ الكائن بعداد مرجعية بقيمة 1. الآن قمتَ بضبط هذا الإكساء للرسم على جهاز الرسم باستخدام IDirect3DDevice9::SetTexture. داخلياً سيقوم جهاز الرسم بزيادة عداد المرجعية لإكسائك بقيمة 1 أيضاً لأنه يحمل مؤشراً له أثناء الرسم. بهذه الطريقة، حتى لو قام برنامجك بالتخلص من الإكساء عن طريق نداء Release، فإن الكائن لن يتم تدميره في الحقيقة حتى ينتهي جهاز الرسم منه ويقوم بإنقاص عداد المرجعية له أيضاً هو الآخر... فتصبح قيمة العداد 0 ويتم تحريره فعلاً بعد الانتهاء من استخدامه في الرسم من قبل جهاز الرسم.

نصيحتي للتعامل مع كائنات دايركت إكس هي استخدام الصنف المساعد CComPtr. باستخدام هذا الصنف لن تحتاج لنداء Release يدوياً، مما سيوفر عليك الكثير من الصداع وتسريب الذاكرة... الاستخدام سهل جداً... مثلاً:

CComPtr m_Texture; // Member variable
...
// لاحقاً
...
D3DDevice->CreateTexture(256,256,1,0,D3DFMT_A8R8G8B8,D3DPOOL_MANAGED,&m_Texture,0);

وهكذا... أي متغير من نوع منحدر من IUnknown ضعه في CComPtr... وداعاً للتسريبات ووجع الرأس... 😄

وسام البهنسي
مبرمج في يونيتي وإنفريمز

موهوب  16mofed84 مشاركة 3

اشكرك على هذه المعلومات
انا استخدم مكتبه اسمها sharpDX ... تحتوي نفس اوامر الC++ التي ذكرتها (و قد فهمتك كل شيء منك) ولكنها على شكل C# ..ولا مشكله في اي لغه تكتب

الا اني لم اتوصل الى النقطه الاساسيه من الموضوع ...وهي:
عندما حاول ان اقوم بدمج عدد n  من الاضات الى المشهد فأننا نستخدم نوعاً ما مثل هذه المعادله (بتجميع كل الاضاءات معاً)


clear(LightBuffer)

for(light l in lights)
{
sceneLighted = ApplyLight(l)
LightBuffer += sceneLighted
}

بعد تطبيق الفكره في الـXNA في السابق كانت تنجح لكن عندما انتقلت للـDirectX ...لم يعد ينجح و هذا ما فعلته بالتحديد
(كود الXNA الذي كنت استخدمه في السابق هو مثل الذي وضعته انا في اول مشاركه)



Game.SetRenderTarget(0, RT4);
Game.SetRenderTarget(1, RT5);


eff.SetTexture("RT4", rt4);//set render target to use it again (add other light to it)
eff.SetTexture("RT5", rt5);

eff.Begin();
eff.BeginPass(3);//apply light to scene
//draw...

//inside shader :
//output1 = DiffuseLight + RT4  //(additive blend)
//output2 = SpecularLight + RT5 //(additive blend)

eff.EndPass();
eff.End();

و المشكله التي حدثت فعلياً هي انه يتم رسم جميع الاضواء ولكن اخر اضاءه هي التي تبقي و هذه هي المشكله بالتحديد

موهوب  16mofed84 مشاركة 4

و بنسبه للأمر Game.SetRenderTarget
فذها ما يوجد في داخله


public void SetRenderTarget(int id, Texture s)
        {
            if (s == null && id == 0)
            {
                var bb = GraphicsDevice.GetBackBuffer(0, 0);
                GraphicsDevice.SetRenderTarget(0, bb);
                bb.Dispose();
            }
            else
            {

                Surface ss;
                if (s != null)
                    ss = s.GetSurfaceLevel(0);
                else ss = null;
                GraphicsDevice.SetRenderTarget(id, ss);
                if (ss != null)
                    ss.Dispose();
            }
        }

موهوب  16mofed84 مشاركة 5

اعتقد اني عرفت السبب .. في الXNA عند انشاء RenderTarget فانه يقوم بأنشاء 2 Texture
و عند استخادمه فأنه يقوم بأستخدام واحد ويبدل الاخر ..وهكذا

اما ما كنت احاول فعله هو استخدام الrenderTarget و طباعه فوق المكتوب ... وهذا لا يجوز ...

و قمت بحل المشكله عبر انشاء renderTarget اخر و ابداله مع كل عمليه رسم لأضاءه جديده ... و الحمدلله☺