你以為我的封面圖只是吸引眼球?
上集說到:用矩陣的變換來操作頂點,使圖形產生相應的變化(移動,選擇,縮放)
這一集將點亮世界之光,讓你對OpenGL的世界有更深的瞭解
普通副本五:黑龍之珠
本副本參照
《Android 3D遊戲開發技術寶典 OpenGL ES 2.0》
但是分析的要詳細一些,書中繪製的方法只是一筆帶過,感覺球面還是需要挖挖的
而且書中原始碼繪製部分寫的也挺亂的,該抽的我抽了一下,看著好看些
1.第一關卡:球面的頂點計算
也就是經緯取分割點,再將這些點拼成三角形形成曲面效果
下面應該很形象的說明了漸變的過程
/**
* 初始化頂點座標資料的方法
*
* @param r 半徑
* @param splitCount 赤道分割點數
*/
public void initVertex(float r, int splitCount) {
// 頂點座標資料的初始化================begin============================
ArrayList<Float> vertixs = new ArrayList<>();// 存放頂點座標的ArrayList
final float dθ = 360.f / splitCount;// 將球進行單位切分的角度
//垂直方向angleSpan度一份
for (float α = -90; α < 90; α = α + dθ) {
// 水平方向angleSpan度一份
for (float β = 0; β <= 360; β = β + dθ) {
// 縱向橫向各到一個角度後計算對應的此點在球面上的座標
float x0 = r * cos(α) * cos(β);
float y0 = r * cos(α) * sin(β);
float z0 = r * sin(α);
float x1 = r * cos(α) * cos(β + dθ);
float y1 = r * cos(α) * sin(β + dθ);
float z1 = r * sin(α);
float x2 = r * cos(α + dθ) * cos(β + dθ);
float y2 = r * cos(α + dθ) * sin(β + dθ);
float z2 = r * sin(α + dθ);
float x3 = r * cos(α + dθ) * cos(β);
float y3 = r * cos(α + dθ) * sin(β);
float z3 = r * sin(α + dθ);
// 將計算出來的XYZ座標加入存放頂點座標的ArrayList
vertixs.add(x1);vertixs.add(y1);vertixs.add(z1);//p1
vertixs.add(x3);vertixs.add(y3);vertixs.add(z3);//p3
vertixs.add(x0);vertixs.add(y0);vertixs.add(z0);//p0
vertixs.add(x1);vertixs.add(y1);vertixs.add(z1);//p1
vertixs.add(x2);vertixs.add(y2);vertixs.add(z2);//p2
vertixs.add(x3);vertixs.add(y3);vertixs.add(z3);//p3
}
}
verticeCount = vertixs.size() / 3;// 頂點的數量為座標值數量的1/3,因為一個頂點有3個座標
// 將vertices中的座標值轉存到一個float陣列中
float vertices[] = new float[verticeCount * 3];
for (int i = 0; i < vertixs.size(); i++) {
vertices[i] = vertixs.get(i);
}
vertexBuffer = GLUtil.getFloatBuffer(vertices);
}
/**
* 求sin值
*
* @param θ 角度值
* @return sinθ
*/
private float sin(float θ) {
return (float) Math.sin(Math.toRadians(θ));
}
/**
* 求cos值
*
* @param θ 角度值
* @return cosθ
*/
private float cos(float θ) {
return (float) Math.cos(Math.toRadians(θ));
}
複製程式碼
2.第二關卡:著色器的程式碼及使用
2.1:片元著色程式碼:ball.frag
新增uR控制程式碼,vPosition獲取頂點座標,根據座標來進行著色
precision mediump float;
uniform float uR;
varying vec3 vPosition;//接收從頂點著色器過來的頂點位置
void main(){
vec3 color;
float n = 8.0;//一個座標分量分的總份數
float span = 2.0*uR/n;//每一份的長度
//每一維在立方體內的行列數
int i = int((vPosition.x + uR)/span);
int j = int((vPosition.y + uR)/span);
int k = int((vPosition.z + uR)/span);
//計算當點應位於白色塊還是黑色塊中
int whichColor = int(mod(float(i+j+k),2.0));
if(whichColor == 1) {//奇數時
color = vec3(0.16078432f,0.99215686f,0.02745098f);//綠
} else {//偶數時為白色
color = vec3(1.0,1.0,1.0);//白色
}
//將計算出的顏色給此片元
gl_FragColor=vec4(color,0);
}
複製程式碼
2.2:頂點著色程式碼:ball.vert
uniform mat4 uMVPMatrix;
attribute vec3 aPosition;
varying vec3 vPosition;
void main(){
gl_Position = uMVPMatrix * vec4(aPosition,1);
vPosition = aPosition;
}
複製程式碼
2.3:著色器的使用
//宣告控制程式碼
private int mPositionHandle;//位置控制程式碼
private int muMVPMatrixHandle;//頂點變換矩陣控制程式碼
private int muRHandle;//半徑的控制程式碼
//獲取控制程式碼
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
muRHandle = GLES20.glGetUniformLocation(mProgram, "uR");
//使用控制程式碼
GLES20.glEnableVertexAttribArray(mPositionHandle);//啟用頂點的控制程式碼
//頂點矩陣變換
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mvpMatrix, 0);
//準備頂點座標資料
GLES20.glVertexAttribPointer(
mPositionHandle,//int indx, 索引
COORDS_PER_VERTEX,//int size,大小
GLES20.GL_FLOAT,//int type,型別
false,//boolean normalized,//是否標準化
vertexStride,// int stride,//跨度
vertexBuffer);// java.nio.Buffer ptr//緩衝
// 將半徑尺寸傳入shader程式
GLES20.glUniform1f(muRHandle, mR * Cons.UNIT_SIZE);
複製程式碼
3.第三關卡:關於UNIT_SIZE
就是一個尺寸的伸縮量而已,定義成常量,方便放大與縮小,沒別的
普通副本六宇宙之光
OpenGL ES 中只有三種光:
環境光:光照的作用全體
散射光:單點光源
鏡面光:鏡面反射
複製程式碼
1.第一關卡:環境光
就像太陽光,我們身處的環境,環境中,光照的作用結果一致
修改起來也比較方便,環境光說白了就是對片元顏色的運算而已
0.35 | 0.75 | 1.00 |
---|---|---|
1.1.片元著色器ball.frag
precision mediump float;
uniform float uR;
varying vec3 vPosition;//接收從頂點著色器過來的頂點位置
varying vec4 vAmbient;//接收從頂點著色器過來的環境光分量
void main(){
vec3 color;
float n = 8.0;//一個座標分量分的總份數
float span = 2.0*uR/n;//每一份的長度
//每一維在立方體內的行列數
int i = int((vPosition.x + uR)/span);
int j = int((vPosition.y + uR)/span);
int k = int((vPosition.z + uR)/span);
//計算當點應位於白色塊還是黑色塊中
int whichColor = int(mod(float(i+j+k),2.0));
if(whichColor == 1) {//奇數時
color = vec3(0.16078432f,0.99215686f,0.02745098f);//綠
} else {//偶數時為白色
color = vec3(1.0,1.0,1.0);//白色
}
//最終顏色
vec4 finalColor=vec4(color,0);
//給此片元顏色值
gl_FragColor=finalColor*vAmbient;
}
複製程式碼
1.2.頂點著色器ball.vert
uniform mat4 uMVPMatrix; //總變換矩陣
attribute vec3 aPosition; //頂點位置
varying vec3 vPosition;//用於傳遞給片元著色器的頂點位置
uniform vec4 uAmbient;
varying vec4 vAmbient;//用於傳遞給片元著色器的環境光分量
void main(){
//根據總變換矩陣計算此次繪製此頂點位置
gl_Position = uMVPMatrix * vec4(aPosition,1);
//將頂點的位置傳給片元著色器
vPosition = aPosition;//將原始頂點位置傳遞給片元著色器
//將的環境光分量傳給片元著色器
vAmbient = vec4(uAmbient);
}
複製程式碼
1.3.使用:控制程式碼拿到傳值而已,也沒什麼難的
private int muAmbientHandle;//環境光控制程式碼
//獲取環境光控制程式碼
muAmbientHandle = GLES20.glGetUniformLocation(mProgram, "uAmbient");
//使用環境光
GLES20.glUniform4f(muAmbientHandle, 0.5f,0.5f,0.5f,1f);
複製程式碼
1.第二關卡:散射光
忙活了好一會,總算搞定了,一個api用錯了,一直崩潰...
相當於打個燈,燈的位置是固定不動的
-1,1,-1 | 1,1,-1 |
---|---|
看下圖的點光源在
(1,1,-1)
你應該知道燈在哪了吧,注意看軸色
2.1:頂點著色器:ball.vert
程式碼有點複雜,做好心理準備
uniform mat4 uMVPMatrix; //總變換矩陣
attribute vec3 aPosition; //頂點位置
varying vec3 vPosition;//用於傳遞給片元著色器的頂點位置
varying vec4 uAmbient;//環境光分量
varying vec4 vAmbient;//用於傳遞給片元著色器的環境光分量
uniform mat4 uMMatrix;//變換矩陣(包括平移、旋轉、縮放)
uniform vec3 uLightLocation;//光源位置
attribute vec3 aNormal;//頂點法向量
varying vec4 vDiffuse; //用於傳遞給片元著色器的散射光分量
//散射光光照計算的方法(法向量,散射光計算結果,光源位置,散射光強度)
void pointLight (in vec3 normal,inout vec4 diffuse,in vec3 lightLocation,in vec4 lightDiffuse){
vec3 normalTarget=aPosition+normal;//計算變換後的法向量
vec3 newNormal=(uMMatrix*vec4(normalTarget,1)).xyz-(uMMatrix*vec4(aPosition,1)).xyz;
newNormal=normalize(newNormal);//對法向量規格化
//計算從表面點到光源位置的向量vp
vec3 vp= normalize(lightLocation-(uMMatrix*vec4(aPosition,1)).xyz);
vp=normalize(vp);//單位化vp
float nDotViewPosition=max(0.0,dot(newNormal,vp));//求法向量與vp向量的點積與0的最大值
diffuse=lightDiffuse*nDotViewPosition;//計算散射光的最終強度
}
void main(){
//根據總變換矩陣計算此次繪製此頂點位置
gl_Position = uMVPMatrix * vec4(aPosition,1);
//將頂點的位置傳給片元著色器
vPosition = aPosition;//將原始頂點位置傳遞給片元著色器
vec4 diffuseTemp=vec4(0.0,0.0,0.0,0.0);
pointLight(normalize(aNormal), diffuseTemp, uLightLocation, vec4(0.8,0.8,0.8,1.0));
vDiffuse=diffuseTemp;//將散射光最終強度傳給片元著色器
//將的環境光分量傳給片元著色器
vAmbient = vec4(uAmbient);
}
複製程式碼
2.2:片元著色器:ball.frag
precision mediump float;
uniform float uR;
varying vec3 vPosition;//接收從頂點著色器過來的頂點位置
varying vec4 vAmbient;//接收從頂點著色器過來的環境光分量
varying vec4 vDiffuse;//接收從頂點著色器過來的散射光分量
void main(){
vec3 color;
float n = 8.0;//一個座標分量分的總份數
float span = 2.0*uR/n;//每一份的長度
//每一維在立方體內的行列數
int i = int((vPosition.x + uR)/span);
int j = int((vPosition.y + uR)/span);
int k = int((vPosition.z + uR)/span);
//計算當點應位於白色塊還是黑色塊中
int whichColor = int(mod(float(i+j+k),2.0));
if(whichColor == 1) {//奇數時
color = vec3(0.16078432f,0.99215686f,0.02745098f);//綠
} else {//偶數時為白色
color = vec3(1.0,1.0,1.0);//白色
}
//最終顏色
vec4 finalColor=vec4(color,0);
//給此片元顏色值
// gl_FragColor=finalColor*vAmbient;//環境光
gl_FragColor=finalColor*vDiffuse;
}
複製程式碼
2.3:使用
新添了三個控制程式碼,用法也是寫爛了...
這裡用一個GLState類管理全域性的狀態
---->[Ball#initProgram]-----------
//獲取頂點法向量的控制程式碼
maNormalHandle = GLES20.glGetAttribLocation(mProgram, "aNormal");
//獲取程式中光源位置引用
maLightLocationHandle = GLES20.glGetUniformLocation(mProgram, "uLightLocation");
//獲取位置、旋轉變換矩陣引用
muMMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMMatrix");
---->[Ball#draw]-----------
//將位置、旋轉變換矩陣傳入著色器程式
GLES20.glUniformMatrix4fv(muMMatrixHandle, 1, false, MatrixStack.getOpMatrix(), 0);
//將光源位置傳入著色器程式
GLES20.glUniform3fv(maLightLocationHandle, 1, GLState.lightPositionFB);
//將頂點法向量資料傳入渲染管線
GLES20.glVertexAttribPointer(maNormalHandle, 3, GLES20.GL_FLOAT, false,
3 * 4, vertexBuffer);
---->[GLState.java]-----------
////////----------設定光源
private static float[] lightLocation = new float[]{0, 0, 0};//定位光光源位置
public static FloatBuffer lightPositionFB;
//設定燈光位置的方法
public static void setLightLocation(float x, float y, float z) {
lightLocation[0] = x;
lightLocation[1] = y;
lightLocation[2] = z;
lightPositionFB = getFloatBuffer(lightLocation);
}
---->[WorldRenderer#onDrawFrame]-----------
GLState.setLightLocation(-1, 1, -1);
複製程式碼
3.第三關卡:鏡面光
真的有些hold不住了...
同一束光,照在粗糙度不同的物體上,越光滑,我們可以看到的部分就越多
單獨寫了一個Ball_M.java的類,以及兩個ball_m.vert,ball_m.frag著色器
平面光就夠喝一壺的了,升級到三維...還是先用著吧,原理等百無聊賴的時候再分析吧
3.1:頂點著色器:ball_m.vert
uniform mat4 uMVPMatrix; //總變換矩陣
uniform mat4 uMMatrix; //變換矩陣
uniform vec3 uLightLocation; //光源位置
uniform vec3 uCamera; //攝像機位置
attribute vec3 aPosition; //頂點位置
attribute vec3 aNormal; //法向量
varying vec3 vPosition; //用於傳遞給片元著色器的頂點位置
varying vec4 vSpecular; //用於傳遞給片元著色器的鏡面光最終強度
void pointLight( //定位光光照計算的方法
in vec3 normal, //法向量
inout vec4 specular, //鏡面反射光分量
in vec3 lightLocation, //光源位置
in vec4 lightSpecular //鏡面光強度
){
vec3 normalTarget=aPosition+normal; //計算變換後的法向量
vec3 newNormal=(uMMatrix*vec4(normalTarget,1)).xyz-(uMMatrix*vec4(aPosition,1)).xyz;
newNormal=normalize(newNormal); //對法向量規格化
//計算從表面點到攝像機的向量
vec3 eye= normalize(uCamera-(uMMatrix*vec4(aPosition,1)).xyz);
//計算從表面點到光源位置的向量vp
vec3 vp= normalize(lightLocation-(uMMatrix*vec4(aPosition,1)).xyz);
vp=normalize(vp);//格式化vp
vec3 halfVector=normalize(vp+eye); //求視線與光線的半向量
float shininess=5.0; //粗糙度,越小越光滑
float nDotViewHalfVector=dot(newNormal,halfVector); //法線與半向量的點積
float powerFactor=max(0.0,pow(nDotViewHalfVector,shininess)); //鏡面反射光強度因子
specular=lightSpecular*powerFactor; //最終的鏡面光強度
}
void main() {
gl_Position = uMVPMatrix * vec4(aPosition,1); //根據總變換矩陣計算此次繪製此頂點的位置
vec4 specularTemp=vec4(0.0,0.0,0.0,0.0);
pointLight(normalize(aNormal), specularTemp, uLightLocation, vec4(0.7,0.7,0.7,1.0));//計算鏡面光
vSpecular=specularTemp; //將最終鏡面光強度傳給片元著色器
vPosition = aPosition; //將頂點的位置傳給片元著色器
}
複製程式碼
3.2:片元著色器:ball_m.vert
precision mediump float;
uniform float uR;
varying vec3 vPosition;//接收從頂點著色器過來的頂點位置
varying vec4 vSpecular;//接收從頂點著色器過來的鏡面反射光分量
void main(){
vec3 color;
float n = 8.0;//一個座標分量分的總份數
float span = 2.0*uR/n;//每一份的長度
//每一維在立方體內的行列數
int i = int((vPosition.x + uR)/span);
int j = int((vPosition.y + uR)/span);
int k = int((vPosition.z + uR)/span);
//計算當點應位於白色塊還是黑色塊中
int whichColor = int(mod(float(i+j+k),2.0));
if(whichColor == 1) {//奇數時為紅色
color = vec3(0.678,0.231,0.129);//紅色
}
else {//偶數時為白色
color = vec3(1.0,1.0,1.0);//白色
}
//最終顏色
vec4 finalColor=vec4(color,0);
//給此片元顏色值
gl_FragColor=finalColor*vSpecular;
}
複製程式碼
3.使用
增加了一個uCamera控制程式碼,增加相機位置的狀態,在
MatrixStack#lookAt
裡初始化
maCameraHandle = GLES20.glGetUniformLocation(mProgram, "uCamera");
---->[Ball_M#draw]-------
GLES20.glUniform3fv(maCameraHandle, 1, GLState.cameraFB);
---->[GLState]-------
////////----------設定相機位置
static float[] cameraLocation = new float[3];//攝像機位置
public static FloatBuffer cameraFB;
//設定燈光位置的方法
public static void setCameraLocation(float x, float y, float z) {
cameraLocation[0] = x;
cameraLocation[1] = y;
cameraLocation[2] = z;
cameraFB = GLUtil.getFloatBuffer(cameraLocation);
}
---->[MatrixStack#lookAt]-------
GLState.setCameraLocation(cx, cy, cz);//設定相機位置
複製程式碼
4.第四關卡:三光同時作用
就是綜合一下而已...,跟書中小不同,這裡我把粗糙度和環境光提出來了
基本上程式碼裡沒有什麼變化,終點在著色器裡
---->[ball.vert]---------------------
uniform mat4 uMVPMatrix; //總變換矩陣
uniform mat4 uMMatrix; //變換矩陣
uniform vec3 uLightLocation; //光源位置
uniform vec3 uCamera; //攝像機位置
uniform float uShininess; //攝像機位置
uniform vec4 uAmbient;//環境光
attribute vec3 aPosition; //頂點位置
attribute vec3 aNormal; //法向量
varying vec3 vPosition; //用於傳遞給片元著色器的頂點位置
varying vec4 vAmbient; //用於傳遞給片元著色器的環境光最終強度
varying vec4 vDiffuse; //用於傳遞給片元著色器的散射光最終強度
varying vec4 vSpecular; //用於傳遞給片元著色器的鏡面光最終強度
void pointLight( //定位光光照計算的方法
in vec3 normal, //法向量
inout vec4 ambient, //環境光最終強度
inout vec4 diffuse, //散射光最終強度
inout vec4 specular, //鏡面光最終強度
in vec3 lightLocation, //光源位置
in vec4 lightAmbient, //環境光強度
in vec4 lightDiffuse, //散射光強度
in vec4 lightSpecular //鏡面光強度
){
ambient=lightAmbient; //直接得出環境光的最終強度
vec3 normalTarget=aPosition+normal; //計算變換後的法向量
vec3 newNormal=(uMMatrix*vec4(normalTarget,1)).xyz-(uMMatrix*vec4(aPosition,1)).xyz;
newNormal=normalize(newNormal); //對法向量規格化
//計算從表面點到攝像機的向量
vec3 eye= normalize(uCamera-(uMMatrix*vec4(aPosition,1)).xyz);
//計算從表面點到光源位置的向量vp
vec3 vp= normalize(lightLocation-(uMMatrix*vec4(aPosition,1)).xyz);
vp=normalize(vp);//格式化vp
vec3 halfVector=normalize(vp+eye); //求視線與光線的半向量
float shininess=uShininess; //粗糙度,越小越光滑
float nDotViewPosition=max(0.0,dot(newNormal,vp)); //求法向量與vp的點積與0的最大值
diffuse=lightDiffuse*nDotViewPosition; //計算散射光的最終強度
float nDotViewHalfVector=dot(newNormal,halfVector); //法線與半向量的點積
float powerFactor=max(0.0,pow(nDotViewHalfVector,shininess)); //鏡面反射光強度因子
specular=lightSpecular*powerFactor; //計算鏡面光的最終強度
}
void main(){
gl_Position = uMVPMatrix * vec4(aPosition,1); //根據總變換矩陣計算此次繪製此頂點位置
vec4 ambientTemp,diffuseTemp,specularTemp; //用來接收三個通道最終強度的變數
pointLight(normalize(aNormal),ambientTemp,diffuseTemp,specularTemp,uLightLocation,
uAmbient,vec4(0.8,0.8,0.8,1.0),vec4(0.7,0.7,0.7,1.0));
vAmbient=ambientTemp; //將環境光最終強度傳給片元著色器
vDiffuse=diffuseTemp; //將散射光最終強度傳給片元著色器
vSpecular=specularTemp; //將鏡面光最終強度傳給片元著色器
vPosition = aPosition; //將頂點的位置傳給片元著色器
}
---->[ball.frag]---------------------
precision mediump float;
uniform float uR;
varying vec3 vPosition;//接收從頂點著色器過來的頂點位置
varying vec4 vAmbient;//接收從頂點著色器過來的環境光分量
varying vec4 vDiffuse;//接收從頂點著色器過來的散射光分量
varying vec4 vSpecular;//接收從頂點著色器過來的鏡面反射光分量
void main()
{
vec3 color;
float n = 8.0;//一個座標分量分的總份數
float span = 2.0*uR/n;//每一份的長度
//每一維在立方體內的行列數
int i = int((vPosition.x + uR)/span);
int j = int((vPosition.y + uR)/span);
int k = int((vPosition.z + uR)/span);
//計算當點應位於白色塊還是黑色塊中
int whichColor = int(mod(float(i+j+k),2.0));
if(whichColor == 1) {//奇數時
color = vec3(0.16078432f,0.99215686f,0.02745098f);//綠
}
else {//偶數時為白色
color = vec3(1.0,1.0,1.0);//白色
}
//最終顏色
vec4 finalColor=vec4(color,0);
//給此片元顏色值
gl_FragColor=finalColor*vAmbient + finalColor*vDiffuse + finalColor*vSpecular;
}
---->[GLState.java]---------------------
////////----------環境光
static float[] eviLight = new float[4];//攝像機位置
public static FloatBuffer eviLightFB;
//設定燈光位置的方法
public static void setEviLight(float r, float g, float b,float a) {
eviLight[0] = r;
eviLight[1] = g;
eviLight[2] = b;
eviLight[3] = a;
eviLightFB = GLUtil.getFloatBuffer(eviLight);
}
---->[Ball.java]---------------------
//粗糙度
muShininessHandle = GLES20.glGetUniformLocation(mProgram, "uShininess");
GLES20.glUniform1f(muShininessHandle, 30);
複製程式碼
普通副本七:龍之盛裝LEVEL2
新手副本
龍之盛裝LEVEL1
中已經簡單知道了紋理的貼圖 這個副本將來深入瞭解一下貼圖
1.第一關卡:紋理座標系
紋理座標系(右側)是一個二維座標,方向和Android中的螢幕座標系一致
書上說貼圖的寬高畫素數必須是2的n次方,但是我試了不是也可以。為免爭議,這裡用的是2的n次方
static float vertexs[] = { //以逆時針順序
-1, 1, 0,
-1, -1, 0,
1, -1, 0,
};
private final float[] textureCoo = {
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
};
複製程式碼
2.第二關卡:矩形
先用三點矩形來畫,比較形象一些,就是兩個三角形拼合
static float vertexs[] = { //以逆時針順序
-1, 1, 0,
-1, -1, 0,
1, -1, 0,
1, -1, 0,
1, 1, 0,
-1, 1, 0
};
private final float[] textureCoo = {
0, 0,
0, 1,
1, 1,
1, 1,
1, 0,
0, 0
};
複製程式碼
3.第三關卡:紋理的裁剪與拉伸
3.1:新增兩個係數控制紋理座標的大小
---->[TextureRectangle]---------
float s = 1;//s紋理座標系數
float t = 1f;//t紋理座標系數
private final float[] textureCoo = {
0, 0,
0, t,
s, t,
s, t,
s, 0,
0, 0
};
複製程式碼
3.2:載入紋理的工具
其中s和t的包裹方式:
GL_CLAMP_TO_EDGE
//---------------紋理載入工具--GLUtil.java-----
/**
* 資源id 載入紋理
*
* @param ctx 上下文
* @param resId 資源id
* @return 紋理id
*/
public static int loadTexture(Context ctx, int resId) {
Bitmap bitmap = BitmapFactory.decodeResource(ctx.getResources(), resId);
return loadTexture(ctx, bitmap);
}
/**
* bitmap 載入紋理
*
* @param ctx 上下文
* @param bitmap bitmap
* @return 紋理id
*/
public static int loadTexture(Context ctx, Bitmap bitmap) {
//生成紋理ID
int[] textures = new int[1];
//(產生的紋理id的數量,紋理id的陣列,偏移量)
GLES20.glGenTextures(1, textures, 0);
int textureId = textures[0];
//繫結紋理id
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
//取樣方式MIN
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
//設定s軸包裹方式---擷取
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
//設定t軸包裹方式---擷取
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
//實際載入紋理(紋理型別,紋理的層次,紋理影像,紋理邊框尺寸)
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle(); //紋理載入成功後釋放圖片
return textureId;
}
複製程式碼
4.第四關卡:紋理的重複
這和css的重複方式挺像的,看一眼就應該明白,我就不廢話了
要改的就兩局程式碼:GLUtil#loadTexture
//設定s軸拉伸方式---重複
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
//設定t軸拉伸方式---重複
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
複製程式碼
5.第五關卡:重複模式的封裝
5.1:重複模式的列舉
/**
* 作者:張風捷特烈<br/>
* 時間:2019/1/16/016:9:31<br/>
* 郵箱:1981462002@qq.com<br/>
* 說明:重複方式列舉
*/
public enum RepeatType {
NONE,//不重複
REPEAT_X,//僅x軸重複
REPEAT_Y,//僅y軸重複
REPEAT//x,y重複
}
複製程式碼
5.1:載入紋理方法的封裝
//---------------紋理載入工具--GLUtil.java-----
/**
* 資源id 載入紋理,預設重複方式:RepeatType.REPEAT
*
* @param ctx 上下文
* @param resId 資源id
* @return 紋理id
*/
public static int loadTexture(Context ctx, int resId) {
return loadTexture(ctx, resId, RepeatType.REPEAT);
}
/**
* 資源id 載入紋理
*
* @param ctx 上下文
* @param resId 資源id
* @param repeatType 重複方式 {@link com.toly1994.picture.world.bean.RepeatType}
* @return 紋理id
*/
public static int loadTexture(Context ctx, int resId, RepeatType repeatType) {
Bitmap bitmap = BitmapFactory.decodeResource(ctx.getResources(), resId);
return loadTexture(bitmap, repeatType);
}
/**
* bitmap 載入紋理
*
* @param bitmap bitmap
* @param repeatType 重複方式 {@link com.toly1994.picture.world.bean.RepeatType}
* @return 紋理id
*/
public static int loadTexture(Bitmap bitmap, RepeatType repeatType) {
//生成紋理ID
int[] textures = new int[1];
//(產生的紋理id的數量,紋理id的陣列,偏移量)
GLES20.glGenTextures(1, textures, 0);
int textureId = textures[0];
//繫結紋理id
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
//取樣方式MIN
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
int wrapS = 0;
int wrapT = 0;
switch (repeatType) {
case NONE:
wrapS = GLES20.GL_CLAMP_TO_EDGE;
wrapT = GLES20.GL_CLAMP_TO_EDGE;
break;
case REPEAT_X:
wrapS = GLES20.GL_REPEAT;
wrapT = GLES20.GL_CLAMP_TO_EDGE;
break;
case REPEAT_Y:
wrapS = GLES20.GL_CLAMP_TO_EDGE;
wrapT = GLES20.GL_REPEAT;
break;
case REPEAT:
wrapS = GLES20.GL_REPEAT;
wrapT = GLES20.GL_REPEAT;
break;
}
//設定s軸拉伸方式---重複
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, wrapS);
//設定t軸拉伸方式---重複
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, wrapT);
//實際載入紋理(紋理型別,紋理的層次,紋理影像,紋理邊框尺寸)
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle(); //紋理載入成功後釋放圖片
return textureId;
}
複製程式碼
當然這也僅是紋理的簡單認識,跟高階的
龍之盛裝
副本,敬請期待
普通副本八:黑龍之型 LEVEL1
你以為我的封面圖只是吸引眼球?
1.第一關卡:3DMAX與.obj檔案
3DMAX大學的時候用過,知道OpenGL ES 可以載入3DMAX的模型,激動之心無法言表
模型自己去網上下,3DMAX裝軟體我也不廢話了,安裝教程一大堆
可見都是點的資料,現在要開始解析資料了,Are you ready?
2.第二關卡:載入與解析點:
參見《Android 3D遊戲開發技術寶典 OpenGL ES 2.0》
//-------------載入obj點集----------------
//從obj檔案中載入僅攜帶頂點資訊的物體
public static float[] loadPosInObj(String name, Context ctx) {
ArrayList<Float> alv = new ArrayList<>();//原始頂點座標列表
ArrayList<Float> alvResult = new ArrayList<>();//結果頂點座標列表
try {
InputStream in = ctx.getAssets().open(name);
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
String temps = null;
while ((temps = br.readLine()) != null) {
String[] tempsa = temps.split("[ ]+");
if (tempsa[0].trim().equals("v")) {//此行為頂點座標
alv.add(Float.parseFloat(tempsa[1]));
alv.add(Float.parseFloat(tempsa[2]));
alv.add(Float.parseFloat(tempsa[3]));
} else if (tempsa[0].trim().equals("f")) {//此行為三角形面
int index = Integer.parseInt(tempsa[1].split("/")[0]) - 1;
alvResult.add(alv.get(3 * index));
alvResult.add(alv.get(3 * index + 1));
alvResult.add(alv.get(3 * index + 2));
index = Integer.parseInt(tempsa[2].split("/")[0]) - 1;
alvResult.add(alv.get(3 * index));
alvResult.add(alv.get(3 * index + 1));
alvResult.add(alv.get(3 * index + 2));
index = Integer.parseInt(tempsa[3].split("/")[0]) - 1;
alvResult.add(alv.get(3 * index));
alvResult.add(alv.get(3 * index + 1));
alvResult.add(alv.get(3 * index + 2));
}
}
} catch (Exception e) {
Log.d("load error", "load error");
e.printStackTrace();
}
//生成頂點陣列
int size = alvResult.size();
float[] vXYZ = new float[size];
for (int i = 0; i < size; i++) {
vXYZ[i] = alvResult.get(i);
}
return vXYZ;
}
複製程式碼
3.第三關卡:繪製:ObjShape.java
3.1:繪製無果
激動人心的時刻到了...點集在手天下我有
然後果然不出所料...沒有出現,我就想不會這麼簡單吧
/**
* 緩衝資料
*/
private void bufferData() {
float[] vertexs = GLUtil.loadPosInObj("obj.obj", mContext);
mVertexCount = vertexs.length / COORDS_PER_VERTEX;
vertexBuffer = GLUtil.getFloatBuffer(vertexs);
}
複製程式碼
3.2:全體縮放
碰到問題怎麼辦? 廢話,debug 啊。然後秒發現座標是200多,暈,怪不得
聰明的你肯定能想到,縮小唄,總算出來了,but違和感十足,座標系都沒了。怎麼辦?
MatrixStack.save();
MatrixStack.rotate(currDeg, 0, 1, 0);
MatrixStack.scale(0.01f,0.01f,0.01f);
mWorldShape.draw(MatrixStack.peek());
MatrixStack.restore();
複製程式碼
3.3:截胡
我在ObjShape裡用個縮放矩陣,截胡不就行了嗎?
然後再移動一下放在中間
---->[WorldRenderer]----------
MatrixStack.save();
MatrixStack.rotate(currDeg, 0, 1, 0);
// MatrixStack.scale(0.01f,0.01f,0.01f);
mWorldShape.draw(MatrixStack.peek());
MatrixStack.restore();
---->[ObjShape]----------
private static float[] mMVPMatrix = new float[16];//最終矩陣
---->[ObjShape#draw]----------
Matrix.scaleM(mMVPMatrix, 0, mvpMatrix, 0, 0.02f, 0.02f, 0.02f);
Matrix.translateM(mMVPMatrix,0,-230,-50,30);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
複製程式碼
本集結束,下一集:九層之臺 敬請期待
後記:捷文規範
1.本文成長記錄及勘誤表
專案原始碼 | 日期 | 備註 |
---|---|---|
V0.1-github | 2018-1-16 | Android多媒體之GLES2戰記第五集--宇宙之光 |
2.更多關於我
筆名 | 微信 | 愛好 | |
---|---|---|---|
張風捷特烈 | 1981462002 | zdl1994328 | 語言 |
我的github | 我的簡書 | 我的掘金 | 個人網站 |
3.宣告
1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大程式設計愛好者共同交流
3----個人能力有限,如有不正之處歡迎大家批評指證,必定虛心改正
4----看到這裡,我在此感謝你的喜歡與支援