DCSIMG
הבלוג של צביקה פאר

הבלוג של צביקה פאר

בבלוג זה אני אנסה לשתף את הקוראים בחקירת מגוון טכנולוגיות נפוצות פחות או יותר

Implements bridge pattern with wcf

באחד מהפרויקטים שעבדתי עליהם לאחרונה היתה דרישה להחליף תוכנת עמדה  במערכת מבזורת בתוכנה אחרת .

התקשורת בין כל העמדות הינה WCF  ,כאשר הcontract  של העמדה החדשה שונה לחלוטין מהcontract  הקיים ואין אפשרות להחליפו לזה שהיה קיים .(העמדה הייתה ונשארה server )

ל Contract החדש אין תאימות חד חד ערכית ברמת הקריאות כלומר חלק מהקריאות של ה Client  היו צריכות מספר קריאות בContract של ה Server החדש חלק היינו צריכים לשמור ב Cache  על מנת לאגד מספר קריאות מה client לקריאה אחת בServer וכד.

1

 

חשבנו על 3 פתרונות אפשריים

פתרון 1 מערכת חיצונית

לכתוב או לקנות מערכת חיצונית שהינה מאין biztalk sever  אשר יבצע את פעולת התרגום .2

 

 

 

יתרונות

מבחינת decoupling מאוד גבוה בין מנגנון ההמרה לאפליקציה , השינוי היחיד שנדרש לבצעו באפליקציה זה החלפת ה address  של ה wcf server .

אפשרות ניטור של המידע הזורם + העבודה המתבצעת באותו מתאם .

תמיכה יכולת גידול לעתיד לdevice ים או שיטות תקשורת שונות .

חסרונות

הבעיה שצריך לתחזק עוד אפליקציה \ service  שאיננו חלק אינטגרלי מהאפליקציה .(תיעוד התקנה קיטלוג )

לקנות אפליקציה חיצונית עולה כסף .

Hop  נוסף של מידע שאין בו צורך . המידע נשלח ל wcf service  ולאחריו ל עמדה הנוספת. 

 

פתרון 2 השיטה הסינית

שיטת הquick and drity  לאתר את כל המקומות בקוד שפונים לwcf client  ולשתול בכל המקומות switch ים של האם לשלוח בintrerface החדש או הישן . + להוסיף את כל הלוגיקה בתוכנה עצמה של ה Client .

יתרונות

למרות שיש הרבה מאוד עבודה העבודה פשוטה אין סיכון טכנולוגי .

חסרונות

מכיוון שבאפליקציה  אין שיכבה אחת מסודרת שקראה לwcf clients  אלא הקריאות התבצעו מכל מקום אפשרי וישנם מאות מקומות כאילו הקוד של האפליקציה יסרבל  עוד יותר כי זה לא רק הקריאה עצמה יש להוסיף לוגיקה לכל קריאה .

כמובן שאפשר לעשות את זה יותר אלגנטי ולא להשתמש בSwitch אלא שימוש ב Factory   שיחזיר Iterface  ל ממשק .

 

הפתרון ה 3 Wcf custom channel

פתרון באמצעות Custom protocol channel שכתבנו שתפקידו לתרגם את ההודעות .

יתרונות

פתרון שהינו מאוד decoupled  אין צורך לפתוח אף אחד מהתוכנות . אפשר באמצעות קונפיגורציה להכיל אותו .

 לא נוצר עוד hop  מיותר

מאתגר מקצועית

אפשר באמצעותו ללמוד wcf  לעומק

מאוד מרשים ראשי צוותים סמי מיקצועיים (כאילו שיועדים מה זה wcf   בשתי מילים אבל אין להם שמץ ניסיון בארכיטקטורה נכונה וכד )שאתה מספר להם ברעיון עבודה שעשית כזה דבר חושבים איזה ילד פלא אתה ,השועלים הותיקים לא יתנו לך לסיים את הטרילוגיה והם כבר יראו לך את הדלת. שתסבך עד מוות את הפרויקט של מישהו אחר  .

חסרונות

מאוד technology aware  כלומר מבוסס על יכולות ההרחבה של wcf  ותלוי ביכולות האלו . ומכיוון ש wcf איינו קוד פתוח ניתן להיתקע עם memory leak בעיית ביצועים או חלקים שבלתי ניתנים למימוש .

 

Wcf channels

התשתית של wcf  מכילה ערימה של channels  המתפקדים כ chain of responsibility

דומה מאוד ל ndis filter drivers  .

3

 

 

בין כל החלקים של ה channel  זורמים אוביקטים מסוג Message .

הMessage

מורכב מ Headers  וPayload  של המידע (הAction  והפרמטרים שלה ).

כל channel  יכול להוריד להוסיף או לשנות את ה header ים .

כמו כן ה Channel יכול לשנות את ה payload כלומר את המידע עצמו שעובר ב Message

(שינוי פשוט של מידע יכול להתבצע באמצעות Behaviors כדוגמת מימוש IOperationBehavior ביתר קלות )

 

הפתרון שלנו התבסס על זה ש Channel במקרים מסוימים יכול להחליט שהוא לא מעביר הלאה הודעה (כלומר חוסם אותה ) או מעביר כמה הודעות במקומה )  או מעביר כמה הודעות בזמנו החופשי בthread  אחר אחרי שההודעה בכלל נסתיימה .

 

הפתרון הזה עובד טוב רק ב interface אסינכרוני .בinterface סנכרוני המצב מסתבך כי המערכת מנסה להחזיר לך תמיד הודעה על כל קריאה באופן סינכרוני ויצירת מצב ששתי הודעות נשלחות ומקבלים רק תשובה אחת מאוד בעיתי , אינני יודע אם אפשרי בכלל.

 אנו כתבנו Custom channel משלנו והכנסנו אותו ל channel  stack  .

15

ה  Channel הכיל את הלוגיקה כלומר את המימוש של ה Bridge  שמתאם בין ההודעות , הchannel  הינו statefull  הוא זכר את כל המידע מהקריאות הקודמות שדרושות לצורך התאום .  

הפיתרון מכיל את ה class ים הבאים :

7

LogicOrcBinding

הcustom binding  שמכיל את ה binding elements  שלנו .

 

4

 

הגדרנו רק 2 elements  כאשר תשתיות ה wcf  מוסיפות את החסר  בהתאם לקונפיגורציה אם זה security  , relaibale session  וכד

התשתית מוסיפה את שאר ה elements במקומות הנכונים כך שהצפנה לדוגמה תתבצע אחרי הטיפול שלנו .

 

 
5
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

 

LogicOrcBindingElement , LogicOrcChannelFactory , LogicOrcChannelListener

מכילים את ה בוילרפארט קוד בד"כ לא נוגעים או משנים אותם. תפקידם לעטוף את היצירה של ה  channel שלנו .

כאשר יש פרמטרים שצריך להעביר מה Binding  עצמו ,צריך לדאוג להעביר דרכם .

 

   

 

LogicOrcBodyWriter

class עזר פשוט שתפקדו לעזור בלכתוב את ה פרמטרים של הAction בMessage

הClass  מכיל event  בשם  writeBodyCallback שנקרא אוטומטית על ידי המערכת לצורך עדכון הפרמטרים .

 

6

 

LogicOrcChannel

זהו לב המערכת

8

 

בCtor אנו מקבלים Reference ל Channel הבא בstack

 9

אנו נשתמש בReference הזה לשני דברים :

1.להפנות מימוש methods למימוש ב Channel הבאה בstack ב methods שאין לנו צורך לממשן

לדוגמא:

10

2.הפניית הMessage אחרי הטיפול לChannel  הבאה ב stack

 

הmethods  שמטפלות בשליחת הנתונים ל channel הבאה בstack הינן :

public void Send(Message message, TimeSpan timeout) ,

public IAsyncResult BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state)

כאן משתלבת הלוגיקה העסקית של הBridge בשליחה של הודעות לServer ,ניתן לא להעביר את הMessage ל channel הבא ובכך לחסום את ההודעה ניתן להעביר הודעה אחרת או מספר הודעות אחרות ,כמו כן ניתן להעביר את הטיפול ל thread אחר ובזמן שונה להעביר הודעה ל Channel הבא ב Stack .

 

11

 

דוגמא למימוש

פונקצית עזר לבניית Message חדש בהסתמך על העתקת ה Header של ה message  שהתקבל מבChannel הקודם ושינוי ה Action

12

 

הפונקציה הבאה מקבלת פקודה אחת ומפצלת אותה ל שניים

 

13

 

הMethods שמטפלות בקריאת הנתונים בצורה סינכרונית או אסינכרונית בהתאמה הינן :

public bool TryReceive(TimeSpan timeout, out Message message)

והצמד

public

IAsyncResult BeginTryReceive(TimeSpan timeout, AsyncCallback callback, object state)

public bool EndTryReceive(IAsyncResult result, out Message message)

 

אחרי ששלחנו שתי הודעות Call1 וCall2 ל Client אמו מקבלים גם שתי תשובות אותן אנו צריכים לאחד לתשובה אחת .את הפעולה הזו אנו עושים ב EndTryReceive

14

 

Exception

בזמן הפיתוח אם מבצעים שגיאה בקריאה של ה Contract לדוגמא קוראים ל Action שאיננו נמצא ב contrat של ה Server המערכת נותנת שגיאה מפורטת :

{"The message with Action 'http://tempuri.org/ILogicOrcSample/CallWithWrongAction' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver.  Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None)."}

