ব্যারেন্সেন্ট্রিক স্থানাঙ্কগুলি সন্ধান করার সর্বাধিক দক্ষ উপায় কোনটি?


45

আমার প্রোফাইলে, বেরিয়েনট্রিক সমন্বয়গুলি সন্ধান করা স্পষ্টতই কিছুটা বাধা is আমি এটিকে আরও দক্ষ করে তুলতে চাই।

এটি শিরলে পদ্ধতি অনুসরণ করে , যেখানে আপনি ত্রিভুজের ভিতরে পয়েন্ট পি এম্বেড করে গঠিত ত্রিভুজগুলির ক্ষেত্রটি গণনা করুন।

bary

কোড:

Vector Triangle::getBarycentricCoordinatesAt( const Vector & P ) const
{
  Vector bary ;

  // The area of a triangle is 
  real areaABC = DOT( normal, CROSS( (b - a), (c - a) )  ) ;
  real areaPBC = DOT( normal, CROSS( (b - P), (c - P) )  ) ;
  real areaPCA = DOT( normal, CROSS( (c - P), (a - P) )  ) ;

  bary.x = areaPBC / areaABC ; // alpha
  bary.y = areaPCA / areaABC ; // beta
  bary.z = 1.0f - bary.x - bary.y ; // gamma

  return bary ;
}

এই পদ্ধতিটি কার্যকর, তবে আমি আরও কার্যকর একটি সন্ধান করছি!


2
সাবধান যে সর্বাধিক দক্ষ সমাধানগুলি কমপক্ষে সঠিক হতে পারে।
পিটার টেলর

আমি আপনাকে এই পদ্ধতিটি ~ 100k বার (বা অনুরূপ কিছু) কল করতে এবং কার্য সম্পাদন পরিমাপ করার জন্য ইউনিট পরীক্ষা করার পরামর্শ দিচ্ছি। আপনি একটি পরীক্ষা লিখতে পারেন যা নিশ্চিত করে যে এটি কোনও মানের চেয়ে কম (উদাহরণস্বরূপ 10 সে) বা আপনি এটি পুরানো বনাম নতুন বাস্তবায়নের মানদণ্ডে সহজভাবে ব্যবহার করতে পারেন।
ashes999

উত্তর:


54

ক্রিসার এরিকসনের রিয়েল-টাইম সংঘাত সনাক্তকরণ (যা ঘটনাক্রমে, একটি দুর্দান্ত বই) থেকে অনুলিপি করা হয়েছে :

// Compute barycentric coordinates (u, v, w) for
// point p with respect to triangle (a, b, c)
void Barycentric(Point p, Point a, Point b, Point c, float &u, float &v, float &w)
{
    Vector v0 = b - a, v1 = c - a, v2 = p - a;
    float d00 = Dot(v0, v0);
    float d01 = Dot(v0, v1);
    float d11 = Dot(v1, v1);
    float d20 = Dot(v2, v0);
    float d21 = Dot(v2, v1);
    float denom = d00 * d11 - d01 * d01;
    v = (d11 * d20 - d01 * d21) / denom;
    w = (d00 * d21 - d01 * d20) / denom;
    u = 1.0f - v - w;
}

এটি কার্যকরভাবে লিনিয়ার সিস্টেমটি সমাধানের জন্য ক্র্যামারের নিয়ম। আপনি এর থেকে বেশি দক্ষ হয়ে উঠতে পারবেন না a যদি এটি এখনও কোনও বাধা হয়ে থাকে (এবং এটি হতে পারে: এটি আপনার বর্তমান অ্যালগরিদমের চেয়ে অনেক বেশি আলাদা গণনা অনুসারে মনে হচ্ছে না), আপনাকে সম্ভবত অন্য কোনও জায়গা খুঁজে বের করতে হবে একটি দ্রুতগতি অর্জন করতে।

মনে রাখবেন যে এখানে একটি শালীন সংখ্যার মান পি- এর থেকে পৃথক necessary এগুলি প্রয়োজন হলে ত্রিভুজ দিয়ে ক্যাশে করা যেতে পারে।


7
অপারেশনগুলির একটি # একটি লাল রঙের হারিং হতে পারে। তারা কীভাবে নির্ভরশীল এবং সময়সূচী আধুনিক সিপিইউগুলিতে অনেক গুরুত্বপূর্ণ। সর্বদা অনুমান এবং কর্মক্ষমতা পরীক্ষা "উন্নতি।"
শান মিডলডিচ

