国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

OpenGL ES入門教程(二)之繪制一個平面桌子

這篇具有很好參考價(jià)值的文章主要介紹了OpenGL ES入門教程(二)之繪制一個平面桌子。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

前言

上一篇文章OpenGL ES入門教程(一)編寫第一個OpenGL程序,我們創(chuàng)建了自己的第一個OpenGL程序,實(shí)現(xiàn)了繪制紅色背景的Activity頁面,算是OpenGL ES的hello world程序吧。本篇文章基于上一篇文章基礎(chǔ)上講解如何使用OpenGL繪制一張平面桌子,桌子由一個長方形構(gòu)成,且長方形中間繪制一條線,長方形兩頭繪制兩個點(diǎn)。文章中提到的示例代碼我都共享到gitee上了,各位博友可以從我的gitee倉庫OpenGL_ES_DEMO下載完整的項(xiàng)目代碼,代碼都有很詳細(xì)的提交記錄。

0. OpenGL繪制圖形的整體框架概述

  1. 要繪制圖形,就要有圖形的位置坐標(biāo)數(shù)據(jù),OpenGL中稱這些位置為頂點(diǎn),因此,首先需要定義頂點(diǎn)數(shù)據(jù)
  2. OpenGL如何操作頂點(diǎn)數(shù)據(jù)呢?答案是通過著色器操作圖形數(shù)據(jù),因此,第二步我們要定義著色器
  3. 定義完著色器需要加載著色器以供OpenGL使用
  4. OpenGL編譯著色器
  5. 將著色器鏈接為一個OpenGL程序?qū)ο?/li>
  6. 通過OpenGL程序?qū)ο髮⒅髋c圖形數(shù)據(jù)相關(guān)
  7. 以上操作算是使用OpenGL的初始化操作,步驟比較固定,編寫完成后,就可以開心的繪制圖形啦

繪制圖形都是在OpenGL渲染器類中完成,所以,本篇文章的所有代碼都是在上一篇文章中定義的AirHockeyRenderer類中編寫。

1. 定義頂點(diǎn)

OpenGL包括三類基礎(chǔ)圖形,點(diǎn),直線,三角形。其余的任何圖形,都是由這三種基本圖形組成。

因此如果我們想繪制一個前言中所述的平面桌子,可以由兩個三角形組成一個長方形,并在長方形的中間繪制一條直線,兩端繪制兩個點(diǎn),如下圖所示:
opengl繪制三維桌子,OpenGL ES,OpenGL,OpenGL ES,繪制圖形,著色器,片段著色器,頂點(diǎn)著色器,java
無論是x還是y坐標(biāo),OpenGL都會把屏幕映射到[-1,1]的范圍內(nèi)。 即屏幕的左邊對應(yīng)x軸的-1,右邊對應(yīng)+1;屏幕的底邊對應(yīng)y軸的-1,頂邊對應(yīng)+1;因此如果將上面的圖形繪制到屏幕中間,需要的頂點(diǎn)坐標(biāo)如下圖所示:
opengl繪制三維桌子,OpenGL ES,OpenGL,OpenGL ES,繪制圖形,著色器,片段著色器,頂點(diǎn)著色器,java
如上圖所示坐標(biāo)數(shù)據(jù),我們將每個頂點(diǎn)(由x坐標(biāo)和y坐標(biāo)組成)的數(shù)據(jù)存儲到數(shù)組中,上圖主要包含的OpenGL基本圖形是兩個三角形,一條線,兩個點(diǎn),其中三角形的頂點(diǎn)我們統(tǒng)一按照逆時針方向進(jìn)行存儲,這一步數(shù)據(jù)準(zhǔn)備,我可以將它放在渲染器類的構(gòu)造函數(shù)中,最終定義的頂點(diǎn)數(shù)據(jù)如下:

private Context context;//后面我們加載著色器需要用到該上下文,因此也通過構(gòu)造函數(shù)傳進(jìn)去
public AirHockeyRenderer(Context context)
    {
        this.context = context;

        float[] tableVerticesWithTriangles = {
                /**
                無論是x還是y坐標(biāo),OpenGL都會把屏幕映射到[-1,1]的范圍內(nèi)。
                即屏幕的左邊對應(yīng)x軸的-1,右邊對應(yīng)+1;
                屏幕的底邊對應(yīng)y軸的-1,頂邊對應(yīng)+1
                */
                // Triangle 1
                -0.5f, -0.5f,
                0.5f,  0.5f,
                -0.5f,  0.5f,

                // Triangle 2
                -0.5f, -0.5f,
                0.5f, -0.5f,
                0.5f,  0.5f,

                // Line 1
                -0.5f, 0f,
                0.5f, 0f,

                // Mallets
                0f, -0.25f,
                0f,  0.25f
        };
    }

OpenGL作為本地系統(tǒng)庫直接運(yùn)行在硬件上,無法直接讀取java程序中定義的數(shù)據(jù),因此我們需要把上面定義的頂點(diǎn)數(shù)據(jù)復(fù)制到本地內(nèi)存中。具體實(shí)現(xiàn)代碼如下:

private Context context;
private static final int BYTES_PER_FLOAT = 4;
private final FloatBuffer vertexData;
public AirHockeyRenderer(Context context)
    {
        this.context = context;

        float[] tableVerticesWithTriangles = {
                /**
                無論是x還是y坐標(biāo),OpenGL都會把屏幕映射到[-1,1]的范圍內(nèi)。
                即屏幕的左邊對應(yīng)x軸的-1,右邊對應(yīng)+1;
                屏幕的底邊對應(yīng)y軸的-1,頂邊對應(yīng)+1
                */
                // Triangle 1
                -0.5f, -0.5f,
                0.5f,  0.5f,
                -0.5f,  0.5f,

                // Triangle 2
                -0.5f, -0.5f,
                0.5f, -0.5f,
                0.5f,  0.5f,

                // Line 1
                -0.5f, 0f,
                0.5f, 0f,

                // Mallets
                0f, -0.25f,
                0f,  0.25f
        };

       vertexData = ByteBuffer
  				//申請本地內(nèi)存空間大小,單位為字節(jié)。tableVerticesWithTriangles中存儲的是float類型數(shù)據(jù),由32bit組成,即4個字節(jié)組成;
                .allocateDirect(tableVerticesWithTriangles.length * BYTES_PER_FLOAT)
                //本地內(nèi)存空間的一種排序方式
                .order(ByteOrder.nativeOrder())
                //轉(zhuǎn)換為我們需要的FloatBuffer類型
                .asFloatBuffer();
        //將tableVerticesWithTriangles中的數(shù)據(jù)拷貝到本地內(nèi)存中
        vertexData.put(tableVerticesWithTriangles);
    }

2. 定義著色器

OpenGL繪制圖形的流程稱為OpenGL管道(pipeline)。上面我們定義了頂點(diǎn)數(shù)據(jù),并將其拷貝到了本地內(nèi)存,下面就是將本地內(nèi)存中的圖形頂點(diǎn)數(shù)據(jù)在OpenGL管道進(jìn)行流動,并通過著色器告訴GPU如何繪制數(shù)據(jù)。

著色器是一種只能運(yùn)行在GPU上的特殊類型程序。OpenGL著色器分為頂點(diǎn)著色器和片段著色器兩種類型:

  • 頂點(diǎn)著色器(vertex shader)用于處理頂點(diǎn)(主要作用是確定位置);
  • 片段著色器用于處理,由點(diǎn),線,三角形組成的片段(主要作用是告訴GPU每個片段的最終顏色)

著色器處理完成后(顏色生成),openGL將它們寫入一塊稱為幀緩沖區(qū)(frame buffer)的內(nèi)存塊,然后,Android把這分幀緩沖區(qū)中的數(shù)據(jù)顯示在屏幕上。

OpenGL管道(pipeline)流程如下:

  • 讀取頂點(diǎn)數(shù)據(jù) -》執(zhí)行頂點(diǎn)著色器-》組裝圖元-》光柵化圖元-》執(zhí)行片段著色器-》寫入幀緩沖器-》顯示在屏幕上

其中,光柵化技術(shù)是指:移動設(shè)備的顯示屏通過大量像素的堆積(紅綠藍(lán)三種顏色不同比例的混合,就足以創(chuàng)造出人眼可見范圍內(nèi)的顏色),在視覺上創(chuàng)造出巨量顏色范圍的技術(shù)。而OpenGL光柵化就是把每個點(diǎn),直線,三角形,分解成大量的小片段,通常情況下,一個片段直接映射到屏幕的一個像素。

OpenGL著色器的定義采用著色器特定的語言(語法結(jié)構(gòu)類似C語言),著色器文件后綴名為glsl(OpenGL shader language)。我們在工程的res目錄下新建一個raw文件夾,并在raw文件夾中創(chuàng)建頂點(diǎn)著色器文件simple_vertex_shader.glsl和片段著色器文件simple_fragment_shader.glsl,如下圖所示:

opengl繪制三維桌子,OpenGL ES,OpenGL,OpenGL ES,繪制圖形,著色器,片段著色器,頂點(diǎn)著色器,java
頂點(diǎn)著色器文件內(nèi)容如下:

/*
attribute:定義頂點(diǎn)類型位置數(shù)據(jù)的特定標(biāo)識
vec4:一種包含4個分量的向量數(shù)據(jù)類型(x,y,z,w)
     其中x,y,z代表頂點(diǎn)的三維位置坐標(biāo),w是一個特殊坐標(biāo),后面會講解
a_Position:變量名稱,該名稱后面OpenGL的glGetAttribLocation方法要用到,
           如果修改后面就要一起修改
*/
attribute vec4 a_Position;     		

//和C語言類似,main函數(shù)是著色器的入口函數(shù)
void main()                    
{   //gl_Position :OpenGL特定的變量名,用于存儲我們定義的頂點(diǎn)數(shù)據(jù)                           
    gl_Position = a_Position;
    //gl_PointSize:OpenGL特定的變量名,用于存儲點(diǎn)的大小
    gl_PointSize = 10.0;
}  

片段著色器內(nèi)容如下:

//OpenGL定義float數(shù)據(jù)類型的精度(lowp;mediump;highp),就像java代碼中浮點(diǎn)型選擇float類型還是double類型。
//精度是以性能為代價(jià)的,這里選擇mediump
precision mediump float; 

