दृश्यम की कयामत। आईओएस में 3 डी-ग्राफिक्स के साथ यैंडेक्स अनुभव

- मैं मरने के लिए बहुत छोटा हूं।


SceneKit iOS में एक उच्च-स्तरीय 3D ग्राफिक्स फ्रेमवर्क है जो एनिमेटेड दृश्यों और प्रभावों को बनाने में मदद करता है। इसमें एक भौतिक इंजन, एक कण जनरेटर और 3 डी वस्तुओं के लिए सरल क्रियाओं का एक सेट शामिल है जो आपको सामग्री - ज्यामिति, सामग्री, प्रकाश, कैमरों के संदर्भ में दृश्य का वर्णन करने की अनुमति देता है - और इन वस्तुओं के परिवर्तनों के विवरण के माध्यम से इसे चेतन करता है।



आज हम सीन को एक चौकस, थोड़े कड़े रूप के साथ देखेंगे, लेकिन पहले, आइए मूल बातों पर जाएं और देखें कि 3 डी दृश्य कैसा है और इसे बनाने के लिए क्या करना होगा।


उनमेंमिति के साथ तीन नोड्स का सबसे सरल दृश्य।
उनमें ज्यामिति के साथ तीन नोड्स का सबसे सरल दृश्य


पहले आपको दृश्य की मूल संरचना बनाने की आवश्यकता होती है, जिसमें दृश्य के नोड या नोड होते हैं। प्रत्येक नोड में ज्यामिति और अन्य नोड्स दोनों हो सकते हैं। ज्यामिति या तो सरल हो सकती है, जैसे गेंद, घन, या पिरामिड, या अधिक जटिल, बाहरी संपादकों में बनाई गई।


ओवरले सामग्री
ओवरले सामग्री


फिर, इस ज्यामिति के लिए, आपको उन सामग्रियों को निर्दिष्ट करने की आवश्यकता है जो वस्तुओं के मूल प्रतिनिधित्व को निर्धारित करेंगे। प्रत्येक सामग्री अपने स्वयं के प्रकाश मॉडल को निर्धारित करती है और, इसके आधार पर, गुणों के एक अलग सेट का उपयोग करती है। इस तरह की प्रत्येक संपत्ति आमतौर पर एक रंग या बनावट होती है, लेकिन आमतौर पर इस्तेमाल किए जाने वाले इन विकल्पों के अलावा, इसमें काइलेयर , एवीप्लेयर और SKScene का उपयोग करने का विकल्प भी है।


प्रकाश स्रोत जोड़ें
प्रकाश स्रोत जोड़ें


उसके बाद, प्रकाश स्रोतों को जोड़ना आवश्यक है जो यह निर्धारित करते हैं कि दृश्य के एक या दूसरे भाग में कितनी अच्छी तरह से ऑब्जेक्ट दिखाई दे रहे हैं। वे, ज्यामिति के साथ सादृश्य द्वारा, एक नोड के अंदर झूठ होना चाहिए। SceneKit कई अलग-अलग प्रकार के प्रकाश व्यवस्था के साथ-साथ कई प्रकार की छायाओं का समर्थन करता है


आउट ऑफ द बॉक्स बोके इफेक्ट
आउट ऑफ द बॉक्स बोके इफेक्ट


फिर आपको एक कैमरा बनाने की ज़रूरत है (और इसे एक अलग नोड में डालें) और इसके लिए बुनियादी पैरामीटर सेट करें। उनमें से बहुत सारे हैं, लेकिन उनकी मदद से आप शांत प्रभाव बना सकते हैं। बॉक्स से बाहर, बोकेह (या धब्बा), अनुकूलन के साथ एचडीआर, चमक, एसएसएओ, और ह्यू / संतृप्ति संशोधनों का समर्थन किया जाता है।


सरलता में seitit
SceneKit में सरल एनिमेशन


अंत में, SceneKit में 3 डी ऑब्जेक्ट्स के लिए कार्यों का एक सरल सेट शामिल है जो आपको समय के साथ दृश्य परिवर्तन सेट करते हैं। SceneKit जावास्क्रिप्ट में वर्णित कार्यों का भी समर्थन करता है, लेकिन यह एक अलग लेख के लिए एक विषय है।


एक भौतिक इंजन के साथ एक कण और की बातचीत बवंडर का कारण बन सकती है!
एक भौतिक इंजन के साथ एक कण जनरेटर की बातचीत एक बवंडर का कारण बन सकती है!


