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

栏目: Android · 发布时间: 6年前

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

上集说到:用矩阵的变换来操作顶点,使图形产生相应的变化(移动,选择,缩放)

这一集将点亮世界之光,让你对OpenGL的世界有更深的了解

普通副本五: 黑龙之珠

本副本参照 《Android 3D游戏开发技术宝典 OpenGL ES 2.0》

但是分析的要详细一些,书中绘制的方法只是一笔带过,感觉球面还是需要挖挖的

而且书中源码绘制部分写的也挺乱的,该抽的我抽了一下,看着好看些

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

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

也就是经纬取分割点,再将这些点拼成三角形形成曲面效果

下面应该很形象的说明了渐变的过程

Android多媒体之GLES2战记第五集--宇宙之光
Android多媒体之GLES2战记第五集--宇宙之光
/**
 * 初始化顶点坐标数据的方法
 *
 * @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

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

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

普通副本六 宇宙之光

OpenGL ES 中只有三种光:

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

1.第一关卡:环境光

就像太阳光,我们身处的环境,环境中,光照的作用结果一致

修改起来也比较方便,环境光说白了就是对片元颜色的运算而已

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

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
Android多媒体之GLES2战记第五集--宇宙之光
Android多媒体之GLES2战记第五集--宇宙之光

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

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

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着色器

平面光就够喝一壶的了,升级到三维...还是先用着吧,原理等百无聊赖的时候再分析吧

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

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.第四关卡:三光同时作用

就是综合一下而已...,跟书中小不同,这里我把粗糙度和环境光提出来了

基本上代码里没有什么变化,终点在着色器里

Android多媒体之GLES2战记第五集--宇宙之光
Android多媒体之GLES2战记第五集--宇宙之光
---->[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 中已经简单知道了纹理的贴图 这个副本将来深入了解一下贴图

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

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

纹理坐标系(右侧)是一个二维坐标,方向和Android中的屏幕坐标系一致

书上说贴图的宽高像素数必须是2的n次方,但是我试了不是也可以。为免争议,这里用的是2的n次方

Android多媒体之GLES2战记第五集--宇宙之光
Android多媒体之GLES2战记第五集--宇宙之光
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.第二关卡:矩形

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

Android多媒体之GLES2战记第五集--宇宙之光
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.第三关卡:纹理的裁剪与拉伸

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

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

Android多媒体之GLES2战记第五集--宇宙之光
//设置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;
}
复制代码
Android多媒体之GLES2战记第五集--宇宙之光

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

普通副本八: 黑龙之型 LEVEL1

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

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

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

3DMAX大学的时候用过,知道OpenGL ES 可以加载3DMAX的模型,激动之心无法言表

模型自己去网上下,3DMAX装软件我也不废话了,安装教程一大堆

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

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

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

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

Android多媒体之GLES2战记第五集--宇宙之光
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里用个缩放矩阵,截胡不就行了吗?

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

然后再移动一下放在中间

---->[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);

复制代码
Android多媒体之GLES2战记第五集--宇宙之光

后记:捷文规范

1.本文成长记录及勘误表

项目源码 日期 备注
V0.1-github 2018-1-16 Android多媒体之GLES2战记第五集--宇宙之光

2.更多关于我

笔名 QQ 微信 爱好
张风捷特烈 1981462002 zdl1994328 语言
我的github 我的简书 我的掘金 个人网站

3.声明

1----本文由张风捷特烈原创,转载请注明

2----欢迎广大编程爱好者共同交流

3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正

4----看到这里,我在此感谢你的喜欢与支持

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Beginning XML with DOM and Ajax

Beginning XML with DOM and Ajax

Sas Jacobs / Apress / 2006-06-05 / USD 39.99

Don't waste time on 1,000-page tomes full of syntax; this book is all you need to get ahead in XML development. Renowned web developer Sas Jacobs presents an essential guide to XML. Beginning XML with......一起来看看 《Beginning XML with DOM and Ajax》 这本书的介绍吧!

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具