OpenGL ES-এর মাধ্যমে গ্রাফিক্স কনফিগার করা

আপনার গেমে অবজেক্ট ও স্প্রাইট আঁকতে, আপনাকে ডিসপ্লে, সারফেস ও কনটেক্সট ভেরিয়েবল কনফিগার করতে হবে, গেম লুপে রেন্ডারিং সেট-আপ করতে হবে এবং প্রতিটি সিন ও অবজেক্ট আঁকতে হবে।

C বা C++ গেমের জন্য স্ক্রিনে ছবি আঁকার দুটি উপায় আছে, যথা OpenGL ES অথবা Vulkan.

শুরু করার আগে

আপনি আগে থেকে এটি না করে থাকলে, আপনার Android প্রোজেক্টে GameActivity অবজেক্ট সেট-আপ করুন।

OpenGL ES ভেরিয়েবল সেট-আপ করা

  1. আপনার গেম রেন্ডার করার জন্য ডিসপ্লে, সারফেস, কনটেক্সট এবং কনফিগারেশন প্রয়োজন হবে। আপনার গেম ইঞ্জিনের হেডার ফাইলে নিম্নলিখিত OpenGL ES ভেরিয়েবল যোগ করুন:

    class NativeEngine {
     //...
     private:
      EGLDisplay mEglDisplay;
      EGLSurface mEglSurface;
      EGLContext mEglContext;
      EGLConfig mEglConfig;
    
      bool mHasFocus, mIsVisible, mHasWindow;
      bool mHasGLObjects;
      bool mIsFirstFrame;
    
      int mSurfWidth, mSurfHeight;
    }
    
  2. আপনার গেম ইঞ্জিনের কনস্ট্রাক্টরে, ভেরিয়েবলের ডিফল্ট ভ্যালু ইনিশিয়ালাইজ করুন।

    NativeEngine::NativeEngine(struct android_app *app) {
      //...
      mEglDisplay = EGL_NO_DISPLAY;
      mEglSurface = EGL_NO_SURFACE;
      mEglContext = EGL_NO_CONTEXT;
      mEglConfig = 0;
    
      mHasFocus = mIsVisible = mHasWindow = false;
      mHasGLObjects = false;
      mIsFirstFrame = true;
    
      mSurfWidth = mSurfHeight = 0;
    }
    
  3. রেন্ডার করার জন্য ডিসপ্লে ইনিশিয়ালাইজ করুন।

    bool NativeEngine::InitDisplay() {
      if (mEglDisplay != EGL_NO_DISPLAY) {
        return true;
      }
    
      mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
      if (EGL_FALSE == eglInitialize(mEglDisplay, 0, 0)) {
        LOGE("NativeEngine: failed to init display, error %d", eglGetError());
        return false;
      }
      return true;
    }
    
  4. সারফেসটি EGL-এর দ্বারা অ্যাসাইন করা অফ-স্ক্রিন বাফার (pbuffer) অথবা Android OS-এর দ্বারা অ্যাসাইন করা উইন্ডো হতে পারে। এই সারফেস ইনিশিয়ালাইজ করুন:

    bool NativeEngine::InitSurface() {
      ASSERT(mEglDisplay != EGL_NO_DISPLAY);
      if (mEglSurface != EGL_NO_SURFACE) {
        return true;
      }
    
      EGLint numConfigs;
      const EGLint attribs[] = {
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, // request OpenGL ES 2.0
        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
        EGL_BLUE_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_RED_SIZE, 8,
        EGL_DEPTH_SIZE, 16,
        EGL_NONE
      };
    
      // Pick the first EGLConfig that matches.
      eglChooseConfig(mEglDisplay, attribs, &mEglConfig, 1, &numConfigs);
      mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, mApp->window,
                                           NULL);
      if (mEglSurface == EGL_NO_SURFACE) {
        LOGE("Failed to create EGL surface, EGL error %d", eglGetError());
        return false;
      }
      return true;
    }
    
  5. রেন্ডারিং কনটেক্সট ইনিশিয়ালাইজ করুন। এই উদাহরণে একটি OpenGL ES 2.0 কনটেক্সট তৈরি করা হয়েছে:

    bool NativeEngine::InitContext() {
      ASSERT(mEglDisplay != EGL_NO_DISPLAY);
      if (mEglContext != EGL_NO_CONTEXT) {
        return true;
      }
    
      // OpenGL ES 2.0
      EGLint attribList[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
      mEglContext = eglCreateContext(mEglDisplay, mEglConfig, NULL, attribList);
      if (mEglContext == EGL_NO_CONTEXT) {
        LOGE("Failed to create EGL context, EGL error %d", eglGetError());
        return false;
      }
      return true;
    }
    
  6. আঁকার আগে আপনার OpenGL ES সেটিংস কনফিগার করুন। এই উদাহরণটি প্রতিটি ফ্রেমের শুরুতে এক্সিকিউট করা হয়। এটি ডেপথ টেস্টিং চালু করে, পরিষ্কার রঙকে কালো হিসেবে সেট করে এবং রঙ ও ডেপথ বাফার মুছে দেয়।

    void NativeEngine::ConfigureOpenGL() {
      glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
      glEnable(GL_DEPTH_TEST);
      glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    }
    

গেম লুপের সাথে রেন্ডার করা

  1. গেম লুপ একটি ফ্রেম রেন্ডার করে এবং ব্যবহারকারী গেম না ছাড়া পর্যন্ত এটি অনির্দিষ্টকালের জন্য চলতে থাকে। দুটি ফ্রেমের মধ্যে, আপনার গেম এগুলি করতে পারে:

    • প্রসেস ইভেন্ট যেমন ইনপুট, অডিও আউটপুট ও নেটওয়ার্কিং ইভেন্ট।

    • গেমের লজিক ও ইউজার ইন্টারফেস আপডেট করুন।

    • ডিসপ্লেতে একটি ফ্রেম রেন্ডার করুন।

    ডিসপ্লেতে ফ্রেম রেন্ডার করতে, গেম লুপে DoFrame মেথডটি অনির্দিষ্টকালের জন্য কল করা হয়:

    void NativeEngine::GameLoop() {
      // Loop indefinitely.
      while (1) {
        int events;
        struct android_poll_source* source;
    
        // If not animating, block until we get an event.
        while ((ALooper_pollAll(IsAnimating() ? 0 : -1, NULL, &events,
                                (void **) &source)) >= 0) {
          // Process events.
          ...
        }
    
        // Render a frame.
        if (IsAnimating()) {
            DoFrame();
        }
      }
    }
    
  2. DoFrame পদ্ধতিতে, বর্তমান সারফেসের ডাইমেনশন কোয়েরি করুন, ফ্রেম রেন্ডার করার জন্য অনুরোধ করুন SceneManager এবং ডিসপ্লে বাফার অদল-বদল করুন।

    void NativeEngine::DoFrame() {
      ...
      // Query the current surface dimension.
      int width, height;
      eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &width);
      eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &height);
    
      // Handle dimension changes.
      SceneManager *mgr = SceneManager::GetInstance();
      if (width != mSurfWidth || height != mSurfHeight) {
        mSurfWidth = width;
        mSurfHeight = height;
        mgr->SetScreenSize(mSurfWidth, mSurfHeight);
        glViewport(0, 0, mSurfWidth, mSurfHeight);
      }
      ...
      // Render scenes and objects.
      mgr->DoFrame();
    
      // Swap buffers.
      if (EGL_FALSE == eglSwapBuffers(mEglDisplay, mEglSurface)) {
        HandleEglError(eglGetError());
      }
    }
    

