परिचय
3 डी ग्राफिक्स रेंडर करना एक आसान काम नहीं है, लेकिन बेहद दिलचस्प और रोमांचक है। यह आलेख उन लोगों के लिए है जो अभी ओपनजीएल से परिचित होना शुरू कर रहे हैं या उन लोगों के लिए जो ग्राफिक पाइपलाइनों में काम करते हैं और क्या हैं। यह आलेख OpenGL संदर्भ और विंडो बनाने के तरीके या आपके पहले OpenGL विंडो एप्लिकेशन को लिखने के तरीके के बारे में सटीक निर्देश प्रदान नहीं करेगा। यह प्रत्येक प्रोग्रामिंग भाषा की विशेषताओं और OpenGL के साथ काम करने के लिए एक पुस्तकालय या ढांचे की पसंद के कारण है (मैं C ++ और
GLFW का उपयोग
करूंगा ), खासकर जब से जिस भाषा में आप रुचि रखते हैं उसके लिए नेटवर्क पर एक ट्यूटोरियल ढूंढना आसान है। लेख में दिए गए सभी उदाहरण अन्य भाषाओं में कमांड के थोड़े बदले हुए शब्दार्थों के साथ काम करेंगे, ऐसा क्यों है, मैं थोड़ा बाद में बताऊंगा।
OpenGL क्या है?
OpenGL एक विनिर्देश है जो दो-आयामी और तीन-आयामी कंप्यूटर ग्राफिक्स का उपयोग करके एप्लिकेशन लिखने के लिए एक प्लेटफ़ॉर्म-स्वतंत्र सॉफ़्टवेयर इंटरफ़ेस को परिभाषित करता है। ओपनजीएल एक कार्यान्वयन नहीं है, लेकिन केवल निर्देशों के उन सेटों का वर्णन करता है जिन्हें लागू किया जाना चाहिए, अर्थात्। एक एपीआई है।
ओपनजीएल के प्रत्येक संस्करण का अपना विनिर्देश है, हम संस्करण 3.3 से संस्करण 4.6 तक काम करेंगे, क्योंकि संस्करण 3.3 से सभी नवाचार उन पहलुओं को प्रभावित करते हैं जो हमारे लिए बहुत कम महत्व रखते हैं। अपना पहला OpenGL एप्लिकेशन लिखना शुरू करने से पहले, मेरा सुझाव है कि आप यह पता करें कि आपके ड्राइवर किन संस्करणों का समर्थन करता है (आप अपने वीडियो कार्ड के विक्रेता की साइट पर ऐसा कर सकते हैं) और ड्राइवर को नवीनतम संस्करण में अपडेट करें।
ओपन डिवाइस
ओपनजीएल की तुलना एक बड़ी राज्य मशीन से की जा सकती है, जिसमें कई राज्यों और उन्हें बदलने के लिए कार्य हैं। OpenGL राज्य मूल रूप से OpenGL संदर्भ को संदर्भित करता है। ओपनजीएल के साथ काम करते समय, हम कई राज्य-बदलते कार्यों से गुजरेंगे जो संदर्भ को बदल देंगे, और ओपनजीएल की वर्तमान स्थिति के आधार पर कार्रवाई करेंगे।
उदाहरण के लिए, यदि हम रेंडर करने से पहले त्रिकोणों के बजाय लाइनों का उपयोग करने के लिए OpenGL को कमांड देते हैं, तो OpenGL सभी बाद के रेंडरिंग के लिए लाइनों का उपयोग करेगा जब तक कि हम इस विकल्प को नहीं बदलते या संदर्भ नहीं बदलते।
OpenGL में वस्तुएँ
ओपनजीएल लाइब्रेरी सी में लिखी गई हैं और उनके लिए विभिन्न भाषाओं के लिए कई एपीआई हैं, लेकिन फिर भी वे सी लाइब्रेरी हैं। सी से कई निर्माणों का उच्च स्तरीय भाषाओं में अनुवाद नहीं किया गया है, इसलिए ओपनगेल को बड़ी संख्या में सार का उपयोग करके विकसित किया गया था, इनमें से एक सार वस्तु है।
ओपनजीएल में एक ऑब्जेक्ट विकल्पों का एक समूह है जो इसकी स्थिति को निर्धारित करता है। ओपनजीएल में किसी भी वस्तु को उसकी आईडी और विकल्पों के सेट द्वारा वर्णित किया जा सकता है जिसके लिए वह जिम्मेदार है। बेशक, प्रत्येक प्रकार की वस्तु के अपने विकल्प होते हैं और ऑब्जेक्ट के लिए गैर-मौजूद विकल्पों को कॉन्फ़िगर करने का प्रयास त्रुटि पैदा करेगा। इसमें OpenGL का उपयोग करने में असुविधा होती है: विकल्पों का एक सेट सी समान संरचना के साथ वर्णित है, जिनमें से पहचानकर्ता अक्सर एक नंबर होता है, जो प्रोग्रामर को संकलन चरण में एक त्रुटि खोजने की अनुमति नहीं देता है, क्योंकि त्रुटिपूर्ण और सही कोड शब्दशः अप्रभेद्य हैं।
glGenObject(&objectId); glBindObject(GL_TAGRGET, objectId); glSetObjectOption(GL_TARGET, GL_CORRECT_OPTION, correct_option);
आप बहुत बार इस तरह के कोड का सामना करेंगे, इसलिए जब आप एक राज्य मशीन स्थापित करने की तरह इसका उपयोग करते हैं, तो यह आपके लिए बहुत आसान हो जाएगा। यह कोड केवल एक उदाहरण दिखाता है कि OpenGL कैसे काम करता है। इसके बाद, वास्तविक उदाहरण प्रस्तुत किए जाएंगे।
लेकिन प्लसस हैं। इन वस्तुओं की मुख्य विशेषता यह है कि हम अपने आवेदन में कई वस्तुओं की घोषणा कर सकते हैं, उनके विकल्प निर्धारित कर सकते हैं, और जब भी हम ओपनजीएल राज्य का उपयोग करके संचालन शुरू करते हैं, तो हम बस अपनी पसंदीदा सेटिंग्स के साथ वस्तु को बांध सकते हैं। उदाहरण के लिए, यह 3D मॉडल डेटा या ऐसी कोई वस्तु हो सकती है जिसे हम इस मॉडल पर आकर्षित करना चाहते हैं। रेंडरिंग प्रक्रिया के दौरान कई ऑब्जेक्ट्स का स्वामित्व उनके बीच स्विच करना आसान बनाता है। इस दृष्टिकोण के साथ, हम फ्रेम के बीच मूल्यवान समय गंवाए बिना अपने राज्यों को प्रस्तुत करने और उपयोग करने के लिए आवश्यक कई वस्तुओं को कॉन्फ़िगर कर सकते हैं।
ओपनजीएल के साथ काम करना शुरू करने के लिए आपको कई बुनियादी वस्तुओं से परिचित होना होगा जिसके बिना हम कुछ भी प्रदर्शित नहीं कर सकते। इन वस्तुओं का एक उदाहरण के रूप में उपयोग करते हुए, हम समझेंगे कि ओपनग्लॉग में डेटा और निष्पादन योग्य निर्देशों को कैसे बांधें।
बेस ऑब्जेक्ट्स: शेडर्स और शेडर प्रोग्राम। =
Shader एक छोटा सा प्रोग्राम है जो ग्राफिक्स पाइपलाइन में एक निश्चित बिंदु पर ग्राफिक्स एक्सेलरेटर (GPU) पर चलता है। यदि हम शेड्स को सार रूप से मानते हैं, तो हम कह सकते हैं कि ये ग्राफिक्स पाइपलाइन के चरण हैं, जो:
- जानिए प्रोसेसिंग के लिए डेटा कहां से मिलेगा।
- इनपुट डेटा को प्रोसेस करने का तरीका जानें।
- वे जानते हैं कि आगे की प्रक्रिया के लिए डेटा कहां लिखना है।
लेकिन ग्राफिक्स पाइपलाइन कैसा दिखता है? बहुत सरल, इस तरह:

अब तक, इस योजना में, हम केवल मुख्य ऊर्ध्वाधर में रुचि रखते हैं, जो वर्टेक्स विशिष्टता के साथ शुरू होता है और फ़्रेम बफ़र के साथ समाप्त होता है। जैसा कि पहले उल्लेख किया गया है, प्रत्येक शेडर का अपना इनपुट और आउटपुट पैरामीटर होता है, जो प्रकार और मापदंडों की संख्या में भिन्न होता है।
हम पाइपलाइन के प्रत्येक चरण का संक्षेप में वर्णन करते हैं ताकि यह समझ सके कि यह क्या करता है:
- वर्टेक्स शेडर - 3 डी समन्वय डेटा और अन्य सभी इनपुट मापदंडों को संसाधित करने की आवश्यकता है। सबसे अधिक बार, वर्टेक्स शेडर स्क्रीन के सापेक्ष वर्टेक्स की स्थिति की गणना करता है, मानदंडों की गणना करता है (यदि आवश्यक हो) और अन्य शेड्स के लिए इनपुट डेटा उत्पन्न करता है।
- Tessellation shader और tessellation control shader - ये दोनों शेड्स, वर्टिकल शेडर से आने वाली प्राइमेटरीज को डिटेल करने और जियोमेट्रिक शेडर में प्रोसेसिंग के लिए डेटा तैयार करने के लिए जिम्मेदार होते हैं। यह वर्णन करना मुश्किल है कि ये दो शेड्स दो वाक्यों में क्या सक्षम हैं, लेकिन पाठकों के लिए थोड़ा विचार करने के लिए, मैं ओवरलैप के निम्न और उच्च स्तर के साथ कुछ छवियां दूंगा:
मैं आपको इस लेख को पढ़ने की सलाह देता हूं यदि आप टेसूलेशन के बारे में अधिक जानना चाहते हैं। लेखों की इस श्रृंखला में हम टेसूलेशन को कवर करेंगे, लेकिन यह जल्द ही नहीं होगा। - ज्यामितीय shader - tessellation shader के आउटपुट से ज्यामितीय प्रिमिटिव के निर्माण के लिए जिम्मेदार है। जियोमेट्रिक शेडर का उपयोग करके, आप बुनियादी ओपनग्ल प्राइमेटिक्स (GL_LINES, GL_POINT, GL_TRIANGLES, आदि) से नई प्राइमेटरी बना सकते हैं, उदाहरण के लिए, ज्यामितीय शेडर का उपयोग करके, आप केवल रंग, क्लस्टर सेंटर, त्रिज्या और घनत्व द्वारा कण का वर्णन करके एक कण प्रभाव बना सकते हैं।
- रास्टराइजेशन शेडर गैर-प्रोग्रामेबल शेड्स में से एक है। एक समझने योग्य भाषा में बोलते हुए, यह सभी आउटपुट ग्राफिक प्रिमिटिव्स को टुकड़ों (पिक्सेल) में अनुवाद करता है, अर्थात। स्क्रीन पर उनकी स्थिति निर्धारित करता है।
- टुकड़े टुकड़े करना ग्राफिक्स पाइपलाइन का अंतिम चरण है। टुकड़ा shader वर्तमान फ्रेम बफर में सेट किया जाएगा कि टुकड़ा (पिक्सेल) के रंग की गणना करता है। सबसे अधिक बार, टुकड़ा shader में, टुकड़े के छायांकन और प्रकाश व्यवस्था, बनावट की मैपिंग और सामान्य नक्शे की गणना की जाती है - ये सभी तकनीक आपको अविश्वसनीय रूप से सुंदर परिणाम प्राप्त करने की अनुमति देती हैं।
ओपनजीएल शेड्स को एक विशेष सी-लाइक जीएलएसएल भाषा में लिखा जाता है जिसमें से उन्हें संकलित किया जाता है और एक शेडर प्रोग्राम में जोड़ा जाता है। पहले से ही इस स्तर पर, ऐसा लगता है कि एक shader कार्यक्रम लिखना एक अत्यंत समय लेने वाला कार्य है, क्योंकि आपको ग्राफिक्स पाइपलाइन के 5 चरणों को निर्धारित करने और उन्हें एक साथ जोड़ने की आवश्यकता है। सौभाग्य से, यह ऐसा नहीं है: टसेसेलेशन और ज्यामिति शेड्स को डिफ़ॉल्ट रूप से ग्राफिक्स पाइपलाइन में परिभाषित किया गया है, जो हमें केवल दो शेड्स - वर्टेक्स और टुकड़ा वाले (कभी-कभी पिक्सेल शेडर कहा जाता है) को परिभाषित करने की अनुमति देता है। क्लासिक उदाहरण के साथ इन दो शेड्स पर विचार करना सबसे अच्छा है:
वर्टेक्स शेडर #version 450 layout (location = 0) in vec3 vertexCords; layout (location = 1) in vec3 color; out vec3 Color; void main(){ gl_Position = vec4(vertexCords,1.0f) ; Color = color; }
घर्षण करने वाला #version 450 in vec3 Color; out vec4 out_fragColor; void main(){ out_fragColor = Color; }
Shader विधानसभा उदाहरण unsigned int vShader = glCreateShader(GL_SHADER_VERTEX);
ये दो सरल शेड कुछ भी गणना नहीं करते हैं, वे सिर्फ पाइप लाइन के नीचे डेटा पास करते हैं। आइए ध्यान दें कि शीर्ष और खंड शेड कैसे जुड़े हुए हैं: शीर्ष छाया में, रंग चर को बाहर घोषित किया जाता है, जिसमें मुख्य कार्य के निष्पादित होने के बाद रंग लिखा जाएगा, जबकि टुकड़े करने वाले में ठीक उसी चर को योग्यता में घोषित किया जाता है, अर्थात जैसा कि पहले बताया गया है, टुकड़े टुकड़े करने वाले को पाइप लाइन के माध्यम से डेटा के एक सरल धक्का के माध्यम से शीर्ष से डेटा प्राप्त होता है (लेकिन वास्तव में यह इतना सरल नहीं है)।
नोट: यदि आप खंड shader में प्रकार vec4 के एक चर को घोषित और प्रारंभ नहीं करते हैं, तो स्क्रीन पर कुछ भी प्रदर्शित नहीं होगा।
चौकस पाठकों ने पहले ही वीआर 3 प्रकार के इनपुट चर की घोषणा को अजीब लेआउट क्वालीफायर के साथ देखा है।
बेस ऑब्जेक्ट्स: बफ़र्स और वर्टेक्स एरेज़
मुझे लगता है कि यह बताने लायक नहीं है कि बफर ऑब्जेक्ट्स क्या हैं, हम बेहतर मानते हैं कि ओपनजीएल में बफर कैसे बनाया जाए और कैसे भरें।
float vertices[] = {
इसमें कुछ भी मुश्किल नहीं है, हम उत्पन्न बफर को वांछित लक्ष्य से जोड़ते हैं (बाद में हमें पता चलेगा कि कौन सा है) और डेटा को उनके आकार और उपयोग के प्रकार को इंगित करते हुए लोड करता है।
GL_STATIC_DRAW - बफर में डेटा नहीं बदला जाएगा।
GL_DYNAMIC_DRAW - बफर में डेटा बदल जाएगा, लेकिन अक्सर नहीं।
GL_STREAM_DRAW - बफर में डेटा हर ड्रॉ कॉल के साथ बदल जाएगा।
महान, अब हमारा डेटा GPU मेमोरी में स्थित है, shader प्रोग्राम संकलित और लिंक किया गया है, लेकिन एक कैविएट है: प्रोग्राम को यह कैसे पता चलता है कि वर्टेक्स shader के लिए इनपुट डेटा कहाँ से मिलेगा? हमने डेटा डाउनलोड किया, लेकिन यह संकेत नहीं दिया कि शेडर प्रोग्राम इसे कहां से प्राप्त करेगा। यह समस्या एक अलग प्रकार की OpenGL ऑब्जेक्ट द्वारा हल की गई है - शीर्ष सरणियाँ।

छवि को इस ट्यूटोरियल से लिया गया है।
बफ़र्स के साथ के रूप में, शीर्ष सरणियों को उनके कॉन्फ़िगरेशन उदाहरण का उपयोग करके सबसे अच्छा देखा जाता है।
unsigned int VBO, VAO; glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); glGenVertexArrays(1, &VAO); glBindVertexArray(VAO);
शीर्ष सरणियाँ बनाना अन्य OpenGL ऑब्जेक्ट बनाने से अलग नहीं है, लाइन के बाद सबसे दिलचस्प शुरू होता है:
glBindVertexArray(VAO);
एक वर्टेक्स ऐरे (VAO) डेटा बाइंडिंग के लिए बफर ऑब्जेक्ट्स के बाइंडिंग सहित इसके साथ किए जाने वाले सभी बाइंडिंग और कॉन्फ़िगरेशन को याद करता है। इस उदाहरण में, केवल एक ही वस्तु है, लेकिन व्यवहार में कई हो सकते हैं। उसके बाद, एक विशिष्ट संख्या के साथ शीर्ष विशेषता को कॉन्फ़िगर किया गया है:
glBindBuffer(GL_ARRAY_BUFFER, VBO); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), nullptr);
हमें यह नंबर कहां से मिला? वर्टेक्स शेडर इनपुट चर के लिए लेआउट क्वालिफायर याद रखें? यह वह है जो यह निर्धारित करता है कि किस चर विशेषता का इनपुट चर बाध्य होगा। अब संक्षेप में फ़ंक्शन तर्कों पर जाएं ताकि कोई अनावश्यक प्रश्न न हों:
- वह विशेषता संख्या जिसे हम कॉन्फ़िगर करना चाहते हैं।
- हम जो आइटम लेना चाहते हैं। (चूंकि लेआउट के साथ वर्टेक्स शेडर का इनपुट चर = 0 प्रकार vec3 का है, तो हम टाइप फ्लोट के 3 तत्व लेते हैं)
- वस्तुओं का प्रकार।
- क्या तत्वों को सामान्य करना आवश्यक है, अगर यह एक वेक्टर है।
- अगले शीर्ष के लिए ऑफसेट (चूंकि हमारे पास निर्देशांक और रंग क्रमिक रूप से स्थित हैं और प्रत्येक में vec3 प्रकार है, फिर हम 6 * साइज़ोफ़ (फ्लोट) = 24 बाइट्स द्वारा स्थानांतरित करते हैं)।
- आखिरी तर्क दिखाता है कि पहले शीर्ष के लिए क्या ऑफसेट करना है। (निर्देशांक के लिए, यह तर्क 0 बाइट्स है, रंगों के लिए 12 बाइट्स)
अब हम अपनी पहली छवि प्रस्तुत करने के लिए तैयार हैं
रेंडर करने से पहले VAO और shader प्रोग्राम को बांधना याद रखें।
{
यदि आपने सब कुछ सही किया है, तो आपको यह परिणाम प्राप्त करना चाहिए:

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