/*
uniform:定義片段顏色的一種特殊標(biāo)識
vec4:一種包含4個分量的向量數(shù)據(jù)類型(r,g,b,a),分別代碼紅,綠,藍(lán),透明度。
     其中rgba的取值范圍是0-1,rgba色彩不了解的可以去其它文章了解一下。
u_Color:變量名稱,該名稱后面OpenGL的glGetUniformLocation方法要用到,
                 如果修改后面就要一起修改
*/      	 								
uniform vec4 u_Color;
          	   								
//和C語言類似,main函數(shù)是著色器的入口函數(shù)
void main()                    		
{                              	
    gl_FragColor = u_Color;
}

3. 加載著色器

加載著色器其實(shí)非常簡單就是通過Java IO流的方式將著色器文件中的內(nèi)容讀取為一個字符,以供OpenGL后面編譯著色器使用。我以前寫過一篇詳細(xì)的Java IO流文章,如果有興趣的博友可以移步去看看Java IO流最全詳解

為了復(fù)用代碼,我們定義一個TextResourceReader類,并在類中實(shí)現(xiàn)一個靜態(tài)方法readTextFileFromResource,專用于加載著色器,具體實(shí)現(xiàn)代碼如下:

public class TextResourceReader {
    public static String readTextFileFromResource(Context context, int resourceId) {
        StringBuilder body = new StringBuilder();
        try {
            InputStream inputStream = context.getResources().openRawResource(resourceId);
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String  newLine;
            while ((newLine = bufferedReader.readLine()) != null)
            {
                body.append(newLine);
                body.append("\n");
            }
        }
        catch (IOException e)
        {
            throw new RuntimeException("Could not open resource: " + resourceId, e);
        }
        catch (Resources.NotFoundException e)
        {
            throw new RuntimeException("Resource not found: " + resourceId, e);
        }
        return body.toString();
    }
}

因?yàn)榧虞d著色器算是OpenGL繪圖的初始化操作,我們可以在surface創(chuàng)建的時候調(diào)用一次該邏輯,即在渲染器類重寫的onSurfaceCreated方法中調(diào)用加載著色器的邏輯,具體代碼如下所示:

	@Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
        //加載著色器
        String vertexShaderSource = TextResourceReader.readTextFileFromResource(context, R.raw.simple_vertex_shader);
        String fragmentShaderSource = TextResourceReader.readTextFileFromResource(context, R.raw.simple_fragment_shader);
    }

4. 編譯著色器

編譯著色器和將著色器鏈接到OpenGL程序?qū)ο蠖际潜容^固定的渲染步驟,為了重復(fù)利用它們,我們定義一個ShaderHelper類,專門用于編譯著色器,將著色器鏈接為OpenGL程序?qū)ο蠛万?yàn)證OpenGL程序?qū)ο蟮挠行浴?/p>

編譯著色的實(shí)現(xiàn)流程比較固定,我們只需要會用就行了,不必死記硬背,具體實(shí)現(xiàn)代碼如下:

     /**
     * Compiles a shader, returning the OpenGL object ID.
     * 1. glCreateShader 創(chuàng)建著色器對象 0代表失敗,檢查創(chuàng)建狀態(tài)
     * 2. glShaderSource 向著色器對象中上傳著色器源碼
     * 3. glCompileShader 著色器對象編譯源碼
     * 4. glGetShaderiv 獲取編譯狀態(tài),若編譯失敗,則刪除著色器對象id,否則返回著色器對象id
     * 5. glGetShaderInfoLog 獲取編譯結(jié)果的詳細(xì)信息
     * 6. 如果編譯失敗,glDeleteShader刪除渲染器對象id
     * 7. 編譯成功,返回渲染器對象id
     * @param type 著色器類型:頂點(diǎn)著色器 GL_VERTEX_SHADER,片段著色器 GL_FRAGMENT_SHADER
     * @param shaderCode 加載的著色器代碼
     * @return
     */
    private static int compileShader(int type, String shaderCode) {
        // Create a new shader object.
        final int shaderObjectId = glCreateShader(type);

        if (shaderObjectId == 0) {
            if (LoggerConfig.ON) {
                Log.w(TAG, "Could not create new shader.");
            }

            return 0;
        }

        // Pass in the shader source.
        glShaderSource(shaderObjectId, shaderCode);

        // Compile the shader.
        glCompileShader(shaderObjectId);

        // Get the compilation status.
        final int[] compileStatus = new int[1];
        glGetShaderiv(shaderObjectId, GL_COMPILE_STATUS, compileStatus, 0);

        if (LoggerConfig.ON) {
            // Print the shader info log to the Android log output.
            Log.v(TAG, "Results of compiling source:" + "\n" + shaderCode + "\n:"
                    + glGetShaderInfoLog(shaderObjectId));
        }

        // Verify the compile status.
        if (compileStatus[0] == 0) {
            // If it failed, delete the shader object.
            glDeleteShader(shaderObjectId);

            if (LoggerConfig.ON) {
                Log.w(TAG, "Compilation of shader failed.");
            }

            return 0;
        }

        // Return the shader object ID.
        return shaderObjectId;
    }
 
    /**
     * Loads and compiles a vertex shader, returning the OpenGL object ID.
     */
    public static int compileVertexShader(String shaderCode) {
        return compileShader(GL_VERTEX_SHADER, shaderCode);
    }

    /**
     * Loads and compiles a fragment shader, returning the OpenGL object ID.
     */
    public static int compileFragmentShader(String shaderCode) {
        return compileShader(GL_FRAGMENT_SHADER, shaderCode);
    }