ग्राफिक्स के अलावा, SceneKit की मुख्य विशेषताएं कण जनरेटर और एक उन्नत भौतिक इंजन है जो आपको जनरेटर से सामान्य वस्तुओं और कणों दोनों के लिए वास्तविक भौतिक गुणों को सेट करने की अनुमति देता है।


इन सभी विशेषताओं के बारे में बड़ी संख्या में विस्तृत ट्यूटोरियल लिखे गए हैं। लेकिन विकास की प्रक्रिया में, हमने व्यावहारिक रूप से इन अवसरों का उपयोग नहीं किया है ...


अरे, उबड़-खाबड़ नहीं


एक बार जब मैंने 3 डी गेम के लिए वास्तविक धूप से बेहतर एक लाइटिंग मॉडल लिखा, तो एनवीडिया 8800 पर एक स्वीकार्य एफपीएस दिया, लेकिन मैंने इंजन को जारी नहीं करने का फैसला किया, क्योंकि भगवान मेरे लिए अच्छा है और मैं इस मामले में अपनी अक्षमता नहीं दिखाना चाहता।
- जॉन कार्मैक

हम एक सरल अध्ययन के साथ एक विस्तृत अध्ययन शुरू करेंगे जो लगभग सभी के लिए उठता है जो सीनिटिट के साथ काफी गंभीरता से काम करता है: जटिल ज्यामिति और जुड़े सामग्रियों, प्रकाश व्यवस्था और यहां तक ​​कि एनिमेशन के साथ एक मॉडल को कैसे लोड किया जाए?


कई तरीके हैं, और वे सभी उनके पेशेवरों और विपक्ष हैं:


  1. SCNScene (नाम :) - एक बंडल से एक दृश्य मिलता है,


  2. SCNScene (url: options :) - URL द्वारा दृश्य लोड करता है,


  3. SCNScene (mdlAsset :) - विभिन्न स्वरूपों के एक दृश्य को परिवर्तित करता है,


  4. SCNReferenceNode (url :) - lazily सीन लोड करता है।



बंडल से दृश्य प्राप्त करें


आप मानक विधि का उपयोग कर सकते हैं: हमारे मॉडल को dae या scnassets बंडल में scn फॉर्मेट में रखें और UIImage (नाम :)) के साथ सादृश्य द्वारा इसे वहां से लोड करें।


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


URL द्वारा दृश्य लोड हो रहा है


आप scn फ़ाइल के URL से दृश्य निर्माता का उपयोग कर सकते हैं। यह विधि न केवल फ़ाइल सिस्टम से, बल्कि नेटवर्क से भी डाउनलोड करने का समर्थन करती है, लेकिन बाद के मामले में, आप संपीड़न के बारे में भूल सकते हैं। इसके अलावा, आपको पहले से ही प्रारूप को एसएन प्रारूप में बदलने की आवश्यकता है। आप निश्चित रूप से डीईई का उपयोग कर सकते हैं, लेकिन इसके साथ प्रतिबंधों का एक सेट आता है। उदाहरण के लिए, शारीरिक रूप से आधारित प्रतिपादन की कमी।


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


विभिन्न स्वरूपों से एक दृश्य परिवर्तित करें


तीसरा विकल्प MDLAsset के साथ कंस्ट्रक्टर का उपयोग करना है। यही है, पहले हम एक MDLAsset बनाते हैं, जो मॉडलियो फ्रेमवर्क में उपलब्ध है, और फिर इसे दृश्य के लिए कंस्ट्रक्टर को पास करते हैं।


यह विकल्प अच्छा है कि यह आपको कई अलग-अलग प्रारूप डाउनलोड करने की अनुमति देता है। आधिकारिक तौर पर, MDLAsset प्रारूप को obj, ply, stl और usd में लोड कर सकता है, लेकिन सभी संभावित प्रारूपों की एक सूची को चलाने के बाद, कम से कम किसी भी तरह से कंप्यूटर ग्राफिक्स से संबंधित, मुझे चार और मिले: abc, bsp, vox और md3, लेकिन वे पूरी तरह से समर्थित नहीं हो सकते हैं या नहीं सभी प्रणालियों में, और उनके लिए आपको आयात की शुद्धता की जांच करने की आवश्यकता है।


