الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

مقدمة إلى لغة Hlsl , الدرس الأول .

بدأه time1 في 9 يوليو 2009 · 2 رد · 2,767 مشاهدة · في DirectX
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكــم ورحمـة الله وبركاتــه ،،

أخواني الاعزاء اهلا وسهلا ومرحبا بكم , واتمنى ان يوفقني المولى في تقديمي ...

سأقوم خلال مجموعة الدروس التالية بعرض مقدمة عن لغة التظليل عالية المستوى او ماتعرف بـ HLSL :High Level Shading Language ... وسأقوم بتفصيل حديثي من خلال مجموعة من النقاط واتمنى ان اوفق في ذلك :

أولا : المقدمة

لغة التظليل HLSL هي عبارة عن مجموعة من الاكواد التي نقوم بكتابتها لتنفيذ بعض المؤثرات الخاصة على المشاهد او النماذج او تقوم بمعالجة حالات الرؤوس والنقاط او تقوم بوظائف اخرى , ويتم تنفيذها داخل الـ GPU .

وقد اتاحة لنا مكتبة DirectX في اصدارها التاسع امكانية التعامل مع هذه اللغة وتوفير الجهد الذي نعانيه من كتابة اكواد الاسمبلي assembly في الاصدارات السابقة .

ثانيا : الأدوات اللازمة .

سنستخدم لكتابة الShading برنامج المفكرة notepad الموجود في الويندوز , حيث سنقوم بكتابة الاكواد داخله ومن ثم سنقوم باستدعائه داخل التطبيق الذي نقوم بكتابته ( C++ مثلا ) حيث سيمر في مجموعة من المراحل التي سنتطرق إليها بعد قليل , من ترجمه واعطاء القيم وتنفيذ الدخل والخرج والدالة الرئيسية .

ثالثا ً : مقدمة للغة HLSL .

يتكون كل مظلل من مجموعة من الاجزاء تمثل لنا اكواد المظلل ويقوم كل جزء منها بتحديد حالات الرؤوس الحالية أو التالية , ويمكن ان يحتوي المظلل على :

أ-المتغيرات أو الثوابت العامة

ب- هيكل الدخل .

ج- هيكل الخرج.

د- الدالة الرئيسية .

هـ- ادوات المستخدم الاخرى .

أولا المتغيرات العامة :

وهي على عدة انواع مشابهه للغة سي / سي ++ تستخدم طبعا على حسب القيمة التي يجب ان يمثلها المتغير :

bool , int,float ,half,fouble,vector,matrix,.... إلخ

* الكلمات المحجوزة :

void,	texture,	asm,	bool,	compile,	const,	float,
int,	sampler,	return,	do,	for,	decl,	input,technique,
vertexshader,	inline	,	false,	in,	out,	pass,	pixelshder,
struct,		vector,		extern,	if,	else,	string,		uniform,
while,	true,		static,	volatile,	shader,	typedef,	true.

ثانيا ً : هيكل الدخل .

وهو هيكل او بنية struct نقوم بكتابتها لتمثل لنا حالة دخول الرأس لـ Shader والذي بدوره سيقوم بإجراء عملية المعالجة على اعضاء هذا الهيكل , وكل عضو ينتهي بنقطتين رأسيتين ( : ) يليها الواصف او المعنى semantic والذي يحدد ماهو هذا العضو , هل هذا العضو يحدد الموضع الحالي للرأس ؟ ام لونه ؟ ام احداثياته النسيجية ؟ ام ماذا ؟

على سبيل المثال :

struct Input
{
	vector Pos:POSITION;
};

ثالثا ً هيكل الخرج .

وهو مشابه لهيكل المدخلات إلا انه يقوم بوصف حالة الرأس او النقطة بعد خروجها من الShader , مثلا يمكن ان يدخل موضع الرأس للShader وتتم عملية المعالجة ويخرج لنا موضعه الجديد ولونه الجديد . على سبيل المثال :

struct Output
{
	vector Pos:POSITION;
	vector Color:Color;
};

ثالثا ً : الدالة الرئيسية .

Main : ليست بالضرورة ان تكون بهذا الاسم وانما هي دالة لها تأخذ هيكل المدخلات كا parameter لها وتعيد هيكل المخرجات كا return value . ويتم داخلها عملية المعالجة الخاصة بالShader .

مثلا ً :

Output Main(Input input)
{
............
............
............
}

هـ- أدوات المستخدم الاخرى .

وتتمثل بما يحتاجه المستخدم من هياكل Structs او دوال Functions اخرى لتساعده على كتابة الShader بشكل افضل واكثر مرونة , وطريقة كتابتة هذه الادوات مشابهه لطريقة كتابتها داخل لغة سي / سي ++ , لذلك مبرمج سي ++ لن يجد اي صعوبة في ذلك .

هناك نقطة يجب الانتباه لها في الدوال , وهي ان تمرير القيم يتم بواسطة by value لذلك يمكن استخدام الكلمات المحجوزة out و in قبل الوسائط لمعالجة عملية عدم استخدام الـ Ptr و الـ Ref .