טעיות בשמות של ה פרמטרים בד"כ יגרמו ל להעברת null בפרמטרים של ה method

 

בדיקות

שתי בדיקות נעשו לצורך ולידציה של הפתרון :

1.בדיקת memory leak שהמערכת לא משאירה אצלה זבל במקרה ונחסמת פקודה מלהגיע ליעדה וכד.על מנת לבצע את הבדיקות עם התוכנית דוגמא יש להוריד את כל ה חלק של ה  Diagnostic מה Configuration files , לבצע את הקריאות הנדרשות בloop ומידי פעם לקרא ל GC.Collect

2.בדיקת ביצועים ש ה Bridge Channel לא פוגע בביצועים כתוצאה מביצוע הלוגיקה שלו

בבדיקה שבצעתי לא נראה כל דליפת זיכרון אך אני ממליץ בחום לכל מי שחושב על הפתרון לבדוק גם את נושא הזיכרון , יציבות , ביצועים של הקונספט האם הם מתאימים לדרישותיו.

קישור לקוד דוגמא:

קוד דוגמא מאוד בסיסי נמצא כאן .

הקוד מבוסס על דוגמא של מיקרוסופט בשם Chunking channel לדוגמא הוכנסו רק הדברים האלמנטריים ביותר לצורך הדגמת הקונספט .

על מנת להשתמש בה יש כמוסן להוסיף לה את כל הקוד הסטנדרטי כגון טיפול ב Timeouts סגירת Channels  ללא exceptions  וכד.

Multithreaded Rendering DX11

ל Directx גירסה 11 נוסף פיצר חדש של Multithreading שמטרתו להפחית את העומס הרב על ה Rendering thread היחיד , על ידי פיזור של חלק מהעבודה שהוא עושה בthreads אחרים או שימוש בbatch ים של פקודות לgpu . בפוסט זה אני אנסה לשפוך אור על הפיצר הזה .

על אף ריבוי המעבדים ב GPU שמגיע לאלפים ,וריבוי ה Cpu ים במערכת. הפקודות ל GPU מה CPU עוברות דרך GPU Commands queue יחיד באופן סינכרוני.

ההזנה שלה Commands queue מתבצעת על ידי Thread יחיד בשם ה Rendering thread .

ניתן לראות ולדבאג את הפקודות הנכנסות ל Command queue באמצעות Pix

mtdx15

.

במצב שבו לא משתמשים ב Directx multitheading כל העבודה של הrendering מתבצעת בthread אחד .מכיוון שהעבודה של ה GPU הינה סנכרונית ל Rendering thread ,נוצרים מצבים שבהם הgpu מחכה בחוסר מעש לפקודות מה cpu.

 

טעינה אסינכרונית של Resource ים

טעינה של Reource ים כדוגמת Textures , Vertex buffersלוקחת זמן ,ביצוע טעינה ב Rendering thread גורם לעיקוב פעולת הGPU ,בד"כ במערכות מורכבות לא ניתן כמו ב Demos לטעון את כל הReource ים בתחילת התוכנית בשל העובדה שהכמות של הReources ים די גדולה  אלא יש לבצע את הטעינה של ה Reources ים On the fly  במהלך התוכנית .

Directx 11 מאפשר לנו ליצור את הResources ב Thread נפרד שונה מה Rendering thread

dx5

Multithreaded Submission

האפשרות לחלק את העבודה של ה Rendering של ה Device בין מספר threads .כל Thread בונה Display List שבסיום הבניה משוגר ל Rendering thread .

mtdx16

 

 

mtdx13

 

Per-Object Display Lists

יכולת זו מאפשרת להכין בthread שאיינו ה Rendering thread   ,אסופה של פקודות Draw שנקראות Display List המרכיבות אוביקט לוגי בSence כדוגמת החייל שמורכב מאוסף הDraws הבאים :

 

mtdx4

mtdx5

mtdx6

mtdx7

לאחר שיצרנו את הBatch אנו יכולים ב Rendering thread להשתמש בו על ידי קריאה ל Execute של אותו Display List

mtdx8

mtdx9

mtdx10

 

אחרי שה Display List נוצר הוא Immutable אין אפשרות לשנותו על ידי הוספה או גריעה של פקודות Draw .לדוגמא להוסיף לחייל חרב , לדמות פגיעות וכד .

ניתן לשנות את הResources שמשויכים לאותו DisplayList החל מדברים בסיסים כמו מטריצות העולם שבו החייל נמצא וכלה במטריצות הסיבוב המשויכות לBones של החייל שנועדו ליצור תנועה .

 

מימוש

עד לגירסה DX11 היה בDirectX רק Interface אחד כדוגמת IDirect3DDevice9 ששימש לפיקוד על ה DX device בDX11 הפיקוד על ה Device מתחלק ל 3 בעלי תפקידם שונים.

mtdx23

ID3D11Device – הinterface הזה יכול להיקרא מכל thread שהוא ולבצע טעינת reosurce וכד במקביל לפעולת הרינדור

Rendering thread ID3D11DeviceContext – הDevice context ששייך ל Renderingthread .אחראי להזרמת הCommands ל Command queue של כרטיס המסך .

Deffered Thread ID3D11DeviceContext – ה context שנוצר בthread שאיינו הrendering thread ויכול לבנות Display List .

mtdx91

בDirectx Sdk האחרון ישנו demo המדגים את הטכנולוגיה הזו .

mt1 

עבדות הrender כאן יכולה להתחלק למספר חלקים בילתי תלויים:

  • ציור ההשתקפות של ה scene במראות
  • ציור הצל 

הDemo בא להראות איך ניתן להשתמש ב Multithreaded rendering על מנת להגדיל את מספר ה fps .

(ניתן להריץ את הdemo בכמעט כל כרטיס שתומך ב dx11 ולראות את השיפור בביצועים )

הdemo מדגים 5 מצבים של Multithreaded rendering .

Immediate

מבצע רגיל בו thead  אחד מבצע את כל העבודה משמש referance

ST Def/scene

בmode זה סתם מדגים את השימוש בDeferrered context ללא כל יתרון של ביצועים

MT Def/scene

בMode זה חלוקה של עבודת הציור לפי נושאים כדוגמת רינודר הצל המראות כל אחד ל thread נפרד .

ST Def/chunk

בmode זה עבודת הציור מחולקת לchunks אשר כולם מבוצעים באופן סינכרוני מיועדלהראות את הapi של חלוקה ל chunks

MT Def/chunk

בmode זה עבודת הציור מחולקת לchunks ואז מתרחשת חלוקה של ה chunks לפי מספר המעבדים .

ניתן לראות לפי המונה של Frame per seconds אשר מראה עליה במספר ה frames שמרונדרים על ידי הכרטיס בהתאם למצבי ה Multi threaded rendering השונים בהתאם לכרטיס המסך ולמספר ה cpu ים במערכת .

הרבה פעמים נתקלתי בקוד review שעשיתי בעבודה כבדה של ה CPU כדוגמת חישוב אילו אוביקטים נמצאים ב view frustum או אפילו עבודות AI בRendering thread, הדבר נובע מזה שברוב ה Demos וגם בDXUT הקריאה ל OnFrameMove callback מתבצעת ב rendering thread .דבר זה גורם לפגיע באיכות הrendering ללא קשר ליכולות הgpu .

הצרכניה וRecoverability של אפליקציה

ביום שישי שעבר בשעה 9 בבוקר בדיוק שהתחלתי לפרוק את המוצרים מהעגלה למסוע של הקופאית בסופר התחילו המסופים של הקופות אחד אחרי השני לקרוס וקול הצווינג של קריאת הbar code נדם  .

הקופאיות ניסו לרסט את המסופים אבל זה לא עזר ,והתור והלחץ של הקונים (יום שישי יום קצר) התחילו לגעוש .

היחידים שנראו מבסוטים מכל העניין היו הקרפיונים שקיבלו חסד של עוד כמה שעות לפני שטוחנים אותם ושמים להם חתיכת גזר על העין .ביחד עם אותו סבאלה שהחליט לספר צ'יזבטים על אותם הימים בהם קנית במכולת חצי כיכר לחם והתווכחת איזה חצי יותר גדול .

מנהל הסופר ניסה להתקשר למרכז התמיכה ולתאר להם את הבעיה שהמקסימום מידע טכני שהוא יכל למסור לאותה מרכזנית זה הודעה הזויה של :Failed to login due to transaction abortion משהו כזה ,משהו שנראה לי שגם למפתח הראשי של האלפיקציה לא היה אומר הרבה .

האפשרות לבקש ממנהל הסופר לייצר קבצי minidump ולשלוח למרכז הבקרה לא היתה ראלית ,כמו כן להגיד למנהל הסופר שכעת שולחים לו קבצי symbols של הגרסה שמורצת אצלו שיוריד בבקשה מה msdn את ה windbg  ויתחיל לדבג את האפליקציה (זה טוב להדגמות עם תכוניות קטנות ).

גם האפשרות לכרוז ברמקול של הסופר :יש פה תכנת בקהל גם לא נראתה משהו. במיוחד אם היו מוצאים תכנתים בקהל הלקוחות סביר להניח שעזרה לא היו מקבלים מהם אלא עצות וחוות דעת על למה צריך לכתוב את כל האפליקציה מחדש , למה כדאי לעבור לענן , אנדרויד , mvc4 silverlight 5 או spring .