यह भी विचार करना आवश्यक है कि इस पद्धति में रूपांतरण के लिए एक उपरि है, और इसे बहुत सावधानी से उपयोग करें।


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


आलसी लोडिंग सीन


चौथा विकल्प SCNReferenceNode का उपयोग करना है । यह एक दृश्य नहीं है, बल्कि एक नोड है, जो स्वयं आलसी (या अनुरोध पर) दृश्य के पूरे पदानुक्रम में लोड कर सकता है। इस प्रकार, यह विधि पहले के समान है, लेकिन यह नकल के साथ सभी समस्याओं को खुद के अंदर छिपाता है।


उसके पास एक चीज है लेकिन: दृश्य के वैश्विक मापदंड खो गए हैं।


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


नतीजतन, हम पहले विकल्प पर बस गए, क्योंकि यह हमारे लिए scn प्रारूप में काम करने के लिए सबसे सुविधाजनक था, और डिजाइनरों के लिए - इसे dae प्रारूप से परिवर्तित करना। इसके अलावा, हमें बूट पर फ़ाइल ट्यूनिंग एनिमेशन की आवश्यकता थी।


सभी समयपूर्व अनुकूलन पर नहीं


लंबे समय तक इस प्रक्रिया से छेड़छाड़ करने के बाद, मैं आपको कुछ सलाह दे सकता हूं।


सबसे महत्वपूर्ण टिप अग्रिम में स्कैन करने के लिए फ़ाइलों को परिवर्तित करने के लिए है। फिर आप Xcode में बिल्ट-इन सीन एडिटर में फाइल खोलकर देख सकते हैं कि आपकी वस्तु SceneKit में कैसे दिखेगी।


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


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


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


मुझे बहुत चोट लगी है


जब मैं एक कार चलाता हूं, तो मैं अक्सर ब्रह्मांड की हार्ड ड्राइव को सुनता हूं, अगली सड़क को लोड करना।
- जॉन कार्मैक

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


भौतिकी के तुरंत बाद दृश्य में बाधाओं को माना जाता है।  और फ्रेम रेंडरिंग पहले से
भौतिकी के तुरंत बाद दृश्य में बाधाओं को माना जाता है। और फ्रेम रेंडर करने से पहले


अड़चन है, तुम कहते हो? स्थिरांक क्या हैं? कुछ लोगों को पता है, और इससे भी ज्यादा इसके बारे में बात करते हैं, लेकिन SceneKit के पास स्थिरांक का अपना सेट है। और यद्यपि वे UIkit में स्थिरांक के रूप में लचीले नहीं हैं, फिर भी आप उनके साथ बहुत सारी दिलचस्प चीजें कर सकते हैं।


SCNReplicatorConstraint
SCNReplicatorConstraint


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


कम शक्ति 10 बार
कम ताकत 10 बार


ताकत प्रभावित करती है कि वस्तु पर कितना परिवर्तन लागू होता है। और चूंकि लक्ष्य वस्तु की स्थिति हर फ्रेम को बदलती है - छाया वस्तु दूरी अंतर के दसवें हिस्से तक पहुंचती है। इस वजह से, एक देरी प्रभाव दिखाई देता है।


वेतन वृद्धि और 10 गुना की कमी
वेतन वृद्धि और ताकत में 10 गुना की कमी


वृद्धिशीलता , बदले में, प्रभावित करती है कि रेंडरिंग के बाद स्थिरांक रद्द हो गया है या नहीं। मान लीजिए हमने इसे बंद कर दिया। फिर हम देखते हैं कि प्रत्येक फ्रेम पर, रेंडरिंग से पहले स्थिरांक लगाया जाता है, और रेंडर करने के बाद इसे रद्द कर दिया जाता है, और इसलिए हर फ्रेम को दोहराया जाता है। नतीजतन, इन दो मापदंडों को मिलाकर, आप घड़ी के हाथों का अधिक दिलचस्प प्रभाव प्राप्त कर सकते हैं।


विमान हमेशा कैमरे का सामना करता है।
विमान हमेशा कैमरे का सामना करता है।


चलो एक और अधिक दिलचस्प स्थिरांक पर चलते हैं: तथाकथित बिलबोर्ड।