দৃশ্য ও অবজেক্ট রেন্ডার করা

  1. গেম লুপ, রেন্ডার করার জন্য দৃশ্যমান দৃশ্য ও অবজেক্টের একটি হায়ারার্কি প্রসেস করে। 'অন্তহীন টানেল' উদাহরণে, SceneManager একাধিক দৃশ্য ট্র্যাক করে, একসাথে শুধুমাত্র একটি দৃশ্য অ্যাক্টিভ থাকে। এই উদাহরণে, বর্তমান দৃশ্যটি রেন্ডার করা হয়েছে:

    void SceneManager::DoFrame() {
      if (mSceneToInstall) {
        InstallScene(mSceneToInstall);
        mSceneToInstall = NULL;
      }
    
      if (mHasGraphics && mCurScene) {
        mCurScene->DoFrame();
      }
    }
    
  2. আপনার গেমের উপর নির্ভর করে, কোনও দৃশ্যে ব্যাকগ্রাউন্ড, টেক্সট, স্প্রাইট ও গেম অবজেক্ট থাকতে পারে। আপনার গেমের জন্য উপযুক্ত ক্রমে সেগুলি রেন্ডার করুন। এই উদাহরণে ব্যাকগ্রাউন্ড, টেক্সট ও উইজেট রেন্ডার করা হয়েছে:

    void UiScene::DoFrame() {
      // clear screen
      glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      glDisable(GL_DEPTH_TEST);
    
      RenderBackground();
    
      // Render the "Please Wait" sign and do nothing else
      if (mWaitScreen) {
        SceneManager *mgr = SceneManager::GetInstance();
        mTextRenderer->SetFontScale(WAIT_SIGN_SCALE);
        mTextRenderer->SetColor(1.0f, 1.0f, 1.0f);
        mTextRenderer->RenderText(S_PLEASE_WAIT, mgr->GetScreenAspect() * 0.5f,
                                  0.5f);
        glEnable(GL_DEPTH_TEST);
        return;
      }
    
      // Render all the widgets.
      for (int i = 0; i < mWidgetCount; ++i) {
        mWidgets[i]->Render(mTrivialShader, mTextRenderer, mShapeRenderer,
              (mFocusWidget < 0) ? UiWidget::FOCUS_NOT_APPLICABLE :
              (mFocusWidget == i) ? UiWidget::FOCUS_YES : UiWidget::FOCUS_NO,tf);
      }
      glEnable(GL_DEPTH_TEST);
    }
    

রিসোর্স

OpenGL ES ও Vulkan সম্পর্কে আরও জানতে নিম্নলিখিত তথ্য পড়ুন:

  • OpenGL ES - Android-এ ছবি ও গ্রাফিক্স।

  • OpenGL ES - Android সোর্সে ওভারভিউ।

  • Vulkan - NDK-তে শুরু করা।

  • Vulkan - এক নজরে Android সোর্সে।

  • Android গেম লুপ বোঝা - ফ্রেমের গতি নির্ধারণ, কিউ বাফার, VSYNC কলব্যাক ম্যানেজ করা এবং থ্রেড ম্যানেজ করা সম্পর্কে জানুন।