সংক্ষিপ্ত উত্তর:
প্রকৃত ফাংশনের আকারের কাছাকাছি একটি অনুমানকারী চয়ন করে মন্টে কার্লো ইন্টিগ্রেশনে পরিবর্তনের পরিমাণ হ্রাস করার জন্য ইমম্পেরেন্স স্যাম্পলিং একটি পদ্ধতি।
পিডিএফ সম্ভাবনা ঘনত্ব ফাংশন জন্য একটি সংক্ষেপণ । একটি হচ্ছে উত্পন্ন একটি র্যান্ডম নমুনা সম্ভাবনা দেয় ।pdf(x)x
দীর্ঘ উত্তর:
শুরু করার জন্য, আসুন পর্যালোচনা করুন মন্টি কার্লো ইন্টিগ্রেশন কী এবং এটি গাণিতিকভাবে কেমন লাগে।
মন্টে কার্লো ইন্টিগ্রেশন একটি অখণ্ডের মান অনুমান করার একটি কৌশল। এটি সাধারণত ব্যবহৃত হয় যখন অবিচ্ছেদের কোনও বন্ধ ফর্ম সমাধান না থাকে। দেখে মনে হচ্ছে:
∫f(x)dx≈1N∑i=1Nf(xi)pdf(xi)
ইংরাজীতে, এটি বলে যে আপনি ফাংশন থেকে ক্রমাগত এলোমেলো নমুনার গড় দিয়ে একটি অবিচ্ছেদ্য আনুমানিক করতে পারেন। হিসাবে বৃহৎ পায়, পড়তা কাছাকাছি এবং কাছাকাছি সমাধান পায়। প্রতিটি এলোমেলো নমুনার সম্ভাব্যতা ঘনত্বের কার্যকারিতা উপস্থাপন করে।Npdf(xi)
আসুন একটি উদাহরণ করা যাক: অবিচ্ছেদ্য এর মান গণনা করুন ।I
I=∫2π0e−xsin(x)dx
মন্টে কার্লো ইন্টিগ্রেশন ব্যবহার করা যাক:
I≈1N∑i=1Ne−xsin(xi)pdf(xi)
এটি গণনা করার জন্য একটি সাধারণ অজগর প্রোগ্রামটি হ'ল:
import random
import math
N = 200000
TwoPi = 2.0 * math.pi
sum = 0.0
for i in range(N):
x = random.uniform(0, TwoPi)
fx = math.exp(-x) * math.sin(x)
pdf = 1 / (TwoPi - 0.0)
sum += fx / pdf
I = (1 / N) * sum
print(I)
আমরা যদি প্রোগ্রামটি চালিত করি তবেI=0.4986941
অংশ দ্বারা বিচ্ছেদ ব্যবহার করে, আমরা সঠিক সমাধান পেতে পারি:
I=12(1−e−2π)=0.4990663
আপনি লক্ষ্য করবেন যে মন্টি কার্লো সলিউশনটি বেশ সঠিক নয়। এটি একটি অনুমান কারণ। এটি বলে যে অনন্তের দিকে যায়, অনুমানের সঠিক উত্তরটি আরও কাছাকাছি আসা উচিত। ইতিমধ্যে কিছু রান সঠিক উত্তরটির সাথে প্রায় একই রকম।NN=2000
পিডিএফ সম্পর্কে একটি নোট: এই সাধারণ উদাহরণে, আমরা সর্বদা একটি অভিন্ন র্যান্ডম নমুনা গ্রহণ করি। একটি অভিন্ন এলোমেলো নমুনা অর্থ প্রতিটি নমুনা নির্বাচিত হওয়ার সঠিক একই সম্ভাবনা থাকে। আমরা পরিসরে নমুনা সুতরাং,[0,2π]pdf(x)=1/(2π−0)
গুরুত্ব স্যাম্পলিং করে কাজ করে না অবিশেষে স্যাম্পলিং। পরিবর্তে আমরা আরও বেশি নমুনা বেছে নেওয়ার চেষ্টা করি যা ফলাফলের জন্য অনেক বেশি অবদান রাখে (গুরুত্বপূর্ণ), এবং কম নমুনা যা ফলাফলের ক্ষেত্রে কেবলমাত্র অবদান রাখে (কম গুরুত্বপূর্ণ)। অতএব নাম, গুরুত্ব নমুনা।
যদি আপনি একটি নমুনা ফাংশন চয়ন করেন যার পিডিএফ খুব ঘনিষ্ঠভাবে এর আকারের সাথে মেলে , তবে আপনি প্রকরণটি হ্রাস করতে পারবেন যার অর্থ আপনি কম নমুনা নিতে পারেন। তবে, আপনি যদি একটি নমুনা ফাংশন চয়ন করেন যার মান থেকে খুব আলাদা , আপনি বৈকল্পিক বাড়িয়ে তুলতে পারেন । নীচের ছবিটি দেখুন:
ওজসিচ জারোজের গবেষণামূলক প্রবন্ধের পরিশিষ্ট এ থেকে প্রাপ্ত চিত্রff
পাথ ট্রেসিংয়ে গুরুত্বের নমুনার একটি উদাহরণ হ'ল কোনও পৃষ্ঠকে আঘাত করার পরে কীভাবে একটি রশ্মির দিক চয়ন করতে হয়। যদি পৃষ্ঠটি পুরোপুরি নিখরচায় থাকে না (যেমন আয়না বা কাচ), বহির্গামী রশ্মিটি গোলার্ধের যে কোনও জায়গায় হতে পারে।
আমরা নতুন রশ্মি উত্পন্ন করতে একইভাবে গোলার্ধের নমুনা করতে পারি। তবে, আমরা এই সত্যটি কাজে লাগাতে পারি যে রেন্ডারিং সমীকরণটির একটি কোসাইন ফ্যাক্টর রয়েছে:
Lo(p,ωo)=Le(p,ωo)+∫Ωf(p,ωi,ωo)Li(p,ωi)|cosθi|dωi
বিশেষ করে, আমরা জানি যে, দিগন্ত কোনো রে প্রচন্ডভাবে ক্ষয়িত করা হবে (বিশেষত )। সুতরাং, দিগন্তের কাছাকাছি উত্পন্ন রশ্মিগুলি চূড়ান্ত মানটিতে খুব বেশি অবদান রাখবে না।cos(x)
এটিকে মোকাবেলায়, আমরা গুরুত্বপূর্ণ নমুনা ব্যবহার করি। যদি আমরা কোনও কোসাইন ওয়েট গোলার্ধ অনুসারে রশ্মি উত্পন্ন করি, তবে আমরা নিশ্চিত করি যে দিগন্তের উপরে আরও কিরণ উত্পন্ন হবে এবং দিগন্তের কাছাকাছি। এটি ভেরিয়েন্স কমিয়ে আওয়াজ কমিয়ে দেবে।
আপনার ক্ষেত্রে, আপনি উল্লেখ করেছেন যে আপনি একটি কুক-টরেন্স, মাইক্রোফেসেট-ভিত্তিক বিআরডিএফ ব্যবহার করবেন। সাধারণ রূপটি হচ্ছে:
f(p,ωi,ωo)=F(ωi,h)G(ωi,ωo,h)D(h)4cos(θi)cos(θo)
কোথায়
F(ωi,h)=Fresnel functionG(ωi,ωo,h)=Geometry Masking and Shadowing functionD(h)=Normal Distribution Function
কুক-টরেন্স বিআরডিএফ কীভাবে নমুনা করা যায় সে সম্পর্কে "এ গ্রাফিকের গাই নোট" ব্লগে একটি দুর্দান্ত রচনা রয়েছে। আমি আপনাকে তার ব্লগ পোস্টে উল্লেখ করব । এটি বলেছিল, আমি নীচে একটি সংক্ষিপ্ত ওভারভিউ তৈরি করার চেষ্টা করব:
এনডিএফ সাধারণত কুক-টরেন্স বিআরডিএফের প্রভাবশালী অংশ, তাই আমরা যদি নমুনাকে গুরুত্ব দিতে যাই, আমাদের এনডিএফের উপর ভিত্তি করে নমুনা করা উচিত।
কুক-টরেন্স ব্যবহারের জন্য একটি নির্দিষ্ট এনডিএফ নির্দিষ্ট করে না; যে কোনও একটি আমাদের অভিনব কল্পনা অনুসারে আমরা স্বাধীন চয়ন করতে পারি। এটি বলেছিল, কয়েকটি জনপ্রিয় এনডিএফ রয়েছে:
প্রতিটি এনডিএফ এর নিজস্ব সূত্র রয়েছে, সুতরাং প্রতিটি পৃথকভাবে নমুনা তৈরি করতে হবে। আমি প্রতিটি জন্য চূড়ান্ত নমুনা ফাংশন প্রদর্শন করতে যাচ্ছি। আপনি কীভাবে সূত্রটি উত্পন্ন হয়েছে তা দেখতে চাইলে ব্লগ পোস্টটি দেখুন।
GGX হিসাবে সংজ্ঞায়িত করা হয়:
DGGX(m)=α2π((α2−1)cos2(θ)+1)2
গোলাকার স্থানাঙ্ক কোণ নমুনা করতে , আমরা সূত্রটি ব্যবহার করতে পারি:θ
θ=arccos(α2ξ1(α2−1)+1−−−−−−−−−−−−√)
যেখানে হল অভিন্ন র্যান্ডম ভেরিয়েবল।ξ
আমরা ধরে নিই যে এনডিএফ আইসোট্রপিক, তাই আমরা সমানভাবে নমুনা করতে পারি :ϕ
ϕ=ξ2
বেকম্যান হিসাবে সংজ্ঞায়িত করা হয়:
DBeckmann(m)=1πα2cos4(θ)e−tan2(θ)α2
যা দিয়ে নমুনাযুক্ত করা যেতে পারে:
θ=arccos(11=α2ln(1−ξ1)−−−−−−−−−−−−−−√)ϕ=ξ2
শেষ অবধি , ব্লিনকে এই হিসাবে সংজ্ঞায়িত করা হয়েছে:
DBlinn(m)=α+22π(cos(θ))α
যা দিয়ে নমুনাযুক্ত করা যেতে পারে:
θ=arccos(1ξα+11)ϕ=ξ2
অনুশীলন করা
আসুন একটি প্রাথমিক পিছনের পথ ট্রেসার দেখুন:
void RenderPixel(uint x, uint y, UniformSampler *sampler) {
Ray ray = m_scene->Camera.CalculateRayFromPixel(x, y, sampler);
float3 color(0.0f);
float3 throughput(1.0f);
// Bounce the ray around the scene
for (uint bounces = 0; bounces < 10; ++bounces) {
m_scene->Intersect(ray);
// The ray missed. Return the background color
if (ray.geomID == RTC_INVALID_GEOMETRY_ID) {
color += throughput * float3(0.846f, 0.933f, 0.949f);
break;
}
// We hit an object
// Fetch the material
Material *material = m_scene->GetMaterial(ray.geomID);
// The object might be emissive. If so, it will have a corresponding light
// Otherwise, GetLight will return nullptr
Light *light = m_scene->GetLight(ray.geomID);
// If we hit a light, add the emmisive light
if (light != nullptr) {
color += throughput * light->Le();
}
float3 normal = normalize(ray.Ng);
float3 wo = normalize(-ray.dir);
float3 surfacePos = ray.org + ray.dir * ray.tfar;
// Get the new ray direction
// Choose the direction based on the material
float3 wi = material->Sample(wo, normal, sampler);
float pdf = material->Pdf(wi, normal);
// Accumulate the brdf attenuation
throughput = throughput * material->Eval(wi, wo, normal) / pdf;
// Shoot a new ray
// Set the origin at the intersection point
ray.org = surfacePos;
// Reset the other ray properties
ray.dir = wi;
ray.tnear = 0.001f;
ray.tfar = embree::inf;
ray.geomID = RTC_INVALID_GEOMETRY_ID;
ray.primID = RTC_INVALID_GEOMETRY_ID;
ray.instID = RTC_INVALID_GEOMETRY_ID;
ray.mask = 0xFFFFFFFF;
ray.time = 0.0f;
}
m_scene->Camera.FrameBuffer.SplatPixel(x, y, color);
}
অর্থাৎ। আমরা দৃশ্যের চারদিকে বাউন্স করি, রঙ হিসাবে এবং আলোতে আলোকপাত করি। প্রতিটি বাউন্সে, রশ্মির জন্য আমাদের একটি নতুন দিক বেছে নিতে হবে। উপরে উল্লিখিত হিসাবে, আমরা নতুন রশ্মি উত্পন্ন করতে একইভাবে গোলার্ধের নমুনা করতে পারি । তবে কোডটি স্মার্ট; এটি বিআরডিএফ-এর ভিত্তিতে নতুন দিকের নমুনাকে গুরুত্ব দেয়। (দ্রষ্টব্য: এটি ইনপুট দিকনির্দেশ, কারণ আমরা পিছনের দিকের পথ অনুসরণকারী)
// Get the new ray direction
// Choose the direction based on the material
float3 wi = material->Sample(wo, normal, sampler);
float pdf = material->Pdf(wi, normal);
যা প্রয়োগ করা যেতে পারে:
void LambertBRDF::Sample(float3 outputDirection, float3 normal, UniformSampler *sampler) {
float rand = sampler->NextFloat();
float r = std::sqrtf(rand);
float theta = sampler->NextFloat() * 2.0f * M_PI;
float x = r * std::cosf(theta);
float y = r * std::sinf(theta);
// Project z up to the unit hemisphere
float z = std::sqrtf(1.0f - x * x - y * y);
return normalize(TransformToWorld(x, y, z, normal));
}
float3a TransformToWorld(float x, float y, float z, float3a &normal) {
// Find an axis that is not parallel to normal
float3a majorAxis;
if (abs(normal.x) < 0.57735026919f /* 1 / sqrt(3) */) {
majorAxis = float3a(1, 0, 0);
} else if (abs(normal.y) < 0.57735026919f /* 1 / sqrt(3) */) {
majorAxis = float3a(0, 1, 0);
} else {
majorAxis = float3a(0, 0, 1);
}
// Use majorAxis to create a coordinate system relative to world space
float3a u = normalize(cross(normal, majorAxis));
float3a v = cross(normal, u);
float3a w = normal;
// Transform from local coordinates to world coordinates
return u * x +
v * y +
w * z;
}
float LambertBRDF::Pdf(float3 inputDirection, float3 normal) {
return dot(inputDirection, normal) * M_1_PI;
}
আমরা ইনপুট দিকনির্দেশ (কোডে 'উই') নমুনা দেওয়ার পরে, আমরা বিআরডিএফের মান গণনা করতে এটি ব্যবহার করি। এবং তারপরে আমরা মন্টি কার্লো সূত্র অনুসারে পিডিএফ দ্বারা ভাগ করব:
// Accumulate the brdf attenuation
throughput = throughput * material->Eval(wi, wo, normal) / pdf;
যেখানে ইভাল () কেবল বিআরডিএফ ফাংশন (ল্যাম্বার্ট, ব্লিন-ফং, কুক-টরেন্স, ইত্যাদি):
float3 LambertBRDF::Eval(float3 inputDirection, float3 outputDirection, float3 normal) const override {
return m_albedo * M_1_PI * dot(inputDirection, normal);
}