לא משנה כמות המוטיבזציה שהיו יוצרים אצל מנהל הסופר על ידי משפטים כדוגמת "עתיד הסופר בידך אין אף אחד מאחוריך!!! " האפשרות להמשיך את יום הקניות כסדרו תלויה עכשיו אך ורק ביכולות הrecoverability של האפליקציה !!

כמעט כל אפליקציה יכולה לקורס אם זה עקב בעיית חומרה או בעיית תוכנה ,אני יודע שבמטוסי נוסעים ישנם לפעמים מספר מחשבים לאותה משימה גיבוי אחד לשני ,אצלנו בסופר בנס ציונה לא נראה לי שזה המצב .

אינני יודע למה לפתע קרסו כל העמדות. יכול להיות שהארכיטקט של המערכת לא לקח בחשבון אפשרות של נתק בין ה clients ל servers , או אפשרות שה server יקרוס והמערכת לא תוכננה כOccasionally Connected Systems Architecture .

יכול להיות שהמערכת תוכננה כ Occasionally Connected Systems Architecture אבל הפצה של poison message לclient שאין נגדו הגנה גרמה לשיתוק של כל העמדות .

דוגמא ל poision message הינה עדכון של פרי חדש (ספוטה שחורה ) בטבלת התמונות של הפירות עם תמונה בגודל 0 שגורמת לexeption

imagesCAW504NL

(הספותה השחורה הינה פרי רעיל כל עוד איננה בשלה כשהיא בשלה ניתן לייצר ממנה עוגות שוקולד מדהימות).

מרחב המקורות ל Malfunction  במערכת client server של 24 קופות שכל הזמן מעדכנות נתונים בשרת וכל הזמן נדחף להם נתונים חדשים כגון מבצעים של יום שישי כמעט ולא יכול להיות אפס .

עם הזמן כמות המידע שנצברת ומשתנה במערכת גורמת למצבים שלא תוכננו כמו שאילת select בהצבה למשתנה שאמורה להחזיר רק ערך אחד מחזירה יותר ואז ה store procedure נופלת .בעיות של buffer overflow , בעיות של race condition שנוצר עקב שילוב של כמות מידע וכד .

בכל מקרה לפי דעתי על הארכיטקט של המערכת לתת את הדעת שמצב של קריסה לוקלית בעמדה אחת או כללית בכל העמדות אפשרי .

ישנו פיתרון build in  לחלק מסוים מהבעיות מנגנון ה Application Recovery and Restart ומנגנון הWindows Error Reporting   ניתן למצא עליו פרטים ב http://msdn.microsoft.com/en-us/library/cc303708.aspx  אבל הפתרון הינו חלקי ולא היה עונה על הבעיה שהתוכנה בקופות לא קרסה לא היה second chance exceptionהמערכת ברוב העמדות אפילו לא נתקעה אלא פשוט הפסיקה לעבוד לקבל barcodes .יכול להיות מצב שרק ה תת מנגנון של בחירת פירות לא עובד והמערכת בלתי שמישה לחלוטין.

Recoverability  היכולת של אפליקציה לעלות מחדש ולהמשיך לעבוד מהנקודה בה היא הפסיקה אחרי נפילה באג או תקיעה .(אם אוטומטית או על ידי מפעיל ).

ברמה של  product management יש מספר עקרונות לתמיכה ב Recoverability

1.זמן recovery ועליה מחדש מינימלי.במשך השעה הזו שהקופות לא פעלו עשרות עגלות של מוצרים ננטשו על ידי קונים ממורמרים כמובן שאין זמן ל production debugging וכד  .

2.אפשרות ניטור הבעיה – מיועד למקרים קיצוניים בהם restart של אפליקציה לא עוזר בד"כ כאשר הבעיה נגרמת על ידי מצב סטאטי כמו בעיית חומרה כגון HD מלא , או בעיית תוכנה מסוג של poison message.

אפשרות לפתוח חלון של קופאי בכיר המציג את הבעיה מגובה תמיד באפשרות לפתוח קובץ לוג באמצעות Notepad ולהבין משם מה הבעיה .

3.החזרת האפליקציה במדויק לstate הקודם

האפשרות שאחרי reset של עמדה יצטרכו להתחיל חשבון שהוא כבר לפני סיום מחדש איננה פיסבילית לחלוטין ,הרי המסוע של המוצרים לפני הקופה כבר מלא במוצרים של הלקוח הבא . בזמן ההמתנה הלקוחות על מנת ליצור לפחות תחושה של התקדמות הערימו ערמות על גבי ערמות של מוצרים על המסוע .להתחיל להחזיר את המוצרים ממסוע הקידמי לזה שלפני הקופה ,תוך כדי שלקוחות מפחדים שיחויבו פעמים …….

בכל מקרה צריך לשים לב שהיו מספר reset ים של האפליקציה לפני שהיא חזרה לעבוד , לכן איפוס של ה state ששמור לצרכי המשך עבודה אחרי כל עליה הינו שגיאה לוגית ,אחרי שהאפליקציה חזרה לסורה למשתמש צריכה להיות האופציה לבחור את ה state של ה Recovery המתאים לו מבין כל הsession הקודמים .

4.מנגנון שמירת ה Session צריך לתמוך במצב  שלאפשר לעבוד בעמדה אחרת במקרה ועמדה התקלקלה לגמרי.

5.אפשרות לעשות roll back למידע חדש שיכול לגרום לmulfunction יכול להיות שזה מידע ב cahce  בלוקלי או מידע שנמצא בשרת ואינו מאפשר למערכות להמשיך לעבוד .

עקרונות ארכיטקטונים לתוכנה שתיתמוך ב Recoverability

הפרדה בין state לפונקציונליות

מבחינה ארכיטקטונית על מנת לתמוך ב Recoverability של האפליקציה יש להקפיד על הפרדה בין state ל פונקציונליות .

state שמופרד מהלוגיקה יכול להישמר תדיר לצרכי recovery על ידי מגנוני הסיראליזציה הקיימים כמעט בכל השפות והטכנולוגיות לדוגמא:מנגנון ה pickle , shelve  בפיטון .

האויב הגדול ביותר של ההפרדה בין state לפקונציונליות זה מימוש לא נכון של class properties כלומר get accessor שמשנה את הstate של הclass ו Set Accessor של property אשר כאשר קוראים לו מספר פעמים עם אותו הערך הstate של ה class לא נשאר אותו הדבר .

שמירה מתמדת על ה state של ה אפליקציה לצרכי שיחזור המצב , הstate השמור לא צריך להיות רק חלק מה db של האפליקציה אלא גם כגיבוי חיצוני חיצוני כדוגמת קובץ או שימוש בNo sql db   מסוג document .

יכול להיות שהstate ששמור הינו מינימלי כגון שלב עריכת החשבון של לקוח , רוב הנתונים כבר נשמרו בdb

Recoverability1

 

 

תיכנון המערכת כ Occasionally Connected Systems Architecture

מערכת שתוכננה כ Occasionally Connected Systems Architecture על אף היותה בהרבה יותר מורכבת ממערכת שהינה client server פשוטה , הRecoverability שלה בהרבה יותר גבוהה ,יכול להיות מצב כמו בדוגמה שלנו שהשרת התקלקל אפשר היה לסיים בשקט את הפעילות תוך כדי שמירה של כל הפרטים לdb לוקלי אשר יסונכרן בoffline מתישהוא .

Recoverability2

 

 

logging

logging  נכון ומפורט נטול מידע בילתי רלוונטי ומידע מיותר שחוזר על עצמו כגון להפציץ את הלוג בהודעות מחזוריות תקינות .לא פעם נתקלתי בלוג של כמה גיגות מפוצץ בהודעות :Message was send או second chance exception occurred מבלי stack trace  .

והכי חביב זה הודעות wrong value ב default של switch case מבלי לציין מהו הworng value .

יכלותי לעביר סדנה של כמה חודשים עם כמות משחקי הניחושים ששיחקתי בחיי בזכות עצלות רגעית של מפתחים .

 

זמן עליה \ ירידה של המערכת סביר

לא צריך להטעין את כל המידע שבעולם ל cache בזמן עליית המערכת .אפשר לטעון מידע בJIT

כלי ניהול

רצוי שכל הניהול של האפליקציה המאפשרים בחינה של ה Log ו ניהול הdb (מחיקה של poision  messages  ) לא יהיו חלק מהאפליקציה לא של ה client  ולא של ה server

Ndis filter driver part 2

בפוסט הקודם דיברתי על מרחב הבעיה , פתרונות אפשריים ותאור הDriver בקליפת אגוז ,הפוסט הזה אני אמשיך וארחיב לגבי המימוש .

לאחר שהעתקנו אלינו את הדוגמא של הDriver מה DDK מ

C:\WinDDK\7600.16385.1\src\network\ndis\filter

קימפלנו את ה code  של Driver (עדיין עם build בסביבת command  כשאצלנו בחברה יוצאים לסרט לתכנתי הdrivers מקרינים במיוחד סרטים מהתקופה של הכלים בהם הם עובדים רק בשבוע שעבר היה זה מבצע באנטבה עם יורם גאון ואריק שני.  אנו מתאפקים עוד קצת עד שvisual studio 11 ישוחרר ותכנתי ה drivers יקפצו 30 שנה קדימה) 

והתקנו את ה filter driver או virtual machine או במחשב אחר .

