الرابض في الظل أو البحث عن العالم التالي


المجمع هو لغتي المفضلة ... لكن الحياة قصيرة جدًا.

أواصل دورة البحث في قضية الظلال المناسبة لبعض الخبز. بعد النشر ، قمت بتهدئة هذا الموضوع مرارًا وتكرارًا ، لكن تأثير الإجراء غير المكتمل يدفعني للعودة إلى تلطيخ وحدات البكسل ، وإكمال الجشطالت .

مع العلم بنفسي ، أنا متأكد من أن اللعبة لن تحصل على تجسيد لها ، ولكن ربما يكون بعض الجمهور مهتمًا بإنجازاتي على هذا المسار الشائك. لذلك دعونا نبدأ.

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

ظل تتبع الأشعة بدون تنفيذ. بتعبير أدق ، من نوعه ، حيث تنتشر شعاع لكل بكسل في الصورة (كتلة من البكسلات) ويتم تحديد مستوى إضاءة النقطة الحالية. الخوارزمية نفسها موصوفة في مقال سابق وليس هناك نقطة في العودة إليها. لتتبع الشعاع العكسي ، كان الرمز أكثر بساطة ، وتمت إزالة كل علم المثلثات بالكامل ، مما قد يعطي نتيجة مقبولة في المستقبل.

محدة ضغط
const tile_size = 32; //   tile_size1 : single = 0.03125; // 1/32 -    block_size = 4; // /    Size_X:Byte = 32; //    X Size_Y:Byte = 24; //    Y //--------------------------------- function is_no_empty(x,y:Integer):Integer; begin if (x>=0) AND (x<Size_X) AND (y>=0) AND (y<Size_Y) then begin if map[x,y]=1 then begin is_no_empty:=1; end else if map[x,y]=2 then begin is_no_empty:=2; end else is_no_empty:=0; end else is_no_empty:=-1; end; //--------------------------------- function crossing(r_view, x,y:Single; xi,yj, i,j:Integer):Byte; var di,dj,ddi,ddj :Shortint; //   k,i2,j2 :integer; //    key:Boolean; last_k, transp_key :Byte; sum_lenX,sum_lenY, Dx,Dy,Dx1,DY1, l :Single; //  sec1,cosec1, temp_x,temp_y, dx0,dy0 :Single; //   i0,j0 :Integer; //       begin temp_x := i*block_size; temp_y := j*block_size; i0 := trunc(temp_x * tile_size1); j0 := trunc(temp_y * tile_size1); l := sqrt(sqr(temp_y-y) + sqr(temp_x-x)) + 0.0000001; transp_key := 0; //     if is_no_empty(xi,yj)>0 then inc(transp_key); if (xi=i0) and (yj=j0) then begin crossing := min(255,transp_key*64+ l * r_view); exit; end; dx0 := (temp_x-x)/l+0.0000001; dy0 := (temp_y-y)/l+0.0000001; key := False; last_k :=0; //   if dx0<0 then begin di :=-1; ddi:= 0; end else begin di := 1; ddi:= 1; end; if dy0<0 then begin dj :=-1; ddj:= 0; end else begin dj := 1; ddj:= 1; end; sum_lenX := 0; sum_lenY := 0; sec1 := 1/dx0; cosec1 := 1/dy0; //       Y temp_x := x-(xi+ddi) * tile_size ; temp_y := y-(yj+ddj) * tile_size ; Dx := sqrt(sqr(temp_x) + sqr(temp_x * sec1 * dy0)); DY := sqrt(sqr(temp_y) + sqr(temp_y * cosec1 * dx0)); //      Y Dx1 := abs(tile_size * sec1); Dy1 := abs(tile_size * cosec1); repeat if sum_lenX+DX < sum_lenY+DY then begin xi += di; k := is_no_empty(xi,yj); sum_lenX += DX; if DX<>Dx1 then DX := Dx1; end else begin yj += dj; k := is_no_empty(xi,yj); sum_lenY += DY; if DY<>Dy1 then DY := Dy1; end; if key Then begin if (xi<>i2) Or (yj<>j2) then begin //  (  ) if last_k=1 then begin crossing := 255; exit; end; //   (  ) if transp_key>2 then begin crossing := 255; exit; end; inc(transp_key); key:= false; end; end; if k>0 then begin i2:=xi; j2:=yj; key:=true; last_k:=k; end; //    if (xi=i0) and (yj=j0) then begin crossing := min(255, transp_key*64+ l * r_view); exit; end; until k=-1; //     end; //--------------------------------- .................. x0:= mouse_x; y0:= mouse_y; //       x1 := x0 div tile_size; y1 := y0 div tile_size; koef := tile_size div block_size; //      (     ) for j:=0 to Size_Y * koef do for i:=0 to Size_X * koef do picture_mask.SetPixel(i, j, BGRA(0,0,0,crossing(x0, y0, x1, y1, i, j))); .................. 


