Ultramodern OpenGL। भाग २



खिड़की के बाहर एक अच्छा मूड और कम तापमान। जैसा कि वादा किया गया था, मैं आधुनिक ओपनजीएल के सुपर-डुपर पर लेख की निरंतरता प्रकाशित कर रहा हूं। पहला भाग - Ultramodern OpenGL को किसने नहीं पढ़ा है भाग 1

शायद आप भाग्यशाली हैं और मैं इस लेख में सभी शेष सामग्री को हिला सकता हूं, यह निश्चित नहीं है ...

सरणी की बनावट


OpenGL 3.0 में बनावट सरणियों को वापस जोड़ा गया था, लेकिन किसी कारण से कुछ लोग उनके बारे में लिखते हैं (जानकारी फ्रेमीसन द्वारा विश्वसनीय रूप से छिपी हुई है)। आप सभी प्रोग्रामिंग से परिचित हैं और जानते हैं कि सरणी क्या है , हालांकि मैं दूसरी तरफ से बेहतर "दृष्टिकोण" हूं।

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

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

लेकिन वापस हमारे मेढ़े ... बनावट सरणी में तीन प्रकार के लक्ष्य हैं:

  • GL_TEXTURE_1D_ARRAY
  • GL_TEXTURE_2D_ARRAY
  • GL_TEXTURE_CUBE_MAP_ARRAY

एक बनावट सरणी बनाने के लिए कोड:

GLsizei width = 512; GLsizei height = 512; GLsizei layers = 3; glCreateTextures(GL_TEXTURE_2D_ARRAY, 1, &texture_array); glTextureStorage3D(texture_array, 0, GL_RGBA8, width, height, layers); 

सबसे चौकस लोगों ने देखा कि हम 2 डी बनावट के लिए एक भंडार का निर्माण कर रहे हैं, लेकिन किसी कारण से हम 3 डी सरणी का उपयोग कर रहे हैं, यहां कोई गलती या टाइपो नहीं है। हम 2D बनावट संग्रहीत करते हैं, लेकिन जब से वे "लेयर्स" में स्थित होते हैं, तो हमें एक 3D सरणी मिलती है (वास्तव में, पिक्सेल डेटा संग्रहीत होता है, बनावट नहीं। 3D सरणी में पिक्सेल डेटा के साथ 2D परतें होती हैं)।

यहां 1 डी बनावट के उदाहरण को समझना आसान है। 2 डी पिक्सेल सरणी में प्रत्येक पंक्ति एक अलग 1 डी परत है। Mipmap बनावट भी स्वचालित रूप से बनाई जा सकती है।

