जावास्क्रिप्ट रे अनुरेखक

TitleImage



क्या आप जानते हैं कि एक किरण अनुरेखक क्या है? यह एक ऐसा कार्यक्रम है जो स्क्रीन पर तीन आयामी दृश्य खींचता है जैसा कि आप इसे देखेंगे। बेशक, यह काफी पसंद नहीं है, लेकिन कुछ किरण ट्रेसर बहुत विश्वसनीय चित्र खींच सकते हैं, उदाहरण के लिए, जैसे अवतार



एक किरण अनुरेखक का विचार बहुत सरल है और इस लेख में मैं आपको बताऊंगा कि यह एल्गोरिदम कैसे काम करता है और यहां तक ​​कि इसे जावास्क्रिप्ट में भी लिखें। चित्र और एक उदाहरण संलग्न हैं।







त्रि-आयामी दृश्यों को कैसे आकर्षित करें?





आज, जहां तक ​​मुझे पता है, एक फ्लैट स्क्रीन पर तीन आयामी दृश्यों को डिजाइन करने के लिए दो तरीके हैं। पहली विधि मैट्रिक्स परिवर्तनों पर आधारित है। उनका विचार भी सरल है, यह तेजी से काम करता है, लेकिन जो वह पेंट करता है वह एक तस्वीर की तरह नहीं दिखता है और केवल खेलों के लिए उपयुक्त है। दूसरी विधि है, रीट्रैक्टिंग। यह बस व्यवस्थित है, यह आपको आसानी से छाया, प्रतिबिंब, अपवर्तन और अन्य प्रकाश प्रभावों को चित्रित करने की अनुमति देता है, लेकिन यह बहुत धीमी गति से काम करता है और इसलिए खेलों के लिए उपयुक्त नहीं है। इसके अलावा, रेअटरिंग एल्गोरिथ्म को आसानी से समानांतर किया जाता है: कितने प्रोसेसर हैं, ठीक उसी समय कई बार त्वरण होगा।



एल्गोरिदम का आइडिया





कल्पना करें कि आप जिस मॉनिटर पर बैठे हैं, वह खिड़की है, और खिड़की के पीछे किसी तरह का दृश्य है। मॉनिटर पर प्रत्येक पिक्सेल का रंग किरण का रंग है जो आंख से निकलता है, उस पिक्सेल से गुजरता है और दृश्य के साथ टकराता है। प्रत्येक पिक्सेल के रंग का पता लगाने के लिए, आपको प्रत्येक पिक्सेल के माध्यम से एक किरण शुरू करने की आवश्यकता है और यह पता लगाना है कि यह किरण दृश्य के साथ कहाँ टकराती है। इसलिए एल्गोरिथ्म का नाम: किरण-अनुरेखण - किरण अनुरेखण।



यह पता चला है कि यह एक फ़ंक्शन लिखने के लिए पर्याप्त है जो बीम के निर्देशांक के अनुसार - अंतरिक्ष में दो बिंदु - सतह के रंग की गणना करता है जहां यह बीम गिरता है। किन स्थितियों पर विचार किया जाना चाहिए? उनमें से कम से कम तीन हैं:



  1. सादा सतह। जब एक किरण इस से टकराती है, तो हम कह सकते हैं कि किरण का रंग इस सतह का रंग है। यह सबसे सरल मामला है।
  2. प्रतिबिंब। एक किरण एक दर्पण से टकरा सकती है और एक ही कोण पर उछल सकती है। इस स्थिति को संभालने के लिए, आपको बीम को प्रतिबिंबित करने में सक्षम होना चाहिए।
  3. अपवर्तन। एक किरण दो मीडिया के नीचे से गुजर सकती है, उदाहरण के लिए, हवा से पानी तक। एक माध्यम से दूसरे में जाने पर किरण अपवर्तित हो जाती है। इस घटना को अपवर्तन कहा जाता है।




इन स्थितियों में से प्रत्येक को आसानी से नियंत्रित किया जाता है, इसलिए एक रेखापुंज लिखना मुश्किल नहीं है।



स्थल