مثال :

void Plus(in int x,in int y,out int reslut)
{
	result=x+y;
}

-------------------------------------------------------------------------------------------------------------------

رابعا ً : تهيئة التطبيق .

لتهيئة التطبيق كي يتعامل مع الـ Shader يلزمنا مجموعة من الخطوات التي تسبق عملية التعامل مع المظلل مثل تعيين قيم المتغيرات و ترجمة المظلل وتهيئة المظلل وغيرها , وهي كالتالي :

أ - ترجمة الـ Shader .

ب- انشاء المظلل .

ج- التعامل مع جدول الثوابت .

د- ضبط المظلل .

أ- ترجمة المظلل .

تتم عملية ترجمة المظلل من خلال الدالة D3DXCompileShaderFromFile, والتي تحتوي على الوسائط التالية :

pSrcFile اسم ملف الـ shader : مثلا myShader.txt .

pDefines و pInclude سنضع فيهمها القيم 0 إلى ان نحتاج لاستخدامهما .

pFunctionName: وهو اسم الدالة الرئيسية التي تطرقنا إليها بالجزء السابق

pTarget: وهو اصدار الـ Shader ,مثلا vs_1_1 , تكتب احرف صغيرة .

Flags :وهي مجموعة من الاعلام يمكن لك الاطلاع على هذه الاعلام وفوئدها من خلال شرحنا اللاحق .

ppShader : وهو مؤشر من نوع ID3DXBuffer ويعيد لنا كود الـ Shader .

ppErrorMsgs : وهو ايضا مؤشر من نوع ID3DXBuffer ويعيد لنا كتلة الرسالة التي تحتوي على الخطأ في حال حدوثه مقترنه برقم سطر الخطأ ان تمكن من ذلك .

ppConstantTable : وهو مؤشر إلى جدول الثوابت الذي يحتوي على ثوابت المظلل وهو من نوع ID3DXConstantTable * .

بعد ذلك نستطيع من خلال برنامج الـ notebad ان نعرض عدد الاسطر من خلال : عرض - شريط المعلومات , وذلك من شأنه مساعدتنا في حال حدوث الخطا على تحديد سطره , ونستطيع ان نعرض رسالة الخطأ من الكتلة التي يحتويها مؤشر EShader ,

	if(EShader)
		::MessageBox(0,(char*)EShader->GetBufferPointer(),"Error",0);

ب- انشاء المظلل ,

وذلك من خلال مؤشر الجهاز الذي نتعامل معه من خلال الدالة CreateVertexShader والتي تأخذ الوسائط التالية :

الوسيط الأول , وهو كتلة الكود التي قمنا بتجهيزها سابقا عند ترجمة الShader في الوسيط ppShader .

الوسيط الثاني , وهو مؤشر من نوع IDirect3DVertexShader9 * والذي بدوره سيمثل لنا الShader على باقي اكواد التطبيق .

مثال :

	pMyDevice->CreateVertexShader((DWORD*)Shader->GetBufferPointer(),&VS);

ج- التعامل مع جدول الثوابت .

تم تهيئة جدول الثوابت سابقا من خلال الوسيط ppConstantTable اثناء عملية ترجمة كود الShader , والان يتم عملية التعامل مع هذه الثوابت اما من خلال اعطائها قيمها الافتراضية او من خلال تعيين قيم لها او من خلال استدعائها .

- يتم استدعاء الثوابت من خلال الحصول على مقبض Handle لها من نوع D3DXHANDLE واستخدام الدالة GetConsatantByName الخاصة بـ ppConstantTable , والتي تحدد مستوى الثابت واسمه وتعيده إلى المقبض المحدد .

	VP=CT->GetConstantByName(0,"ViewProj");

- وتتم عملية اسناد القيم الافتراضية من خلال SetDefaults التابعة ل ppConstantTable

	CT->SetDefaults(pMyDevice);

- كما تتم عملية تعيين القيم للثوابت من خلال SetX التابعة لـ ppConstantTable على ان تكون X هي نوع المتغير المراد اعطائه قيمه , مثلا :

SetVector,SetMatrix,SetBool ... وهكذا ,

		CT->SetMatrix(pMyDevice,VP,&MVP);

د- ضبط المظلل

وتتم داخل دالة العرض من خلال الجهاز الذي نعمل عليه والدالة SetVertexShader والذي يأخذ الوسيط IDirect3DVertexShader9 * الذي قمنا بتهيئته داخل اكواد التطبيق سابقا ً .

		pMyDevice->SetVertexShader(VS);

--------------------------------------------------------------------------------------------------------

خامسا ً : كتابة مثال بسيط .

لنأخذ المثال التالي :

post-82612-1247135453_thumb.jpg

نرغب بكتابة مظلل بسيط يقوم بتغيير ألوان الرؤس بشكل عشوائي ومتغير في كل اطار .

أولا سنقوم بكتابة المظلل وفق لما قمنا به سابقا ً .

matrix ViewProj;//By App
vector Color;//By App
vector BColor={1.0f,1.0f,0.0f,1.0f};
struct Input
{
	vector Pos:POSITION;
};
struct Output
{
	vector Pos:POSITION;
	vector Color:COLOR;
};