(בVirtual machine יש להקפיד שהקונפיגורציה של הרשת תהייה חיבור מלא אחרת זה לא יעבוד  )

ההתקנה מאוד פשוטה מהNetwork connections .

network1

 

לאחר שהתקנו את ה driver  אנו רוצים לראות שאכן ה driver עובד והודעות שנשלחות לNIC אכן עוברות דרך ה Filter Driver שלנו .

הContainer של הודעות המידע (בשונה מהודעות הoid עליהן ארחיב פעם אחרת ) שעוברות ב Ndis drviers לסוגיו (אם זה בminiports , Filters ,protocols driverts etc' ) הינו struct מסוג Network buffers list .

מבנה ה Network buffer list

ndis2_7

 

הפרמטר של ה  NetBufferLists בcallbacks של :

SendNetBufferListsHandler,

ReturnNetBufferListsHandler,

SendNetBufferListsCompleteHandler

ReceiveNetBufferListsHandler

מורכב מ linked list של NET_BUFFER_LIST struct 

כל NetBufferList מצביע על struct של linked list of  NET_BUFFER

אשר מצביע על Strcut של MDL  linked list

כל MDL strcut מצביע על buffer של מידע .

על מנת להרגיש את הפעולה של ה filter יצרתי תוכנית קצרה ששולחת הודעת udp ,העדפתי udp כי כמות המידע שזורמת בהודעת udp קטנה בהרבה מזו של tcp אין צורך בתעבורת handshake וכד

בכוונה אני עוצר את התוכנית רגע לפני שליחת ההודעה על מנת להימנע לעצור ב breakpoints שנגרמים כתוצאה מביצוע פעולת GetHostEntery .

 

ndis2_6

 

לעניות דעתי כלי ה Debug של ndis שמוכלים ב Ndiskd.dll מאוד חזקים ומאפשרים ניתוח מהיר ופשוט של הן המידע והן הstack של ה ndis

http://msdn.microsoft.com/en-us/library/windows/hardware/ff552270(v=vs.85).aspx

אחרי שהתחברתי למחשב שמריץ את הfilter driver טענתי symbols והרצתי את תוכנית הבדיקה ,אני שם breakpoint בcallback של FilterSendNetBufferList

 

ndis2_4

מיד אחרי שליחת ההודעה אני נעצר ב breakpoint

 

 

ndis2_3

באמצאות כלי הdebug של ndis בחנתי את תוכן ההודעה .

פקודת ndiskd.nbl פורסת אוטומטית את הnbl בהינתן ה Address של ה nbl הראשון .

כפי שרואים באיור הבא ההודעה מורכבת משני mdl ים אחד מכיל את הheader של הודעת הudp והשני את הpayload של המידע ששלחנו .

 

 

ndis2_2

הNBL מכיל metadata נוסף כדוגמת ה SourceHandle המוצג באיור הבא ממנו ניתן ללמוד על מיקום ה driver שלנו בndis stack ואיך ה ndis stack  בנוי ומורכב .

 

 

ndis2_5

Direct3d11 Shader reflection interface

אחד השינויים המהותיים ב Direct3D11 הינו שהשימוש בקבצי  Effect המרכזים את כל ה shaders ואת ההגדרה של ה resources שshaders צריכים הוחלף  בשימוש בקבצים דיסקרטיים עבור כל shader כלומר שימוש בקבצים מופרדים עבור ה pixel shader , vertex shader ה shaders של ה tesselation ה geometry shaders וכד .

ההגדרה של technique שמכילה את כל הflow החל מהכניסה ל  graphic pipeline ועד ל הגדרות של הz-buffer הועברה מהeffect file לקוד של האפליקציה .

שינוי נוסף ב Direct3D11 הינו שהhlsl5 הפך להיות  Object orientedנוספה תמיכה  בinterfaces , ב ירושות ,

בדומה לאבולוציה של ++C מ C .(מעניין אם יום אחד נראה קוד hlsl דינמי סטייל python או php משהו כמו if ( !isset ($light)) die ();

אם בגירסות קודמות שלן directx  היינו יוצרים תצרותיות של הקוד על ידי שימוש ב if define  כעת  .hlsl5 נותן לנו את היכולת להגדיר class קונקרטי עבור כל טכניקה של רינדור .

מכיוון שיש decouple בין האפליקציה ל shaderים ניתן לשכלל את ה shader ים ולהרחיבם מבלי לשנות את האפליקציה , הם לא צריכים להיות חלק מתהליך הקומפילציה ובלבד שכל הresources מסופקים להם .

לעיתים נוצר צורך מצד האפליקציה לתחקר את ה shader הנטען על ידה לגבי איזה class ים הוא מכיל איזה interface ים הוא ממש ואיזה resource ים הוא מגדיר זאת על מנת שהאפליקציה תוכל להפעילו .

הדבר דומה ל  reflection ב net.

DX3D11 נותן לנו כלים לביצוע התחקור באמצעות מנגנון :Shader Reflection API אשר במרכזו נמצא com interface  :

ID3D11ShaderReflection

לאחר שטענו את ה shader אנו יכולים לקבל את ה Reference  ל ID3D11ShaderReflection על ידי:

code1

הקוד הינו רלוונטי לכל סוגי ה shaders כלומר אין משתנה שמגדיר האם אנו עובדים עם ps , vs וכד .

דוגמא לשימוש בקוד הזה נמצאת ב DynamicShaderlinkageFX11 אשר ב Direct3d sdk

Dynamic Shader Linkage 11 Sample

 

הדוגמא מראה לנו איך אפשר לשנות את סוגי התאורה על ה scene באמצעות שימוש ב dynamic linkage של shaders .

החלק שנוגע לתחקור הshader מתבצע בפונקציה

OnD3D11CreateDevice

אשר נמצאת בקובץ DynamicShaderLinkage11\DynamicShaderLinkage11.cpp

בדוגמא מודגם  אפשרות של טעינה ושינוי דינמי של  תהליך חישוב ה pixel shader באמצעות class ים שונים הממשיים class אבסטרקטי  של חישוב מנת תאורה ל pixel .

כידוע חישוב מנת האור לpixel מורכב מכמה מרכיבים :

float3 Lighting = saturate( Ambient + Diffuse + Specular

 

עבור כל אחד מהרכיבים אפשר לחשב את המקדם של עוצמת האור בדרכים שונות בתלות בסיבוך של החישוב כמות ה resource ים שנדרשים וכד

code2

 

כאן אחרי שדגמנו את ה texture אנו מכפילים את הערך בפונקציה וירטאולית :IlluminateAmbient

 

בדוגמא ניתנים שני מימושים שונים קונקרטיים ל IlluminateAmbient

אחד הפשוט :

code3

והשני מסתמך על

Hemispheres of light כלומר התחשבות גם של האור הישיר של השמים וגם האור שמוקרן מהאדמה

code4

ה shader מגדיר שני הטכנולוגיות לחישוב הAmbient light  בעוד האפליקציה צריכה לבחור עם איזה מימוש משתמשים .

האפליקציה צריכה למצא ב shader  את המשתנה שמחזיק את ה reference   ל class שממש את חישוב האור :abstractAmbientLighting ולהציב לתוכו את ה instance של ה class הנבחר.

 

שלבים בתחקור של ה shader בsample ובהצבה:

1.קבלת ID3D11ShaderReflection

2.שאילתה לגבי  כמות ה interface slots ב Shader

כלומר שאילתה באמצעות הפונקציה

pReflector->GetNumInterfaceSlots

שמחזירה את כמות ה interface pointers בshader

הסיבה לשאילתה הזו הקצאת  מערך של

ID3D11ClassInstance

שיכיל את כל הreference   ים ל class ים הקונקרטיים כמו כן נשתמש בערך הזה בהמשך כאשר נציב ל shader את מערך instance ים שבחרנו להשתמש בהם

3.עבור כל pointer ל interface ספציפי אני מעוניין לדעת את מיקומו היחסי ב shader

זאת מתבצע באמצעות :

code5

 

כעת עלינו לקבל מ הshader את ה instance עבור כל אחד מה class ים הקונקרטים :

code6

 

לקבוע עבור ה render את הps איתו הוא יעבוד ,כאשר הפרמטר השני מכיל את טבלת הinterface ים של ה class ים הקונקרטיים מסודרים לפי מספר ה slot אותם תחקרנו והפרמטר השלישי מכיל את גודל טבלת הclass ים הקונקרטיים .

code7

 

כמובן שאפשר לשחק עם ה shader במהלך תהליך ה render לחלקים שונים להפעיל class ים שונים ,דוגמא זו שהינה פשוטה קובעת את ה ps רק פעם אחת במהלך render ,ניתן כמובן לחלקים קרובים לקבוע pixel shader מורכב ויקר מבחינת צריכת משאבים ולחלקים רחוקים להשתמש ב ps יותר פשוט .

Ndis filter driver

באחד מהפרויקטים עליהם עבדתי לאחרונה היתה דרישה ליירט כל network packet  שנכנס או יוצא מהNIC (ללא כל קשר לאפליקציה המקבלת או שולחת אותו ) לחפש pattern מסוים באותו הpacket של רצף bytes מסויים ואם הpattern  מופיע לבצע באותו packet שינוי .

כמובן ששינוי ה packet מצריך חישוב מחדש של ה checksum בהתחשב בפרוטוקול של ההודעה הנשלחת .

שתי טכנולוגיות נשקלו על ידנו לטובת מימוש הדרישה :

WFP קיצור של Windows Filtering Platform שהינו פלטפורמה לירוט של תעבורת רשת ,שהינה זמינה החל מwindows vista . זו גם הטכנולוגיה המומלצת בכל פה על ידי אנשי מיקרוסופט כמו אולר שוויצרי לצרכים כאילו .

NDIS filter driver שהינה סוג של kernel driver שמשמ לירוט ושינוי של תעבורת רשת.

אל אף היתרון הגדול של wfp המאפשר עבודה בuser mode  בחרנו לממש את הדרישה כNDIS filter driver . וזאת מכמה סיבות בהן:

גמישות –NDIS filter drivers  חושפים יכולות אשר WFP לא חושף לדוגמא אפשרות לעבד OID requests

מבחינת פשטות של קידוד הפתרון יותר פשוט לכתוב NDIS Filter driver  מאשר WFP .המבנה של אפליקצית wfp יותר מסובך מndis filter driver .

מכיוון שיש לנו בקבוצה יותר ניסיון בכתיבת ותחזוקת  NDIS drivers מאשר ב WFP .

כמו כן ישנה דוגמא שהינה השלד של ה פתרון שלנו בWindows driver kit ורוב הדוגמאות שמצאנו של WFP היו בנושא של inspection של packets  ופילטור של packets ולא של שינוי.

בניגוד לחלק מהקטגוריות של wdm drivers  ,דיבאגינג והרצה של NDIS drivers ניתן לעשות בvirtual  machine דבר שמקל מאוד את הפיתוח .

הדוגמא שיצאנו ממנה הינה :NDISLWF.SYS אשר נמצאת בnetwork\ndis\filter שהינה דוגמא של ndis 6.0 filter driver .

פעולות ההכנה שביצענו בדוגמא היו :

1.הוספת תמיכה ב tracing באמצאות wpp חשבנו על האפשרות של תמיכה מלאה ב etw  עם אפשרות של יכולות לשאילתות לגבי מספר ה packets  ששונו , יחסית למספר הpackets  שהתקבלו וכד אבל נראה כי לא היה לזה צורך מבחינת הנדסת המוצר ומכיוון שהוספת תמיכה בetw בהרבה יותר מורכבת מאשר wpp ,הסתפקנו בwpp .

מכיוון שעבודה עם virtual machine לצורך פיתוח dirvers לא רואים את ה output של DbgPrint החלפנו את כל הקריאות של DbgPrint ב DoTraceMessage של ה Wpp

2.הוספת תמיכה ב kmdf שזה nice to have וממש לא must .

המרחב הטריטוריאלי של הFilter driver בndis stack נמצא בין ה miniport adapter ל protocol driver כלומר המידע שמגיע עליו הינו מידע גולמי ישר מהNIC או row data שצריך להישלח לndis ,אין לנו בזמן העיבוד אינדיקציה לגבי הprotocol של ההודעה אבל אנו יודעים שהתשתית איננו מערבבת בין הודעות כלומר לא תגיע חצי הודעה או שתי הודעות באותה קריאה .

 

IC506632

הapi של מימוש ה driver מבוסס על פקודות מספריית הndis .

המבנה של ה Ndis filter driver הינו מאוד פשוט ומבוסס על רישום להודעות callback של התשתית.רישום לחלק מההודעות הינו חובה ולחלקן כאופציה . בסיום הטיפול בcallback הdriver צריך לקרא לפקודת ndis שמזרימה את ההודעה ל filter driver הבאה בתור.

כך שהpattern של ה driver נראה כ Observable pattern בשילוב עם chain of responsibility.

במהלך הביצוע של הDriverEntery ה Driver מבצע רושם את עצמו כ Ndis filter driver באמצעות קריאה לNdisFRegisterFilterDriver.

NdisFRegisterFilterDriver מקבל כפרמטר את הstruct של

NDIS_FILTER_DRIVER_CHARACTERISTICS שמכיל את הpointers ל callbacks של NDIS לדוגמא :

SendNetBufferListsHandler נקרא עבור  הודעות שנשלחות ל nic .

הcallback של SendNetBufferListsHandler מכיל referance ל NBL שמכיל את ה row data שנשלח בצורה של network buffer list .

בסיום עיבוד המידע הdriver חייב לקרא ל NdisMSendNetBufferListsComplete על מנת להמשיך את דרכו של ה NBL לעבר ה Miniport adapter של ה ndis

בישום שלנו היינו צריכים רק לשנות את ה payload בתנאים מסוימים כך שכל הודעה שהתקבלה ב callback אחרי עיבדו סינכרוני של המידע מיד המשכנו את התהליך על ידי קריאה ל NdisMSendNetBufferListsComplete ,יחד עם זאת ניתן לעבד את המידע אסינכרונית לקבוץ ולהפריד הודעות (חשוב להקפיד על הסדר של השליחה מהcallback).

 

הישום שלנו ממש את הטיפול ב SendNetBufferListsHandler   בצורה הבאה

 

 

Drawing2

 

בפוסטים הבאים אני אפרט את המימוש של ה filter לעומק.

 

 

 

 

 

Mirrors in 3D scene Part 1

קיבלתי את השאלה הזו באיימיל :

לצבי שלום

אנו כותבים אפליקצית תלת מימד ב directx 11 ומעונינים להוסיף ל scene שלנו מראות .

איך אפשר לעשות את זה באופן פשוט ?

ובכן יש מספר שיטות להוספת מראות ל scene .

בדרך כלל אנו מקבלים מהגרפיקאי הגדרות של מיקום המראות בנוסף להגדרות shading  של האוביקטים שצוירו במראה .

(הShading של התמונה במראה נועדה למנוע תחושה של השתקפות של 100% כמו מראה מושלמת אלא לבצע אפקטים שישנו את הדמות המשתקפת )

תפקידנו כתכנתים לדאוג להפיח חיים במראה כלומר לדאוג שלתהליך ההשתקפות הדינמי שכאשר דמות עוברת ליד המראה המראה תשקף אותה .

השיטה שאני אציג כאן מורכבת משלושה תהליכים מרכזים :

השלב הראשון הינו רינדור שטח המראה לתוך ה stencil buffer כלומר יצירת שטח ב stensil buffer אשר מיצג את שטח המראה .

השלב השני רינודר הscene מנקודת מבט של המראה .

השלב השלישי סידור הz – buffer  וה Setnecil buffer של ה Scene כך שפעולות הציור הבאות התור לא ישנו דברים בדמות שצוירה במראה .

מכיוון שאנו עובדים עם Direct3d11 התהליך יכול להתבצע בdeffred context על מנת למקבל את תהליך הרינדור כמו כן יש לזכור שתהליך רינדור כל

ה scene בשביל ההשתקפות במראה הינו תהליך יקר מבחינת ביצועים בדרך כלל המראה יחסית למסך קטנה ויש מקום להרבה אפילו הרבה מאוד אופטימיזציות .

חשוב לא לרנדר מראות שלא מופיעות בscene ברגע נתון לעבוד עם back buffer culling נכון ועוד .

בדמו MultithreadedRendering11 אשר נמצא בsdk  יש דוגמא לרנדור מראות בשיטה הזו .

Mirror1

 

הרינדור של המראות בDemo הזה מתבצע בפונקציה :RenderMirror אשר מקבלת את ה ID3D11DeviceContext

שעליו צריכים לצייר את המראות .

 

התרשים זרימה של הפונקציה הזו הינה :

mirror2

 

בפוסטים הבאים אני אנסה לתאר לעומק את כל אחד משהלבים הן מבחינת Direct3d api והן מבחינה מתמטית של הscene

 

 

 

 

 

 

הטעות של מיקרוסופט לפי דעתי עם מנגנון ההפצה של אפליקציות לסלולאר

מיקרוסופט נכנסה לתחום של סלולאר בעמדה נחותה .בשוק ישנם כבר כמה שחקנים ראשים :אפל ואנדרואיד

שכשהם מסתכלים אחורנית הם לא רואים את מיקרוסופט בתחום .

הנקודה המאוד חזקה ואולי היחידה של מיקרוסופט לפי דעתי הינה כלי הפיתוח .

תכנת יכול בחינם להוריד את2010  Visual studio express  מאוד בקלות להתקין את התוספים לצורך פיתוח ב Windows phone 7  התכנות בsilver light הינו קל פשוט תוך פרק זמן מינימלי אפשר להרים אפליקציה בינונית. וגם אפליקציה מורכבת זה לא דבר מסובך.

הטעות הפטאלית שלצערי מיקרוסופט עושה זה שהיא מונעת בכל מחיר את הקשר הישיר בין המפתח לצרכן על ידי זה שהיא מחייבת שכל אפליקציה שמפותחת לwindows phone 7 תימכר לצרכן דרך Windows Phone Marketplace שלה תוך כדי שהיא גוזרת קופון . ומכתיבה תנאים לתוכן האפליקציה וכד .

הקשר הישיר בין המפתח לצרכן לפי דעתי זה מה שבנה את מיקרוסופט ללא המודל הזה ,אם על כל אתר אינטרנט שנכתב או כל אפליקציה שנכתבה לwindows היה המפתח מחויב למכור אותו דרך מיקרוסופט ,מיקרוסופט לא היתה מגיע לאן שהיא הגיע .

אומנם אפל עובדת באותה שיטה והיא מצליחה אבל מיקרוסופט צריכה להביא את הadd in value שלה על מנת להאבק בנחיתות החומרתית ובמספר האפליקציות שהמתחרים מציגים .

אם לי יש סוכנות של מוצר צריכה עם 4 עובדים משווקים ואני יכולתי לרכוש עבורם מכשיר נייד ולהשקיע מינימום שבמינימום בפיתוח אפליקציה שרובה hard coded ופעם בשנה לבצע עדכונים זה היה פותח שוק גדול בפני מיקרוסופט לפי דעתי .

לפי דעתי מפתחים ויזמים לא אוהבים את הרעיון שמישהו יפלטר אותם ויכתיב להם תנאים והגבלות הן לאפליקציה והן למודל העסקי .

Hardware Tessellation

אחת הבעיות הגדולות שיש לנו בסימולטור הינה  אובייקטים תלת מממדיים המכילים כמות עצומה של פוליגונים ישנם אובייקטים המגיעים לסדר גודל של מיליון פוליגונים באוביקטים כאילו הVertex Buffer יכול להגיע לסביבות 30M הindexBuffer יכול להגיע ל 15M .כאשר יש מספר אוביקטים כאילו מפורטים בscene המרונדרים בLOD 1 המערכת נחנקת גם בחומרה מקצועית ביותר .

בעיה נוספת שיוצרים אובייקטים כאילו גדולים הינה ברגע שצריכים לבצע חישובים כדוגמת Collosion Detection הדבר כמעט בלתי אפשרי מכיוון שמעבר על כל ה vertex ים בחישובים דורש כח חישוב רב .

בעבר כבר הומצאו הרבה פיתרונות לבעיה כדוגמת Bump Maps שהינה texture resource המכיל ערכי Normal של surface .כאשר משתמשים בטכנולוגיה של bump map ב pixel shader בזמן חישוב את עוצמת האור של pixel מתחשבים עם בערך הדגם מ Bump map וכך יוצרים אשלייה יש משטח לא חלק , זו שיטה שאומנם עובדת ובהרבה אפליקציות משתמשים בה אל היא די פרמטייבית בעלת הרבה חסרונות בעיקר מכיוון שהיא בעצם לא משנה את האוביקט ואז לדוגמא הצל של האוביקט שונה ממה שהוא היה צריך להיות במציאות .

 

השיטה שכרגע נחשבת לטובה ביותר הינה displacement map שיטה זו בניגוד ל שיטות כמו  bump mapping, normal mapping, and parallax mapping כן משנה את מיקום הvertex . (בהזדמנות אני אכתוב על displacement map לעומק ).כדי להשתמש ב displacement map יש להכין את ה mesh עליו רוצים לעבוד על ידי חלוקתו לתתי פוליגונים .

 

ht2

 

כפי שרואים בתמונה בשורה השניה התהליך של Displacment map מתחיל על ידי mesh בעל רזולוציה נמוכה העובר פעולת tesselation כלומר כל vertex מחולק לתת פוליגונים אשר מוזזים בשלב השני על פי הdisplacment map. השורה הראשונה באיור מציגה את tessaltion של map לפי רמות .

לשיטה הזו גם יתרונות לגבי lod של פריטים ב scene לדוגמא אם ניקח עדר של חיות ,בעלי החיים שאנו רוצים ל רנדר עם lod גבוהה אנו נבצע להם פעולת tesselation עמוקה לאומת חיות שנמצאות רחוק מהמצלמה ועליהם או שלא נבצע כלל או שנבצע tesselation ברמה נמוכה .

 

פעולת הtesselation  צורכת כח חישוב רב שעד לא מזמן לא היה אפשרי לבצעו ב render חי של משחק וכד , אפליקציות שהיו צריכות לבצע tesselation ביצועו את הפעולה ב offline  .

ב Direct3d 10 כבר התחילו לנסות ולפתור את הבעיה על ידי חלוקת הmesh לתת פוליגונים באמצעות הGeometry shader  , זה עבד בחומרה חזקה בצורה סבירה אבל עדיין זה לא היה זה .

Direct3d 11, כבר תומך בפעולה הזו מול רכיבים ייעודיים בחומרה שמטרתם לבצע את פעולת הdisplacment . (המוצר הראשון שתמך ביכולת הזו אומנם בצורה פחות חזקה הינו הxbox 360 ).

 

פעולת הtesselation מתבצעת מול כל סוגי הprimitive או ה patches ( ב Direct3d 11 נוספו עוד סוגי resource בנוסף לprimitive שיכולים להיכנס ל Input assembler stage  שמיועדים לתמיכה ב tesselatio ה petches) .

 

ht4

 

התהליך של הtesselation מורכב מ חלוקה (sub divition ) של ה primitive ומיפוי כל vertex חדש ל mesh .

 

לצורך תמיכה ב tesselation נוצרו עוד 3 שלבים ב direct3d pipline של direct3d 11:

ht5

שלושת השלבים החדשים אשר מופעים בירוק יעדם תמיכה ב hardware tesselation .

כאשר 2 מהם הם ניתנים לתכנות ואחד הינו קבוע אבל אפשר לשלוט על הפרמטרים של דרכי פעולתו .

הVertex shader

מבצע את אותן הפעולות כמו בעיבודים שאינם כוללים tesselation

אם זה טיפול בכל הנושא של  transformation של המרות ל מרחבים הרצויים של ה vertexים וכד

הHull Shader

זהו שלב ההכנה ל tesselation זהו שלב שהוא fully programmable

לשלב הזה יש שני תפקידים

1.לקבוע את מקדם החלוקה של כל נקודה: tessellation factors , כל נקודה יכולה לקבל ערך בין 1 ל 64 (כאשר ערכים שלא בתחום כמו 0 יגרמו לנקודה לא להשתתף בתהליך ) בכניסה לפונקציה שמבצעת את הtesselation מקבלים את כל הנקודות בpatch עבורן יש לקבוע את הערך של ה tessellation factors , היתרון בזה שאפשר לקבוע לכל נקודה באופן פרטני את ערך ה tesselation שלה הינו לצורך טיפול בlod )

2.התפקיד השני של ה hull shader זה לפעמים צריך לבצע טרנספורמציה של הנקודה בהתאם לאלגוריתם שיתאים ל tesselation לא תמיד מתבצעת טרנספורמציה בשלב הזה הרבה פעמים הcontrol points מעוברות כמו שהן .

הכניסה ל Hull shader ישנם בין 1 ל 32 נקודות שמרכיבות את ה patch עליו צריכים לבצע tesselation

והוא כאמור מוציא הם את הcontrol points שעברו טרנספורמציה אם נדרשת ואת ערכי ה factor .

 

ht6

כמו כן באמצעות attribute על הפונקציה של ה hull shader בדומה לattribute שלC# ב net . נקבעים פרמטרים בשביל השלב הקבוע של ה tesselation .

 

ה Tesselator

זה בעצם השלב שמבצע את פעולת הtesselation הוא אינו programmable אבל אפשר לשלוט עליו באמצעות פרמטרים התוצאה של שלב זה הינם בערכי uv של texture .

 

הDoamin Shader stage

הינו השלב שתפקידו לבצע את הטרנספורמציה של התוצר של ה tesselator שבא בצורת ערכי uv בחזרה לvertex ים .

 

הVertexים שנוצרו ב Domain shader ממשיכים ל geometry shader ומשם ההתנהגות  של ה pipeline רגילה לחלוטין .

בpostים הבאים אני אפרט על כל השלבים , אופן התכנות שלהם וכמו כן אכתוב על הdisplacement map ושימושו.

 

 

 

 

 

 

 

 

 

 

 

 

 

#F ובגדי המלך החדשים

אחרי שקראתי את http://blogs.microsoft.co.il/blogs/pavely/archive/2010/07/20/how-to-be-a-cool-c-programmer.aspx

חשבתי מה זה בעצם השטויות האילו של הסינטקס המסובך הזה בC#:

אני חושב שבאמת היתרון הגדול ביותר ובעצם המטרה של כל הSyntex ים של lambda expression  , Linq וכד הינו ששימוש תדיר בהם חוסך לחלוטין ארועי code review מכוננים על ידי ראשי צוותים ומנהלים שפתאום הקוד נהפך בשבילם לבלתי קריא בעליל .

באמת פעם היו עושים צוותים שלמים + מנהלים ישיבות code review  משותפת ארוע שנעלם כמו השקמים בתל אביב החיים בלי פאלפון ,טלביזיה עם ערוץ אחד  ומערכת הפעלה לטלפוניים ניידים ,מחשבי כף יד מבית מיקרוסופט  ישנם אגדות עם שפעם עשו את זה .

כמו בסיפור בגדי המלך החדשים ראשי הצוותים והמנהלים שלא מתעסקים ברמה היום יומית בקוד  לא יודו שהם לא מבינים כלום .אלא רק יסתכלו על הקוד עם עיני עגל משתהים וחושבים בלב :מה קרא לנו ? איך פתאום אנו לא מבנים כלום בתכנות ,אסור שאף אחד  יגלה את זה אחרת אנו אבודים במקרה ויצמצמו צוותים ונצטרך לשוב להיות תכנתים .יכול להיות  שכולם מבינים מה כתוב כאן חוץ ממני ?.

איזה שטות עשיתי שהסכמתי שנעבור ל.net  מה רע היה לנו עם visual studio 6.0 C++  הבנתי אז יופי את הכל ,בעצם כמעט את הכל חוץ מsmart pointers  ו templates מסובכים . בעצם גם על copy constractor מעולם לא הייתי סגור.

בכל מקרה אני חייב להגיד עכשיו משהו חכם ומהר . אולי אני העיר שאין מספיק הערות בקוד ? לא לא רעיון טוב הם יחשבו שאני רוצה הערות כי אני לא מבין את הקוד.

 צריך להמשיך להביט בבליל מילים וסימנים האילו בפרצוף רציני שנראה כאילו קוזמו קרמר בפרק שהוא הצטרף לאיזשהיא עבודה במקרה כי הוא גרי וגורג חיפשו שירותים בבנין כתב את הקוד .

הסינטקס המוזר התחיל בהרבה קודם מ Linq  ו lambda expressions  כאשר הם נתקלו בsyntex של c# property רק עם get;set;

אני חושב שמיקרוסופט הבינו את הצורך והמצוקה של התכנתים שראשי הצוותים ומנהלים לסוגיהם לא יתערבו להם בקוד ויתנו לפתח בשקט backdoors ,פיצרים שהם קולים אבל באמת שאף אחד לא צריך וכד ואז מיקרוסופט פיתחו רעיון גאוני בצורת שפת תכנות חדשה שאף אחד לא מבין

שפת תכנות המבוססת על צופן גרמני סודי היחיד של פוצח על ידי המודעין הבריטי

קבלו את השלמה ארצי של כל שפות התכנות #F

אני יודע שבאחת מהתעשיות הביטחוניות  במספר צוותים כולל הצוות שעובד על מערכות האוויוניקה של מטוס הקרב העתידי של מדינת ישראל הלביא (אין לכם מושג איזה ביטחון תעסוקתי נותן ועד עובדים חזק שלא מרשה שסתם כך יסגרו פרויקטים על ימין ושמאל מבלי סיבה רצינית)  התחילו לפתח בF# ההוראה שיצאה מהקב"ט של החברה הינה שמחשבים הנושאים קוד F# ללא קשר לסיווג הפרויקט יכולים להיות מחוברים ישירות לinternet אפילו בלי fire wall או אנטי וירוס הסינטקס של השפה מספק הגנה אולטימטיבית מפני כל מי שמנסה לפענח את הקוד של שפה שהיא בעצם שילוב של דוטפרוסטראקטור ושפת תיכנות.

והוא כלומר הקב"ט מתקשה מאוד לדמיין את אחמידניגד , נאסארלה ,או  חאפז אל אסד גוניור   וב"ב בכובע של השחקן מנפלאות התבונה מנסה לפצח את הסוד השמור מכולם איך לכול הרוחות והשדים סוגרים לולאה בשפה המקוצמת הזו .

אני זוכר שבבית ספר התיכון (הייתי בבית ספר תיכון של חנונים לא של יובל בנאי ) לימדו אותנו ששפת תיכנות אז קראו לזה שפות הדור השלישי תוכננו כך שהם היו דומות לשפה אנושית ויחסכו את הצורך להתעסק עם assembler  ב F#  נראה לי שעוברים לassembler  כדי להבין למה הקוד מתכוון .

לאן נעלם הדמו של DisplacementMapping10 ?

בגרסאות קודמות של Direct3d הופיע Demo בשם DisplacementMapping10 שבא להדגים את היכולת של Displacement Mapping בהשוואה ל לטכניקות פשוטות יותר כמו Normal mapping .

Displacment Mapping demo

התוצאה של הDemo הינה רידנור של לטאה כאשר משתמשים ב Displacement Mapping ניתן לשנות את המעטפת של הMesh (רינדור הסנפיר על הגב של הלטאה ) דבר שהינו בילתי אפשרי בטכנולוגית Normal mapping המצויה .

disp1

בהשוואה ל רינדור עם Displacment mapping

dis2

 

הבעיה עם ה Demo הזה לפי דעתי היתה איטיות גם בכרטיסים לא חזקים לכן אולי הוא ירד .

הטכניקה של Displacment mapping  (קימות כל מיני וריציות ) קיבלה boost  בDirect3d11 בשל השימוש ב Hardware tessellation stage  שמאפשר פיצול פוליגונים שהינם חלק מה mesh  לפוליגונים קטנים .

מכיוון שפעולה זו מתבצעת על ידי החומרה של כרטיסי מסך תומכי Direct3d11 יש שיפור ניכר בביצועים של אפליקציות וdemos שמשתמשים בטכנולוגיה של Displacement mapping .

Tangent space

Tangent space שלפעמים נקרא גם Texture Space  הינו מרחב בעולם התלת מימד שנוסף ל World, View ,Projection  שייעודו הינו לחסוך משאבי GPU של חישובי טרנספורמציות  של  בPixel Shader   .

כאשר מיישמים Per Pixel lighting  יש הרבה טכנולוגיות שדורשות שלכל Texel בTexture  יוצמד גם ערך של Normal  דוגמא לטכנולוגיה כזו הינה : Normal mapping.

בד"כ כלל לאובייקטים התלת מימדיים עליהם אנו צריכים לישם טכנולוגית הקשורות ל per pixel light  אנו מקבלים מהגרפיקאי שני  קבצי Resource :קובץ של Color map וקובץ של Normal map   כך שלכל Texel  ב Color Map  יש את וקטור הנורמל המשלים שלו ב Normal Map.

ערכי ה Texture מבוטאים על ידי color ערכי ה Normal  מבוטאים על ידי vector מנורמל .

אם מגדירים  את הערכים ב Normal Map  כוקטורים ב  object Space  מכיוון שכאשר מיישמים חישוב של light  ב Per pixel light  גם ה Light Direction  וגם ה Camera vector  משתתפים בחישוב היינו צריכים על כל אחד מערכי ה Normal  שדגמנו מה Normal Map  לבצע טרנספורמציה למרחב בו מוגדרים הוקטורים של ה Light  ו ה Camera  . דבר זה יגרום לעומס עבודה ב Pixel shader  .

על מנת לחסוך את הטרנספורמציה הזו מגדירים מרחב חדש בשם Tangent space 

מרחב הצירים בTangent space  מוגדר כ:

ה Vector  X  מוגדר לאורך ה U  של ה Texture  מוגדר כ Tangent

ה Vector   Y מוגדר לאורך הV של ה Texture  מוגדר Bitangent

ה Vector  Z מוגדר כנורמל של ה Surface  .  מוגדר כ Normal  

כאשר עובדים עם Tangent Space  הNormals ב Normal maps מוגדרים ב Tangent space  .

ערכי ה Normal  נדגמים מהResource view על ידי ה PixelShader בדומה לדרך שבה דוגמים Texture רגיל כאשר ערכי ה xyz  נמצאים ב ערכי הrgb

float3 n = normalize(tex2D(normalMap, IN.texCoord).rgb * 2.0f - 1.0f);

עבור כל polygon  את ה light direction  וה Camera vector  אנו צריכים להמיר ל.Tangent space  פעולה זו מתבצעת עבור כל וקטור ב Vertex Shader  וכך אנו מקבלים ב Pixel shader  את כל הוקטורים באותו מרחב (Tangent space ) דבר שחוסך לנו טרנספורמציות עבור כל texel  .

על מנת להשתמש עם Tangent space  אנו צריכים לבצע את הפעולות האילו

נצרף אלמנט של Tangent  ל

D3D10_INPUT_ELEMENT_DESC

   { "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 32, D3D10_INPUT_PER_VERTEX_DATA, 0 },

אשר יוכנס לפקודת  CreateInputLayout

מכיוון שיש לנו גם את הערך של ה Tangent  וגם את הערך של הNormal  אנו חוסכים את המרכיב של ה Bitangent  בכל Vertex אותו נחשב בVertex Shader  באמצעות Cross product .

במקרה והobject  שאנו הולכם לרנדר לא מכיל ערכי Tangent  מחושבים עבור כל ווקטור  אנו צריכים לחשב את הערכים האילו לפני ששולחים את הVertexים לInput-Assembler Stage .

יש הרבה דוגמאות קוד ברשת לקוד שעובר על כל הפוליגונים של ישות ומחולל ומחולל לכל Vertex את ערכי ה Tangent שלו .

ב Vertex Shader  אנו כאמור צריכים להמיר את המרחב של ה Viewer vector  והlight direction  ל Tangent space 

השלבים ביצירת מטריצת ההמרה לTangent space הינם :

אנו כופלים את הערכים של ה Vertex  במטריצה ההופכית המשוחלפת של ה World transformation

  float3 n = mul(IN.normal, (float3x3)worldInverseTransposeMatrix);

      float3 t = mul(IN.tangent.xyz, (float3x3)worldInverseTransposeMatrix);

אנו מחשבים את רכיב ה Bitangent  על ידי Corss product  כאשר ה ערך של IN.tangent.w מכי לערכים או של 1- או 1 שמצבעים על הקוטביות של ה Tangenet space coordinates

      float3 b = cross(n, t) * IN.tangent.w;

מרכיבים את מטריצת ההמרה למרחב ה Tangent sapce

      float3x3 tbnMatrix = float3x3(t.x, b.x, n.x,

                                    t.y, b.y, n.y,

                                    t.z, b.z, n.z);

המטריצה קודם מחזירה את הvetcor ל מרחב העצם ממרחב העולם ואח"כ מעבירה את העצם למרחב ה Tangent

כעת שיש לנו את מטריצת ההמרה אנו מכפילים את ערכי ה Light direction  וה camera vector  ב מטריצת ההמרה .

את הערכים המומרים שקיבלו אנו מציבים בstruct  התוצאה של ה Vertex shader  והם ישמשו ב חישוב ה light  ב pixel shader .

כאשר בשלב של ה Pixel shader כל הוקטורים או הערכים שמשמשים לחישובי אור וכד נמצאים במרחב ה Texture דבר שחוסך לנו המרות של וקטורים ב pixel shader .

שילוב בין Directshow ל Direct3d

איזה דוד שאל שאלה בפורום תמיכה של Codeproject .

יש לו אפליקציה שמרנדרת Scene  של Direcrt3D והוא מעוניין לשלב את התוצאה שלה עם streaming של וידיאו באמצעות DirectShow filter .

הוא שואל איך עושים את זה ?

ובכן לפי דעתי הכיוון הוא לא נכון .שילוב נכון של directshow video  ו Direct3d עדיף שיתבצע באמצעות התשתית של Direct3d ולא של Directshow  .

לכך יש מספר סיבות .

1.התשתית של Direct3D מתעדכנת טכנולוגית ומקבלת תשומת לב ממיקרוסופט בניגוד ל Directshow שתקוע כבר למעלה מ10 שנים באותו מקום .

2.על מנת לעבוד את התוצאה של הdx pipeline בתוך directshow filter  צריכים גישה של הcpu לתוצר של הoutput merger stage.

אומנם הרינדור של Direct3d יכול להתבצע על Resource מסוג Texture אבל הResource חייב להיות מוגדר עםdefault usage שהכוונה שרק ל GPU יש הרשאת כתיבה וקריאה מה Resource הCPU  לא יכול לגשת לTexture שהוגדר כ ID3D10RenderTargetView .

3.גם אם היינו משיגים את התוצאה של הdx pipeline המיזוג שלה עם הsurface של הdirectshow הינה פעולה יקרת משאבים מבחינת CPU סביר להניח שהפורמטים שונים ויש מצב שעל כל byte נצטרך לבצע פקודת if בCPU + הצבות וחישובים ,וכל frame מורכב מ 640 * 480 קריאות כאילו .

מניסיון מחשב חזק יכול לתמוך בפעולה כזו אבל חלק ניכר מהמשאבים כל הזמן ילך על הפעולה הזו .

4.בdirectshow filter  אין גישה לכל נתוני התוצר של הoutput merger stage הכוונה לZ- Buffer ו stencil Buffer דבר שלא יאפשר לבצע חישובי הסתרות וכד כלומר הוידיאו לא חלק מהעולם של ה3D.

5.הוידיאו לא יהיה חלק אינטגרלי מהעולם כלומר כל חישובי ההצללה והתאורה לא יכללו בvideo מכיוון שהם נעשים בPixel shader .

בפוסט הבא אני אשתדל לכתוב איך לפתור את הבעיה באמצעות הפיכת ה Video Surface לDirect3D texture resource ושילובה בrendering של ה Sence .

שילוב בין שתי אפליקציות המרנדרות dx sences

לפעמים יש צורך לשלב בין שני scene ים ששתי אפליקציות Direct3D שונות יוצרות לתוך Scene   אחד .

לדוגמא: אני רוצה להשתמש באפליקציה שלי בBing maps  לצורך יצירת ועדכון הterrain ומעל הTerrain  אני צריך לרנדר  את האובייקטים שלי .

בניגוד למקרה עם אפליקציות 2D שאולי אפשר לשלב תמונות על ידי משחק עם ערכי alpha המצב בעולם של התלת מימד מורכב בהרבה .

הטכנולוגיות יכולות להיות מדור שונהBing  עובד מול Direct3d9  ואני עובד עם Direct3d11  .כל ה api והאובייקטים שונים מאוד .

פעולת הrender חייבת להיות משותפת על מנת שהZ-buffer filter  יפעל גם על האובייקטים שלי וגם על האובייקטים של Bing .כלומר אם מכונית שאני מרנדר נמצאת מאחורי הר שbing  מרדנר ההר צריך להסתיר את המכונית שאני מרנדר .

פעולות בshaders  של הצללה ואור צריכות להיות משותפות .(הקונספט של שמש בגבעון דום וירח בעמק איילון לא תופס כאן ) אותו מקור תאורה משפיע הן על האובייקטים שלי והן על האובייקטים המיובאים .

כמו כן יש אוביקטים בscene  של Bing  שאני לא רוצה שירונדרו אצלי באפליקציה אני רוצה רק את הTerrain  נטו מבלי כפתורים שBing מרנדרת על הscene  שלה .

מכיוון שאי אפשר לשתף את הsurface ים בין האפליקציות וbing לא פותח לנו api  של קבלת הResource ים מbing  Vertexים והTexture ים ) אנו צריכים להוציא את הResources מBing לא בדרך המלך.

הפתרון שמימשנו

הפתרון מתבסס על יירוט של הקריאות ל dx api  על ידי Bing .

יצרנו אפליקצית .net win form שמריצה WebBrowser Control ולתוך ה WebBrowser control  אנו טוענים עמוד html  מקומי שמציג את Bing  אפשר לשלוט על Bing  באמצעות פקודות של automation  פשוטת של בחירת המיקום בעולם שהמפה מציגה וכד .

אנו יוצרים  proxy dll   שתפקידו ליירט קריאות מBing  ל d3d9.dll ישנם מספר דוגמאות לשלד של dll  כזה במרחבי ה Web  . הDLL בשם  d3d9.dll  ממוקם בספריה שמריצה את האפליקציה עם Bing ולכן Bing טוען את הProxy dll שלנו כאשר הוא קורא לCreateDevice

אנו כתבנו proxy ל interface ים החשובים של Dx9 אשר הם נקראים במקום האורגינלים .

ברגע שאנו מיירטים פקודה שBing פקד וזו פקודה שמעדכנת Resource  אנו מעתיקם את המידע שלה ל shared memory  שמשותף ל dll  המירט ו האפליקציה שלנו וכך יש לנו ביד את הpre resource  ואנו מכניסים אותו ל pipline  שלנו בתהליך הRender  שלנו .

אנו צריכים לקלף או להתאים את הResource לDX11  יש שוני מהותי בין העבודה עם Resources  בDX9 שהשתנה עם המעבר ל DX10 יש שונה בהדרת הSurfaceים וכד

אין צורך שתהליך הירוט יעביר את קריאות הRender  ל d3d9.dll האורגינל חבל על תעבורה על הBus המיותרת ועבודת הכרטיס מסך.יש פקודות בעיקר אתחול שכן צריכות לקרא ל d3d9.dll המקורי על מנת ש bing  יוכל לאתחל את העבודה שלו. הכל בתלות במשחק של trial and error .

 

המשתלה

בפוסט הקודם כתבתי על שימוש ב Billboard לטובת יצירת עצים על Terrain התוצאה של Billboard הינה בסדר אבל לא מצוינת הרי עצים לא יכולים להיות סימטריים לחלוטין מכל כיוון ותנועה של המצלמה מוגבלת לציר Y .

אצלנו באפליקציה נדרשנו לעצים יותר מציאותיים וכמו שחקלאי בעל מטע אבוקדו לא מגדל את העצים שלו לבד מאפס מגלעין בתוך כוס של אשל עם קיסמי שיניים אלא קונה שתילים מוכנים ככה יש חברה שמוכרת עצים תלת מימדים באינטרנט: SpeedTree . הם מוכרים גן בוטני שלם תלת מימדי. קצת הרבה יקר אבל יחסית להעסיק מעצב גרפי + תכנת שיצרו עצים מ 0 זה סיפור בהרבה יותר יקר.

לשמחתי לא אישרו לי לקנות עצים כאילו מוכנים לכן נאלצתי להפשיל שרוולים וליצור עצים לבד עם סיוע של מעצב גרפי ומאיה .

ברינדור העץ יש שני חלקים כאשר כל חלק עם האתגר שלו .

הענפים והעלים :שכאן האתגר הינו בעיקר להתמודד עם הכמות הגדולה של עלים וענפים כאשר איננו רוצים לחנוק את הbus ואת ה gpu עם מיליוני הvertexים  כאן הGeometry Shader הינו מכשיר יעיל ביותר כאן אני אגע בנושא זה בהמשך .

הגזע של ה עץ : עצים מכיוון שהם יצורים אורגנים חיים הם לא בנויים כגוף חלק כמו מטוס אלא מהרבה הרבה בליטות וזיזים .כאשר החלק המאתגר נמצא בעיקר בשוליים של העץ. הריבועים הכחולים בתמונה .

IMG_3004_2

 

את הפנים של העץ (ריבוע צהוב ) ניתן לרנדר באמצעות טכנולוגיות פשוטת ועתיקות כגון BumpMapping .

הבעיה עם BumpMapping שטכנולוגיה זו איננה יודעת להתמודד עם השוליים של אוביקט כמו כן כאשר המצלמה זזה לא רואים את השינוי בעומק של הקליפה.

יש מספר טכנולוגיות שמשפרות את הביצועים של bumpmapping  כגון :

parallax occlusion mapping

Relief mapping 

displacement mapping

כאשר לכל אחד מהן יש את המגרעות שלה ( displacement mapping  למרות שמסוגלת אלגוריטמית לתת את הפתרון חונקת כל כרטיס מסך שאיננו תותח על ).

בפוסטים הבאים אני אתאר את הפתרון שלנו לגזע ובהמשך לעלים .

More Posts Next page »