因?yàn)榫幾g著色器算是OpenGL繪圖的初始化操作,我們可以在surface創(chuàng)建的時候調(diào)用一次該邏輯,即在渲染器類重寫的onSurfaceCreated方法中調(diào)用編譯著色器的邏輯,具體代碼如下所示:

	@Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
        //加載著色器
        String vertexShaderSource = TextResourceReader.readTextFileFromResource(context, R.raw.simple_vertex_shader);
        String fragmentShaderSource = TextResourceReader.readTextFileFromResource(context, R.raw.simple_fragment_shader);
        //編譯著色器
        int vertexShader = ShaderHelper.compileVertexShader(vertexShaderSource);
        int fragmentShader = ShaderHelper.compileFragmentShader(fragmentShaderSource);
    }

5. 將著色器鏈接為OpenGL程序?qū)ο?/h3>

將著色器鏈接為OpenGL程序?qū)ο蟮膶?shí)現(xiàn)流程也比較固定,我們只需要會用就行了,不必死記硬背,具體實(shí)現(xiàn)代碼如下:

 	/**
     *
     * Links a vertex shader and a fragment shader together into an OpenGL
     * program. Returns the OpenGL program object ID, or 0 if linking failed.
     *
     * 1. glCreateProgram 創(chuàng)建OpenGL鏈接程序?qū)ο?,獲取對象id, 對象id為0代表創(chuàng)建失敗
     * 2. glAttachShader 附上著色器
     * 3. glLinkProgram 鏈接程序,把著色器聯(lián)合起來
     * 4. glGetProgramiv 獲取鏈接狀態(tài),若成功則返回鏈接對象id,否則glDeleteProgram刪除鏈接對象
     *
     * @param vertexShaderId 頂點(diǎn)著色器對象id
     * @param fragmentShaderId 片段著色器對象id
     * @return
     */
    public static int linkProgram(int vertexShaderId, int fragmentShaderId) {
        // Create a new program object.
        final int programObjectId = glCreateProgram();

        if (programObjectId == 0) {
            if (LoggerConfig.ON) {
                Log.w(TAG, "Could not create new program");
            }

            return 0;
        }

        // Attach the vertex shader to the program.
        glAttachShader(programObjectId, vertexShaderId);
        // Attach the fragment shader to the program.
        glAttachShader(programObjectId, fragmentShaderId);

        // Link the two shaders together into a program.
        glLinkProgram(programObjectId);

        // Get the link status.
        final int[] linkStatus = new int[1];
        glGetProgramiv(programObjectId, GL_LINK_STATUS, linkStatus, 0);

        if (LoggerConfig.ON) {
            // Print the program info log to the Android log output.
            Log.v(TAG, "Results of linking program:\n"
                    + glGetProgramInfoLog(programObjectId));
        }

        // Verify the link status.
        if (linkStatus[0] == 0) {
            // If it failed, delete the program object.
            glDeleteProgram(programObjectId);
            if (LoggerConfig.ON) {
                Log.w(TAG, "Linking of program failed.");
            }
            return 0;
        }

        // Return the program object ID.
        return programObjectId;
    }

     /**
     * Validates an OpenGL program. Should only be called when developing the application.
     * 1. glValidateProgram驗(yàn)證鏈接到OpenGL程序?qū)ο蟮挠行?     * 2. glGetProgramiv獲取OpenGL程序?qū)ο笥行缘臓顟B(tài),如果返回0代表無效,否則代表鏈接OpenGL程序?qū)ο蟪晒?     * @param programObjectId
     * @return
     */
    public static boolean validateProgram(int programObjectId) {
        glValidateProgram(programObjectId);

        final int[] validateStatus = new int[1];
        glGetProgramiv(programObjectId, GL_VALIDATE_STATUS, validateStatus, 0);
        Log.v(TAG, "Results of validating program: " + validateStatus[0]
                + "\nLog:" + glGetProgramInfoLog(programObjectId));

        return validateStatus[0] != 0;
    }

因?yàn)閷⒅麈溄訛镺penGL程序?qū)ο笏闶荗penGL繪圖的初始化操作,我們可以在surface創(chuàng)建的時候調(diào)用一次該邏輯,即在渲染器類重寫的onSurfaceCreated方法中實(shí)現(xiàn)鏈接OpenGL程序?qū)ο蟮倪壿?,具體代碼如下所示:

    private int program;
	@Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
        //加載著色器
        String vertexShaderSource = TextResourceReader.readTextFileFromResource(context, R.raw.simple_vertex_shader);
        String fragmentShaderSource = TextResourceReader.readTextFileFromResource(context, R.raw.simple_fragment_shader);
        //編譯著色器
        int vertexShader = ShaderHelper.compileVertexShader(vertexShaderSource);
        int fragmentShader = ShaderHelper.compileFragmentShader(fragmentShaderSource);
        //將著色器鏈接到OpenGL程序
        program = ShaderHelper.linkProgram(vertexShader, fragmentShader);
        //打印OpenGL程序?qū)ο蟮挠行孕畔?/span>
        if (LoggerConfig.ON) {
            ShaderHelper.validateProgram(program);
        }
    }

