Android OpenGL ES 開發(二)— 繪製三角形
在前一篇部落格我們知道了Android中OpenGL ES是什麼,然後知道了怎麼搭建一個OpenGL ES的執行環境,現在我們就來開始繪製我們自己想要的圖形了(繪製圖片會在後面講解,因為繪製圖形是繪製圖片的基礎),我們最先開始繪製一個三角形,因為三角形是很多圖形的基礎。
一、頂點座標系
在繪製之前,我們需要先了解Android中OpenGL ES的頂點座標系是怎樣的,如圖:
其中:中心座標(0,0)就是我們手機螢幕的中心,然後到最左邊是(-1,0)、最右邊是(1,0)、最上邊是(0,1)、最下邊是(0,-1)這樣就把我們的手機螢幕分成了一箇中心座標為(0,0)上下左右長度分別為1的矩形。不管我們的手機(具體來講是我們的GLSurfaceView)的大小是多少,都會對映到這個矩形中。這也是OpenGL中歸一化
二、設定繪製的三角形所需要的三個頂點
因為我們繪製的是三角形,所以我們需要三個頂點來確定我們的三角形的位置,比如我們要繪製如圖的三角形:
由圖我們知道要繪製的三角形的三個頂點座標分別為:(-1,0)、(0,1)和(1,0)
三、本地化三角形頂點
所謂本地化就是跳出java VM(Java虛擬機器)的約束(垃圾回收)範圍,使我們的頂點在程式執行時一直都有自己分配的記憶體地址,不會因為java的GC而把頂點記憶體地址給回收掉,導致頂點不存在,從而引起OpenGL找不到頂點位置等錯誤,所以在OpenGL中我們需要把頂點座標給本地化。
這裡我們就需要分2個步驟來完成頂點的本地化:
3.1、用float陣列來儲存我們的頂點座標,因為頂點座標範圍是在(-1f~1f)之間的所有小數都可以,所以我們先建立頂點陣列:
float[] vertexData = {
-1.0f, 0.0f,//三角形左下角
0.0f, 1.0f,//三角形右下角
1.0f, 0.0f//三角形頂點
};
3.2、然後根據頂點陣列分配底層記憶體地址,因為需要本地化,所以就和c/c++一樣需要我們手動分配記憶體地址,這裡用到了ByteBuffer這個類:
FloatBuffer vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)//分配記憶體空間(單位位元組) .order(ByteOrder.nativeOrder())//記憶體bit的排序方式和本地機器一致 .asFloatBuffer()//轉換成float的buffer,因為我們是放float型別的頂點 .put(vertexData);//把資料放入記憶體中 vertexBuffer.position(0);//把索引指標指向開頭位置
首先用allocateDirect分配記憶體大小,其大小為float陣列長度乘以每一個float的大小,而float佔4個位元組,所以就是:vertexData.length * 4;然後設定其在記憶體中的對齊方式(分大端和小端對其)這裡就和本地對齊方式一樣:order(ByteOrder.nativeOrder());然後設定是儲存float型別資料的記憶體空間:asFloatBuffer();最後再用float陣列(vertexData)初始化記憶體中的資料:put(vertexData)。為了能從開頭訪問這塊記憶體地址,還需要設定其position為0:vertexBuffer.position(0);。
這樣我們的三角形的頂點記憶體地址就已經分配好了,並且做了本地持久化。
四、開始頂點著色器的編寫(shader)
OpenGL的操作需要我們自己編寫著色器(shader)程式給它,然後它會用GPU執行這個著色器程式,最終反饋執行結果給我們。我們用glsl語言來編寫著色器程式,其語法方式和c語言類似,這裡就不展開講了,當學會了OpenGL程式設計後,可以自己學習glsl語法,然後就可以根據自己的能力編寫“吊炸天”的效果了。
4.1、編寫頂點著色器(vertex_shader.glsl),位置我們放在:res/raw/路徑下
attribute vec4 av_Position;//用於在java程式碼中獲取的屬性
void main(){
gl_Position = av_Position;//gl_Position是內建變數,opengl繪製頂點就是根據它的值繪製的,所以我們需要把我們自己的值賦值給它。
}
這段shader很短,但是足夠說明OpenGL中頂點座標的使用方法了:
首先解釋一下attribute vec4 av_Position這句的意思:
attribute是表示頂點屬性的,只能用在頂點座標裡面,然後在應用程式(java程式碼)中可以獲取其變數,然後為其賦值。vec4是一個包含4個值(x,y,z,w)的向量,x和y表示2d平面,加上z就是3d的影象了,最後的w是攝像機的距離,因為我們繪製的是2d圖形,所以最後z和w的值可以不用管,OpenGL會有預設值1。所以這句話的意思就是:聲明瞭一個名字叫av_Position的包含4個向量的attribute型別的變數,用於我們在java程式碼中獲取並把我們的頂點(FloatBuffer vertexBuffer)值賦值給它。這樣OpenGL執行這段著色器程式碼(程式)時,就有了具體的頂點資料,就會在相應的頂點之間繪製圖形(我們定義的三角形)了。
然後void main(){}是程式中函式,和c中是一樣的。
最後是gl_Position = av_Position,這裡的gl_Position是glsl中內建的最終頂點變數,我們要繪製的頂點就是傳遞給它。這段程式碼就是把我們設定的頂點資料傳遞給gl_Position,然後OpenGL就知道在哪裡繪製頂點了。
五、片元著色器程式編寫(shader)
上面我們只是寫了我們的三角形繪製頂點的著色器程式,而三角形是 “頂點+顏色(樣式)” 組成的,所以我們就需要告訴OpenGL我們繪製的三角形是什麼顏色的,這就需要片元著色器程式了。
- 編寫片元著色器程式(fragment_shader.glsl)
precision mediump float;//宣告用中等精度的float
uniform vec4 af_Color;//用於在java層傳遞顏色資料
void main(){
gl_FragColor = af_Color;//gl_FragColor內建變數,opengl渲染的顏色就是獲取的它的值,這裡我們把我們自己的值賦值給它。
}
這裡的precision mediump float 表明用中等精度的float型別來儲存變數,其他還可以設定高精度和低精度,一般中等精度就可以了,精度不同,執行的效率也會有差別。
然後這裡是用了uniform這個型別來宣告變數,uniform是用於應用程式(java程式碼中)向頂點和片元著色器傳遞資料,和attribute的區別在於,attribute是隻能用在頂點著色器程式中,並且它裡面包含的是具體的頂點的資料,每次執行時都需要從頂點記憶體裡面獲取新的值,而uniform始終都是用同一個變數。vec4 af_Color也是宣告一個4個分量的變數af_Color,這個裡面儲存的是顏色的值了(rgba四個分量)。
最後gl_FragColor也是glsl中內建的變數,用於最終渲染顏色的賦值,這裡我們就把我們自己的顏色賦值給gl_FragColor就行,是操作每一個畫素的rgba。
通過第四步和第五步,我們已經設定好了頂點和顏色的著色器程式,接下來就可以讓OpenGL載入這2個著色器程式,然後執行裡面的程式碼,最終繪製出我們想要的圖形(三角形)了。
六、載入並編譯著色器語言
6.1、通過GLES20.glCreateShader(shaderType)建立(頂點或片元)型別的程式碼程式,如:
建立頂點型別的著色器程式碼程式:
int vertexShader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER)
片元傳入:GLES20.GL_FRAGMENT_SHADER。
6.2、載入shader原始碼並編譯shader
GLES20.glShaderSource(shader, source);//這裡更加我們建立的型別載入相應型別的著色器(如:頂點型別)
GLES20.glCompileShader(shader);//編譯我們自己寫的著色器程式碼程式
6.3、實際建立並返回一個渲染程式(program)
int program = GLES20.glCreateProgram();//建立一個program程式
6.4、將著色器程式新增到渲染程式中
GLES20.glAttachShader(program, vertexShader);//把頂點著色器加入program程式中
GLES20.glAttachShader(program, fragmentShader);//把片元著色器加入program程式中
6.5、連結源程式
GLES20.glLinkProgram(program);//最終連結頂點和片元著色器,後面在program中就可以訪問頂點和片元著色器裡面的屬性了。
通過上面5個步驟,我們就將用glsl寫的著色器程式變成了我們可以在應用程式(java程式碼)中可以獲取裡面的變數並操作變數的具體的程式(program)了。
七、接下來就是傳遞頂點座標和顏色值給著色器程式:
7.1、獲取頂點變數
int aPositionHandl = GLES20.glGetAttribLocation(programId, "av_Position");//獲取頂點屬性,後面會給它賦值(即:把我們的頂點賦值給它)
這裡的av_Position就是頂點著色器中的attribute變數,後續操作就可以用返回值aPositionHandl這個控制代碼了。
7.2、獲取顏色變數
int afColor = GLES20.glGetUniformLocation(program, "af_Color");//獲取片元變數,後面可以通過它設定片元要顯示的顏色。
這裡的af_Color就是片元著色器中的uniform變數。後面可以對它賦值來改變三角形的顏色。
7.3、開始執行著色器程式
GLES20.glUseProgram(programId);//開始繪製之前,先設定使用當前programId這個程式。
7.4、首先啟用頂點屬性
GLES20.glEnableVertexAttribArray(aPositionHandl);//啟用頂點屬性陣列,啟用後才能對它賦值
7.4、向頂點屬性傳遞頂點陣列的值
GLES20.glVertexAttribPointer(aPositionHandl, 2, GLES20.GL_FLOAT, false, 8,
vertexBuffer);//現在就是把我們的頂點vertexBuffer賦值給頂點著色器裡面的變數。
第一引數就是我們的頂點屬性的控制代碼
第二個引數是我們用的幾個分量表示的一個點,這裡用的(x,y)2個分量,所以就填入2
第三個引數表示頂點的資料型別,因為我們用的float型別,所以就填入GL_FLOAT型別
第四個引數是是否做歸一化處理,如果我們的座標不在(-1,1)之間,就需要,由於我們的座標是在(-1,1)之間,所以不需要,填入false
第五個引數是每個點所佔空間大小,因為是(x,y)2個點,每個點是4個位元組,所以一個點佔8個空間大小,這個設定好後,OpenGL才知道8個位元組表示一個點,就能按照這個規則,依次取出所有的點的值。
第六個引數就是OpenGL要從哪個記憶體中取出這些點的資料。
7.4、最後繪製這些頂點
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);//繪製三角形,從我們的頂點數組裡面第0個位置開始,繪製頂點的個數為3(因為三角形只有三個頂點)
這裡是以頂點陣列的方式來繪製圖形
第一個引數表示繪製的方式:GLES20.GL_TRIANGLES,單個三角形的方式,還有其他方式,我們後面會講解。
第二個引數表示從哪個位置開始繪製,因為頂點座標裡面只有3個座標點,所以從0開始繪製。
第三個引數表示繪製多少個點,這裡顯然繪製三個點。
以上就是OpenGL的執行過程:
座標點(頂點或紋理)->編寫著色器程式->載入著色器程式並編譯生成program->獲取program中的變數->program變數賦值->最終繪製。
注:在載入著色器程式的時候還需要檢查是否載入成功等結果,還有繪製圖形時的清屏操作會在例項程式碼中給出完整的例子。
八、核心程式碼
8.1、載入著色器程式生成program(WlShaderUtil.java)
package com.ywl5320.opengldemo;
import android.content.Context;
import android.opengl.GLES20;
import android.util.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class WlShaderUtil {
public static String readRawTxt(Context context, int rawId) {
InputStream inputStream = context.getResources().openRawResource(rawId);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuffer sb = new StringBuffer();
String line;
try
{
while((line = reader.readLine()) != null)
{
sb.append(line).append("\n");
}
reader.close();
}
catch (Exception e)
{
e.printStackTrace();
}
return sb.toString();
}
public static int loadShader(int shaderType, String source)
{
int shader = GLES20.glCreateShader(shaderType);
if(shader != 0)
{
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
int[] compile = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compile, 0);
if(compile[0] != GLES20.GL_TRUE)
{
Log.d("ywl5320", "shader compile error");
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
public static int createProgram(String vertexSource, String fragmentSource)
{
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
if(vertexShader == 0)
{
return 0;
}
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
if(fragmentShader == 0)
{
return 0;
}
int program = GLES20.glCreateProgram();
if(program != 0)
{
GLES20.glAttachShader(program, vertexShader);
GLES20.glAttachShader(program, fragmentShader);
GLES20.glLinkProgram(program);
int[] linsStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linsStatus, 0);
if(linsStatus[0] != GLES20.GL_TRUE)
{
Log.d("ywl5320", "link program error");
GLES20.glDeleteProgram(program);
program = 0;
}
}
return program;
}
}
8.2、WlRender.java
package com.ywl5320.opengldemo;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class WlRender implements GLSurfaceView.Renderer{
private Context context;
private final float[] vertexData ={
-1f, 0f,
0f, 1f,
1f, 0f
};
private FloatBuffer vertexBuffer;
private int program;
private int avPosition;
private int afColor;
public WlRender(Context context)
{
this.context = context;
vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vertexData);
vertexBuffer.position(0);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
String vertexSource = WlShaderUtil.readRawTxt(context, R.raw.vertex_shader);
String fragmentSource = WlShaderUtil.readRawTxt(context, R.raw.fragment_shader);
program = WlShaderUtil.createProgram(vertexSource, fragmentSource);
if(program > 0)
{
avPosition = GLES20.glGetAttribLocation(program, "av_Position");
afColor = GLES20.glGetUniformLocation(program, "af_Color");
}
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
GLES20.glUseProgram(program);
GLES20.glUniform4f(afColor, 1f, 0f, 0f, 1f);
GLES20.glEnableVertexAttribArray(avPosition);
GLES20.glVertexAttribPointer(avPosition, 2, GLES20.GL_FLOAT, false, 8, vertexBuffer);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
}
}
這裡用到了通過uniform型別變數傳值的方式:
GLES20.glUniform4f(afColor, 1f, 0f, 0f, 1f);//分別設定片元變數的rgba四個值(前面的glUniform4f:表示這是uniform型別的變數的4個float型別的值)
給片元著色器中的顏色變數afColor設定argb的值為:(1f, 0f, 0f,1f)——紅色
然後其他的程式碼和上一篇部落格一樣。
九、最終效果如下:
十、總結
通過本篇文章,我們瞭解了Android中OpenGL ES的頂點座標載入過程,在OpenGL ES中最複雜的影象就是三角形,其他任意影象都可以通過三角形來組合出來,這也為我們後續的功能打下了基礎,務必好好理解裡面的流程和邏輯。
相關推薦
Android OpenGL ES 開發(二)— 繪製三角形
在前一篇部落格我們知道了Android中OpenGL ES是什麼,然後知道了怎麼搭建一個OpenGL ES的執行環境,現在我們就來開始繪製我們自己想要的圖形了(繪製圖片會在後面講解,因為繪製圖形是繪製圖片的基礎),我們最先開始繪製一個三角形
一步一步學習Android TV/盒子開發(二)
TV、機頂盒開發除錯不能像手機一樣通過USB線連線除錯,可通過ADB連線除錯 連線電視 adb connect 10.74.84.199 1 2 連線後就可以開始開發除錯了! 斷開連線 // 斷開某個裝置 adb disconnect 10.74.84
OpenGL ES 光照(二)
散射光(Diffuse): 從物體表面向全方位360度均勻反射的光,反射後的散射光在各個方向是均勻的,但反射光的反射強度與入射光的強度以及入射角度密切相關。垂直地照射到物體表面時比斜照時要亮。 散射光具體計算公式: 其中設光照入射角為α. 散射光照射結果=材質的反射係數×散射
新版OpenGL學習入門(二)——繪製圖形
教程連結:你好,三角形 這一章學的東西超級多,學完也算基本入門啦 那就從最基礎的開始吧 頂點輸入 首先是座標軸,它是高中數學學的直角座標系的座標軸,理解特別簡單。 對應的數值需要在-1和1之間,大概類似百分比吧,最後的f代表浮點數。 和頂點對應的是頂點緩衝物
Android Studio Jni開發(二)實現Native呼叫java方法和Native呼叫Android API
這一篇主要內容是Native呼叫java方法和Native呼叫Android API,以及External Tools快速生成.h檔案,依然是使用NDK方式編譯,如果是複製貼上黨,建議跟本文用一樣的工程名,本文後面會提供demo連結 一、建立工程 1.建立名為Jnites
Android藍芽開發(二) BLE4.0低功耗藍芽
一、BLE4.0低功耗藍芽 Bluetooth Low Energy,藍芽低功耗,是從藍芽4.0開始支援的技術。相較傳統藍芽,傳輸速度更快、覆蓋範圍廣、安全性高、延時短、耗電低等特點。 二、關鍵術語 1.GATT(通用屬性配置):通用屬性配置檔案,用於ble鏈路上傳送和接
Android微信開發(二):程式碼分析
package com.example.teststudyshare.wxapi; import java.io.ObjectOutputStream.PutField; import android.R; import android.app.Activity; import android.content
Android快樂貪吃蛇遊戲實戰專案開發教程-03虛擬方向鍵(二)繪製一個三角形
一、繪製三角形 在上一篇文章中,我們已經新建了虛擬方向鍵的自定義控制元件DirectionKeys類,下面我們繼續。 本專案中的虛擬方向鍵的背景是4個三角形組成的矩形,其實是4個三角形的按鈕。 系統自帶的按鈕是矩形的,怎麼做一個三角形按鈕呢? 首先我需要了解,所有控制元件的外觀都是畫出來的,當然不
android的百度地圖開發(二) 定位
頻率 update 殺死 一次 ddr animate 語義 pri des 參考:http://blog.csdn.net/mr_wzc/article/details/51590485 第一步,初始化LocationClient類 //獲取地圖控件引用
Android OpenGL ES 開發教程 22 繪製一個球體
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
Android開發(二)SQLite視覺化工具SQLiteStudio的用法
一、SQLiteStudio下載地址: http://www.pc6.com/softview/SoftView_86552.html#download二、SQLiteStudio使用教程第一步:建庫 開啟SQLite Studio,點選“新增資料庫”按鈕第二步:資料
Android 百萬級視訊應用開發(二)
在完成可之前的思考之後,我感覺我也是絞盡腦汁了。 7月25號的時候出了效果圖,和切圖,我26號下午完成了整個軟體的頁面搭建。歷史8.5小時26個頁面。但是我想我應該可以做的更好,因為,我的封裝做的還不徹底,既然複用那麼複用率就應該達到100%!既然封裝,那麼就
通俗易懂的 OpenGL ES 3.0(二)渲染三角形
前言 學習了OpenGL有一段時間,在繪製出屬於自己的三角形之前,會接觸許多理論上的知識。用簡單的方式寫下自己對OpenGL的一些見解。望大家取其精華去其糟粕 最終效果:改變背景色,並且繪製渲染一個暗紅色的三角形 必備知識 OpenGL需要我們至少設定一個
自學Android開發(二) AndroidStudio 匯入Github上下載的專案,以及使用Genymotion所遇到的問題
因為是自學Android自然是想找一些原始碼,自己跑著試試,然後自己再去解讀這些程式碼~~本著這樣的想法,於是就在Github上找了一個Android的小遊戲https://github.com/HurTeng/StormPlane 連線奉上~~ -。-高高興興的下
Android Gallery3d原始碼學習總結(二)——繪製流程drawThumbnails
此函式控制相簿表格頁、相片表格頁、時間分類表格頁的展示,非常重要。以下以相簿表格頁為例進行講解,其他的就舉一反三吧。準備輸入引數 final GridDrawables drawables = mDrawables; final DisplayList d
Android NDK 開發(二)JNI 傳遞引數和返回值
前言 我們在使用 JNI 時最常問到的是 JAVA 和 C/C++之間如何傳遞資料,以及資料型別之間如何 互相對映。我們從整數等基本型別和陣列、字串等普通的物件型別開始講述。至於如何傳遞任意物件,將在後面會更新。 正文 繼JNI簡介及呼叫流程這
Android 自定義View(二),點,線的繪製
public class PointLine extends View { Paint mLinePaint; Paint mPointPaint; float width; float height; float pointAddress
【學習OpenGL】(二)——繪製矩形(視口與裁剪區)
繪製矩形 在前面第一個建立視窗的程式的基礎之上,新增繪製矩形的函式 #include <gl/glut.h> void RenderScene(void) { // 用當前的清除顏色清除視窗 glClear(GL_COLOR
android Bluetooth 開發(二):開啟、關閉、搜尋、允許搜尋、檢視
相關專案的下載連結繼本專案之後實現了語音識別:點選開啟連結1.承接上一篇文章,本篇文章主要實現了藍芽的開啟 關閉 允許搜尋 檢視配對裝置2. BluetoothInit,主要實現了部件的初始化,按鈕的點選事件,通過ListVIew顯示本地配對的藍芽裝置,ListView的點選
Android OpenGL ES 開發:繪製圖形
# OpenGL 繪製圖形步驟 上一篇介紹了 OpenGL 的相關概念,今天來實際操作,使用 OpenGL 繪製出圖形,對其過程有一個初步的瞭解。 OpenGL 繪製圖形主要概括成以下幾個步驟: 1. 建立程式 2. 初始化著色器 3. 將著色器加入程式 4. 連結並使用程式 6. 繪製圖形 上述每個