OpenGL ES

‫Android از گرافیک‌های دوبعدی و سه‌بعدی با عملکرد بالا با «کتابخانه گرافیک باز» (OpenGL®)، به‌ویژه «میانای برنامه‌سازی کاربردی» OpenGL ES پشتیبانی می‌کند. ‫OpenGL یک میانای برنامه‌سازی کاربردی گرافیکی چندسکویی است که یک میانای نرم‌افزاری استاندارد برای سخت‌افزار پردازش گرافیک سه‌بعدی را مشخص می‌کند. OpenGL ES نوعی از مشخصات OpenGL است که برای دستگاه‌های جاسازی‌شده درنظر گرفته شده است. ‫Android از چندین نسخه از OpenGL ES API پشتیبانی می‌کند:

  • ‫OpenGL ES 2.0 - این مشخصات API توسط Android 2.2 (سطح API 8) و بالاتر پشتیبانی می‌شود.
  • OpenGL ES 3.0 - این مشخصات میانای برنامه‌سازی کاربردی توسط Android 4.3 (سطح میانای برنامه‌سازی کاربردی ۱۸) و بالاتر پشتیبانی می‌شود.
  • OpenGL ES 3.1 - این مشخصات API توسط Android 5.0 (سطح API 21) و بالاتر پشتیبانی می‌شود.
  • OpenGL ES 3.2 - این مشخصات میانای برنامه‌سازی کاربردی توسط Android 7.0 (سطح میانای برنامه‌سازی کاربردی ۲۴) و بالاتر پشتیبانی می‌شود.

احتیاط: صرف‌نظر از نسخه پلاتفرم Android، دستگاه نمی‌تواند از OpenGL ES 3.0 API پشتیبانی کند، مگر اینکه سازنده دستگاه پیاده‌سازی این خط لوله گرافیکی را ارائه دهد. اگر در مانیفست مشخص کنید که OpenGL ES 3.0 الزامی است، می‌توانید مطمئن باشید که این نسخه در دستگاه وجود خواهد داشت. اگر مشخص کنید که نسخه سطح پایین‌تری لازم است اما بخواهید از ویژگی‌های نسخه ۳.۰ درصورت دردسترس بودن استفاده کنید، باید در زمان اجرا بررسی کنید که دستگاه از کدام نسخه OpenGL پشتیبانی می‌کند. برای کسب اطلاعات درباره نحوه انجام این کار، بررسی نسخه OpenGL ES را ببینید.

توجه: ‫Android از OpenGL ES 1.0 و 1.1 پشتیبانی می‌کند، اما این نسخه‌های API منسوخ شده‌اند و نباید در برنامه‌های مدرن استفاده شوند.

توجه: میانای برنامه‌سازی کاربردی خاصی که چارچوب Android ارائه می‌دهد مشابه میانای برنامه‌سازی کاربردی J2ME JSR239 OpenGL ES است، اما دقیقاً یکسان نیست. اگر با مشخصات J2ME JSR239 آشنا هستید، مراقب تغییرات باشید.

همچنین ببینید

اصول اولیه

‫Android از OpenGL هم ازطریق API چارچوب و هم ازطریق «کیت توسعه بومی» (NDK) پشتیبانی می‌کند. این موضوع بر میاناهای چارچوب Android تمرکز دارد. برای اطلاعات بیشتر درباره NDK، به Android NDK مراجعه کنید.

دو کلاس بنیادی در چارچوب Android وجود دارد که به شما امکان می‌دهد بااستفاده از OpenGL ES API گرافیک ایجاد و دستکاری کنید: GLSurfaceView و GLSurfaceView.Renderer. اگر هدف شما استفاده از OpenGL در برنامه Android است، درک نحوه پیاده‌سازی این کلاس‌ها در یک فعالیت باید اولین هدف شما باشد.

GLSurfaceView
این کلاس یک View است که در آن می‌توانید بااستفاده از فراخوانی‌های OpenGL API اشیا را رسم و دستکاری کنید و عملکردی مشابه SurfaceView دارد. با ایجاد نمونه‌ای از GLSurfaceView و افزودن Renderer به آن می‌توانید از این کلاس استفاده کنید. بااین‌حال، اگر می‌خواهید رویدادهای صفحه لمسی را ضبط کنید، باید کلاس GLSurfaceView را گسترش دهید تا شنوندگان لمس را پیاده‌سازی کنید، همان‌طور که در درس آموزشی OpenGL، پاسخ دادن به رویدادهای لمسی نشان داده شده است.
GLSurfaceView.Renderer
این واسط روش‌های لازم برای ترسیم گرافیک در GLSurfaceView را تعریف می‌کند. باید پیاده‌سازی این میانای کاربری را به‌عنوان کلاس جداگانه‌ای ارائه دهید و آن را بااستفاده از GLSurfaceView.setRenderer() به نمونه GLSurfaceView خود پیوست کنید.