6. 將著色器需要的數(shù)據(jù)與拷貝到本地的數(shù)組相關(guān)聯(lián)

至此,我們已經(jīng)完成了所要繪制圖形的頂點(diǎn)數(shù)據(jù)定義,著色器定義、加載、編譯,以及OpenGL程序?qū)ο螳@取。下面我們需要通過OpenGL程序?qū)ο螳@取著色器中定義的屬性,并將著色器需要的數(shù)據(jù)與我們拷貝到本地的數(shù)據(jù)相關(guān)聯(lián),繼而完成OpenGL繪圖的所有前置操作。因?yàn)檫@一步仍然屬于OpenGL繪圖的初始化或者前置工作,我們可以在surface創(chuàng)建的時候調(diào)用一次該邏輯,即在渲染器類重寫的onSurfaceCreated方法中調(diào)用實(shí)現(xiàn)該邏輯,具體實(shí)現(xiàn)代碼如下:

    //這個字符串一定要和片段著色器中定義的屬性名一致
    private static final String U_COLOR = "u_Color";
    private int uColorLocation;
    //這個字符串一定要和頂點(diǎn)著色器中定義的屬性名一致
    private static final String A_POSITION = "a_Position";
    private int aPositionLocation;
    //每個頂點(diǎn)由兩個浮點(diǎn)數(shù)組成:x,y
    private static final int POSITION_COMPONENT_COUNT = 2;
	@Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
        
        //加載著色器
        String vertexShaderSource = TextResourceReader.readTextFileFromResource(context, R.raw.simple_vertex_shader);
        String fragmentShaderSource = TextResourceReader.readTextFileFromResource(context, R.raw.simple_fragment_shader);
        
        //編譯著色器
        int vertexShader = ShaderHelper.compileVertexShader(vertexShaderSource);
        int fragmentShader = ShaderHelper.compileFragmentShader(fragmentShaderSource);
        
        //將著色器鏈接到OpenGL程序
        program = ShaderHelper.linkProgram(vertexShader, fragmentShader);
        //打印OpenGL程序?qū)ο蟮挠行孕畔?/span>
        if (LoggerConfig.ON) {
            ShaderHelper.validateProgram(program);
        }
        
        /*
        將著色器需要的數(shù)據(jù)與拷貝到本地的數(shù)組相關(guān)聯(lián)
        */
        //告訴OpenGL在繪制任何東西到屏幕上時候,使用這里定義程序
        glUseProgram(program);
        //獲取片段著色器中uniform的顏色屬性
        uColorLocation = glGetUniformLocation(program, U_COLOR);
        //獲取頂點(diǎn)著色器中(attribute)位置屬性
        aPositionLocation = glGetAttribLocation(program, A_POSITION);
        //告訴OpenGL從vertexData中讀取a_Position的數(shù)據(jù)
        vertexData.position(0);//將緩沖區(qū)數(shù)據(jù)中的指針指向第一個數(shù)據(jù),即從第一個數(shù)據(jù)開始讀
        /*
        將著色器中的位置屬性與本地頂點(diǎn)數(shù)據(jù)相關(guān)聯(lián)。
        aPositionLocation:著色器中定義的位置屬性
        POSITION_COMPONENT_COUNT:每次從本地?cái)?shù)組中讀取兩個數(shù)據(jù)(即x,y代表一個頂點(diǎn)坐標(biāo))
        GL_FLOAT:OpenGL采用的數(shù)據(jù)類型,因?yàn)槲覀兌x的是浮點(diǎn)數(shù)數(shù)組,所以采用GL_FLOAT
        vertexData:要關(guān)聯(lián)的本地?cái)?shù)據(jù)列表
        */
        glVertexAttribPointer(aPositionLocation, POSITION_COMPONENT_COUNT, GL_FLOAT,
                false, 0, vertexData);
        //使能頂點(diǎn)數(shù)組
        glEnableVertexAttribArray(aPositionLocation);
    }

glVertexAttribPointer方法所需參數(shù)的詳細(xì)說明如下圖所示:
opengl繪制三維桌子,OpenGL ES,OpenGL,OpenGL ES,繪制圖形,著色器,片段著色器,頂點(diǎn)著色器,java

7. 在屏幕上繪制圖形

至此,我們已經(jīng)完成了使用OpenGL繪制圖形的所有必要的前置步驟,下面就是繪制圖形了,繪制圖形的邏輯在渲染類中重寫的onDrawFrame方法中實(shí)現(xiàn)。

