ब्लिन-फोंग के साथ अत्यधिक तिरछे कोणों पर स्पेक्युलर लाइट के साथ समस्या
मैं अपने मूल ब्लाइन-फोंग रेंडरर के साथ एक समस्या है, जब वस्तुओं को बहुत तिरछे कोणों से देखता हूं:
मुझे नहीं लगता कि यह मेरे कोड के साथ कोई समस्या है, हालांकि मैं अपने टुकड़े के GLSL को नीचे पोस्ट करूंगा। बल्कि, यह तब प्रतीत होता है जब डॉट (नॉर्मल, लाइट) <= 0. (जो हर कोई आपको करने के लिए कहता है।) पर प्रकाश के स्पेक्युलर घटक को काटने का एक आवश्यक परिणाम है। लेकिन इसका मतलब यह है कि वहाँ यह असंगतता होगी। टर्मिनेटर। क्लैम्पिंग को हटाने से अन्य समस्याएं होती हैं; वहाँ कोई और अधिक दृश्यमान सीम नहीं है, लेकिन अब स्पेक्युलर हाइलाइट गोला के अंधेरे पक्ष के आसपास जारी है।
क्या इसके आस-पास एक सरल तरीका है, या क्या यह ब्लाइन-फोंग मॉडल का एक अपरिहार्य नकारात्मक पहलू है?
संपादित करें
TL; DR: ऐसा लगता है कि यह ब्लिन-फोंग मॉडल का सिर्फ एक नकारात्मक पहलू नहीं है।
मैंने BRDFs में कुछ और शोध किए और इस पेपर को पाया: RADIANCE के लिए बाउंडेड अलबेडो और फिटिंग रिफ्लेक्शन डेटा वाला एक नया वार्ड BRDF मॉडल , जिसमें वार्ड मॉडल की कमी पर चर्चा की गई, विशेष रूप से उच्च चराई कोणों के बारे में (बिल्कुल मेरा मुद्दा!), और कैसे उन्होंने इसे ठीक करने के लिए मॉडल को ट्विक किया। वार्ड एक अनिसोट्रोपिक मॉडल है, लेकिन इसे आइसोट्रोपिक होने के लिए सरलीकृत किया जा सकता है, और जब आप पृष्ठ 22 से उनके कार्यान्वयन-तैयार रूप में ऐसा करते हैं, तो आप प्राप्त करते हैं:
मैंने इसे अपने कोड में प्लग किया (नीचे अद्यतन किया गया), आअन्ननंद ... कोई पासा नहीं। यह सामान्य मामलों में बहुत सुंदर लगता है, लेकिन किनारे पर एक ही विफलता मोड प्रदर्शित करता है, और कुछ और भी शानदार किनारे से परे (यहां तक कि ब्लिन-फोंग से भी बदतर):
नोट: दोनों मॉडल "चमकदार" परम का उपयोग कर रहे हैं, लेकिन उनका मतलब प्रत्येक मॉडल के लिए अलग-अलग चीजें हैं। मूल स्क्रीनशॉट चमकदार = .8 के साथ थे, वार्ड 1 के लिए मुझे इसे बंद करना पड़ा ।1।
#version 150
#extension GL_ARB_conservative_depth : enable
in Frag {
vec3 color;
vec3 coord;
vec3 center;
float R;
};
out vec4 color_out;
layout (depth_greater) out float gl_FragDepth;
uniform mat4 VIEW;
uniform mat4 PROJ;
const vec3 gamma = vec3(1.0 / 2.2);
const float ambientPower = .15;
const float diffusePower = .75;
const bool PHONG = false;
const float specHardness = 60.0;
const float shiny = .1;
const bool WARD = true;
void main() {
// Find intersection of ray (given by coord) with sphere
vec3 eyeNormal = normalize(coord);
float b = dot(center, eyeNormal);
float c = b * b - (dot(center, center) - R * R);
if (c < 0.0) {
discard; // Doesn't intersect sphere
}
vec3 point = (b - sqrt(c)) * eyeNormal;
// Redo depth part of the projection matrix
gl_FragDepth = (PROJ[2].z * point.z + PROJ[3].z) / -point.z;
// Lighting begins here
// The light dir is in world-space, unlike the others, so we have to project it to view space.
// The direction (0, 1, 0) corresponds to the 2nd column. By the properties of the view matrix
// (the 3x3 part is an orthogonal matrix), this is already normalized.
vec3 lightNormal = VIEW[1].xyz;
vec3 normal = normalize(point - center);
float diffuse = dot(lightNormal, normal);
float specular = 0.0;
if (PHONG) {
// Have to reverse sign for eyeNormal so it points out
vec3 halfway = normalize(lightNormal - eyeNormal);
specular = diffuse <= 0.0 ? 0.0 : pow(max(0.0, dot(halfway, normal)), specHardness);
} else if (WARD) {
const float PI = 3.14159265359;
const float alpha = .15;
const float invAlpha2 = 1 / (alpha * alpha);
// Would move this computation to CPU and pass invAlpha2 as uniform if alpha were a parameter
const float cFactor = invAlpha2 / PI;
// Have to reverse sign for eyeNormal so it points out, note this is *unnormalized*
vec3 halfway = lightNormal - eyeNormal;
float dotP = dot(halfway, normal);
float invDot2 = 1 / (dotP * dotP);
float semiNormalizedInvDot = dot(halfway, halfway) * invDot2;
// Note: You can't factor the exp(invAlpha2) part out as a constant term,
// you'll blow out the floating-point range if you try.
specular = cFactor * exp(invAlpha2-invAlpha2*semiNormalizedInvDot) * semiNormalizedInvDot * invDot2;
}
diffuse = max(0.0, diffuse);
vec3 colorPre = (ambientPower + diffusePower * diffuse) * color
+ specular * shiny * vec3(1);
color_out = vec4(pow(colorPre, gamma), 0);
}
जवाब
TL; DR: अपने स्पेक्युलर वैल्यू को डॉट (सामान्य, लाइटनॉर्मल) से गुणा करें। (और कर क्लैंप कि 0 की एक न्यूनतम करने के लिए उत्पाद डॉट!)
मुझे और मुझे संदेह है (सभी ट्यूटोरियल को बाहर देखने से) कई अन्य, यह सब गलत कर रहे हैं।
मैं सीधे द्विदिश परावर्तन वितरण समारोह (AKA BRDF) का उपयोग कर रहा था सीधे स्पेक्युलर तीव्रता की गणना करने के लिए। हालांकि, बीआरडीएफ वास्तव में एक अंतर मात्रा को परिभाषित करता है जिसे रेडियोमेट्रिक इंटीग्रल में प्लग किया जाना है। महत्वपूर्ण रूप से, जिसे आमतौर पर बीआरडीएफ कहा जाता है , उसमें एक कॉस (that) शब्द का अभाव होता है जो समग्र अभिन्न अंग का हिस्सा होता है। यदि आपके पास गणित के लिए पेट है, तो यहां और अधिक विस्तार से चर्चा की गई है:http://www.pbr-book.org/3ed-2018/Color_and_Radiometry/Surface_Reflection.html#TheBRDF
जब हम बिंदु प्रकाश स्रोतों के साथ काम कर रहे होते हैं, तो हमें संपूर्ण अभिन्न मूल्यांकन करने की आवश्यकता नहीं होती है; एक प्रकाश स्रोत के रूप में इंटीग्रल की सीमा एक बिंदु हो जाती है जो इंटीग्रैंड में जाती है। लेकिन कॉस (the) शब्द अभी भी महत्वपूर्ण है।
व्यवहार में इसका मतलब यह है कि shader को थोड़ा ट्वीक की आवश्यकता है। परिणाम:
ऊपर ब्लिन-फोंग, तय किया गया है। यह पहले की तरह ही चमक के साथ है; क्योंकि ब्लाइन-फोंग में एक फ्रेस्नेल शब्द नहीं है, क्योंकि कॉस (causes) फिक्स तीव्रता को चराई के कोणों पर नाटकीय रूप से गिरने का कारण बनता है। लेकिन असंगति दूर हो गई है, कम से कम।
यह निश्चित वार्ड है, जो पहले की दूसरी छवि के समान ही चमक के साथ है। फ्रेज़ेल प्रतिबिंब (मोटे तौर पर अधिक तिरछे कोणों पर उच्च परावर्तन) को वार्ड में तैयार किया जाता है, इसलिए यह अच्छा दिखता है। और कोई असंगति नहीं!