मंच पर दो तरह की वस्तुएं होती हैं: जिन वस्तुओं को स्क्रीन और प्रकाश स्रोतों पर खींचा जाना चाहिए। सादगी के लिए, केवल गेंदें, क्यूब्स (पैरेल्लेपिपेड्स) और प्रकाश के बिंदु स्रोत होंगे जो उनके चारों ओर सभी दिशाओं में समान रूप से चमकते हैं। किसी भी विषय में तीन काम करने में सक्षम होना चाहिए या दूसरे शब्दों में, तीन तरीके हैं:



  1. मानक (पी) पी पर वस्तु की सतह के लिए सामान्य पाता है। सामान्य बाहरी है और इसकी लंबाई 1 है।
  2. color (p) कहता है कि बिंदु p पर वस्तु की सतह पर कौन सा रंग है।
  3. ट्रेस (किरण) किरण किरण के साथ जाती है और रुक जाती है जहां किरण वस्तु की सतह को काटती है। यह विधि चौराहे के निर्देशांक और बीम की शुरुआत से चौराहे के बिंदु तक दूरी लौटाती है।




इस तरह से इन तरीकों को देखा जाता है:



sphere.norm = function(at) { return vec.mul(1 / this.r, vec.sub(at, this.q)) } sphere.trace = function(ray) { var a = ray.from var aq = vec.sub(a, this.q) var ba = ray.dir var aqba = vec.dot(aq, ba) if (aqba > 0) return var aq2 = vec.dot(aq, aq) var qd = aq2 - this.r * this.r var D = aqba * aqba - qd if (D < 0) return var t = qd > 0 ? -aqba - Math.sqrt(D) : -aqba + Math.sqrt(D) var sqrdist = t * t var at = vec.add(a, vec.mul(t, ba)) return {at:at, sqrdist:sqrdist} } sphere.color = function(p) { return [1, 0, 0] // red color }
      
      







इस तरह, जैसे व्यक्तिगत संकेतन का अर्थ अब महत्वपूर्ण नहीं है: आप आसानी से अपने क्षेत्र को लिख सकते हैं। यह सब मायने रखता है कि इन तीन तरीकों को लिखना बहुत सरल है। इसी तरह, एक घन का वर्णन किया गया है।



रे ट्रेसर





अब रे ट्रेसर कोड पर चलते हैं। इसके कई बुनियादी कार्य हैं:







यह विचार करना बाकी है कि एक बिंदु पर रंग की गणना कैसे करें। फैलाना, प्रतिबिंब और अपवर्तन कार्यों को लागू करने के लिए अलग-अलग दृष्टिकोण हैं। मैं उनमें से कुछ पर विचार करूंगा।



लैम्बर्ट मॉडल





यह सतह के रंग की गणना करने के लिए एक मॉडल है, जो इस बात पर निर्भर करता है कि रंग स्रोत कैसे चमकता है। इस मॉडल के अनुसार, एक बिंदु का प्रकाश प्रकाश स्रोत की शक्ति और कोण के कोसाइन के उत्पाद के बराबर है जिस पर वह बिंदु पर चमकता है। आइए लैम्बर्ट मॉडल का उपयोग करते हुए फैलाना फ़ंक्शन लिखें:



 rt.diffuse = function(r, hit) { var obj = hit.owner var m = obj.mat var sumlight = 0 for (var j in rt.lights) { var light = rt.lights[j] if (rt.inshadow(hit.at, light.at)) continue var dir = vec.norm(vec.sub(hit.at, light.at)) var cos = -vec.dot(dir, hit.norm) sumlight += light.power * cos } return vec.mul(sumlight, obj.color) }
      
      







फ़ंक्शन सभी प्रकाश स्रोतों पर पुनरावृत्ति करता है और जांचता है कि हिट बिंदु छाया में है या नहीं। यदि यह प्रबुद्ध क्षेत्र में है, तो वेक्टर डायर की गणना की जाती है - प्रकाश स्रोत प्रकाश से हिट बिंदु तक दिशा। फ़ंक्शन तब हिट के सामान्य के बीच के कोण के कोसाइन को ढूँढता है। हिट में सतह पर और गति की दिशा। यह कोसाइन स्केलर उत्पाद dir • hit.norm के बराबर है। अंत में, फ़ंक्शन लैम्बर्ट के अनुसार रोशनी पाता है: light.power • cos।



यदि आप केवल इस प्रकाश मॉडल को लागू करते हैं तो क्या होता है:



LambertModelExample



फोंग मॉडल





लोंगबर्ट मॉडल की तरह फोंग मॉडल, एक बिंदु के प्रकाश का वर्णन करता है। लैम्बर्ट मॉडल के विपरीत, यह मॉडल इस बात को ध्यान में रखता है कि हम सतह को किस कोण पर देखते हैं। फोंग रोशनी की गणना निम्नानुसार की जाती है:



  1. हम सतह पर सवाल करने के लिए प्रकाश स्रोत से बिंदु तक एक किरण खींचते हैं और इस किरण को सतह से दर्शाते हैं।
  2. हम परावर्तित बीम और उस दिशा के बीच के कोण का कोसाइन पाते हैं जिसमें हम सतह को देखते हैं।
  3. इस कोसाइन को कुछ सीमा तक बढ़ाएं और प्रकाश स्रोत की शक्ति से परिणामी संख्या को गुणा करें।




इस मॉडल के अनुसार, सतह पर एक बिंदु की स्पष्ट रोशनी अधिकतम होगी यदि हम इस सतह में प्रकाश स्रोत का प्रतिबिंब देखते हैं, अर्थात्। यह आंख में सही परिलक्षित होता है। प्रासंगिक प्रसार कोड:



 rt.diffuse = function(r, hit) { var obj = hit.owner var m = obj.mat var sumlight = 0 for (var j in rt.lights) { var light = rt.lights[j] if (rt.inshadow(hit.at, light.at)) continue var dir = vec.norm(vec.sub(hit.at, light.at)) var lr = vec.reflect(dir, hit.norm) var vcos = -vec.dot(lr, r.dir) if (vcos > 0) { var phong = Math.pow(vcos, m.phongpower) sumlight += light.power * phong } } return vec.mul(sumlight, obj.color) }
      
      







यह कैसा दिखता है:



PhongModelExample



यह देखा जा सकता है कि अच्छी रोशनी के लिए अकेले फोंग पर्याप्त नहीं है, लेकिन यदि आप एक वेट गुणांक के साथ फोंग लाइटिंग लेते हैं और एक अलग वेट गुणांक के साथ लैम्बर्ट लाइटिंग जोड़ते हैं, तो आपको निम्न चित्र मिलते हैं:



LambertPlusPhong



मैं संबंधित डिफ्यूज़ फ़ंक्शन के लिए कोड प्रस्तुत नहीं करता हूं: यह पिछले दो डिफ्यूज़ का संयोजन है और उदाहरण में rt.js फ़ाइल में पाया जा सकता है।



प्रतिबिंब





परिलक्षित किरण के रंग की गणना करने के लिए, आपको इस किरण को सामान्य वेक्टर का उपयोग करके सतह से परावर्तित करना होगा और परावर्तित किरण के लिए पहले से ही लिखित फ़ंक्शन rt.color चलाना होगा। केवल एक सूक्ष्मता है: सतह बीम की सभी ऊर्जा को प्रतिबिंबित नहीं करती है, लेकिन केवल एक निश्चित प्रतिशत है, इसलिए हम शुरुआत और दिशा के निर्देशांक के अलावा, बीम में ऊर्जा जोड़ देंगे। यह पैरामीटर कहेगा कि क्या यह बीम के रंग की गणना करने के लिए अभी भी प्रासंगिक है, क्योंकि यदि ऊर्जा छोटा है, तो बीम का रंग, जो कुछ भी है, वह rt.color में प्राप्त कुल रंग में एक छोटा सा योगदान देगा।



 rt.reflection = function(r, hit) { var refl = hit.owner.mat.reflection if (refl * r.power < math.eps) return var q = {} q.dir = vec.reflect(r.dir, hit.norm) q.from = hit.at q.power = refl * r.power return rt.color(q) } vec.reflect = function(a, n) { var an = vec.dot(a, n) return vec.add(a, vec.mul(-2 * an, n)) }
      
      







अब प्रत्येक वस्तु में एक प्रतिबिंब गुणांक होना चाहिए - यह दर्शाता है कि किरण की ऊर्जा सतह से कितनी परिलक्षित होती है। इस फ़ंक्शन को लिखने के बाद, हमें निम्नलिखित चित्र मिलता है:



प्रतिबिंब



अपवर्तन





जब प्रकाश की किरण एक माध्यम से दूसरे माध्यम से गुजरती है, तो यह अपवर्तित होती है। इसे विकिपीडिया पर पढ़ा जा सकता है। अपवर्तन का कार्यान्वयन लगभग प्रतिबिंब के समान है:



 rt.refraction = function(r, hit) { var m = hit.owner.mat var t = m.transparency if (t * r.power < math.eps) return var dir = vec.refract(r.dir, hit.norm, m.refrcoeff) if (!dir) return var q = {} q.dir = dir q.from = hit.at q.power = t * r.power return rt.color(q) } vec.refract = function(v, n, q) { var nv = vec.dot(n, v) if (nv > 0) return vec.refract(v, vec.mul(-1, n), 1/q) var a = 1 / q var D = 1 - a * a * (1 - nv * nv) var b = nv * a + Math.sqrt(D) return D < 0 ? undefined : vec.sub(vec.mul(a, v), vec.mul(b, n)) }
      
      







अब प्रत्येक वस्तु में एक पारदर्शिता गुणांक है - प्रकाश का वह अंश जो वह सतह से होकर गुजरता है, और अपवर्तक सूचकांक - अपवर्तित किरण की दिशा की गणना करने में शामिल संख्या।



अपवर्तन



Fresnel गुणांक





परावर्तित प्रकाश की मात्रा उस कोण पर निर्भर करती है जिस पर बीम सतह और अपवर्तक सूचकांक से टकराता है। आप विकिपीडिया पर सूत्र देख सकते हैं। मैंने रेसर में इस प्रभाव को ध्यान में नहीं रखा, क्योंकि इसने अदृश्य परिवर्तन किए।



चौरसाई





यदि प्रत्येक पिक्सेल के माध्यम से एक किरण लॉन्च की जाती है, तो तीन आयामी स्थान में चिकनी रेखाएं डिजाइन के बाद स्क्रीन पर कदम रखेगी। इससे बचने के लिए, आप प्रत्येक पिक्सेल के माध्यम से कई किरणें चला सकते हैं, प्रत्येक के लिए रंग गिन सकते हैं और उनके बीच औसत पा सकते हैं।



उदाहरण





यहां चित्र 1000 × 1000 है (आरपीएस का अर्थ है रेज प्रति सेकंड - ब्राउज़र की किरणों की संख्या एक सेकंड में गणना करने का प्रबंधन करती है), और यहां दूसरी तस्वीर 800 × 800 है । एक उदाहरण इस लिंक से डाउनलोड किया जा सकता है। मैंने विभिन्न ब्राउज़रों में रेंडरिंग गति की तुलना की। यह निम्नलिखित निकला:



ओपेरा 33,000 आरपीएस
क्रोम 38,000 आरपीएस
फ़ायरफ़ॉक्स 16,000 आरपीएस
एक्सप्लोरर 20,000 आरपीएस
सफारी 13,000 आरपीएस




मैंने 5 फरवरी 2011 को नवीनतम ब्राउज़रों का उपयोग किया।



इस किरण अनुरेखक में क्या नहीं है?





मैंने एक राइजर की बुनियादी विशेषताओं की जांच की। क्या होगा अगर विषय दर्पण के सामने खड़ा है और आप दर्पण में चमकते हैं? विषय का पिछला भाग परावर्तित प्रकाश द्वारा प्रकाशित किया जाएगा। यदि आप एक कांच की गेंद पर चमकते हैं तो क्या होगा? वह लेंस की तरह प्रकाश की किरणों को एकत्रित करेगा और इसके नीचे एक स्टैंड पर एक चमकदार बिंदु होगा। क्या होगा यदि कमरे में केवल एक छोटी खिड़की है जिसके माध्यम से प्रकाश प्रवेश करता है? पूरे कमरे में हल्की रोशनी होगी। माना गया किरण अनुरेखक इसमें से कोई भी करने में सक्षम नहीं है, लेकिन इसे जोड़ना मुश्किल नहीं है, क्योंकि किरण अनुरेखक का मूल विचार इसकी अनुमति देता है।



आप देख सकते हैं कि सभी कार्यों की गणना करने के लिए - लैम्बर्ट लाइटिंग, फोंग लाइटिंग, प्रतिबिंब और अपवर्तन - उन्हें केवल वैक्टर जोड़ने की क्षमता की आवश्यकता होती है, उन्हें एक संख्या से गुणा करें और अदिश उत्पाद खोजें। वैक्टर पर ये ऑपरेशन अंतरिक्ष के आयाम पर निर्भर नहीं करते हैं, जिसका अर्थ है कि आप चार-आयामी अंतरिक्ष की किरण अनुरेखक लिख सकते हैं, जिससे कोड में कुछ बदलाव हो सकते हैं।



All Articles