OpenGL繪制基本圖形的步驟如下

  1. 通過glUniform4f方法,更新片段著色器的顏色,該顏色將應(yīng)用于下面它后面繪制的所有圖形,直至再次更新顏色。
    glUniform4f方法的定義如下:
   public static native void glUniform4f(
        int location,//片段著色器的顏色屬性
        float x, //rgb色彩的r分量
        float y, //rgb色彩的g分量
        float z, //rgb色彩的b分量
        float w  //rgb色彩的透明度分量
    );
  1. 通過glDrawArrays方法,繪制基本圖形
    glDrawArrays方法的定義如下:
   public static native void glDrawArrays(
        int mode, //要繪制的基本圖元類型:三角形 GL_TRIANGLES;直線 GL_LINES;點(diǎn) GL_POINTS
        int first,//從本地頂點(diǎn)數(shù)組中讀取數(shù)據(jù)的開始位置
        int count //一共讀取多少個頂點(diǎn),上面glVertexAttribPointer方法中我定義了一個頂點(diǎn)由2個數(shù)據(jù)組成
    );

因此,如果我們想繪制一個白色的三角形,實(shí)現(xiàn)代碼如下:

//更新著色器中u_Color的值(白色)
glUniform4f(uColorLocation, 1.0f, 1.0f, 1.0f, 1.0f);
//因?yàn)槿切斡扇齻€頂點(diǎn)組成,因此我們從本地頂點(diǎn)數(shù)據(jù)列表中的第一個頂點(diǎn)開始,連續(xù)讀取3個頂點(diǎn)
glDrawArrays(GL_TRIANGLES, 0, 3);

