منظور رسم الخرائط الصحيح. قد يكون حساب المسافة Z خاطئًا

StackOverflow https://stackoverflow.com/questions/2068134

  •  20-09-2019
  •  | 
  •  

سؤال

أنا أقوم بعمل مجموعة من البرمجيات ، وقد واجهت القليل من العقبة: لا يبدو لي أن أحصل على رسم خرائط للملمس المنظور.

الخوارزمية الخاصة بي هي فرز الإحداثيات أولاً y. هذا يعيد أعلى وأدنى ونقطة مركز. ثم أمشي عبر خطوط المسح باستخدام دلتا:

// ordering by y is put here

order[0] = &a_Triangle.p[v_order[0]];
order[1] = &a_Triangle.p[v_order[1]];
order[2] = &a_Triangle.p[v_order[2]];

float height1, height2, height3;

height1 = (float)((int)(order[2]->y + 1) - (int)(order[0]->y));
height2 = (float)((int)(order[1]->y + 1) - (int)(order[0]->y));
height3 = (float)((int)(order[2]->y + 1) - (int)(order[1]->y));

// x 

float x_start, x_end;
float x[3];
float x_delta[3];

x_delta[0] = (order[2]->x - order[0]->x) / height1;
x_delta[1] = (order[1]->x - order[0]->x) / height2;
x_delta[2] = (order[2]->x - order[1]->x) / height3;

x[0] = order[0]->x;
x[1] = order[0]->x;
x[2] = order[1]->x;

ثم نعرض من order[0]->y ل order[2]->y, ، زيادة x_start و x_end من دلتا. عند تقديم الجزء العلوي ، تكون الدلتا x_delta[0] و x_delta[1]. عند تقديم الجزء السفلي ، تكون الدلتا x_delta[0] و x_delta[2]. ثم نقوم بالالتفاف خطيًا بين X_Start و X_END على خط المسح الخاص بنا. يتم استيعاب إحداثيات الأشعة فوق البنفسجية بنفس الطريقة ، التي تم طلبها من قبل Y ، بدءًا من البداية والنهاية ، والتي يتم تطبيق دلتا على كل خطوة.

هذا يعمل بشكل جيد إلا عندما أحاول القيام بمنظور تصحيح رسم الخرائط للأشعة فوق البنفسجية. الخوارزمية الأساسية هي أن تأخذ UV/z و 1/z لكل قمة الرأس والالتفاف بينهما. لكل بكسل ، يصبح تنسيق الأشعة فوق البنفسجية UV_current * z_current. ومع ذلك ، هذه هي النتيجة:

alt text

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

إليك ما أستخدمه لحساب z في نقطة في الفضاء:

float GetZToPoint(Vec3 a_Point)
{
    Vec3 projected = m_Rotation * (a_Point - m_Position);

    // #define FOV_ANGLE 60.f
    // static const float FOCAL_LENGTH = 1 / tanf(_RadToDeg(FOV_ANGLE) / 2);
    // static const float DEPTH = HALFHEIGHT * FOCAL_LENGTH; 
    float zcamera = DEPTH / projected.z;

    return zcamera;
}

هل أنا على حق ، هل هي قضية عازلة من الألف إلى الياء؟

هل كانت مفيدة؟

المحلول

Zbuffer لا علاقة له به.

يكون Zbuffer مفيدًا فقط عندما تتداخل المثلثات وتريد التأكد من رسمها بشكل صحيح (على سبيل المثال أمر بشكل صحيح في z). سيحدد Zbuffer ، لكل بكسل من المثلث ، ما إذا كان بكسل تم وضعه مسبقًا أقرب إلى الكاميرا ، وإذا كان الأمر كذلك ، فلا رسم بكسل المثلث الخاص بك.

نظرًا لأنك ترسم مثلثتين لا تتداخل ، فإن هذا لا يمكن أن يكون المشكلة.

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

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

تعديل:

Peudocode من نقطي (فقط u و v و z يتم الاعتبار ... إذا كنت تريد أيضًا أن تفعل Gouraud ، عليك أيضًا أن تفعل كل شيء من أجل RG و B مماثلة لما تفعله من أجلك و V و Z:

الفكرة هي أنه يمكن تقسيم المثلث في جزأين. الجزء العلوي والجزء السفلي. الجزء العلوي من y [0] إلى y [1] والجزء السفلي من y [1] إلى y [2]. لكلتا المجموعتين ، تحتاج إلى حساب متغيرات الخطوة التي تتداخل معها. يوضح لك المثال أدناه كيفية القيام الجزء العلوي. إذا لزم الأمر ، يمكنني توفير الجزء السفلي أيضًا.

يرجى ملاحظة أنني أحسب بالفعل إزاحة الاستيفاء المطلوبة للجزء السفلي في جزء "الكود الكاذب" أدناه

  • الترتيب الأول منسقات (x ، y ، z ، u ، v) بالترتيب بحيث يكون التنسيق [0] .y <coord [1
  • تحقق التالي إذا كانت أي مجموعتين من الإحداثيات متطابقة (تحقق فقط من x و y). إذا كان الأمر كذلك لا ترسم
  • استثناء: هل المثلث لديه قمة مسطحة؟ إذا كان الأمر كذلك ، فإن المنحدر الأول سيكون غير محدود
  • استثناء 2: هل يحتوي المثلث على قاع مسطح (نعم يمكن أن يكون للمثلثات هذه أيضًا ؛^)) ثم الميل الأخير سيكون غير محدود أيضًا
  • حساب 2 منحدرات (الجانب الأيسر واليمين)
    LeftDeltax = (x [1] - x [0]) / (y [1] -y [0]) و rightdeltax = (x [2] - x [0]) / (y [2] -y [0] )
  • يتم حساب الجزء الثاني من المثلث يعتمد على: إذا كان الجانب الأيسر من المثلث الآن على الجانب الأيسر (أو يحتاج إلى تبديل)

شظية الكود:

 if (leftDeltaX < rightDeltaX)
 {
      leftDeltaX2 = (x[2]-x[1]) / (y[2]-y[1])
      rightDeltaX2 = rightDeltaX
      leftDeltaU = (u[1]-u[0]) / (y[1]-y[0]) //for texture mapping
      leftDeltaU2 = (u[2]-u[1]) / (y[2]-y[1])
      leftDeltaV = (v[1]-v[0]) / (y[1]-y[0]) //for texture mapping
      leftDeltaV2 = (v[2]-v[1]) / (y[2]-y[1])
      leftDeltaZ = (z[1]-z[0]) / (y[1]-y[0]) //for texture mapping
      leftDeltaZ2 = (z[2]-z[1]) / (y[2]-y[1])
 }
 else
 {
      swap(leftDeltaX, rightDeltaX);
      leftDeltaX2 = leftDeltaX;
      rightDeltaX2 = (x[2]-x[1]) / (y[2]-y[1])
      leftDeltaU = (u[2]-u[0]) / (y[2]-y[0]) //for texture mapping
      leftDeltaU2 = leftDeltaU
      leftDeltaV = (v[2]-v[0]) / (y[2]-y[0]) //for texture mapping
      leftDeltaV2 = leftDeltaV
      leftDeltaZ = (z[2]-z[0]) / (y[2]-y[0]) //for texture mapping
      leftDeltaZ2 = leftDeltaZ
  }
  • اضبط Currentleftx و CurrentRightx على حد سواء على x [0
  • قم بتعيين Currentleftu على Leftdeltau و Currentleftv على LeftDeltav و Currentleftz على Leftdeltaz
  • calc start and endpoint for first y Range: Starty = ceil (y [0]) ؛ endy = ceil (y [1])
  • Prestep X و U و V و Z للجزء الكسري من Y لدقة البكسل الفرعي (أعتقد أن هذا مطلوب أيضًا للعوامات) لخوارزميات نقطة ثابتة ، كانت هناك حاجة لجعل الخطوط والقوام تعطي الوهم في التحرك في خطوات أدق بكثير بعد ذلك حل الشاشة)
  • احسب أين يجب أن يكون x في y [1]: halfwayx = (x [2] -x [0]) * (y [1] -y [0]) / (y [2] -y [0]) + x [0] ونفس الشيء لـ U و V و Z: Halfwayu = (u [2] -u [0]) * (y [1] -y [0]) / (y [2] -y [0]) + ش [0
  • واستخدام HALDWAYX احسب السائر لـ U و V و Z: if (Halfwayx - X [1] == 0) {slopeu = 0 ، slopev = 0 ، slopez = 0} else {slopeu = (halfwayu - u [1 ]) /(halfwayx - x [1])} // (ونفس الشيء عن V و Z)
  • قم بقص أعلى y (لذا احسب حيث سنبدأ في الرسم في حالة أن يكون الجزء العلوي من المثلث خارج الشاشة (أو خارج مستطيل القطع))
  • ل y = Starty ؛ y <endy ؛ y ++) {
    • هل يئت Y في الجزء السفلي من الشاشة؟ توقف عن التقديم!
    • calc startx و endx لأول خط أفقي LeftCurx = ceil (startx) ؛ LeftCury = Ceil (endy) ؛
    • قص المقطع الخط ليتم سحبه إلى الحدود الأفقية اليسرى للشاشة (أو منطقة القطع)
    • قم بإعداد مؤشر إلى المخزن المؤقت للوجهة (القيام بذلك من خلال فهارس الصفيف في كل مرة بطيئة للغاية) غير موقعة غير موقعة buf = deStbuf + (yالملعب) + startx ؛ (غير موقعة في حالة قيامك بتقديم 24 بت أو 32 بت) قم أيضًا بإعداد مؤشر Zbuffer الخاص بك هنا (إذا كنت تستخدم هذا)
    • لـ (x = startx ؛ x <endx ؛ x ++) {
      • الآن للحصول على تعيين نسيج المنظور (باستخدام أي استيفاء من Bilineair تقوم بما يلي):

شظية الكود:

         float tv = startV / startZ
         float tu = startU / startZ;
         tv %= texturePitch;  //make sure the texture coordinates stay on the texture if they are too wide/high
         tu %= texturePitch;  //I'm assuming square textures here. With fixed point you could have used &=
         unsigned int *textPtr = textureBuf+tu + (tv*texturePitch);   //in case of fixedpoints one could have shifted the tv. Now we have to multiply everytime. 
         int destColTm = *(textPtr);  //this is the color (if we only use texture mapping)  we'll be needing for the pixel
  • خط وهمية
    • خط وهمية
      • خط وهمية
      • اختياري: تحقق من Zbuffer إذا كان البيكسل الذي تم رسمه مسبقًا في هذا الإحداثيات أعلى أو أقل ثم لدينا.
      • ارسم بكسل
      • Startz += Slopez ؛ Startu+= Slopeu ؛ startv += slopev ؛ // تحديث جميع التداخل
    • } نهاية x حلقة
    • LeftCurx+= LeftDeltax ؛ RightCurx += rightDeltax ؛ LeftCuru+= RightDeltau ؛ LeftCurv += RightDeltav ؛ LeftCurz += RightDeltaz ؛ // تحديث y interpolators
  • } نهاية حلقة y

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

آسف على "الخطوط الوهمية" .. كانت هناك حاجة للحصول على رموز التخفيض في المزامنة. (استغرق مني بعض الوقت للحصول على كل شيء من نوعه يبدو كما هو مقصود)

اسمحوا لي أن أعرف ما إذا كان هذا يساعدك على حل المشكلة التي تواجهها!

نصائح أخرى

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

إذا كنت تتداخل 1/z, ، تحتاج إلى الضرب UV/z بواسطة z, ، ليس 1/z. على افتراض أن لديك هذا:

UV = UV_current * z_current

و z_current يداخل 1/z, ، يجب عليك تغييره إلى:

UV = UV_current / z_current

ثم قد ترغب في إعادة تسمية z_current لشيء مثل one_over_z_current.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top