1
আপনি যদি কেবলমাত্র স্কেলার গণিতের অপশনগুলিতে দেখেন তবে প্রশ্নে থাকা দুটি সংস্করণের সমালোচনামূলক পথে প্রায় অভিন্ন বিলম্ব রয়েছে। আমি এটির বিষয়ে যে জিনিসটি পছন্দ করি তা হ'ল কেবল দুটি ফ্লোটের জন্য জায়গা প্রদান করে, আপনি একটি বিয়োগফল এবং একটি বিভাগকে সমালোচনামূলক পথ থেকে শেভ করতে পারেন। কি যে এটা মূল্য? কেবলমাত্র একটি পারফরম্যান্স পরীক্ষা নিশ্চিতরূপে জানে ...
জন কলসবিেক

1
তিনি বর্ণনা করেছেন যে কীভাবে তিনি 137-138 পৃষ্ঠায় এটি পেয়েছেন "ত্রিভুজের নিকটতম বিন্দুতে বিন্দুতে"
অংশে

1
মাইনর নোট: pএই ফাংশনটির জন্য কোনও যুক্তি নেই ।
বার্ট

2
গৌণ বাস্তবায়ন দ্রষ্টব্য: যদি সমস্ত 3 পয়েন্ট একে অপরের উপরে থাকে তবে আপনি "0 দ্বারা বিভাজন" ত্রুটি পাবেন, সুতরাং আসল কোডে অবশ্যই সেই ক্ষেত্রেটি পরীক্ষা করে দেখুন।
frodo2975

9

এটি সমাধানের সর্বোত্তম উপায় ক্র্যামারের নিয়ম হওয়া উচিত। আমি কোনও গ্রাফিক লোক নই, তবে আমি ভাবছিলাম যে রিয়েল-টাইম সংঘর্ষ সনাক্তকরণ বইয়ে তারা নিম্নলিখিত সহজ কাজটি কেন করে না:

// Compute barycentric coordinates (u, v, w) for
// point p with respect to triangle (a, b, c)
void Barycentric(Point p, Point a, Point b, Point c, float &u, float &v, float &w)
{
    Vector v0 = b - a, v1 = c - a, v2 = p - a;
    den = v0.x * v1.y - v1.x * v0.y;
    v = (v2.x * v1.y - v1.x * v2.y) / den;
    w = (v0.x * v2.y - v2.x * v0.y) / den;
    u = 1.0f - v - w;
}

এটি সরাসরি 2x2 লিনিয়ার সিস্টেমকে সমাধান করে

v v0 + w v1 = v2

বই থেকে পদ্ধতি সিস্টেমকে সলভ করে

(v v0 + w v1) dot v0 = v2 dot v0
(v v0 + w v1) dot v1 = v2 dot v1

আপনার প্রস্তাবিত সমাধান তৃতীয় ( .z) মাত্রা (বিশেষত, এটি বিদ্যমান নেই) সম্পর্কে অনুমান করে না?
কর্নস্টালক

1
কেউ যদি 2 ডি তে কাজ করে তবে এটি এখানে সেরা পদ্ধতি। কেবল একটি সামান্য উন্নতি: দুটি বিভাগের পরিবর্তে দুটি গুণ এবং একটি বিভাগ ব্যবহার করার জন্য একটিকে দ্বিখণ্ডকের পারস্পরিক গণনা করা উচিত।
রুবিক

8

কিছুটা দ্রুত: ডিনোমিনেটরকে পূর্ববর্তী করুন, এবং ভাগের পরিবর্তে গুণ করুন। বিভাগগুলি গুণনের চেয়ে অনেক বেশি ব্যয়বহুল।

// Compute barycentric coordinates (u, v, w) for
// point p with respect to triangle (a, b, c)
void Barycentric(Point a, Point b, Point c, float &u, float &v, float &w)
{
    Vector v0 = b - a, v1 = c - a, v2 = p - a;
    float d00 = Dot(v0, v0);
    float d01 = Dot(v0, v1);
    float d11 = Dot(v1, v1);
    float d20 = Dot(v2, v0);
    float d21 = Dot(v2, v1);
    float invDenom = 1.0 / (d00 * d11 - d01 * d01);
    v = (d11 * d20 - d01 * d21) * invDenom;
    w = (d00 * d21 - d01 * d20) * invDenom;
    u = 1.0f - v - w;
}

আমার বাস্তবায়নে যাইহোক, আমি সমস্ত স্বাধীন ভেরিয়েবলকে ক্যাশে করেছি। আমি নির্মাতায় নিম্নলিখিতগুলি প্রাক-গণনা করি:

Vector v0;
Vector v1;
float d00;
float d01;
float d11;
float invDenom;

সুতরাং চূড়ান্ত কোডটি এর মতো দেখাচ্ছে:

// Compute barycentric coordinates (u, v, w) for
// point p with respect to triangle (a, b, c)
void Barycentric(Point a, Point b, Point c, float &u, float &v, float &w)
{
    Vector v2 = p - a;
    float d20 = Dot(v2, v0);
    float d21 = Dot(v2, v1);
    v = (d11 * d20 - d01 * d21) * invDenom;
    w = (d00 * d21 - d01 * d20) * invDenom;
    u = 1.0f - v - w;
}

2

জন পোস্ট করেছেন এমন সমাধানটি আমি ব্যবহার করব, তবে আমি এসএসএস 4.2 ডট ইন্টারসিনিক এবং এসএসসি আরসিপিএস ইন্টার্নিক ফোরহে ডিভেশন ব্যবহার করব, ধরে নিই যে আপনি নিজেকে ঠিক নেহালেম এবং আরও নতুন প্রক্রিয়াতে সীমাবদ্ধ করছেন এবং সীমিত নির্ভুলতা রেখেছেন।

বিকল্পভাবে আপনি 4 বা 8x স্পিডআপের জন্য sse বা avx ব্যবহার করে একবারে বেশ কয়েকটি বারিসেন্ট্রিক সমন্বয় গণনা করতে পারেন।


1

অক্ষ-সারিবদ্ধ প্লেনগুলির মধ্যে একটির প্রজেক্টের মাধ্যমে আপনি আপনার 3 ডি সমস্যাটিকে 2D সমস্যায় রূপান্তর করতে পারেন এবং ব্যবহারকারীর প্রস্তাবিত পদ্ধতিটি ব্যবহার করতে পারেন 35302। এর ফলে আপনার ত্রিভুজটি কোনও লাইনে প্রজেক্ট না হয়ে থাকে তা নিশ্চিত হওয়া অবধি ঠিক একই বেরিসেন্ট্রিক সমন্বয়গুলির ফলাফল হবে। অক্ষটি-প্রান্তিকৃত বিমানটিতে প্রোজেক্ট করা সেরা যা আপনার ত্রিভুজের অভিমুখের যতটা সম্ভব কাছাকাছি। এটি সহ-লৈখিকতা সমস্যা এড়ায় এবং সর্বাধিক নির্ভুলতা নিশ্চিত করে।

দ্বিতীয়ত আপনি ডিনোমিনিটরটি প্রাক-গণনা করতে পারেন এবং প্রতিটি ত্রিভুজের জন্য এটি সঞ্চয় করতে পারেন। এটি পরে গণনা সংরক্ষণ করে।


1

আমি @ নিলডাব্লু কোডটি সি ++ তে অনুলিপি করার চেষ্টা করেছি, তবে আমি সঠিক ফলাফল পাইনি।

Https://en.wikedia.org/wiki/Barycentric_coordinate_system#Barycentric_coordinates_on_triangles পড়া এবং সেখানে প্রদত্ত ল্যাম্বডা 1/2/3 গণনা করা সহজ ছিল (কোনও ভেক্টরের প্রয়োজন নেই)।

যদি পি (0..2) হয় তবে এক্স / ওয়াই / জেড সহ ত্রিভুজের বিন্দু:

ত্রিভুজ জন্য প্রাক্কাল:

double invDET = 1./((p(1).y-p(2).y) * (p(0).x-p(2).x) + 
                   (p(2).x-p(1).x) * (p(0).y-p(2).y));

তারপরে একটি পয়েন্ট "পয়েন্ট" এর জন্য ল্যাম্বডাস

double l1 = ((p(1).y-p(2).y) * (point.x-p(2).x) + (p(2).x-p(1).x) * (point.y-p(2).y)) * invDET; 
double l2 = ((p(2).y-p(0).y) * (point.x-p(2).x) + (p(0).x-p(2).x) * (point.y-p(2).y)) * invDET; 
double l3 = 1. - l1 - l2;

0

ত্রিভুজ এবিসির ভিতরে প্রদত্ত বিন্দু N এর জন্য, আপনি ত্রিভুজ AB সি এর মোট ক্ষেত্রফল দ্বারা সাবট্রিঙ্গন ABN এর ক্ষেত্রফলকে বিভাজন করে বিন্দু C এর ব্যারেন্সেন্ট্রিক ওজন পেতে পারেন

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.