Android多媒体之GLES2战记第五集--宇宙之光

2023-10-24 01:30

本文主要是介绍Android多媒体之GLES2战记第五集--宇宙之光,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

你以为我的封面图只是吸引眼球?

上集说到:用矩阵的变换来操作顶点,使图形产生相应的变化(移动,选择,缩放)
这一集将点亮世界之光,让你对OpenGL的世界有更深的了解


普通副本五:黑龙之珠

本副本参照《Android 3D游戏开发技术宝典 OpenGL ES 2.0》
但是分析的要详细一些,书中绘制的方法只是一笔带过,感觉球面还是需要挖挖的
而且书中源码绘制部分写的也挺乱的,该抽的我抽了一下,看着好看些

9414344-63f0c8f056b69990.gif
球面的拼接.gif

1.第一关卡:球面的顶点计算

也就是经纬取分割点,再将这些点拼成三角形形成曲面效果
下面应该很形象的说明了渐变的过程

9414344-a12229fafbb915ef.png
增加切割点数.png
9414344-3f6c123f4d1462c8.jpg
经纬度
/*** 初始化顶点坐标数据的方法** @param r          半径* @param splitCount 赤道分割点数*/
public void initVertex(float r, int splitCount) {// 顶点坐标数据的初始化================begin============================ArrayList<Float> vertixs = new ArrayList<>();// 存放顶点坐标的ArrayListfinal 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坐标加入存放顶点坐标的ArrayListvertixs.add(x1);vertixs.add(y1);vertixs.add(z1);//p1vertixs.add(x3);vertixs.add(y3);vertixs.add(z3);//p3vertixs.add(x0);vertixs.add(y0);vertixs.add(z0);//p0vertixs.add(x1);vertixs.add(y1);vertixs.add(z1);//p1vertixs.add(x2);vertixs.add(y2);vertixs.add(z2);//p2vertixs.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

就是一个尺寸的伸缩量而已,定义成常量,方便放大与缩小,没别的

9414344-a1aedfc376bcb177.png
UNIT_SIZE.png

普通副本六宇宙之光

OpenGL ES 中只有三种光:

环境光:光照的作用全体
散射光:单点光源
镜面光:镜面反射

1.第一关卡:环境光

就像太阳光,我们身处的环境,环境中,光照的作用结果一致
修改起来也比较方便,环境光说白了就是对片元颜色的运算而已

0.350.751.00
9414344-e60d4e4f7f7c2b23.gif
9414344-19dccf135e010d1b.gif
9414344-9d5a5d24899e20c0.gif

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,-11,1,-1
9414344-3b0575d2ff94ce27.gif
9414344-5b41be1e92c27f87.gif

看下图的点光源在(1,1,-1) 你应该知道灯在哪了吧,注意看轴色

9414344-e8e45f4c21aff488.png
散射光.png

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);//对法向量规格化//计算从表面点到光源位置的向量vpvec3 vp= normalize(lightLocation-(uMMatrix*vec4(aPosition,1)).xyz);vp=normalize(vp);//单位化vpfloat 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着色器
平面光就够喝一壶的了,升级到三维...还是先用着吧,原理等百无聊赖的时候再分析吧

9414344-0b5dc79b22a1dfae.png
镜面反射.png

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);//计算从表面点到光源位置的向量vpvec3 vp= normalize(lightLocation-(uMMatrix*vec4(aPosition,1)).xyz);vp=normalize(vp);//格式化vpvec3 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.第四关卡:三光同时作用

就是综合一下而已...,跟书中小不同,这里我把粗糙度和环境光提出来了
基本上代码里没有什么变化,终点在着色器里

9414344-e86552cba1f82f4f.gif
三光.gif
9414344-c24baaf9a62aa12b.png
三光.png
---->[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);//计算从表面点到光源位置的向量vpvec3 vp= normalize(lightLocation-(uMMatrix*vec4(aPosition,1)).xyz);vp=normalize(vp);//格式化vpvec3 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中已经简单知道了纹理的贴图
这个副本将来深入了解一下贴图

9414344-999fea5ecb400929.gif
贴图展示.gif

1.第一关卡:纹理坐标系

纹理坐标系(右侧)是一个二维坐标,方向和Android中的屏幕坐标系一致
书上说贴图的宽高像素数必须是2的n次方,但是我试了不是也可以。为免争议,这里用的是2的n次方

9414344-58318ac63de2c3a0.png
点位坐标与纹理坐标.png
9414344-28d4a29657bb53c4.png
贴图1.png
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.第二关卡:矩形

先用三点矩形来画,比较形象一些,就是两个三角形拼合

