আকার আঁকুন

আপনি OpenGL দিয়ে আঁকার জন্য আকারগুলি সংজ্ঞায়িত করার পরে, আপনি সম্ভবত সেগুলি আঁকতে চান। OpenGL ES 2.0 এর সাথে আকৃতি আঁকতে আপনার কল্পনার চেয়ে একটু বেশি কোড লাগে, কারণ API গ্রাফিক্স রেন্ডারিং পাইপলাইনের উপর প্রচুর নিয়ন্ত্রণ প্রদান করে।

এই পাঠটি ব্যাখ্যা করে কিভাবে আপনি OpenGL ES 2.0 API ব্যবহার করে পূর্ববর্তী পাঠে সংজ্ঞায়িত আকারগুলি আঁকবেন।

আকার শুরু করুন

আপনি কোনো অঙ্কন করার আগে, আপনি আঁকতে পরিকল্পনা করা আকারগুলি শুরু এবং লোড করতে হবে। যতক্ষণ না আপনি আপনার প্রোগ্রামে যে আকারগুলি ব্যবহার করেন তার গঠন (মূল স্থানাঙ্ক) কার্যকর করার সময় পরিবর্তন না হয়, আপনার মেমরি এবং প্রক্রিয়াকরণ দক্ষতার জন্য আপনার রেন্ডারারের onSurfaceCreated() পদ্ধতিতে শুরু করা উচিত।

কোটলিন

class MyGLRenderer : GLSurfaceView.Renderer {
    ...
    private lateinit var mTriangle: Triangle
    private lateinit var mSquare: Square

    override fun onSurfaceCreated(unused: GL10, config: EGLConfig) {
        ...
        // initialize a triangle
        mTriangle = Triangle()
        // initialize a square
        mSquare = Square()
    }
    ...
}

জাভা

public class MyGLRenderer implements GLSurfaceView.Renderer {

    ...
    private Triangle mTriangle;
    private Square   mSquare;

    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        ...
        // initialize a triangle
        mTriangle = new Triangle();
        // initialize a square
        mSquare = new Square();
    }
    ...
}

একটি আকৃতি আঁকুন

OpenGL ES 2.0 ব্যবহার করে একটি সংজ্ঞায়িত আকৃতি আঁকার জন্য একটি উল্লেখযোগ্য পরিমাণ কোডের প্রয়োজন, কারণ আপনাকে অবশ্যই গ্রাফিক্স রেন্ডারিং পাইপলাইনে প্রচুর বিবরণ প্রদান করতে হবে। বিশেষত, আপনাকে অবশ্যই নিম্নলিখিতগুলি সংজ্ঞায়িত করতে হবে:

  • Vertex Shader - একটি আকৃতির শীর্ষবিন্দু রেন্ডার করার জন্য OpenGL ES গ্রাফিক্স কোড।
  • ফ্র্যাগমেন্ট শেডার - রং বা টেক্সচার সহ একটি আকৃতির মুখ রেন্ডার করার জন্য OpenGL ES কোড।
  • প্রোগ্রাম - একটি OpenGL ES অবজেক্ট যাতে আপনি এক বা একাধিক আকার আঁকার জন্য ব্যবহার করতে চান এমন শেডার ধারণ করে।

একটি আকৃতি আঁকতে আপনার কমপক্ষে একটি ভার্টেক্স শেডার এবং সেই আকৃতিটি রঙ করার জন্য একটি টুকরো শেডার প্রয়োজন। এই শেডারগুলিকে অবশ্যই কম্পাইল করতে হবে এবং তারপরে একটি OpenGL ES প্রোগ্রামে যোগ করতে হবে, যা পরে আকৃতি আঁকতে ব্যবহৃত হয়। Triangle শ্রেণীতে একটি আকৃতি আঁকতে আপনি ব্যবহার করতে পারেন এমন মৌলিক শেডারগুলি কীভাবে সংজ্ঞায়িত করবেন তার একটি উদাহরণ এখানে রয়েছে:

কোটলিন

class Triangle {

    private val vertexShaderCode =
            "attribute vec4 vPosition;" +
            "void main() {" +
            "  gl_Position = vPosition;" +
            "}"

    private val fragmentShaderCode =
            "precision mediump float;" +
            "uniform vec4 vColor;" +
            "void main() {" +
            "  gl_FragColor = vColor;" +
            "}"