میانای GLSurfaceView.Renderer ملزم می‌کند که روش‌های زیر را پیاده‌سازی کنید:

  • ‫onSurfaceCreated(): سیستم این روش را یک‌بار، هنگام ایجاد GLSurfaceView، فرا می‌خواند. از این روش برای انجام کنش‌هایی استفاده کنید که فقط یک‌بار باید انجام شوند، مثل تنظیم پارامترهای محیط OpenGL یا مقداردهی اولیه کردن اشیای گرافیکی OpenGL.
  • ‫onDrawFrame(): سیستم این روش را در هر بازطراحی GLSurfaceView فرا می‌خواند. از این روش به‌عنوان نقطه اجرای اصلی برای کشیدن (و دوباره کشیدن) اشیای گرافیکی استفاده کنید.
  • onSurfaceChanged(): سیستم این روش را زمانی فراخوانی می‌کند که هندسه GLSurfaceView تغییر کند، ازجمله تغییرات در اندازه GLSurfaceView یا جهت صفحه‌نمایش دستگاه. برای مثال، وقتی دستگاه از جهت عمودی به افقی تغییر می‌کند، سیستم این روش را فراخوانی می‌کند. از این روش برای پاسخ دادن به تغییرات در محتوی GLSurfaceView استفاده کنید.

بسته‌های OpenGL ES

پس‌از اینکه بااستفاده از GLSurfaceView و GLSurfaceView.Renderer نمای دربرگیرنده OpenGL ES را ایجاد کردید، می‌توانید بااستفاده از کلاس‌های زیر، شروع به فراخوانی میاناهای برنامه‌سازی کاربردی OpenGL کنید:

  • کلاس OpenGL ES 2.0 API
    • android.opengl.GLES20 - این بسته واسط OpenGL ES 2.0 را ارائه می‌دهد و از Android 2.2 (میانای برنامه‌سازی کاربردی سطح ۸) دردسترس است.
  • بسته‌های API ‏OpenGL ES 3.0/3.1/3.2
    • android.opengl - این بسته میانایی برای کلاس‌های OpenGL ES 3.0/3.1 ارائه می‌دهد. نسخه ۳.۰ از Android 4.3 (سطح API 18) دردسترس است. نسخه ۳.۱ از Android 5.0 (سطح API 21) دردسترس است. نسخه ۳.۲ از Android 7.0 (سطح میانای برنامه‌سازی کاربردی ۲۴) دردسترس است.

اگر می‌خواهید بلافاصله ساختن برنامه با OpenGL ES را شروع کنید، کلاس نمایش گرافیک با OpenGL ES را دنبال کنید.

اعلام الزامات OpenGL

اگر برنامه‌تان از ویژگی‌های OpenGL استفاده می‌کند که در همه دستگاه‌ها دردسترس نیست، باید این الزامات را در فایل AndroidManifest.xml خود بگنجانید. در اینجا رایج‌ترین اظهارنامه‌های مانیفست OpenGL آورده شده است:

  • الزامات نسخه OpenGL ES - اگر برنامه شما به نسخه خاصی از OpenGL ES نیاز دارد، باید این الزام را با افزودن تنظیمات زیر به مانیفست خود، همان‌طور که در زیر نشان داده شده است، اعلام کنید.

    برای OpenGL ES 2.0:

    <!-- Tell the system this app requires OpenGL ES 2.0. -->
    <uses-feature android:glEsVersion="0x00020000" android:require>d="true" /

    افزودن این اعلامیه باعث می‌شود Google Play برنامه شما را برای نصب در دستگاه‌هایی که از OpenGL ES 2.0 پشتیبانی نمی‌کنند محدود کند. اگر برنامه شما منحصراً برای دستگاه‌هایی است که از OpenGL ES 3.0 پشتیبانی می‌کنند، می‌توانید این را در مانیفست خود نیز مشخص کنید:

    برای OpenGL ES 3.0:

    <!-- Tell the system this app requires OpenGL ES 3.0. -->
    <uses-feature android:glEsVersion="0x00030000" android:require>d="true" /

    برای OpenGL ES 3.1:

    <!-- Tell the system this app requires OpenGL ES 3.1. -->
    <uses-feature android:glEsVersion="0x00030001" android:require>d="true" /

    برای OpenGL ES 3.2:

    <!-- Tell the system this app requires OpenGL ES 3.2. -->
    <uses-feature android:glEsVersion="0x00030002" android:require>d="true" /

    توجه: میانای برنامه‌سازی کاربردی OpenGL ES 3.x با میانای برنامه‌سازی کاربردی 2.0 سازگار با نسخه قدیمی است، که یعنی می‌توانید در پیاده‌سازی OpenGL ES در برنامه‌تان انعطاف‌پذیرتر باشید. با اعلام OpenGL ES 2.0 API به‌عنوان یک الزام در مانیفست خود، می‌توانید از آن نسخه API به‌عنوان پیش‌فرض استفاده کنید، دسترسی به API نسخه 3.x را در زمان اجرا بررسی کنید و سپس اگر دستگاه از ویژگی‌های OpenGL ES 3.x پشتیبانی کند، از آن‌ها استفاده کنید. برای اطلاعات بیشتر درباره بررسی نسخه OpenGL ES پشتیبانی‌شده توسط دستگاه، بررسی نسخه OpenGL ES را ببینید.

  • الزامات فشرده‌سازی بافت - اگر برنامه‌تان از قالب‌های فشرده‌سازی بافت استفاده می‌کند، باید قالب‌هایی را که برنامه‌تان پشتیبانی می‌کند در فایل مانیفست خود بااستفاده از <supports-gl-texture> اعلام کنید. برای اطلاعات بیشتر درباره قالب‌های فشرده‌سازی بافت دردسترس، به پشتیبانی فشرده‌سازی بافت مراجعه کنید.

    اعلام کردن الزامات فشرده‌سازی بافت در مانیفست باعث می‌شود برنامه شما از دید کاربرانی که دستگاه‌هایشان از حداقل یکی از انواع فشرده‌سازی اعلام‌شده شما پشتیبانی نمی‌کند پنهان شود. برای اطلاعات بیشتر درباره نحوه عملکرد فیلتر کردن Google Play برای فشرده‌سازی بافت، به بخش فیلتر کردن فشرده‌سازی بافت و Google Play در اسناد <supports-gl-texture> مراجعه کنید.