Output Main(Input input)
{
	Output output=(Output)0;
	output.Pos=mul(input.Pos,ViewProj);
	output.Color=BColor*Color;
	return output;
}

فيما سبق نجد مايلي :

قمنا بتعريف مصفوفة العرض والاسقاط والتي سنقوم بتهيئتها داخل التطبيق , وقمنا بالاعلان عن متغيرين احدهما Color وهو سيمثل لنا اللون العشوائي و Bcolor وهو لون قمنا بتعريف مركباته rgba .

نجد هنا ان هيكل المدخلات يحتوي فقط على موقع الرأس بينما جدول المخرجات يحتوي على الموقع الجديد و اللون الجديد .

تتم عملية المعالجة داخل الدالة الرئيسية Main من خلال تهيئة متغير من هيكل المخرجات والتعامل مع الموضع واللون على النحو التالي :

الموضع: هو حاصل ضرب موضع الرأس المدخل في مصفوفة العرض والاسقاط من خلال الدالة mul .

اللون: وهو حاصل ضرب اللون الاساسي باللون العشوائي .

داخل التطبيق

أولا نحتاج إلى تهيئة الرسم بأي رسم نرغب به على سبيل المثال انا قمت بتحميل مجسم الطائرة الذي يأتي مع حزمة DX وبالامكان الاستعاضة بأي مجسم اخر بسيط لاجراء هذا التطبيق .

- لدينا مقبضين احدهما للون والاخر لمصفوفة العرض والاسقاط وهذا كان واضحا عندما اضفنا تعليق By App داخل كود الـ Shader .

D3DXHANDLE VP,Color;

- نحتاج كما اسلفنا سابقا إلى تعريف جدول الثوابت والذي سيمثل لنا ثوابت الكود .

ID3DXConstantTable *CT=NULL;

- نحتاج إيضا إلى التصريح عن الShader الذي سنتعامل معه على طول التطبيق .

IDirect3DVertexShader9 *VS=NULL;

- بعدها سنقوم بتجهيز القيم العشوائية Rand وذلك امر غاية بالبساطة لكل مبرمج من خلال استدعاء الملفات الرئسية وتعيين الدالة srand.

	srand(time(0));

- بعد ذلك نقوم بترجمه كود الShader كما اسلفنا سابقا ً .

	ID3DXBuffer *Shader,*EShader;

	D3DXCompileShaderFromFile("color.txt",
0,
0,
"Main",
"vs_1_1",
		D3DXSHADER_DEBUG,
&Shader,
&EShader,
&CT);

نستطيع بعدها في حال حدوث اي خطأ ان نعرض رسالة الخطا , ومن خلال برنامج المفكرة notepad يمكن لنا من خلال عرض شريط المعلومات تحديد ارقام الاسطر .

	if(EShader)
		::MessageBox(0,(char*)EShader->GetBufferPointer(),"Error",0);

الان تأتي مرحلة إنشاء المظلل :

	pMyDevice->CreateVertexShader((DWORD*)Shader->GetBufferPointer(),&VS);

-بعد ذلك نقوم بالحصول على المقابض وتعيين القيم الافتراضية للثوابت :

	VP=CT->GetConstantByName(0,"ViewProj");
	Color=CT->GetConstantByName(0,"Color");
	CT->SetDefaults(pMyDevice);

بعدها نقوم بإكمال اجراءات التطبيق الاعتيادية من تعيين قيم مصفوفة العرض والاسقاط , الا انه في هذه المرة لن نجري لها تحويل وانما سنقوم بضربهما ونضع النتيجة في مصفوفة جديدة ونقوم بتعيينها للمتغير ViewProj في الـ Shader .

		MVP=View*Proj;
		CT->SetMatrix(pMyDevice,VP,&MVP);

والان في دالة العرض .

		static float r=0.0f,g=0.0f,b=0.0f,a=1.0f;
		r=(rand()%10)*0.1f;
		g=(rand()%10)*0.1f;
		b=(rand()%10)*0.1f;
		D3DXVECTOR4 C(r,g,b,a);

		CT->SetVector(pMyDevice,Color,&C);

والان في النهاية وقبل رسم المجسم نقوم بتفعيل الـ Shader .

		pMyDevice->SetVertexShader(VS);

والنتيجة على التوالي .

post-82612-1247135477_thumb.jpg

................................................................................

................................................

إلى هنا أكون قد انتهيت من درسي الأول ,, اسأل الله ان اكون قد وفقت في طرحه وإلى اللقاء في الدرس التالي .. :blush:

والسلام عليكم ورحمة الله وبركاته .,,,,

llback.jpg

اشهد ان لا إله إلا الله وان محمدا ً رسول الله

#2

السلام عليكم

شرح رائع ودروس نحن فى حاجه اليها وفقق الله وجعله فى ميزان حسناتك

163374357.gif

#3

جميل جدا أخيرا shaders :)

banner_60_468.gif

NOTHING IS IMPOSSIBLE

مواضيع مشابهة