मान लीजिए, यह आवश्यक है कि कोई वस्तु हमेशा "सामना" कर रही है। ऐसा करने के लिए, बस SCNBillboardConstraint का उपयोग करें , इंगित करें कि कौन सी कुल्हाड़ी चारों ओर घूम सकती है। इसके अलावा, प्रत्येक फ्रेम की गणना करने से पहले (भौतिकी के साथ एक कदम के बाद), सभी स्थिरांक को संतुष्ट करने के लिए सभी वस्तुओं की स्थिति और झुकाव को अपडेट किया जाएगा।


यहां आप लुक अ कांस्ट्रेन्ड का उल्लेख कर सकते हैं: यह बिलबोर्ड के समान है, केवल वस्तु को वर्तमान कैमरे के बजाय दृश्य में किसी अन्य वस्तु का सामना करना पड़ सकता है।


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


वस्तुओं के बीच दूरी बनाए रखता है
वस्तुओं के बीच दूरी बनाए रखता है


SCNDistanceConstraint आपको किसी अन्य ऑब्जेक्ट की स्थिति के लिए न्यूनतम और / या अधिकतम दूरी निर्धारित करने की अनुमति देता है। और हां, आप इसका इस्तेमाल सांप बनाने में कर सकते हैं। :) इस कसौटी का उपयोग कैमरा को चरित्र में बाँधने के लिए भी किया जा सकता है, हालाँकि कैमरे की स्थिति आमतौर पर अधिक जटिल होती है, और इसे केवल कॉन्ट्रास्ट के साथ वर्णन करना एक आसान काम नहीं है। भौतिक इंजन में वसंत को जोड़कर एक ही प्रभाव प्राप्त किया जा सकता है, लेकिन इस वसंत को एक तनाव के साथ पूरक किया जा सकता है, जब आपको वसंत के अत्यधिक खिंचाव या संपीड़न के साथ समस्याओं से बचने की आवश्यकता होती है।


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


SCNSliderConstraint
SCNSliderConstraint


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


काम पर उलटा कीन्मेटीएक्स
काम पर उलटा कीनेमेटीक्स


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


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


एक विमान है, लेकिन यह नहीं है
एक विमान है, लेकिन यह नहीं है


ऐसा लगता है कि यह एक इंजन में आसान हो सकता है जो छाया बनाने की तुलना में छाया का समर्थन करता है? लेकिन कभी-कभी छायाओं को पूरी तरह से पारदर्शी विमान पर डालना पड़ता है। यह ARKit में बहुत उपयोगी है, क्योंकि कैमरे की छवि को विमान के पीछे प्रदर्शित किया जाता है, और छाया कहीं डाली जानी चाहिए। चाल काफी सरल हो जाती है: पहले आपको सामग्री टैब में विमान के सभी घटकों में आस्थगित छाया को सक्षम करने और रिकॉर्डिंग बंद करने की आवश्यकता होती है, और छाया इसे ओवरलैप करना जारी रखेगा। एकमात्र समस्या यह है कि यह विमान इसके पीछे की वस्तुओं को ओवरलैप करेगा।


लेकिन छाया SceneKit में केवल खराब अध्ययन प्रभाव नहीं हैं। आइए अब दर्पणों से निपटें।


SCNFloor दर्पण - क्या सरल हो सकता है
SCNFloor दर्पण - क्या सरल हो सकता है


हर कोई जो SceneKit के साथ खेला जाता है, वह शायद scnfloor के बारे में जानता है, जो फर्श पर दर्पण प्रतिबिंब को जोड़ता है। लेकिन किसी कारण से, बहुत कम लोग इसे ईमानदार दर्पण प्रतिबिंबों के लिए उपयोग करते हैं, क्योंकि आप अपने मॉडल को फर्श ज्यामिति पर रख सकते हैं, इसे थोड़ा झुका सकते हैं और इसे मोड़ सकते हैं ... एक साधारण दर्पण में।



कांच और एक घुमावदार दर्पण पर ड्रिप करें
कांच और एक घुमावदार दर्पण पर ड्रिप करें


लेकिन, जो भी कम ज्ञात है, इस लिंग के लिए एक सामान्य नक्शा निर्धारित किया जा सकता है। इसके कारण, बदले में, आप कई अलग-अलग दिलचस्प प्रभाव पैदा कर सकते हैं, जैसे कि धारियाँ या घुमावदार दर्पण का प्रभाव।


अल्ट्रा हिंसा