للأسف ، كانت النتيجة أسوأ بكثير مما كان متوقعًا ، فقد كان من المفيد نشر الصورة في وضع ملء الشاشة ، وسعى FPS إلى الوحدات.



إن تجميع البيكسلات في وحدات الماكرو لتقليل العمليات الحسابية وتطبيق التجانس اللاحق لم يحسن الأداء كثيرًا. التأثير بصراحة لم يعجبه الكلمة على الإطلاق.



كانت الخوارزمية متوازية تمامًا ، لكن لم يكن من المنطقي استخدام الكثير من التدفقات ، فقد بدا التأثير أسوأ بكثير مما كان في المقالة السابقة ، حتى مع جودة صورة أفضل.
اتضح أن يكون طريق مسدود. كان لا بد لي من الاعتراف ، وحدة المعالجة المركزية في حساب الرسومات في عيني قد استنفدت نفسها. الستار.

استطراد 1
خلال العقد الماضي ، لم يكن هناك أي تقدم تقريبًا في تطوير معالجات للأغراض العامة. إذا اقترب المستخدم من ذلك ، فإن أقصى زيادة ملحوظة في الأداء لا تزيد عن 30٪ لكل قلب. التقدم ، بعبارة ملطفة ، ليس له أهمية. إذا تجاهلنا امتداد طول تعليمات الموجه ، وبعض التسارع في كتل الناقل ، فهذه زيادة في عدد النوى العاملة. لا يزال العمل الآمن مع مؤشرات الترابط من دواعي سروري ، وليس كل المهام يمكن موازتها بنجاح. أرغب في الحصول على مجموعة عمل أساسية ، وإن كان ذلك ، ولكن إذا كان الأمر كذلك ، فهي أسرع من 5 إلى 10 ، ولكن للأسف وأوه ، كما يقولون.
هنا على حبري ، هناك سلسلة ممتازة من المقالات "الحياة في عصر السيليكون" الظلام "، والتي تشرح بعض الشروط المسبقة للحالة الراهنة للأمور ، ولكنها تعود أيضًا من السماء إلى الأرض. في العقد القادم ، لا يمكنك توقع أي زيادة كبيرة في الحوسبة لكل نواة. ولكن يمكننا أن نتوقع مزيدا من التطوير لعدد النوى GPU وتسارعها العام. حتى على جهاز الكمبيوتر المحمول القديم ، يكون إجمالي أداء وحدة معالجة الرسومات المقدرة أعلى 20 مرة من مؤشر ترابط وحدة المعالجة المركزية واحد. حتى لو قمت بتحميل جميع معالجات النوى الأربعة بفعالية ، فهذا أقل بكثير مما نود.
أشيد بمطوري رسومات الماضي ، الذين صنعوا روائعهم بدون مسرعات أجهزة ، أسياد حقيقية.

لذلك ، نحن نتعامل مع GPU. اتضح لي أنه من غير المتوقع إلى حد ما أنه في الممارسة العملية ، قلة قليلة من الناس ببساطة تنتشر المضلعات في الشكل. يتم إنشاء جميع الأشياء المثيرة للاهتمام باستخدام تظليل . بعد التخلص من المحركات ثلاثية الأبعاد النهائية ، حاولت دراسة مخلفات التكنولوجيا لأنها في مستوى عميق. نفس المعالجات هي المجمّع نفسه ، فقط مجموعة قليلة من التعليمات المقطوعة وتفاصيل العمل الخاصة بهم. بالنسبة للاختبار ، توقفت عند GLSL ، وهو بناء جملة يشبه C ، والبساطة ، والكثير من دروس التدريب والأمثلة ، بما في ذلك Habr.
منذ أن كنت معتادًا على الكتابة في Pascal ، كانت المهمة هي كيفية توصيل برنامج OpenGL
إلى المشروع. تمكنت من العثور على طريقتين للاتصال: مكتبة GLFW وملف رأس dglOpenGL . الشيء الوحيد في البداية لم أتمكن من توصيل التظليل ، ولكن يبدو أن هذا من انحناء يدي.

استطراد 2
يسألني العديد من الأصدقاء لماذا أكتب في باسكال؟ من الواضح ، هذه لغة مهددة بالانقراض ، مجتمعها يتراجع بثبات ، لا يوجد أي تطور تقريبًا. يفضل مهندسو النظام ذو المستوى المنخفض C و Java و Python و Ruby أو أي شيء في ذروتهم الآن.
بالنسبة لي ، فإن باسكال يشبه الحب الأول. منذ عقدين من الزمن ، في أيام Turbo Pascal 5.5 ، غرقت في روحي وكانت تمشي معي منذ ذلك الحين ، سواء كان ذلك دلفي أو في السنوات الأخيرة لعازر . أنا أحب القدرة على التنبؤ باللغة ، المستوى المنخفض النسبي (المجمع يدرج وعرض تعليمات المعالج) ، والتوافق مع C. والشيء الرئيسي هو أن يتم تجميع التعليمات البرمجية وتنفيذها دون مشاكل ، ولكن حقيقة أنه ليس من المألوف ، وليس هناك بعض الميزات ، وهذا هراء. تقول الشائعات ، لا يزال هناك أشخاص ما زالوا يكتبون على LISP ، لكن له بشكل عام لمدة نصف قرن.