    ...
}

জাভা

public class Triangle {

    private final String vertexShaderCode =
        "attribute vec4 vPosition;" +
        "void main() {" +
        "  gl_Position = vPosition;" +
        "}";

    private final String fragmentShaderCode =
        "precision mediump float;" +
        "uniform vec4 vColor;" +
        "void main() {" +
        "  gl_FragColor = vColor;" +
        "}";

    ...
}

শেডার্সে OpenGL শেডিং ল্যাঙ্গুয়েজ (GLSL) কোড থাকে যা OpenGL ES পরিবেশে ব্যবহার করার আগে অবশ্যই কম্পাইল করা উচিত। এই কোড কম্পাইল করতে, আপনার রেন্ডারার ক্লাসে একটি ইউটিলিটি পদ্ধতি তৈরি করুন:

কোটলিন

fun loadShader(type: Int, shaderCode: String): Int {

    // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
    // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
    return GLES20.glCreateShader(type).also { shader ->

        // add the source code to the shader and compile it
        GLES20.glShaderSource(shader, shaderCode)
        GLES20.glCompileShader(shader)
    }
}

জাভা

public static int loadShader(int type, String shaderCode){

    // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
    // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
    int shader = GLES20.glCreateShader(type);

    // add the source code to the shader and compile it
    GLES20.glShaderSource(shader, shaderCode);
    GLES20.glCompileShader(shader);

    return shader;
}

আপনার আকৃতি আঁকতে, আপনাকে অবশ্যই শেডার কোড কম্পাইল করতে হবে, সেগুলিকে একটি OpenGL ES প্রোগ্রাম অবজেক্টে যুক্ত করতে হবে এবং তারপর প্রোগ্রামটিকে লিঙ্ক করতে হবে। আপনার আঁকা বস্তুর কনস্ট্রাক্টরে এটি করুন, তাই এটি শুধুমাত্র একবার করা হয়।

দ্রষ্টব্য: OpenGL ES শেডার কম্পাইল করা এবং প্রোগ্রাম লিঙ্ক করা CPU চক্র এবং প্রক্রিয়াকরণ সময়ের পরিপ্রেক্ষিতে ব্যয়বহুল, তাই আপনার এটি একাধিকবার করা এড়ানো উচিত। আপনি রানটাইমে আপনার শেডারগুলির বিষয়বস্তু না জানলে, আপনার কোডটি এমনভাবে তৈরি করা উচিত যাতে সেগুলি শুধুমাত্র একবার তৈরি হয় এবং তারপরে পরে ব্যবহারের জন্য ক্যাশে করা হয়।

কোটলিন

class Triangle {
    ...

    private var mProgram: Int

    init {
        ...

        val vertexShader: Int = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)
        val fragmentShader: Int = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode)

        // create empty OpenGL ES Program
        mProgram = GLES20.glCreateProgram().also {

            // add the vertex shader to program
            GLES20.glAttachShader(it, vertexShader)

            // add the fragment shader to program
            GLES20.glAttachShader(it, fragmentShader)

            // creates OpenGL ES program executables
            GLES20.glLinkProgram(it)
        }
    }
}

জাভা

public class Triangle() {
    ...

    private final int mProgram;

    public Triangle() {
        ...

        int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
                                        vertexShaderCode);
        int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
                                        fragmentShaderCode);

        // create empty OpenGL ES Program
        mProgram = GLES20.glCreateProgram();

        // add the vertex shader to program
        GLES20.glAttachShader(mProgram, vertexShader);

        // add the fragment shader to program
        GLES20.glAttachShader(mProgram, fragmentShader);

        // creates OpenGL ES program executables
        GLES20.glLinkProgram(mProgram);
    }
}

এই মুহুর্তে, আপনি প্রকৃত কলগুলি যোগ করতে প্রস্তুত যা আপনার আকার আঁকতে পারে। OpenGL ES এর সাথে আকৃতি আঁকার জন্য আপনি রেন্ডারিং পাইপলাইনকে কী আঁকতে চান এবং কীভাবে এটি আঁকবেন তা বলার জন্য আপনাকে বেশ কয়েকটি পরামিতি নির্দিষ্ট করতে হবে। যেহেতু অঙ্কনের বিকল্পগুলি আকৃতি অনুসারে পরিবর্তিত হতে পারে, তাই আপনার আকৃতির ক্লাসে তাদের নিজস্ব অঙ্কন যুক্তি থাকা একটি ভাল ধারণা।