आँखों से खोलने के एक बार मैं एक लड़की चूमा। लड़की ने क्लिपिंग के पास के विमान से उसका चेहरा काट दिया। तब से, मैं केवल चुंबन अपनी आंखों से बंद कर दिया।
- जॉन कार्मैक

छाया, दर्पण - दिलचस्प प्रभाव। लेकिन एक प्रभाव है कि, जब कुशलता से उपयोग किया जाता है, तो यह और भी दिलचस्प हो सकता है - वीडियो बनावट।



साधारण और ऊंचाई वाला वीडियो
साधारण और ऊंचाई वीडियो


खेल के अंदर वीडियो दिखाने के लिए आपको उनकी आवश्यकता हो सकती है। लेकिन यह अधिक दिलचस्प है कि वीडियो बनावट की सहायता से आप ज्यामिति को संशोधित कर सकते हैं। ऐसा करने के लिए, आपको अपनी सामग्री के विस्थापन गुण में ऊँचाई के नक्शे के साथ वीडियो बनावट रखने की आवश्यकता होती है और एक विमान पर सामग्री का उपयोग पर्याप्त रूप से बड़ी संख्या में सेगमेंट के साथ किया जाता है । यह समझना बाकी है कि इसे वहां कैसे रखा जाए।


मैंने दृश्य निर्माण प्रक्रिया के वर्णन में उल्लेख किया है कि आप SKScene को एक भौतिक संपत्ति के रूप में उपयोग कर सकते हैं , और यह एक SpriteKit दृश्य है। SpriteKit, SceneKit की तरह है, लेकिन 2D ग्राफिक्स के लिए। इसमें SKVideoNode का उपयोग करके वीडियो प्रदर्शित करने के लिए समर्थन है। आपको बस SKVideoNode को SKScene, और SKScene को SCNMaterialProperty में डालना होगा, और आपका काम पूरा हो जाएगा।


लेकिन परिणामी 3 डी दृश्य को निर्यात करने और इसे कहीं और खोलने के बाद, हम एक काला वर्ग देखेंगे। स्कैन फाइल के माध्यम से, मुझे इसका कारण मिला। यह पता चलता है कि वीडियो कोड सहेजते समय, यह वीडियो URL को नहीं बचाता है। ऐसा लगता है कि आप लेते हैं और शासन करते हैं। लेकिन सब कुछ इतना सरल नहीं है: स्कैन फ़ाइल एक तथाकथित बाइनरी प्लिस्ट है, जिसमें NSKeyedArviver का परिणाम होता है। और सामग्री, जो स्प्राइटकिट दृश्य है, एक ही बाइनरी प्लिस्ट है, जो, यह पता चला है, पहले से ही एक अन्य बाइनरी मुट्ठी के अंदर है! यह अच्छा है कि घोंसले के शिकार के केवल दो स्तर हैं।


ठीक है, अब हम प्रभाव पर भी आगे बढ़ेंगे, लेकिन एक ऐसे उपकरण से जिससे आप किसी भी तरह का प्रभाव पैदा कर सकते हैं। ये shader संशोधक हैं।


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


ठीक है, shader संशोधक आपको मानक shaders के परिणामों को GLSL या धातु छायांकन भाषा में बदलने की अनुमति देता है। वे एक दृश्य संपादक में भी उपलब्ध हैं, जो आपको वास्तविक समय में संशोधक में परिवर्तन देखने की अनुमति देता है।



फर और लंबन मैपिंग
फर और लंबन मैपिंग


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


#pragma arguments texture2d bg; texture2d height; float depth; float layers; #pragma transparent #pragma body constexpr sampler sm = sampler(filter::linear, s_address::repeat, t_address::repeat); float3 bitangent = cross(_surface.tangent, _surface.normal); float2 direction = float2(-dot(_surface.view.rgb, _surface.tangent), dot(_surface.view.rgb, _surface.bitangent)); _output.color.rgba = float4(0); for(int i = 0; i < int(floor(layers)); i++) { float coeff = float(i) / floor(layers); float2 defaultCoords = _surface.diffuseTexcoord + direction * (1 - coeff) * depth; float2 adjustment = float2(scn_frame.sinTime + defaultCoords.x, scn_frame.cosTime) * depth * coeff * 0.1; float2 coords = defaultCoords + adjustment; _output.color.rgb += bg.sample(sm, coords).rgb * coeff * (height.sample(sm, coords).r + 0.1) * (1.0 - coeff); _output.color.a += (height.sample(sm, coords).r + 0.1) * (1.0 - coeff); } return _output; 