مختصات نقشه‌برداری برای اشیای ترسیم‌شده

یکی از مشکلات اساسی در نمایش گرافیک در دستگاه‌های Android این است که صفحه‌نمایش آن‌ها می‌تواند ازنظر اندازه و شکل متفاوت باشد. ‫OpenGL سیستم مختصات مربعی و یکنواختی را فرض می‌کند و به‌طور پیش‌فرض، این مختصات را با خوشحالی روی صفحه‌نمایش غیرمربعی شما طوری رسم می‌کند که انگار کاملاً مربعی است.

شکل ۱. سیستم مختصات پیش‌فرض OpenGL (چپ) که به صفحه‌نمایش دستگاه Android معمولی (راست) نگاشت شده است.

تصویر بالا سیستم مختصات یکنواختی را که برای قاب OpenGL در سمت چپ درنظر گرفته شده است نشان می‌دهد، و اینکه این مختصات درواقع چگونه به صفحه دستگاه معمولی در جهت افقی در سمت راست تخصیص داده می‌شوند. برای حل این مشکل، می‌توانید حالت‌های تصویرسازی OpenGL و نماهای دوربین را برای تبدیل مختصات اعمال کنید تا اشیاء گرافیکی شما در هر نمایشگری نسبت‌های صحیح را داشته باشند.

برای اعمال نماهای دوربین و طرح‌ریزی، یک ماتریس طرح‌ریزی و یک ماتریس نمای دوربین ایجاد می‌کنید و آن‌ها را در خط لوله پردازش OpenGL اعمال می‌کنید. ماتریس تصویر مختصات گرافیک شما را دوباره محاسبه می‌کند تا به‌درستی روی صفحه‌نمایش دستگاه Android نگاشت شود. ماتریس نمای دوربین تبدیلی ایجاد می‌کند که اشیا را از موقعیت چشم خاصی رندر می‌کند.

نمای دوربین و تصویرسازی در OpenGL ES 2.0 و بالاتر