如果我們想要實(shí)現(xiàn)繪制上面提到的如下圖形,具體實(shí)現(xiàn)代碼如下:
opengl繪制三維桌子,OpenGL ES,OpenGL,OpenGL ES,繪制圖形,著色器,片段著色器,頂點(diǎn)著色器,java

	@Override
    public void onDrawFrame(GL10 gl) {
        glClear(GL_COLOR_BUFFER_BIT);
        /*
        繪制桌子
         */
        //更新著色器中u_Color的值(白色)
        glUniform4f(uColorLocation, 1.0f, 1.0f, 1.0f, 1.0f);
        //參數(shù)1:繪制三角形;參數(shù)2:從頂點(diǎn)數(shù)組的開頭開始讀頂點(diǎn);參數(shù)3:讀取6個頂點(diǎn)(即繪制兩個三角形)
        //之前glVertexAttribPointer告訴過OpenGL每個頂點(diǎn)的位置包含兩個浮點(diǎn)分量,因此OpenGL會使用vertexData中如下12個浮點(diǎn)數(shù)繪制兩個三角形
        /**
         // Triangle 1
         -0.5f, -0.5f,
         0.5f,  0.5f,
         -0.5f,  0.5f,

         // Triangle 2
         -0.5f, -0.5f,
         0.5f, -0.5f,
         0.5f,  0.5f,
         */
        glDrawArrays(GL_TRIANGLES, 0, 6);

        /*
        繪制分割線
         */
        //更新u_Color的值(紅色)
        glUniform4f(uColorLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        //參數(shù)1:繪制直線; 參數(shù)2:從頂點(diǎn)數(shù)組的第6個頂點(diǎn)之后(即第7個頂點(diǎn))開始讀??;參數(shù)3:讀取兩個頂點(diǎn)
        glDrawArrays(GL_LINES, 6, 2);

        /*
        繪制兩個木槌
         */
        //更新u_Color的值(藍(lán)色)
        glUniform4f(uColorLocation, 0.0f, 0.0f, 1.0f, 1.0f);
        glDrawArrays(GL_POINTS, 8, 1);
        //更新u_Color的值(綠色)
        glUniform4f(uColorLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        glDrawArrays(GL_POINTS, 9, 1);
    }

至此,我們已經(jīng)實(shí)現(xiàn)了使用OpenGL繪制一個平面桌子的全部代碼,可以從我的gitee倉庫OpenGL_ES_DEMO下載完整的項(xiàng)目代碼,并執(zhí)行g(shù)it reset --hard f1a8e96f0e126814be1a4275459bd7be37c183c0切到文章目前所實(shí)現(xiàn)代碼的節(jié)點(diǎn),運(yùn)行程序效果如下:
opengl繪制三維桌子,OpenGL ES,OpenGL,OpenGL ES,繪制圖形,著色器,片段著色器,頂點(diǎn)著色器,java

8. 讓桌子有邊框的效果

為了更好的驗(yàn)證我們是否掌握了使用OpenGL靈活繪制圖形的能力,可以在以上繪制圖形的基礎(chǔ)上,再在長方形內(nèi)添加一個小的長方形,形成桌子邊框的效果,各位博友如果能夠獨(dú)立實(shí)現(xiàn)這一功能,那么應(yīng)該對OpenGL基本圖形的繪制有了比較熟練的掌握。具體實(shí)現(xiàn)思路和代碼如下:

  1. 既然要再加一個小長方形,第一步就需要定義它的頂點(diǎn)坐標(biāo),添加小長方形后的頂點(diǎn)坐標(biāo)數(shù)組如下:
float[] tableVerticesWithTriangles = {
                /**
                無論是x還是y坐標(biāo),OpenGL都會把屏幕映射到[-1,1]的范圍內(nèi)。
                即屏幕的左邊對應(yīng)x軸的-1,右邊對應(yīng)+1;
                屏幕的底邊對應(yīng)y軸的-1,頂邊對應(yīng)+1
                */
                // Triangle 1
                -0.5f, -0.5f,
                0.5f,  0.5f,
                -0.5f,  0.5f,

                // Triangle 2
                -0.5f, -0.5f,
                0.5f, -0.5f,
                0.5f,  0.5f,

                // Triangle 3
                -0.4f, -0.4f,
                0.4f,  0.4f,
                -0.4f,  0.4f,

                // Triangle 4
                -0.4f, -0.4f,
                0.4f, -0.4f,
                0.4f,  0.4f,

                // Line 1
                -0.5f, 0f,
                0.5f, 0f,

                // Mallets
                0f, -0.25f,
                0f,  0.25f
        };
  1. 頂點(diǎn)坐標(biāo)數(shù)據(jù)有了后,就可以開始使用OpenGL繪制圖形了,添加一個小長方形后的繪制代碼如下:
@Override
    public void onDrawFrame(GL10 gl) {
        glClear(GL_COLOR_BUFFER_BIT);
        /*
        繪制桌子
         */
        //更新著色器中u_Color的值(藍(lán)色)
        glUniform4f(uColorLocation, 0.0f, 0.0f, 1.0f, 1.0f);
        //參數(shù)1:繪制三角形;參數(shù)2:從頂點(diǎn)數(shù)組的開頭開始讀頂點(diǎn);參數(shù)3:讀取6個頂點(diǎn)(即繪制兩個三角形)
        //之前glVertexAttribPointer告訴過OpenGL每個頂點(diǎn)的位置包含兩個浮點(diǎn)分量,因此OpenGL會使用vertexData中如下12個浮點(diǎn)數(shù)繪制兩個三角形
        /**
         // Triangle 1
         -0.5f, -0.5f,
         0.5f,  0.5f,
         -0.5f,  0.5f,

         // Triangle 2
         -0.5f, -0.5f,
         0.5f, -0.5f,
         0.5f,  0.5f,
         */
        glDrawArrays(GL_TRIANGLES, 0, 6);

        /**
         * 繪制第二個內(nèi)長方形,形成邊框的效果
         */
        //更新著色器中u_Color的值(白色)
        glUniform4f(uColorLocation, 1.0f, 1.0f, 1.0f, 1.0f);
        glDrawArrays(GL_TRIANGLES, 6, 6);

        /*
        繪制分割線
         */
        //更新u_Color的值(紅色)
        glUniform4f(uColorLocation, 1.0f, 0.0f, 0.0f, 1.0f);
        //參數(shù)1:繪制直線; 參數(shù)2:從頂點(diǎn)數(shù)組的第6個頂點(diǎn)之后(即第7個頂點(diǎn))開始讀?。粎?shù)3:讀取兩個頂點(diǎn)
        glDrawArrays(GL_LINES, 12, 2);

        /*
        繪制兩個木槌
         */
        //更新u_Color的值(黑色)
        glUniform4f(uColorLocation, 0.0f, 0.0f, 0.0f, 1.0f);
        glDrawArrays(GL_POINTS, 14, 1);
        //更新u_Color的值(綠色)
        glUniform4f(uColorLocation, 0.0f, 1.0f, 0.0f, 1.0f);
        glDrawArrays(GL_POINTS, 15, 1);
    }

下載完整的項(xiàng)目代碼后,執(zhí)行g(shù)it reset --hard bd8d6607592abcddd1547a5e36a01688772eda64切到實(shí)現(xiàn)帶邊框效果的桌面節(jié)點(diǎn),運(yùn)行程序效果如下:
opengl繪制三維桌子,OpenGL ES,OpenGL,OpenGL ES,繪制圖形,著色器,片段著色器,頂點(diǎn)著色器,java文章來源地址http://www.zghlxwxcb.cn/news/detail-796436.html

到了這里,關(guān)于OpenGL ES入門教程(二)之繪制一個平面桌子的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • OpenGL ES 2.0 for Android教程(三):編譯著色器并繪制到屏幕

    OpenGL ES 2.0 for Android教程(三):編譯著色器并繪制到屏幕

    文章傳送門 OpenGL ES 2.0 for Android教程(一) OpenGL ES 2.0 for Android教程(二) OpenGL ES 2.0 for Android教程(四) OpenGL ES 2.0 for Android教程(五) OpenGL ES 2.0 for Android教程(六) OpenGL ES 2.0 for Android教程(七) OpenGL ES 2.0 for Android教程(八) OpenGL ES 2.0 for Android教程(九) 本章將繼續(xù)我

    2023年04月26日
    瀏覽(23)
  • OpenCV 入門教程:尋找和繪制輪廓

    尋找和繪制輪廓是圖像處理中常用的技術(shù)之一,用于識別、定位和分析圖像中的目標(biāo)區(qū)域。在 OpenCV 中,尋找和繪制輪廓可以通過邊緣檢測和形態(tài)學(xué)操作實(shí)現(xiàn)。本文將以尋找和繪制輪廓為中心,為你介紹使用 OpenCV 進(jìn)行輪廓處理的基本步驟和實(shí)例。 ??#x

    2024年02月14日
    瀏覽(25)
  • ES教程:從入門到入土

    ES教程:從入門到入土

    建議直接用docker。 docker啟動參數(shù)說明: -d 后臺啟動 -p 9200:9200 將虛擬機(jī)9200端口映射到elasticsearch的9200端口(web通信默認(rèn)使用9200端口) -p 9300:9300 將虛擬機(jī)9300端口映射到elasticsearch的9300端口(分布式情況下,各個節(jié)點(diǎn)之間通信默認(rèn)使用9300端口) –name MyEs7 指定一個名字(MyEs

    2024年02月07日
    瀏覽(17)
  • 【OpenGL ES】三維圖形繪制

    【OpenGL ES】三維圖形繪制

    不積跬步,無以至千里;不積小流,無以成江海。要沉下心來,詩和遠(yuǎn)方的路費(fèi)真的很貴! 顏色的簡單搭配: 不紅+不綠+不藍(lán) = 黑 紅+綠+藍(lán) = 白 紅+綠 = 黃 紅+藍(lán) = 紫 綠+藍(lán) = 青藍(lán) 投影主要分為 正交投影 和 透視投影 兩種。 正交投影 沒有近大遠(yuǎn)小的效果,是平行投影,投影

    2023年04月08日
    瀏覽(24)
  • 數(shù)據(jù)可視化神器!Matplotlib Python教程 | 從入門到精通繪制各種類型的圖形和保存圖形

    數(shù)據(jù)可視化神器!Matplotlib Python教程 | 從入門到精通繪制各種類型的圖形和保存圖形

    大家好,我是愛吃熊掌的魚,今天我要給大家?guī)硪黄腥ら_朗的Matplotlib Python教程。Matplotlib是Python中最流行的數(shù)據(jù)可視化庫之一,它可以幫助我們將數(shù)據(jù)轉(zhuǎn)化為易于理解的圖表和圖形。無論你是初學(xué)者還是專業(yè)人士,Matplotlib都是一個非常有用的工具。讓我們開始吧! 在開

    2023年04月21日
    瀏覽(24)
  • OpenGL ES 繪制一張圖片

    OpenGL ES 繪制一張圖片

    GLSL 的修飾符與數(shù)據(jù)類型 GLSL 中變量的修飾符 const:修飾不可被外界改變的常量 attribute:修飾經(jīng)常更改的變量,只可以在頂點(diǎn)著色器中使用 uniform:修飾不經(jīng)常更改的變量,可用于頂點(diǎn)著色器和片段著色器 varying:修飾在頂點(diǎn)著色器計(jì)算,然后傳遞到片元著色器中使用的變量

    2024年02月06日
    瀏覽(15)
  • 掌握Linux指令和權(quán)限:一個入門教程

    掌握Linux指令和權(quán)限:一個入門教程

    語法格式 :ls [選項(xiàng)][目錄或者文件] 功能 :對于目錄,該命令列出該目錄下的所有子目錄與文件。對于文件,將列出文件名以及其他信息。 a 列出目錄下的所有文件,包括以 . 開頭的隱含文件。 -d 將目錄象文件一樣顯示,而不是顯示其下的文件。 如:ls –d 指定目錄 -i 輸出文

    2023年04月23日
    瀏覽(26)
  • 03.Three.js的入門教程(二)如何創(chuàng)建一個3D地球?

    03.Three.js的入門教程(二)如何創(chuàng)建一個3D地球?

    前言:通過上節(jié)課?02.Three.js的入門課程(一),我們了解了Three.js的最小案例DEMO,熟悉了幾個重要組成部分。這節(jié)課帶領(lǐng)大家編寫一個3D地球。 一、通過紋理圖渲染一個地球 1.1. 創(chuàng)建一個紋理加載器對象TextureLoader,可以加載圖片作為紋理貼圖; 1.2.完整代碼結(jié)構(gòu) 二、小球標(biāo)

    2024年02月04日
    瀏覽(89)
  • 【Python】pyqt5入門教程之第一個UI界面

    【Python】pyqt5入門教程之第一個UI界面

    1.pyqt5工具安裝 (1)使用pip工具安裝PyQt5工具: (2)安裝Qt Designer圖形界面開發(fā)工具: 安裝完成后所在路徑 (3)安裝QtDesigner 安裝完成后所在路徑 designer.exe路徑 啟動QtDesigner 2.第一個QT窗口程序 Pycharm配置 找到py文件,右鍵External Tool — QTDesigner—就可以啟動 QTDesigner .ui文件轉(zhuǎn)換

    2024年02月15日
    瀏覽(26)
  • LearnOpenGL - Android OpenGL ES 3.0 繪制三角形

    LearnOpenGL - Android OpenGL ES 3.0 繪制三角形

    LearnOpenGL 筆記 - 入門 01 OpenGL LearnOpenGL 筆記 - 入門 02 創(chuàng)建窗口 LearnOpenGL 筆記 - 入門 03 你好,窗口 LearnOpenGL 筆記 - 入門 04 你好,三角形 OpenGL - 如何理解 VAO 與 VBO 之間的關(guān)系 經(jīng)過一段時間 OpenGL 的學(xué)習(xí),我們已經(jīng)掌握了如何使用 glwf 在桌面端繪制簡單圖形?,F(xiàn)在讓我們把目光

    2024年02月12日
    瀏覽(47)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包