रियल-टाइम कास्टिक के साथ रे कास्टिंग।
रियल-टाइम कास्टिक्स के साथ रे कास्टिंग


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


दुःस्वप्न!


मुझे ब्लिंक करना पसंद नहीं है, क्योंकि बंद पलकें प्रकाश की कमी के कारण बीडीपीटी के लिए GPU को तेजी से लोड करती हैं।
- जॉन कार्मैक

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


आइए पहले सबसे सरल समाधान की ओर मुड़ें : रिप्लेकिट । पता करें कि यह क्यों फिट नहीं है। सामान्यतया, यह समाधान आपको कोड की कई पंक्तियों में स्क्रीन प्रविष्टि बनाने और सिस्टम पूर्वावलोकन के माध्यम से सहेजने की अनुमति देता है। लेकिन। इसका एक बड़ा माइनस है - यह स्क्रीन पर सभी बटन सहित, पूरे यूआई को रिकॉर्ड करता है। यह हमारा पहला निर्णय था, लेकिन स्पष्ट कारणों से इसे उत्पादन में जाने देना असंभव था: उपयोगकर्ताओं को वीडियो को साझा करना था, और इसे सिस्टम पूर्वावलोकन से साझा नहीं करना था।


हमने खुद को एक ऐसी स्थिति में पाया जहां समाधान को खरोंच से लिखे जाने की आवश्यकता थी। खरोंच से बिल्कुल। तो, आइए देखें कि आईओएस में आप अपना वीडियो कैसे बना सकते हैं और वहां अपने फ्रेम रिकॉर्ड कर सकते हैं। सब कुछ काफी सरल है:


रिकॉर्डिंग प्रक्रिया
रिकॉर्डिंग प्रक्रिया


हमें एक ऐसी इकाई बनाने की आवश्यकता है जो फ़ाइलों को रिकॉर्ड करेगी - AVAssetWriter , इसमें एक वीडियो स्ट्रीम - AVAssetWriterInput जोड़ें और इस स्ट्रीम के लिए एक एडेप्टर बनाएं जो स्ट्रीम द्वारा आवश्यक प्रारूप में हमारे पिक्सेल बफ़र को परिवर्तित कर देगा - AVAsetsetWriterPixelBufferAdaptor


बस मामले में, मैं आपको याद दिलाता हूं कि पिक्सेल बफर एक इकाई है, जो कि मेमोरी का एक टुकड़ा है जहां पिक्सेल के लिए डेटा किसी तरह लिखा जाता है। यह मूल रूप से चित्र का निम्न-स्तरीय प्रतिनिधित्व है।


लेकिन इस पिक्सेल बफर को कैसे प्राप्त करें? समाधान सरल है। SCNView में एक अद्भुत .snapshot () फ़ंक्शन है जो UIImage लौटाता है। हमें बस इस UIImage से एक पिक्सेल बफर बनाने की आवश्यकता है।


 var unsafePixelBuffer: CVPixelBuffer? CVPixelBufferPoolCreatePixelBuffer(NULL, self.pixelBufferPool, &unsafePixelBuffer) guard let pixelBuffer = maybePixelBuffer else { return } CVPixelBufferLockBaseAddress(pixelBuffer, 0) let data = CVPixelBufferGetBaseAddress(pixelBuffer) let rgbColorSpace = CGColorSpaceCreateDeviceRGB() let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue) let rowBytes = NSUInteger(CVPixelBufferGetBytesPerRow(pixelBuffer)) let context = CGContext( data: data, width: image.width, height: image.height, bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer), space: rgbColorSpace, bitmapInfo: bitmapInfo.rawValue ) context?.draw(image, in: CGRect(x: 0, y: 0, width: image.width, height: image.height)) CVPixelBufferUnlockBaseAddress(pixelBuffer, 0) self.appendPixelBuffer(pixelBuffer, withPresentationTime: presentationTime) 

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



अब आपको इसे हर फ्रेम में करने की जरूरत है। ऐसा करने के लिए, हम एक डिस्प्ले लिंक बनाते हैं जो प्रत्येक फ्रेम के लिए कॉलबैक कहेगा, जहां हम, बदले में, स्नैपशॉट विधि को कॉल करेंगे और छवि से एक पिक्सेल बफर बनाएंगे। सब कुछ सरल है!