در ES 2.0 و 3.0 API، ابتدا با افزودن یک عضو ماتریس به سایه‌زن‌های رأس اشیای گرافیکی، نمای دوربین و تصویر را اعمال می‌کنید. با اضافه شدن این عضو ماتریس، می‌توانید ماتریس‌های نمایش دوربین و طرح‌افکنی را برای اشیایتان تولید و اعمال کنید.

  1. افزودن ماتریس به سایه‌زن‌های رأس - متغیری برای ماتریس تصویر نمایشی ایجاد کنید و آن را به‌عنوان ضریب موقعیت سایه‌زن اضافه کنید. در کد سایه‌زن راس مثال زیر، عضو uMVPMatrix گنجانده‌شده به شما امکان می‌دهد تا ماتریس‌های نمای دوربین و تصویرسازی را روی مختصات اشیایی که از این سایه‌زن استفاده می‌کنند اعمال کنید.

    کاتلین

    private val vertexShaderCode =
    
        // This matrix member variable provides a hook to manipulate
        // the coordinates of objects that use this vertex shader.
        "uniform mat4 uMVPMatrix;   \n" +
    
        "attribute vec4 vPosition;  \n" +
        "void main(){               \n" +
        // The matrix must be included as part of gl_Position
        // Note that the uMVPMatrix factor *must be first* in order
        // for the matrix multiplication product to be correct.
        " gl_Position = uMVPMatrix * vPosition; \n" +
    
        "}  \n"

    جاوا

    private final String vertexShaderCode =
    
        // This matrix member variable provides a hook to manipulate
        // the coordinates of objects that use this vertex shader.
        "uniform mat4 uMVPMatrix;   \n" +
    
        "attribute vec4 vPosition;  \n" +
        "void main(){               \n" +
        // The matrix must be included as part of gl_Position
        // Note that the uMVPMatrix factor *must be first* in order
        // for the matrix multiplication product to be correct.
        " gl_Position = uMVPMatrix * vPosition; \n" +
    
        "}  \n";

    توجه: مثال بالا یک عضو ماتریس تبدیل واحد را در سایه‌زن رأس تعریف می‌کند که در آن ماتریس نمای دوربین و ماتریس تصویرسازی ترکیبی را اعمال می‌کنید. بسته به الزامات برنامه، ممکن است بخواهید اعضای ماتریس تصویرسازی و ماتریس نمای دوربین جداگانه‌ای را در سایه‌زن‌های رأس تعریف کنید تا بتوانید آن‌ها را به‌صورت مستقل تغییر دهید.

  2. دسترسی به ماتریس سایه‌زن - پس‌از ایجاد قلاب در سایه‌زن‌های رأس برای اعمال ماتریس‌های نمایش دوربین و تصویر، می‌توانید به آن متغیر دسترسی پیدا کنید تا ماتریس‌های نمایش دوربین و تصویر را اعمال کنید. کد زیر نحوه اصلاح روش onSurfaceCreated() پیاده‌سازی GLSurfaceView.Renderer را برای دسترسی به متغیر ماتریس تعریف‌شده در سایه‌زن رأس در بالا نشان می‌دهد.

    کاتلین

    override fun onSurfaceCreated(gl: GL10, config: EGLConfig) {
        ...
        muMVPMatrixHandle = GLES20.glGetUniformLocation(program, "uMVPMatrix")
        ...
    }

    جاوا

    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        ...
        muMVPMatrixHandle = GLES20.glGetUniformLocation(program, "uMVPMatrix");
        ...
    }
  3. ایجاد ماتریس‌های نمایش و نمای دوربین - ماتریس‌های نمایش و نمای دوربین را تولید کنید تا روی اشیای گرافیکی اعمال شوند. کد نمونه زیر نشان می‌دهد چگونه روش‌های onSurfaceCreated() و onSurfaceChanged() پیاده‌سازی GLSurfaceView.Renderer را برای ایجاد ماتریس نمای دوربین و ماتریس تصویر براساس نسبت ابعادی صفحه دستگاه تغییر دهید.

    کاتلین

    override fun onSurfaceCreated(gl: GL10, config: EGLConfig) {
        ...
        // Create a camera view matrix
        Matrix.setLookAtM(vMatrix, 0, 0f, 0f, -3f, 0f, 0f, 0f, 0f, 1.0f, 0.0f)
    }
    
    override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) {
        GLES20.glViewport(0, 0, width, height)
    
        val ratio: Float = width.toFloat() / height.toFloat()
    
        // create a projection matrix from device screen geometry
        Matrix.frustumM(projMatrix, 0, -ratio, ratio, -1f, 1f, 3f, 7f)
    }

    جاوا

    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        ...
        // Create a camera view matrix
        Matrix.setLookAtM(vMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
    }
    
    public void onSurfaceChanged(GL10 unused, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
    
        float ratio = (float) width / height;
    
        // create a projection matrix from device screen geometry
        Matrix.frustumM(projMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
    }
  4. اعمال ماتریس‌های نمایش و نمای دوربین - برای اعمال تبدیل‌های نمای دوربین و نمایش، ماتریس‌ها را در هم ضرب کنید و سپس آن‌ها را در سایه‌زن رأس تنظیم کنید. کد مثال زیر نشان می‌دهد چگونه روش onDrawFrame() پیاده‌سازی GLSurfaceView.Renderer را تغییر دهید تا ماتریس تصویر و نمای دوربین ایجادشده در کد بالا را ترکیب کنید و سپس آن را روی اشیا گرافیکی اعمال کنید تا با OpenGL رندر شوند.

    کاتلین

    override fun onDrawFrame(gl: GL10) {
        ...
        // Combine the projection and camera view matrices
        Matrix.multiplyMM(vPMatrix, 0, projMatrix, 0, vMatrix, 0)
    
        // Apply the combined projection and camera view transformations
        GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, vPMatrix, 0)
    
        // Draw objects
        ...
    }

    جاوا

    public void onDrawFrame(GL10 unused) {
        ...
        // Combine the projection and camera view matrices
        Matrix.multiplyMM(vPMatrix, 0, projMatrix, 0, vMatrix, 0);
    
        // Apply the combined projection and camera view transformations
        GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, vPMatrix, 0);
    
        // Draw objects
        ...
    }

برای مشاهده نمونه کامل نحوه اعمال نمای دوربین و تصویرسازی با OpenGL ES 2.0، به کلاس نمایش گرافیک با OpenGL ES مراجعه کنید.

شکل دادن به سطوح و پیچش

در OpenGL، وجه یک شکل سطحی است که با سه یا چند نقطه در فضای سه‌بعدی تعریف می‌شود. مجموعه‌ای از سه یا چند نقطه سه‌بعدی (که در OpenGL به آن‌ها رأس گفته می‌شود) دارای یک وجه جلو و یک وجه عقب هستند. چگونه تشخیص می‌دهید کدام چهره از روبرو و کدام از پشت است؟ سؤال خوبی است. پاسخ مربوط به پیچش یا جهتی است که در آن نقاط یک شکل را تعریف می‌کنید.

مختصات در
رأس‌های مثلث

شکل ۱. تصویرسازی از فهرست مختصات که به ترتیب رسم پادساعت‌گرد ترجمه می‌شود.

در این مثال، نقاط مثلث به‌ترتیبی تعریف شده‌اند که در جهت پادساعت‌گرد رسم شوند. ترتیب ترسیم این مختصات جهت پیچش شکل را تعریف می‌کند. به‌طور پیش‌فرض، در OpenGL، وجهی که پادساعت‌گرد رسم می‌شود وجه جلویی است. مثلث نشان‌داده‌شده در شکل ۱ به‌گونه‌ای تعریف شده است که شما به وجه جلویی شکل نگاه می‌کنید (همان‌طور که توسط OpenGL تفسیر می‌شود) و طرف دیگر وجه پشتی است.

