内容简介:上集说到:用矩阵的变换来操作顶点,使图形产生相应的变化(移动,选择,缩放)这一集将点亮世界之光,让你对OpenGL的世界有更深的了解普通副本五:
上集说到:用矩阵的变换来操作顶点,使图形产生相应的变化(移动,选择,缩放)
这一集将点亮世界之光,让你对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----看到这里,我在此感谢你的喜欢与支持
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 如果平行宇宙也有编程语言
- 软件产品创新与宇宙奇点大爆炸
- 《蜘蛛侠:平行宇宙》的视觉解析与滤镜实现
- “宇宙最强” IDE,Visual Studio 2019 正式发布
- 漫威宇宙其实也就讲了一件事
- Metasploit Framework 5.0 发布,“宇宙最强”渗透测试框架
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
从规范出发的程序设计
[美] Carroll Morgan / 裘宗燕 / 机械工业出版社 / 2002-8 / 45.00元
本书详细论述了有关规范程序设计的内容,包括:程序和精化、谓词演算、选择、迭代、构造类型、模块和封装等,最后几章还包含了大量的实例研究和一些更高级的程序设计技术。本书提倡一种严格的程序开发方法,分析问题要用严格方式写出程序的规范,而后通过一系列具有严格理论基础的推导,最终得到可以运行的程序。 本书是被世界上许多重要大学采用的教材,适于计算机及相关专业的本科生和研究生使用。一起来看看 《从规范出发的程序设计》 这本书的介绍吧!