इस पर, सभी कठिनाइयां समाप्त हो जाती हैं और एक विशिष्ट परत में एक छवि जोड़ना काफी सरल है:

 glTextureSubImage3D(texarray, mipmap_level, offset.x, offset.y, layer, width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 

सरणियों का उपयोग करते समय, हमें shader को थोड़ा बदलना होगा

 #version 450 core layout (location = 0) out vec4 color; layout (location = 0) in vec2 texture_0; uniform sampler2DArray texture_array; uniform uint diffuse_layer; float getCoord(uint capacity, uint layer) { return max(0, min(float(capacity - 1), floor(float(layer) + 0.5))); } void main() { color = texture(texture_array, vec3(texture_0, getCoord(3, diffuse_layer))); } 

सबसे अच्छा विकल्प छायादार के बाहर वांछित परत की गणना करना होगा, इसके लिए हम यूबीओ / एसएसबीओ का उपयोग कर सकते हैं (इसका उपयोग मैट्रिस और कई अन्य डेटा को स्थानांतरित करने के लिए भी किया जाता है, लेकिन यह किसी अन्य समय है)। यदि कोई भी tyk_1 और tyk_2 की प्रतीक्षा नहीं कर सकता है, तो आप पढ़ सकते हैं।

आकारों के लिए, अर्थात् GL_MAX_ARRAY_TEXTURE_LAYERS जो OpenGL 3.3 में 20 और OpenGL 4.5 में 2048 है।

यह सांपलर ऑब्जेक्ट (एरे बनावट से संबंधित नहीं है, लेकिन एक उपयोगी चीज) के बारे में बताने योग्य है - यह एक ऐसी वस्तु है जिसका उपयोग एक बनावट इकाई की स्थिति को समायोजित करने के लिए किया जाता है, इस बात पर ध्यान दिए बिना कि कौन सी वस्तु वर्तमान में इकाई से जुड़ी है। यह एक विशेष बनावट वस्तु से अलग-अलग नमूने वाले राज्यों को मदद करता है, जो अमूर्तता में सुधार करता है।

 GLuint sampler_state = 0; glGenSamplers(1, &sampler_state); glSamplerParameteri(sampler_state, GL_TEXTURE_WRAP_S, GL_REPEAT); glSamplerParameteri(sampler_state, GL_TEXTURE_WRAP_T, GL_REPEAT); glSamplerParameteri(sampler_state, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glSamplerParameteri(sampler_state, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glSamplerParameterf(sampler_state, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f); 

मैंने सिर्फ एक नमूना वस्तु बनाई है, किसी भी बनावट इकाई के लिए रैखिक फ़िल्टरिंग और 16x अनिसोट्रोपिक फ़िल्टरिंग सक्षम किया है।

 GLuint texture_unit = 0; glBindSampler(texture_unit, sampler_state); 

यहां हम सिर्फ नमूना को वांछित बनावट इकाई से बांधते हैं, और जब यह इस इकाई के लिए वांछित बाइंडिम 0 होना बंद हो जाता है।

 glBindSampler(texture_unit, 0); 

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

जब ऑब्जेक्ट को हटाने का समय आता है, तो हम बस इस फ़ंक्शन को कॉल करते हैं:

 glDeleteSamplers(1, &sampler_state); 

बनावट का दृश्य


मैं इसका अनुवाद "एक बनावट सूचक के रूप में करूंगा (यह लिंक से अधिक सही हो सकता है, मैं xs)", क्योंकि मैं सबसे अच्छा अनुवाद नहीं जानता।

OpenGL के परिप्रेक्ष्य में संकेत क्या हैं?

सब कुछ बहुत सरल है, यह एक अपरिवर्तनीय (अर्थात्, परस्पर) बनावट के डेटा के लिए एक संकेतक है, जैसा कि हम नीचे दी गई तस्वीर में देखते हैं।



वास्तव में, यह एक वस्तु है जो एक निश्चित बनावट की वस्तु के टेक्सल डेटा को साझा करती है, सादृश्य के लिए हम C ++ से std :: shared_ptr का उपयोग कर सकते हैं। जब तक कम से कम एक बनावट सूचक है, तब तक मूल बनावट ड्राइवर द्वारा नहीं निकाली जाएगी।

विकी को अधिक विस्तार से वर्णित किया गया है, साथ ही साथ बनावट और लक्ष्य के प्रकारों के बारे में पढ़ने के लायक है (उन्हें मेल नहीं खाता)

पॉइंटर बनाने के लिए, हमें glGenTexture (कोई इनिशियलाइज़ेशन की आवश्यकता नहीं है) और फिर glTextureView कहकर टेक्सचर डिस्क्रिप्टर प्राप्त करने की आवश्यकता है।

 glGenTextures(1, &texture_view); glTextureView(texture_view, GL_TEXTURE_2D, source_name, internal_format, min_level, level_count, 5, 1); 

बनावट पॉइंटर्स मिपमैप के एनटी स्तर की ओर इशारा कर सकते हैं, जो काफी उपयोगी और सुविधाजनक है। पॉइंटर्स या तो टेक्सचर एरे, एरे के हिस्से, इस एरे में एक विशिष्ट लेयर हो सकते हैं या यह 2 डी टेक्सचर के रूप में 3 डी टेक्सचर का स्लाइस हो सकता है।

सूचकांक और शीर्ष के लिए एकल बफर


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

यहां बताया गया है कि हम यह कैसे करेंगे:

 GLint alignment = GL_NONE; glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &alignment); const GLsizei ind_len = GLsizei(ind_buffer.size() * sizeof(element_t)); const GLsizei vrt_len = GLsizei(vrt_buffer.size() * sizeof(vertex_t)); const GLuint ind_len_aligned = align(ind_len, alignment); const GLuint vrt_len_aligned = align(vrt_len, alignment); GLuint buffer = GL_NONE; glCreateBuffers(1, &buffer); glNamedBufferStorage(buffer, ind_len_aligned + vrt_len_aligned, nullptr, GL_DYNAMIC_STORAGE_BIT); glNamedBufferSubData(buffer, 0, ind_len, ind_buffer.data()); glNamedBufferSubData(buffer, ind_len_aligned, vrt_len, vrt_buffer.data()); GLuint vao = GL_NONE; glCreateVertexArrays(1, &vao); glVertexArrayVertexBuffer(vao, 0, buffer, ind_len_aligned, sizeof(vertex_t)); glVertexArrayElementBuffer(vao, buffer); 


टेशन और कम्प्यूटिंग छायांकन


मैं आपको tessellation shader के बारे में नहीं बताऊंगा, क्योंकि इस बारे में (रूसी में) Google में बहुत सारी सामग्री है, यहां कुछ पाठ हैं: 1 , 2 , 3 । हम गणना के लिए शेडर पर विचार करने के लिए आगे बढ़ते हैं (ब्लिन, बहुत सारी सामग्री, मैं आपको संक्षेप में बताऊंगा)।

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

एक तस्वीर, मुझे नहीं पता कि इसे क्या कहा जाए (जैसे धाराएँ समूहीकृत हैं)।



हम किस चीज का उपयोग कर सकते हैं?

  • छवि प्रसंस्करण
    1. ब्लर
    2. टाइल-आधारित एल्गोरिदम (विलंबित छायांकन)
  • सिमुलेशन
    1. कणों
    2. पानी

इसके अलावा, मुझे लिखने का कोई कारण नहीं दिखता है, Google में बहुत सारी जानकारी भी है, यहाँ उपयोग का एक सरल उदाहरण है:

 //     glUseProgramStages( pipeline, GL_COMPUTE_SHADER_BIT, cs); // ,    / glBindImageTexture( 0, tex, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8); // 80x45   (  1280720) glDispatchCompute( 80, 45, 1); 


यहाँ एक खाली गणना shader का एक उदाहरण है:
 #version 430 layout(local_size_x = 1, local_size_y = 1) in; layout(rgba32f, binding = 0) uniform image2D img_output; void main() { // base pixel color for image vec4 pixel = vec4(0.0, 0.0, 0.0, 1.0); // get index in global work group ie x,y position ivec2 pixel_coords = ivec2(gl_GlobalInvocationID.xy); // // interesting stuff happens here later // // output to a specific pixel in the image imageStore(img_output, pixel_coords, pixel); } 


यहाँ 1 , 2 , 3 , 4 पर एक गहरी नज़र के लिए कुछ लिंक दिए गए हैं।

पथ प्रतिपादन


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

मूल अवधारणा एक स्टैंसिल है, फिर एक आवरण (मूल में कवर)। पथ स्टैंसिल सेट करें, फिर पिक्सेल की कल्पना करें।

प्रबंधन के लिए, मानक ग्लिंट का उपयोग किया जाता है, और बनाने और हटाने के कार्यों में मानक नामकरण सम्मेलन होता है।

 glGenPathsNV //  glDeletePathsNV //  


यहाँ थोड़ा सा बताया गया है कि हम कैसे रास्ता निकाल सकते हैं:
  • स्ट्रिंग में SVG या पोस्टस्क्रिप्ट
     glPathStringNV 
  • इसी निर्देशांक के साथ आदेशों की सरणी
     glPathCommandsNV 
    और डेटा को अपडेट करने के लिए
     glPathSubCommands, glPathCoords, glPathSubCoords 
  • फोंट
     glPathGlyphsNV, glPathGlyphRangeNV 
  • मौजूदा रास्तों का रैखिक संयोजन (एक, दो या अधिक पथों का प्रक्षेप)
     glCopyPathNV, glInterpolatePathsNV, glCombinePathsNV 
  • किसी मौजूदा पथ का रैखिक परिवर्तन
     glTransformPathNV 

मानक आदेशों की सूची:

  • मूव-टू (x, y)
  • क्लोज-पथ
  • लाइन-टू (x, y)
  • द्विघात-वक्र (X1, y1, x2, y2)
  • घन-वक्र (X1, y1, x2, y2, x3, y3)
  • चिकनी-द्विघात-वक्र (x, y)
  • चिकनी-घन-वक्र (X1, y1, x2, y2)
  • अण्डाकार-चाप (आरएक्स, आरवाई, एक्स-एक्सिस-रोटेशन, लार्ज-आर्क-फ्लैग, स्वीप-फ्लैग, एक्स-वाई)

यहाँ पोस्टस्क्रिप्ट में पथ स्ट्रिंग कैसा दिखता है:

 "100 180 moveto 40 10 lineto 190 120 lineto 10 120 lineto 160 10 lineto closepath” // "300 300 moveto 100 400 100 200 300 100 curveto 500 200 500 400 300 300 curveto closepath” // 

और यहां एसवीजी में:

 "M100,180 L40,10 L190,120 L10,120 L160,10 z” // "M300 300 C 100 400,100 200,300 100,500 200,500 400,300 300Z” // 

वहाँ अभी भी भराई, किनारों, झुकता के प्रकार के साथ सभी प्रकार के बन्स हैं:



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

यहां प्रस्तुतिकरण की सूची दी गई है

  • घन घटता
  • द्विघात वक्र
  • पंक्तियां
  • फ़ॉन्ट ग्लिफ़
  • आर्क्स
  • डैश और एंडकैप स्टाइल

यहाँ कुछ कोड है, और फिर बहुत सारा पाठ है:

 // SVG  glPathStringNV( pathObj, GL_PATH_FORMAT_SVG_NV, strlen(svgPathString), svgPathString); //  glStencilFillPathNV( pathObj, GL_COUNT_UP_NV, 0x1F); // //  ( ) glCoverFillPathNV( pathObj, GL_BOUNDING_BOX_NV); 

वह सब है।

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

जैसा कि वादा किया गया है, मैं ड्रॉ कॉल को अनुकूलित करने और कम करने के बारे में निम्नलिखित लेख लिखूंगा। मैं आपको टिप्पणियों में लिखने के लिए कहना चाहता हूं कि आप और क्या पढ़ना चाहते हैं और इसमें आपकी क्या रुचि है:
  • Cocos2d-x पर एक खेल लिखना (केवल अभ्यास, कोई पानी नहीं)
  • वल्कन पर लेखों की एक श्रृंखला का अनुवाद
  • OpenGL पर कुछ विषय (quaternions, नई कार्यक्षमता)
  • कंप्यूटर ग्राफिक्स एल्गोरिदम (प्रकाश, अंतरिक्ष स्क्रीन परिवेश रोड़ा, अंतरिक्ष स्क्रीन प्रतिबिंब)
  • आपके विकल्प


ध्यान देने के लिए आप सभी का धन्यवाद।

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


All Articles