چرا مهم است که بدانیم کدام وجه شکل، وجه جلویی است؟ پاسخ به یکی از ویژگی‌های پرکاربرد OpenGL مربوط می‌شود که «حذف چهره» نام دارد. حذف سطح گزینه‌ای برای محیط OpenGL است که به خط لوله رندرینگ اجازه می‌دهد سطح پشتی شکل را نادیده بگیرد (محاسبه یا رسم نکند) و در زمان، حافظه، و چرخه‌های پردازش صرفه‌جویی کند:

کاتلین

gl.apply {
    // enable face culling feature
    glEnable(GL10.GL_CULL_FACE)
    // specify which faces to not draw
    glCullFace(GL10.GL_BACK)
}

جاوا

// enable face culling feature
gl.glEnable(GL10.GL_CULL_FACE);
// specify which faces to not draw
gl.glCullFace(GL10.GL_BACK);

اگر بدون اینکه بدانید کدام طرف‌های شکل‌هایتان جلو و عقب هستند از ویژگی حذف سطح استفاده کنید، گرافیک OpenGL شما کمی نازک به‌نظر می‌رسد یا اصلاً نشان داده نمی‌شود. بنابراین، همیشه مختصات شکل‌های OpenGL خود را در ترتیب رسم پادساعت‌گرد تعریف کنید.

توجه: می‌توان محیط OpenGL را طوری تنظیم کرد که سطح ساعت‌گرد را به‌عنوان سطح جلویی درنظر بگیرد، اما انجام این کار به کد بیشتری نیاز دارد و احتمالاً وقتی از توسعه‌دهندگان باتجربه OpenGL کمک می‌خواهید، آن‌ها را گیج می‌کند. پس این کار را نکنید.

نسخه‌های OpenGL و سازگاری دستگاه

مشخصات میانای برنامه‌سازی کاربردی OpenGL ES 1.0 و 1.1 از Android 1.0 پشتیبانی شده‌اند. برنامه‌نویسی گرافیکی با OpenGL ES 1.0/1.1 API به‌طور قابل‌توجهی با استفاده از نسخه‌های 2.0 و بالاتر متفاوت است. OpenGL ES 2.0 توسط همه دستگاه‌های Android از Android 2.2 (سطح API 8) به بعد پشتیبانی می‌شود و قدیمی‌ترین نسخه توصیه‌شده برای برنامه‌های جدیدی است که با OpenGL ES توسعه می‌یابند. ‫OpenGL ES 3.0 در Android 4.3 (سطح API 18) و بالاتر در دستگاه‌هایی که پیاده‌سازی API‏ OpenGL ES 3.0 را ارائه می‌دهند پشتیبانی می‌شود. برای کسب اطلاعات درباره تعداد نسبی دستگاه‌های مجهز به Android که از نسخه معینی از OpenGL ES پشتیبانی می‌کنند، داشبورد نسخه OpenGL ES را ببینید.

باید الزامات گرافیکی را به‌دقت درنظر بگیرید و نسخه میانای برنامه‌سازی کاربردی‌ای را انتخاب کنید که برای برنامه‌تان بهترین عملکرد را دارد. برای اطلاعات بیشتر، به انتخاب نسخه OpenGL API مراجعه کنید.

«میانای برنامه‌سازی کاربردی» OpenGL ES 3.0 ویژگی‌های بیشتری نسبت به «میانای برنامه‌سازی کاربردی» 2.0 ارائه می‌دهد و عملکرد بهتری دارد و با نسخه‌های قدیمی‌تر نیز سازگار است. این یعنی می‌توانید برنامه‌تان را با هدف قرار دادن OpenGL ES 2.0 بنویسید و به‌طور مشروط ویژگی‌های گرافیکی OpenGL ES 3.0 را درصورت دردسترس بودن اضافه کنید. برای اطلاعات بیشتر درباره بررسی دردسترس بودن API 3.0، به بررسی نسخه OpenGL ES مراجعه کنید

پشتیبانی از فشرده‌سازی بافت

فشرده‌سازی بافت می‌تواند با کاهش الزامات حافظه و استفاده کارآمدتر از پهنای باند حافظه، عملکرد برنامه OpenGL شما را به‌طور قابل‌توجهی افزایش دهد. چارچوب Android از قالب فشرده‌سازی ETC1 به‌عنوان ویژگی استاندارد پشتیبانی می‌کند، ازجمله کلاس کمکی ETC1Util و ابزار فشرده‌سازی etc1tool (واقع در «کیت توسعه نرم‌افزار Android» در <sdk>/tools/). برای نمونه‌ای از برنامه Android که از فشرده‌سازی بافت استفاده می‌کند، نمونه کد CompressedTextureActivity را در «کیت توسعه نرم‌افزار Android» (<sdk>/samples/<version>/ApiDemos/src/com/example/android/apis/graphics/) ببینید.

همه دستگاه‌های Android که از OpenGL ES 2.0 یا بالاتر پشتیبانی می‌کنند از قالب ETC1 پشتیبانی می‌کنند.

توجه: قالب فشرده‌سازی بافت ETC1 از بافت‌های دارای شفافیت (کانال آلفا) پشتیبانی نمی‌کند. اگر برنامه شما به بافت‌های شفاف نیاز دارد، باید قالب‌های فشرده‌سازی بافت دیگری را که در دستگاه‌های هدف شما دردسترس است بررسی کنید. یک روش برای رندر کردن بافت‌های کانال آلفا بااستفاده از ETC1، متصل کردن دو شیء بافت ETC1 است: اولی با داده‌های رنگ، دومی با داده‌های کانال آلفا و سپس ترکیب مقادیر از دو بافت در سایه‌زن قطعه.