আকৃতি আঁকার জন্য একটি draw() পদ্ধতি তৈরি করুন। এই কোডটি আকৃতির ভার্টেক্স শেডার এবং ফ্র্যাগমেন্ট শেডারে অবস্থান এবং রঙের মান সেট করে এবং তারপর অঙ্কন ফাংশনটি চালায়।

কোটলিন

private var positionHandle: Int = 0
private var mColorHandle: Int = 0

private val vertexCount: Int = triangleCoords.size / COORDS_PER_VERTEX
private val vertexStride: Int = COORDS_PER_VERTEX * 4 // 4 bytes per vertex

fun draw() {
    // Add program to OpenGL ES environment
    GLES20.glUseProgram(mProgram)

    // get handle to vertex shader's vPosition member
    positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition").also {

        // Enable a handle to the triangle vertices
        GLES20.glEnableVertexAttribArray(it)

        // Prepare the triangle coordinate data
        GLES20.glVertexAttribPointer(
                it,
                COORDS_PER_VERTEX,
                GLES20.GL_FLOAT,
                false,
                vertexStride,
                vertexBuffer
        )

        // get handle to fragment shader's vColor member
        mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor").also { colorHandle ->

            // Set color for drawing the triangle
            GLES20.glUniform4fv(colorHandle, 1, color, 0)
        }

        // Draw the triangle
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount)

        // Disable vertex array
        GLES20.glDisableVertexAttribArray(it)
    }
}

জাভা

private int positionHandle;
private int colorHandle;

private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

public void draw() {
    // Add program to OpenGL ES environment
    GLES20.glUseProgram(mProgram);

    // get handle to vertex shader's vPosition member
    positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

    // Enable a handle to the triangle vertices
    GLES20.glEnableVertexAttribArray(positionHandle);

    // Prepare the triangle coordinate data
    GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX,
                                 GLES20.GL_FLOAT, false,
                                 vertexStride, vertexBuffer);

    // get handle to fragment shader's vColor member
    colorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

    // Set color for drawing the triangle
    GLES20.glUniform4fv(colorHandle, 1, color, 0);

    // Draw the triangle
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

    // Disable vertex array
    GLES20.glDisableVertexAttribArray(positionHandle);
}

একবার আপনার কাছে এই সমস্ত কোড হয়ে গেলে, এই অবজেক্টটি আঁকার জন্য আপনার রেন্ডারারের onDrawFrame() পদ্ধতির মধ্যে থেকে draw() পদ্ধতিতে একটি কল প্রয়োজন:

কোটলিন

override fun onDrawFrame(unused: GL10) {
    ...

    mTriangle.draw()
}

জাভা

public void onDrawFrame(GL10 unused) {
    ...

    mTriangle.draw();
}

আপনি যখন অ্যাপ্লিকেশনটি চালান, তখন এটির মতো দেখতে হবে:

চিত্র 1. একটি অভিক্ষেপ বা ক্যামেরা ভিউ ছাড়া আঁকা ত্রিভুজ।

এই কোড উদাহরণের সাথে কয়েকটি সমস্যা আছে। প্রথমত, এটি আপনার বন্ধুদের প্রভাবিত করবে না। দ্বিতীয়ত, ত্রিভুজটি কিছুটা স্কোয়াশ হয় এবং আপনি যখন ডিভাইসের স্ক্রীন অভিযোজন পরিবর্তন করেন তখন আকৃতি পরিবর্তন করে। আকৃতিটি তির্যক হওয়ার কারণ হল যে বস্তুর শীর্ষবিন্দুগুলি স্ক্রীন এরিয়ার অনুপাতের জন্য সংশোধন করা হয়নি যেখানে GLSurfaceView প্রদর্শিত হয়৷ আপনি পরবর্তী পাঠে একটি প্রজেকশন এবং ক্যামেরা ভিউ ব্যবহার করে সেই সমস্যাটি সমাধান করতে পারেন।

সবশেষে, ত্রিভুজটি স্থির, যা কিছুটা বিরক্তিকর। অ্যাড মোশন পাঠে, আপনি এই আকৃতিটি ঘোরান এবং OpenGL ES গ্রাফিক্স পাইপলাইনের আরও আকর্ষণীয় ব্যবহার করুন।