लेकिन नहीं। इस तरह के एक समाधान, यहां तक ​​कि शक्तिशाली फोन पर, भयानक लैग और एफपीएस ड्रॉडाउन का कारण बनता है। चलो अनुकूलन करते हैं।



मान लें कि हमें 60 FPS की आवश्यकता नहीं है। हम 25 तारीख से भी प्रसन्न होंगे। लेकिन इस परिणाम को प्राप्त करने का सबसे आसान तरीका क्या है? बेशक, आपको बस पृष्ठभूमि के धागे पर यह सब लगाने की जरूरत है। इसके अलावा, डेवलपर्स के अनुसार, यह फ़ंक्शन थ्रेड सुरक्षित है।



हम्म, अंतराल कम हो गया है, लेकिन वीडियो ने रिकॉर्डिंग बंद कर दी है ...


सब कुछ सरल है। जैसा कि वे कहते हैं, यदि आपके पास कोई समस्या है, और आप इसे कई थ्रेड्स की मदद से हल करेंगे, तो आपको 2 समस्याएं होंगी।


यदि आप पिछले रिकॉर्ड किए गए समय से कम समय के साथ एक पिक्सेल बफर रिकॉर्ड करने की कोशिश करते हैं, तो पूरा वीडियो अमान्य होगा।



पिछले लेखन के समाप्त होने तक नया बफर न लिखें।



हम्म, यह बहुत बेहतर हो गया। लेकिन सभी एक ही, लैग शुरू में क्यों दिखाई दिए?



यह पता चलता है कि .snapshot () फ़ंक्शन, जिसके साथ हमें स्क्रीन से एक छवि मिलती है, प्रत्येक कॉल के लिए एक नया रेंडरर बनाता है, खरोंच से एक फ्रेम खींचता है और इसे वापस करता है, न कि स्क्रीन पर जो छवि है। यह मजेदार प्रभाव की ओर जाता है। उदाहरण के लिए, शारीरिक सिमुलेशन दोगुना तेज है।


लेकिन प्रतीक्षा करें - हम हर बार एक नए फ्रेम को प्रस्तुत करने का प्रयास क्यों करते हैं? निश्चित रूप से कहीं न कहीं आप स्क्रीन पर प्रदर्शित बफर को पा सकते हैं। वास्तव में, इस तरह के एक बफर तक पहुंच है, लेकिन यह बहुत nontrivial है। हमें धातु से CAMetalDrawable प्राप्त करने की आवश्यकता है।


दुर्भाग्य से, SCNView से सीधे धातु प्राप्त करना एक काफी समझ में आने वाले कारण के लिए इतना आसान नहीं है - SceneKit में आप API प्रकार को स्वयं चुन सकते हैं, लेकिन यदि आप हुड के नीचे देखते हैं और परत को देखते हैं, तो आप देख सकते हैं कि यह धातु के रूप में कार्य करता है, CAMetalLayer


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


समाधान वास्तव में मौजूद है। तथ्य यह है कि स्क्रीन से गायब होने के बाद, बफर को डीलक्लॉक नहीं किया गया है, लेकिन केवल पूल में वापस रखा गया है। वास्तव में, दो या तीन बफ़र्स पर्याप्त होने पर हर बार मेमोरी क्यों आवंटित करें: एक स्क्रीन पर दिखाया गया है, दूसरा रेंडरिंग के लिए और तीसरा, उदाहरण के लिए, पोस्टप्रोसेसिंग के लिए, यदि आपके पास एक है।


यह पता चला है कि बफर प्रदर्शित करने के बाद, इससे डेटा कहीं भी गायब नहीं होता है और आप सुरक्षित रूप से और सुरक्षित रूप से उन्हें एक्सेस कर सकते हैं।


और अगर हम उत्तराधिकारी में प्रत्येक कॉल के जवाब में शुरू करने के लिए अगले () इसे बचाने के लिए, हम लगभग हम क्या जरूरत है। समस्या यह है कि सहेजी गई CAMetalDrawable वह है जिसमें छवि अभी खींची जा रही है।


वास्तविक समाधान पर कूदना बहुत सरल है - हम वर्तमान ड्राबल और पिछले दोनों को बचाते हैं।