قالب‌های فشرده‌سازی بافت ETC2/EAC هنگام استفاده از OpenGL ES 3.0 API تضمین می‌شود که دردسترس باشند. این قالب بافت نسبت‌های فشرده‌سازی عالی با کیفیت بصری بالا ارائه می‌دهد و این قالب از شفافیت (کانال آلفا) نیز پشتیبانی می‌کند.

فراتر از قالب‌های ETC، دستگاه‌های Android براساس مجموعه‌های تراشه GPU و پیاده‌سازی‌های OpenGL، از فشرده‌سازی بافت پشتیبانی‌های متفاوتی دارند. باید پشتیبانی از فشرده‌سازی بافت را در دستگاه‌هایی که هدف‌یابی می‌کنید بررسی کنید تا مشخص شود برنامه شما باید از چه نوع فشرده‌سازی پشتیبانی کند. برای تعیین اینکه کدام قالب‌های بافت در دستگاه خاصی پشتیبانی می‌شود، باید دستگاه را پُرسمان کنید و نام‌های افزونه OpenGL را بررسی کنید، که مشخص می‌کند کدام قالب‌های فشرده‌سازی بافت (و سایر ویژگی‌های OpenGL) توسط دستگاه پشتیبانی می‌شود. برخی‌از قالب‌های فشرده‌سازی بافت که معمولاً پشتیبانی می‌شوند به شرح زیر است:

  • Adaptable Scalable Texture Compression (ASTC) - قالب فشرده‌سازی بافت که برای جایگزینی قالب‌های قبلی طراحی شده است. به‌دلیل پشتیبانی از اندازه‌های مختلف بلوک، انعطاف‌پذیرتر از قالب‌های قبلی است.
    • GL_KHR_texture_compression_astc_ldr
    • ‫GL_KHR_texture_compression_astc_hdr(دامنه پویای بالا)
  • S3TC (DXTn/DXTC) - فشرده‌سازی بافت S3‏ (S3TC) چندین تغییر قالب دارد (DXT1 تا DXT5) و کمتر دردسترس است. این قالب از بافت‌های RGB با کانال‌های آلفای ۴ بیتی یا آلفای ۸ بیتی پشتیبانی می‌کند. این قالب‌ها با نام افزونه OpenGL زیر نشان داده می‌شوند: نام:
    • GL_EXT_texture_compression_s3tc
    برخی‌از دستگاه‌ها فقط از نوع قالب DXT1 پشتیبانی می‌کنند؛ این پشتیبانی محدود با نام افزونه OpenGL زیر نشان داده می‌شود:
    • GL_EXT_texture_compression_dxt1

قالب‌های فشرده‌سازی بافت زیر به‌عنوان قالب‌های قدیمی درنظر گرفته می‌شوند و برای استفاده در برنامه‌های جدید توصیه نمی‌شوند:

  • ATITC (ATC) - فشرده‌سازی بافت ATI (ATITC یا ATC) در انواع مختلفی از دستگاه‌ها دردسترس است و از فشرده‌سازی نرخ ثابت برای بافت‌های RGB با و بدون کانال آلفا پشتیبانی می‌کند. این قالب ممکن است با چندین نام افزونه OpenGL نشان داده شود، برای مثال:
    • GL_AMD_compressed_ATC_texture
    • GL_ATI_texture_compression_atitc
  • PVRTC - فشرده‌سازی بافت PowerVR (PVRTC) در طیف گسترده‌ای از دستگاه‌ها دردسترس است و از بافت‌های ۲ بیتی و ۴ بیتی در هر پیکسل با یا بدون کانال آلفا پشتیبانی می‌کند. این قالب با نام افزونه OpenGL زیر نشان داده می‌شود:
    • GL_IMG_texture_compression_pvrtc
  • 3DC - فشرده‌سازی بافت 3DC‏ (3DC) قالبی است که کمتر دردسترس است و از بافت‌های RGB با کانال آلفا پشتیبانی می‌کند. این قالب با نام افزونه OpenGL زیر نشان داده می‌شود:
    • GL_AMD_compressed_3DC_texture

هشدار: این قالب‌های فشرده‌سازی بافت در همه دستگاه‌ها پشتیبانی نمی‌شوند. پشتیبانی از این قالب‌ها ممکن است بسته به سازنده و دستگاه متفاوت باشد. برای اطلاعات بیشتر درباره نحوه تعیین اینکه کدام قالب‌های فشرده‌سازی بافت در یک دستگاه خاص وجود دارد، بخش بعدی را ببینید.

توجه: پس‌از اینکه تصمیم گرفتید برنامه‌تان از کدام قالب‌های فشرده‌سازی بافت پشتیبانی کند، حتماً آن‌ها را بااستفاده از <supports-gl-texture> در مانیفست خود اعلام کنید. استفاده از این اظهارنامه امکان فیلتر کردن توسط سرویس‌های خارجی مثل Google Play را فراهم می‌کند تا برنامه شما فقط روی دستگاه‌هایی نصب شود که از قالب‌های موردنیاز برنامه‌تان پشتیبانی می‌کنند. برای جزئیات، به اظهارات مانیفست OpenGL مراجعه کنید.

تعیین افزونه‌های OpenGL