9414344-b4d354348c92393d.png
矩形.png
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.第三关卡:纹理的裁剪与拉伸
9414344-6d1b5a86100015db.png
剪裁.png
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) {//生成纹理IDint[] textures = new int[1];//(产生的纹理id的数量,纹理id的数组,偏移量)GLES20.glGenTextures(1, textures, 0);int textureId = textures[0];//绑定纹理idGLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);//采样方式MINGLES20.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

9414344-6aa8d71615aee288.png
重复
//设置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) {//生成纹理IDint[] textures = new int[1];//(产生的纹理id的数量,纹理id的数组,偏移量)GLES20.glGenTextures(1, textures, 0);int textureId = textures[0];//绑定纹理idGLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);//采样方式MINGLES20.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;
}
9414344-3860ee44a0ce0a72.png
封装重复方式.png

当然这也仅是纹理的简单认识,跟高级的龙之盛装副本,敬请期待


普通副本八:黑龙之型 LEVEL1

你以为我的封面图只是吸引眼球?

9414344-548472f42d83c845.gif
效果.gif

1.第一关卡:3DMAX与.obj文件

3DMAX大学的时候用过,知道OpenGL ES 可以加载3DMAX的模型,激动之心无法言表
模型自己去网上下,3DMAX装软件我也不废话了,安装教程一大堆

9414344-5986a5cd784b1638.png
导出obj.png

可见都是点的数据,现在要开始解析数据了,Are you ready?

9414344-0381d056d800aaac.png
内容.png

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违和感十足,坐标系都没了。怎么办?

9414344-cacbbb7d9c5a3df5.png
缩小100倍.png
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里用个缩放矩阵,截胡不就行了吗?

9414344-be55c435f00278b9.png
截胡缩小.png

然后再移动一下放在中间

---->[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);
9414344-8410ebbb59feac69.png
移动.png
本集结束,下一集:九层之台 敬请期待

后记:捷文规范
1.本文成长记录及勘误表
项目源码日期备注
V0.1-github2018-1-16Android多媒体之GLES2战记第五集--宇宙之光
2.更多关于我
笔名QQ微信爱好
张风捷特烈1981462002zdl1994328语言
我的github我的简书我的掘金个人网站
3.声明

1----本文由张风捷特烈原创,转载请注明
2----欢迎广大编程爱好者共同交流
3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
4----看到这里,我在此感谢你的喜欢与支持


9414344-8a0c95a090041a0d.png
icon_wx_200.png

这篇关于Android多媒体之GLES2战记第五集--宇宙之光的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/271783

相关文章

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Android 10.0 mtk平板camera2横屏预览旋转90度横屏拍照图片旋转90度功能实现

1.前言 在10.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的 时候,默认预览图像也是需要横屏显示的,在上一篇已经实现了横屏预览功能,然后发现横屏预览后,拍照保存的图片 依然是竖屏的,所以说同样需要将图片也保存为横屏图标了,所以就需要看下mtk的camera2的相关横屏保存图片功能, 如何实现实现横屏保存图片功能 如图所示: 2.mtk

android应用中res目录说明

Android应用的res目录是一个特殊的项目,该项目里存放了Android应用所用的全部资源,包括图片、字符串、颜色、尺寸、样式等,类似于web开发中的public目录,js、css、image、style。。。。 Android按照约定,将不同的资源放在不同的文件夹中,这样可以方便的让AAPT(即Android Asset Packaging Tool , 在SDK的build-tools目

Android fill_parent、match_parent、wrap_content三者的作用及区别

这三个属性都是用来适应视图的水平或者垂直大小,以视图的内容或尺寸为基础的布局,比精确的指定视图的范围更加方便。 1、fill_parent 设置一个视图的布局为fill_parent将强制性的使视图扩展至它父元素的大小 2、match_parent 和fill_parent一样,从字面上的意思match_parent更贴切一些,于是从2.2开始,两个属性都可以使用,但2.3版本以后的建议使

Android Environment 获取的路径问题

1. 以获取 /System 路径为例 /*** Return root of the "system" partition holding the core Android OS.* Always present and mounted read-only.*/public static @NonNull File getRootDirectory() {return DIR_ANDR

Android逆向(反调,脱壳,过ssl证书脚本)

文章目录 总结 基础Android基础工具 定位关键代码页面activity定位数据包参数定位堆栈追踪 编写反调脱壳好用的脚本过ssl证书校验抓包反调的脚本打印堆栈bilibili反调的脚本 总结 暑假做了两个月的Android逆向,记录一下自己学到的东西。对于app渗透有了一些思路。 这两个月主要做的是代码分析,对于分析完后的持久化等没有学习。主要是如何反编译源码,如何找到

android系统源码12 修改默认桌面壁纸--SRO方式

1、aosp12修改默认桌面壁纸 代码路径 :frameworks\base\core\res\res\drawable-nodpi 替换成自己的图片即可,不过需要覆盖所有目录下的图片。 由于是静态修改,则需要make一下,重新编译。 2、方法二Overlay方式 由于上述方法有很大缺点,修改多了之后容易遗忘自己修改哪些文件,为此我们采用另外一种方法,使用Overlay方式。