और यहाँ यह तैयार है - CAMetalDrawable के माध्यम से मेमोरी तक सीधी पहुँच।


 var unsafePixelBuffer: CVPixelBuffer? CVPixelBufferPoolCreatePixelBuffer(NULL, self.pixelBufferPool, &unsafePixelBuffer) guard let pixelBuffer = maybePixelBuffer else { return } CVPixelBufferLockBaseAddress(pixelBuffer, 0) let data = CVPixelBufferGetBaseAddress(pixelBuffer) let width: NSUInteger = lastDrawable.texture.width let height: NSUInteger = lastDrawable.texture.height let rowBytes: NSUInteger = NSUInteger(CVPixelBufferGetBytesPerRow(pixelBuffer) lastDrawable.texture.getBytes( data, bytesPerRow: rowBytes, fromRegion: MTLRegionMake2D(0, 0, width, height), mipmapLevel: 0 ) CVPixelBufferUnlockBaseAddress(pixelBuffer, 0) self.appendPixelBuffer(pixelBuffer, withPresentationTime: presentationTime) 

तो, अब हम एक संदर्भ नहीं बनाते हैं और इसमें एक UIImage आकर्षित करते हैं, लेकिन स्मृति के एक टुकड़े को दूसरे में कॉपी करते हैं। सवाल उठता है: पिक्सेल प्रारूप के बारे में क्या? ..


यह DeviceColorSpace के साथ मेल नहीं खाता है ... और यह आमतौर पर इस्तेमाल किया रंग रिक्त स्थान के साथ मेल नहीं खाता ...


यह ठीक वही बिंदु है जहाँ एक ही कार्य को करने वाले सार्वजनिक चूल्हों का लेखक टूट गया । बाकी सभी को यहां तक ​​नहीं मिला।



खैर, ये सभी तरकीबें - खौफनाक फिल्टर की खातिर?


खैर, नहीं! ARKit के बारे में लेख में, आप एक उल्लेख पा सकते हैं कि कैमरे से छवि मानक रंग स्थान का उपयोग नहीं करती है, लेकिन विस्तारित होती है। और यहां तक ​​कि रंग अंतरिक्ष परिवर्तन का एक मैट्रिक्स भी प्रस्तुत किया गया है। लेकिन आप इस प्रारूप में सीधे रिकॉर्ड करने की कोशिश कर सकते हैं, तो परिवर्तन में क्यों संलग्न हैं? यह पता लगाना बाकी है कि यह 60 में से किस प्रारूप में उपलब्ध है ...



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


नतीजतन, चालीसवें प्रारूप के बारे में, हमें इसका नाम मिलता है। यह kCVPixelFormatType_30RGBLEPackedWideGamut के अलावा और कोई नहीं है। मैंने कैसे अनुमान नहीं लगाया?



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


अच्छे पुराने 32RGBA के लिए व्यापक-सरगम को बदलते हुए, मुझे काम करने का रिकॉर्ड मिलता है! यह समझने के लिए बना रहता है कि यह कैसे निर्धारित किया जाए कि डिवाइस व्यापक-सरगम का समर्थन करता है। विभिन्न प्रकार के डिस्प्ले के साथ आईपैड हैं, और मैंने सोचा कि निश्चित रूप से आप सिस्टम से ENUM प्रकार का डिस्प्ले प्राप्त कर सकते हैं। दस्तावेज़ीकरण के माध्यम से अफवाह , मैंने पाया - यह यूआईट्रेट कॉलेक्शन में डिस्प्लेगुट है


परीक्षकों को विधानसभा देने के बाद, मुझे उनसे सुखद समाचार मिला - सब कुछ बिना किसी लाग के, यहां तक ​​कि पुराने उपकरणों पर भी!


अंत में, मैं आपको बताना चाहता हूं - 3 डी-ग्राफिक्स करें! हमारे आवेदन में, जिसके लिए संवर्धित वास्तविकता मुख्य उपयोग का मामला नहीं है, सप्ताहांत में, लोगों ने 2,000 किलोमीटर से अधिक की यात्रा की, 3,000 से अधिक वस्तुओं को देखा और उनके साथ 1,000 से अधिक वीडियो रिकॉर्ड किए! सोचिए अगर आप खुद ऐसा करें तो आप क्या कर सकते हैं।

Source: https://habr.com/ru/post/hi431880/


All Articles