پیاده‌سازی‌های OpenGL در دستگاه‌های Android ازنظر افزونه‌های پشتیبانی‌شده در «میانای برنامه‌سازی کاربردی OpenGL ES» متفاوت است. این افزونه‌ها شامل فشرده‌سازی بافت هستند، اما معمولاً شامل افزونه‌های دیگری نیز برای مجموعه ویژگی‌های OpenGL می‌شوند.

برای تعیین اینکه کدام قالب‌های فشرده‌سازی بافت و دیگر افزونه‌های OpenGL در یک دستگاه خاص پشتیبانی می‌شوند:

  1. کد زیر را در دستگاه‌های هدف اجرا کنید تا مشخص شود چه قالب‌های فشرده‌سازی بافتی پشتیبانی می‌شود:

    کاتلین

    var extensions = gl.glGetString(GL10.GL_EXTENSIONS)

    جاوا

    String extensions = gl.glGetString(GL10.GL_EXTENSIONS);

    هشدار: نتایج این تماس بسته به مدل دستگاه متفاوت است! برای تعیین اینکه معمولاً از چه نوع فشرده‌سازی پشتیبانی می‌شود، باید این تماس را در چندین دستگاه هدف اجرا کنید.

  2. برونداد این روش را مرور کنید تا مشخص کنید کدام افزونه‌های OpenGL در دستگاه پشتیبانی می‌شوند.

بسته افزونه Android (AEP)

AEP تضمین می‌کند که برنامه شما از مجموعه استانداردی از افزونه‌های OpenGL فراتر از مجموعه اصلی شرح‌داده‌شده در مشخصات OpenGL 3.1 پشتیبانی می‌کند. بسته‌بندی این افزونه‌ها با هم مجموعه عملکردی یکسانی را در دستگاه‌های مختلف تشویق می‌کند، درحالی‌که به توسعه‌دهندگان اجازه می‌دهد از جدیدترین دستگاه‌های واحد پردازش گرافیکی همراه نهایت استفاده را ببرند.

‫AEP همچنین پشتیبانی از تصاویر، میان‌گیرهای ذخیره‌سازی سایه‌زن، و شمارنده‌های اتمی را در سایه‌زن‌های قطعه بهبود می‌بخشد.

برای اینکه برنامه‌تان بتواند از AEP استفاده کند، مانیفست برنامه باید اعلام کند که AEP الزامی است. علاوه‌براین، نسخه پلاتفرم باید از آن پشتیبانی کند.

همه ویژگی‌های اضافی مشخص‌شده در AEP در مشخصات پایه OpenGL ES 3.2 گنجانده شده است. اگر برنامه‌تان به OpenGL ES 3.2 نیاز دارد، نیازی نیست که به AEP نیاز داشته باشید.

الزام AEP را به‌صورت زیر در مانیفست اعلام کنید:

<uses-feature android:name="android.hardware.opengles.aep"
              android:require>d="true" /

برای تأیید اینکه نسخه پلاتفرم از AEP پشتیبانی می‌کند، از روش hasSystemFeature(String) استفاده کنید و FEATURE_OPENGLES_EXTENSION_PACK را به‌عنوان آرگومان ارسال کنید. تکه‌کد زیر نمونه‌ای از نحوه انجام این کار را نشان می‌دهد:

کاتلین

var deviceSupportsAEP: Boolean =
        packageManager.hasSystemFeature(PackageManager.FEATURE_OPENGLES_EXTENSION_PACK)

جاوا

boolean deviceSupportsAEP = getPackageManager().hasSystemFeature
     (PackageManager.FEATURE_OPENGLES_EXTENSION_PACK);

اگر روش مقدار درست را برگرداند، AEP پشتیبانی می‌شود.

برای کسب اطلاعات بیشتر درباره AEP، به صفحه آن در ثبت Khronos OpenGL ES مراجعه کنید.

درحال بررسی نسخه OpenGL ES

چندین نسخه از OpenGL ES در دستگاه‌های Android دردسترس است. می‌توانید حداقل نسخه API موردنیاز برنامه‌تان را در مانیفست مشخص کنید، اما ممکن است بخواهید هم‌زمان از ویژگی‌های موجود در API جدیدتر نیز بهره‌مند شوید. برای مثال، میانای برنامه‌سازی کاربردی OpenGL ES 3.0 با نسخه 2.0 این میانای برنامه‌سازی کاربردی سازگار است، بنابراین شاید بخواهید برنامه‌تان را طوری بنویسید که از ویژگی‌های OpenGL ES 3.0 استفاده کند، اما اگر میانای برنامه‌سازی کاربردی 3.0 دردسترس نبود، از میانای برنامه‌سازی کاربردی 2.0 استفاده کند.

قبل‌از استفاده از ویژگی‌های OpenGL ES از نسخه‌ای بالاتر از حداقل موردنیاز در مانیفست برنامه، برنامه شما باید نسخه API دردسترس در دستگاه را بررسی کند. این کار را می‌توانید به یکی از دو روش زیر انجام دهید:

  1. تلاش برای ایجاد بافت OpenGL ES سطح بالاتر (EGLContext) و بررسی نتیجه.
  2. یک زمینه OpenGL ES با حداقل پشتیبانی ایجاد کنید و مقدار نسخه را بررسی کنید.

