編輯:關於Android編程
左側是Opengl默認的坐標系,右邊是典型的android設備屏幕的坐標系。左側的瘦瘦的三角形映射到android屏幕上就變成了胖胖的三角形(屏幕橫向的時候),我們可以使用
camera和投影解決這個問題,具體怎麼解決這裡就先不累述了。這裡我們只需要知道屏幕的左上角是(-1.0,1.0)橫向向右為X軸正向,縱向向下為Y軸
負向,其范圍都是從 -1到 +1。
定義三角形頂點:
我們在第一個android的小demo的工程裡新建一個包shape,然後新建一個類Triangle 。然後我們在該類中定義三角形的三個頂點的數據:
此時該類如下(Triangle.java):
package com.cumt.shape;
import android.content.Context;
public class Triangle {
private Context context;
// 數組中每個頂點的坐標數
static final int COORDS_PER_VERTEX = 2;
// 每個頂點的坐標數 X , Y
static float triangleCoords[] = { 0.0f, 0.5f , // top
-0.5f, -0.5f , // bottom left
0.5f, -0.5f }; // bottom right
public Triangle(Context context){
this.context = context;
}
}
package com.cumt.shape;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import android.content.Context;
public class Triangle {
private Context context;
private static final int BYTES_PER_FLOAT = 4;
private FloatBuffer vertexBuffer;
// 數組中每個頂點的坐標數
static final int COORDS_PER_VERTEX = 2;
// 每個頂點的坐標數 X , Y
static float triangleCoords[] = { 0.0f, 0.5f , // top
-0.5f, -0.5f , // bottom left
0.5f, -0.5f }; // bottom right
public Triangle(Context context){
this.context = context;
vertexBuffer = ByteBuffer
.allocateDirect(triangleCoords.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
// 把坐標們加入FloatBuffer中
vertexBuffer.put(triangleCoords);
// 設置buffer,從第一個坐標開始讀
vertexBuffer.position(0);
}
}
attribute vec4 a_Position;
void main()
{
gl_Position = a_Position;
}
gl_Position即opengl定義的頂點的坐標,我們目的就是通過這個來告訴opengl我們的頂點數據。vec4是著色器語言中的向量類型的一種,包含了四個浮點數的向量。
接下來在raw文件夾內新建文件simple_fragment_shader.glsl ,其內容如下:
precision mediump float;
uniform vec4 u_Color;
void main()
{
gl_FragColor = u_Color;
}
在utils包下新建類 LoggerConfig 用於管理我們的日志(是否輸出Logcat的信息),代碼如下(LoggerConfig.java):
package com.cumt.utils;
public class LoggerConfig {
public static final boolean ON = true;
}
再在該包下新建類ShaderHelper用於加載著色器程序以及編譯著色器:
package com.cumt.utils;
import static android.opengl.GLES20.GL_COMPILE_STATUS;
import static android.opengl.GLES20.GL_FRAGMENT_SHADER;
import static android.opengl.GLES20.GL_LINK_STATUS;
import static android.opengl.GLES20.GL_VALIDATE_STATUS;
import static android.opengl.GLES20.GL_VERTEX_SHADER;
import static android.opengl.GLES20.glAttachShader;
import static android.opengl.GLES20.glCompileShader;
import static android.opengl.GLES20.glCreateProgram;
import static android.opengl.GLES20.glCreateShader;
import static android.opengl.GLES20.glDeleteProgram;
import static android.opengl.GLES20.glDeleteShader;
import static android.opengl.GLES20.glGetProgramInfoLog;
import static android.opengl.GLES20.glGetProgramiv;
import static android.opengl.GLES20.glGetShaderInfoLog;
import static android.opengl.GLES20.glGetShaderiv;
import static android.opengl.GLES20.glLinkProgram;
import static android.opengl.GLES20.glShaderSource;
import static android.opengl.GLES20.glValidateProgram;
import android.util.Log;
public class ShaderHelper {
private static final String TAG = "ShaderHelper";
/**
* 加載並編譯頂點著色器,返回得到的opengl id
* @param shaderCode
* @return
*/
public static int compileVertexShader(String shaderCode) {
return compileShader(GL_VERTEX_SHADER, shaderCode);
}
/**
* 加載並編譯片段著色器,返回opengl id
* @param shaderCode
* @return
*/
public static int compileFragmentShader(String shaderCode) {
return compileShader(GL_FRAGMENT_SHADER, shaderCode);
}
/**
* 加載並編譯著色器,返回opengl id
* @param type
* @param shaderCode
* @return
*/
private static int compileShader(int type, String shaderCode) {
// 建立新的著色器對象
final int shaderObjectId = glCreateShader(type);
if (shaderObjectId == 0) {
if (LoggerConfig.ON) {
Log.w(TAG, "不能創建新的著色器.");
}
return 0;
}
// 傳遞著色器資源代碼.
glShaderSource(shaderObjectId, shaderCode);
//編譯著色器
glCompileShader(shaderObjectId);
// 獲取編譯的狀態
final int[] compileStatus = new int[1];
glGetShaderiv(shaderObjectId, GL_COMPILE_STATUS,
compileStatus, 0);
if (LoggerConfig.ON) {
//打印log
Log.v(TAG, "代碼編譯結果:" + "\n" + shaderCode
+ "\n:" + glGetShaderInfoLog(shaderObjectId));
}
// 確認編譯的狀態
if (compileStatus[0] == 0) {
// 如果編譯失敗,則刪除該對象
glDeleteShader(shaderObjectId);
if (LoggerConfig.ON) {
Log.w(TAG, "編譯失敗!.");
}
return 0;
}
// 返回著色器的opengl id
return shaderObjectId;
}
/**
* 鏈接頂點著色器和片段著色器成一個program
* 並返回這個pragram的opengl id
* @param vertexShaderId
* @param fragmentShaderId
* @return
*/
public static int linkProgram(int vertexShaderId, int fragmentShaderId) {
// 新建一個program對象
final int programObjectId = glCreateProgram();
if (programObjectId == 0) {
if (LoggerConfig.ON) {
Log.w(TAG, "不能新建一個 program");
}
return 0;
}
// Attach the vertex shader to the program.
glAttachShader(programObjectId, vertexShaderId);
// Attach the fragment shader to the program.
glAttachShader(programObjectId, fragmentShaderId);
// 將兩個著色器連接成一個program
glLinkProgram(programObjectId);
// 獲取連接狀態
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));
}
// 驗證連接狀態
if (linkStatus[0] == 0) {
// If it failed, delete the program object.
glDeleteProgram(programObjectId);
if (LoggerConfig.ON) {
Log.w(TAG, "連接 program 失敗!.");
}
return 0;
}
// Return the program object ID.
return programObjectId;
}
/**
* Validates an OpenGL program. Should only be called when developing the
* application.
*/
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;
}
/**
* /**
* 編譯,連接 ,返回 program 的 ID
* @param vertexShaderSource
* @param fragmentShaderSource
* @return
*/
public static int buildProgram(String vertexShaderSource,
String fragmentShaderSource) {
int program;
// Compile the shaders.
int vertexShader = compileVertexShader(vertexShaderSource);
int fragmentShader = compileFragmentShader(fragmentShaderSource);
// Link them into a shader program.
program = linkProgram(vertexShader, fragmentShader);
if (LoggerConfig.ON) {
validateProgram(program);
}
return program;
}
}
以後我們就可以很方便的一直使用該工具類來加載編譯著色器了。只需要調用buildProgram,傳入兩個著色器的String文本就可以獲得program的id是不是很方便。
我們似乎遺忘了什麼,對的我們的兩個著色器的文本內容該怎麼活的呢,我們再在該目錄下定義一個工具類TextResourceReader類來獲得文本的內容,返回String類型
代碼如下(TextResourceReader.java):
package com.cumt.utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import android.content.Context;
import android.content.res.Resources;
public class TextResourceReader {
/**
* Reads in text from a resource file and returns a String containing the
* text.
*/
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 nextLine;
while ((nextLine = bufferedReader.readLine()) != null) {
body.append(nextLine);
body.append('\n');
}
} catch (IOException e) {
throw new RuntimeException(
"Could not open resource: " + resourceId, e);
} catch (Resources.NotFoundException nfe) {
throw new RuntimeException("Resource not found: " + resourceId, nfe);
}
return body.toString();
}
}
package com.cumt.shape;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import com.cumt.openglestwo_test_one.R;
import com.cumt.utils.ShaderHelper;
import com.cumt.utils.TextResourceReader;
import android.content.Context;
import android.opengl.GLES20;
public class Triangle {
private Context context;
private static final int BYTES_PER_FLOAT = 4;
private FloatBuffer vertexBuffer;
// 數組中每個頂點的坐標數
static final int COORDS_PER_VERTEX = 2;
// 每個頂點的坐標數 X , Y
static float triangleCoords[] = { 0.0f, 0.5f , // top
-0.5f, -0.5f , // bottom left
0.5f, -0.5f }; // bottom right
private int program;
public Triangle(Context context){
this.context = context;
vertexBuffer = ByteBuffer
.allocateDirect(triangleCoords.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
// 把坐標們加入FloatBuffer中
vertexBuffer.put(triangleCoords);
// 設置buffer,從第一個坐標開始讀
vertexBuffer.position(0);
getProgram();
}
//獲取program的id
private void getProgram(){
//獲取頂點著色器文本
String vertexShaderSource = TextResourceReader
.readTextFileFromResource(context, R.raw.simple_vertex_shader);
//獲取片段著色器文本
String fragmentShaderSource = TextResourceReader
.readTextFileFromResource(context, R.raw.simple_fragment_shader);
//獲取program的id
program = ShaderHelper.buildProgram(vertexShaderSource, fragmentShaderSource);
GLES20.glUseProgram(program);
}
}
至此我們已經勝利在望了,接下來思考一下,我們應該做哪些工作?顯然我們需要將定義的數據傳入著色器中來使用。詳細的步驟見下面的代碼(Triangle.java):
package com.cumt.shape;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import com.cumt.openglestwo_test_one.R;
import com.cumt.utils.ShaderHelper;
import com.cumt.utils.TextResourceReader;
import android.content.Context;
import android.opengl.GLES20;
public class Triangle {
private Context context;
private static final int BYTES_PER_FLOAT = 4;
private FloatBuffer vertexBuffer;
//---------第四步:定義坐標元素的個數,這裡有三個頂點
private static final int POSITION_COMPONENT_COUNT = 3;
// 數組中每個頂點的坐標數
static final int COORDS_PER_VERTEX = 2;
// 每個頂點的坐標數 X , Y
static float triangleCoords[] = { 0.0f, 0.5f , // top
-0.5f, -0.5f , // bottom left
0.5f, -0.5f }; // bottom right
private int program;
//------------第一步 : 定義兩個標簽,分別於著色器代碼中的變量名相同,
//------------第一個是頂點著色器的變量名,第二個是片段著色器的變量名
private static final String A_POSITION = "a_Position";
private static final String U_COLOR = "u_Color";
//------------第二步: 定義兩個ID,我們就是通ID來實現數據的傳遞的,這個與前面
//------------獲得program的ID的含義類似的
private int uColorLocation;
private int aPositionLocation;
public Triangle(Context context){
this.context = context;
vertexBuffer = ByteBuffer
.allocateDirect(triangleCoords.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
// 把坐標們加入FloatBuffer中
vertexBuffer.put(triangleCoords);
// 設置buffer,從第一個坐標開始讀
vertexBuffer.position(0);
getProgram();
//----------第三步: 獲取這兩個ID ,是通過前面定義的標簽獲得的
uColorLocation = GLES20.glGetUniformLocation(program, U_COLOR);
aPositionLocation = GLES20.glGetAttribLocation(program, A_POSITION);
//---------第五步: 傳入數據
GLES20.glVertexAttribPointer(aPositionLocation, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false, 0, vertexBuffer);
GLES20.glEnableVertexAttribArray(aPositionLocation);
}
//獲取program
private void getProgram(){
//獲取頂點著色器文本
String vertexShaderSource = TextResourceReader
.readTextFileFromResource(context, R.raw.simple_vertex_shader);
//獲取片段著色器文本
String fragmentShaderSource = TextResourceReader
.readTextFileFromResource(context, R.raw.simple_fragment_shader);
//獲取program的id
program = ShaderHelper.buildProgram(vertexShaderSource, fragmentShaderSource);
GLES20.glUseProgram(program);
}
//----------第七步:繪制
public void draw(){
GLES20.glUniform4f(uColorLocation, 0.0f, 0.0f, 1.0f, 1.0f);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, POSITION_COMPONENT_COUNT);
}
}
package com.cumt.render;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import com.cumt.shape.Triangle;
import android.content.Context;
import android.opengl.GLSurfaceView.Renderer;
import android.util.Log;
import static android.opengl.GLES20.glClear;
import static android.opengl.GLES20.glClearColor;
import static android.opengl.GLES20.glViewport;
import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
public class MyRender implements Renderer {
private Context context;
public MyRender(Context context){
this.context = context;
}
//定義三角形對象
Triangle triangle;
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
Log.w("MyRender","onSurfaceCreated");
// TODO Auto-generated method stub
//First:設置清空屏幕用的顏色,前三個參數對應紅綠藍,最後一個對應alpha
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
triangle = new Triangle(context);
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
Log.w("MyRender","onSurfaceChanged");
// TODO Auto-generated method stub
//Second:設置視口尺寸,即告訴opengl可以用來渲染的surface大小
glViewport(0,0,width,height);
}
public void onDrawFrame(GL10 gl) {
Log.w("MyRender","onDrawFrame");
// TODO Auto-generated method stub
//Third:清空屏幕,擦除屏幕上所有的顏色,並用之前glClearColor定義的顏色填充整個屏幕
glClear(GL_COLOR_BUFFER_BIT);
//繪制三角形
triangle.draw();
}
}
Android網絡編程(二)--Http協議詳解
在上一篇中我們討論了計算機網絡的體系結構和各層次的作用,在我們編程中TCP或UDP都提供了socket接口進行實現,實現的例子在上一篇中,這一篇我們主要討論一下Http協
Android 6.0 運行時權限管理最佳實踐
這是一篇遲來的博客,Android M已經發布一年多了(6.0的變化),在Android M中權限系統被重新設計,發生了顛覆性的變化,很多人把握不好這個變化,一是對這個權
Android 動畫之AlphaAnimation應用詳解
android中提供了4中動畫: AlphaAnimation 透明度動畫效果 ScaleAnimation 縮放動畫效果 TranslateAnimation 位移動畫
Android Multimedia框架總結(十)Stagefright框架之音視頻輸出過程
前言:上篇文中最後介紹了數據解碼放到Buffer過程,今天分析的是stagefright框架中音視頻輸出過程:先看下今天的Agenda:一張圖回顧數據處理過程 視頻渲染器