لذلك ، دعونا الغوص في التنمية. بالنسبة لخطوة الاختبار ، لن نأخذ نماذج تظليل واقعية دقيقة ، ولكن نحاول تنفيذ ما جربناه بالفعل من قبل ، ولكن مع أداء وحدة معالجة الرسومات ، إذا جاز التعبير ، لمقارنة واضحة.

في البداية ، فكرت في الحصول على ظل لهذا الشكل تقريبًا ، باستخدام مثلثات لكائن.



لإنشاء تأثير دائرة ناعمة ، تحتاج إلى الكثير من المضلعات. ولكن ماذا لو كنت تستخدم المثلثات إلى الحد الأدنى ، باستخدام تظليل بكسل لإنشاء ثقب في الشكل. جاءت الفكرة لي بعد قراءة مقال من قبل سيد محترم ، حيث تم فتح الفرصة لإنشاء كرة ذات تظليل.



إذا قمت بتمديد المثلث خارج حدود الشاشة ، فإن النتيجة هي:



تحولت حدود الظل إلى أن تكون صلبة للغاية وصعدت أيضا. ولكن هناك طريقة للحصول على نتيجة مقبولة دون استخدام أخذ العينات الزائدة ، وهذا يستخدم الحدود الملساء . للقيام بذلك ، قم بتغيير المخطط قليلاً. سوف تجعل زوايا المضلعات عند تقاطع الظل إلى الدائرة شفافة.



النتيجة أفضل ، لكنها لا تزال تبدو غير طبيعية.



أضف القليل من تنعيم الدائرة لتضفي نعومة وتغيير شكل التدرج من خطي إلى قوة.



إنها نتيجة مقبولة.
وفي النهاية ، سنضيف أشياء تشبه العقبات إلى النموذج.



كود شادر
//

#version 330 core
layout (location = 0) in vec2 aVertexPosition;
void main(void) {
gl_Position = vec4(aVertexPosition.xy, 0, 1.0);
}

//

#version 330 core
layout (points) in;
layout (triangle_strip, max_vertices = 5) out;
uniform mat4 uModelViewMatrix;
uniform float uRadius;
uniform vec2 uHeroPoint;
out float fTransparency;
out vec2 vCenter;
void main(){
vCenter = gl_in[0].gl_Position.xy;
vec2 d = uHeroPoint - vCenter;
float l = length(d);
float i = uRadius / l;
float ii = i*i;
float ij = i * sqrt(1 - ii);
vec2 p1 = vec2(vCenter.x + dx*ii - dy*ij , vCenter.y + dx*ij + dy*ii);
vec2 p2 = vec2(vCenter.x + dx*ii + dy*ij , vCenter.y - dx*ij + dy*ii);
d = uHeroPoint - p1;
vec2 p3 = vec2(p1 - d/length(d)*1000000);
d = uHeroPoint - p2;
vec2 p4 = vec2(p2 - d/length(d)*1000000);
fTransparency = 0;
gl_Position = uModelViewMatrix * vec4(p1, 0, 1);
EmitVertex();
fTransparency = 1;
gl_Position = uModelViewMatrix * vec4(p3, 0, 1);
EmitVertex();
gl_Position = uModelViewMatrix * vec4(vCenter, 0, 1);
EmitVertex();
gl_Position = uModelViewMatrix * vec4(p4, 0, 1);
EmitVertex();
fTransparency = 0;
gl_Position = uModelViewMatrix * vec4(p2, 0, 1);
EmitVertex();
EndPrimitive();
}

//

#version 330 core
precision mediump float;
varying float fTransparency;
varying vec2 vCenter;
uniform float uRadius;
uniform vec2 uScreenHalfSize;
uniform float uShadowTransparency;
uniform float uShadowSmoothness;
out vec4 FragColor;
void main(){
float l = distance(vec2((gl_FragCoord.xy - uScreenHalfSize.xy)/uScreenHalfSize.y), vCenter.xy);
if (l<uRadius) {discard;}
else {FragColor = vec4(0, 0, 0, min(pow(fTransparency, uShadowSmoothness), (l-uRadius)/uRadius*10)*uShadowTransparency);}
}


آمل أن تكون مفيدة

خادمك المتواضع ، المعذب بكسل ، إعادة بناء.

أنا أرفق تجريبي صغير. (EXE Windows)

ملاحظة: يحتوي عنوان المقال على بيضة عيد الفصح ، في إشارة إلى ثلاثية سيالا كرونيكل . عمل ممتاز في أساليب الخيال ، حول مصائب القرون ، من أليكسي Pekhov.

Source: https://habr.com/ru/post/ar446986/


All Articles