کد نمونه زیر نحوه بررسی نسخه OpenGL ES دردسترس را با ایجاد یک EGLContext و بررسی نتیجه نشان می‌دهد. این مثال نشان می‌دهد چگونه نسخه OpenGL ES 3.0 را بررسی کنید:

کاتلین

private const val EGL_CONTEXT_CLIENT_VERSION = 0x3098
private const val glVersion = 3.0
private class ContextFactory : GLSurfaceView.EGLContextFactory {

    override fun createContext(egl: EGL10, display: EGLDisplay, eglConfig: EGLConfig): EGLContext {

        Log.w(TAG, "creating OpenGL ES $glVersion context")
        return egl.eglCreateContext(
                display,
                eglConfig,
                EGL10.EGL_NO_CONTEXT,
                intArrayOf(EGL_CONTEXT_CLIENT_VERSION, glVersion.toInt(), EGL10.EGL_NONE)
        ) // returns null if 3.0 is not supported
    }
}

جاوا

private static double glVersion = 3.0;

private static class ContextFactory implements GLSurfaceView.EGLContextFactory {

  private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;

  public EGLContext createContext(
          EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {

      Log.w(TAG, "creating OpenGL ES " + glVersion + " context");
      int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, (int) glVersion,
              EGL10.EGL_NONE };
      // attempt to create a OpenGL ES 3.0 context
      EGLContext context = egl.eglCreateContext(
              display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
      return context; // returns null if 3.0 is not supported;
  }
}

اگر روش createContext() نشان‌داده‌شده در بالا مقدار null برگرداند، کد شما باید به‌جای آن زمینه OpenGL ES 2.0 ایجاد کند و به استفاده از فقط آن API برگردد.

مثال کد زیر نشان می‌دهد که چگونه با ایجاد یک زمینه پشتیبانی‌شده حداقلی و سپس بررسی رشته نسخه، نسخه OpenGL ES را بررسی کنید:

کاتلین

// Create a minimum supported OpenGL ES context, then check:
gl.glGetString(GL10.GL_VERSION).also {
    Log.w(TAG, "Version: $it")
}
 // The version format is displayed as: &<quot;>O<penGL> ES major.minor"
 // followed by optional content provided by the implementation.

جاوا

// Create a minimum supported OpenGL ES context, then check:
String version = gl.glGetString(GL10.GL_VERSION);
Log.w(TAG, "Version: " + version );
// The version format is displayed as: &<quot;>O<penGL> ES major.minor"
// followed by optional content provided by the implementation.

با این رویکرد، اگر متوجه شدید که دستگاه از نسخه API سطح بالاتر پشتیبانی می‌کند، باید زمینه حداقل OpenGL ES را ازبین ببرید و زمینه جدیدی با نسخه API دردسترس بالاتر ایجاد کنید.

انتخاب نسخه OpenGL API

نسخه ۲.۰ و نسخه ۳.۰ هر دو OpenGL ES میاناهای گرافیکی با عملکرد بالا برای ایجاد بازی‌های سه‌بعدی، تصاویرسازی، و میاناهای کاربری ارائه می‌دهند. برنامه‌نویسی گرافیک برای OpenGL ES 2.0 و 3.0 تا حد زیادی مشابه است، با این تفاوت که نسخه 3.0 نشان‌دهنده یک مجموعه فوقانی از API نسخه 2.0 با ویژگی‌های اضافی است. برنامه‌نویسی برای OpenGL ES 1.0/1.1 API درمقایسه با OpenGL ES 2.0 و 3.0 تفاوت قابل‌توجهی دارد و برای برنامه‌های جدید توصیه نمی‌شود. توسعه‌دهندگان باید قبل‌از شروع توسعه با این میاناهای برنامه‌سازی کاربردی، عوامل زیر را به‌دقت درنظر بگیرند:

  • سازگاری دستگاه - توسعه‌دهندگان باید انواع دستگاه‌ها، نسخه‌های Android، و نسخه‌های OpenGL ES دردسترس برای مشتریانشان را درنظر بگیرند. برای اطلاعات بیشتر درباره سازگاری OpenGL در دستگاه‌های مختلف، بخش نسخه‌های OpenGL و سازگاری دستگاه را ببینید.
  • پشتیبانی از بافت - «میانای برنامه‌سازی کاربردی» OpenGL ES 3.0 بهترین پشتیبانی را از فشرده‌سازی بافت دارد زیرا دردسترس بودن قالب فشرده‌سازی ETC2 را که از شفافیت پشتیبانی می‌کند تضمین می‌کند. پیاده‌سازی‌های API نسخه ۲.۰ شامل پشتیبانی از ETC1 است، اما این قالب بافت از شفافیت پشتیبانی نمی‌کند. برای پیاده‌سازی شفافیت با بافت‌های فشرده‌شده، باید از دو بافت ETC1 (تقسیم‌شده بین رنگ و آلفا) استفاده کنید یا منابع را در قالب‌های فشرده‌سازی دیگری که دستگاه‌های هدف شما پشتیبانی می‌کنند ارائه دهید. برای اطلاعات بیشتر، پشتیبانی فشرده‌سازی بافت را ببینید.

اگرچه سازگاری و پشتیبانی از بافت ممکن است بر تصمیم شما تأثیر بگذارد، باید نسخه OpenGL API را براساس آنچه فکر می‌کنید بهترین تجربه را برای کاربران شما فراهم می‌کند انتخاب کنید.