你以为我的封面图只是吸引眼球?
上集说到:用矩阵的变换来操作顶点,使图形产生相应的变化(移动,选择,缩放) 这一集将点亮世界之光,让你对OpenGL的世界有更深的了解
普通副本五:黑龙之珠
本副本参照
《Android 3D游戏开发技术宝典 OpenGL ES 2.0》
但是分析的要详细一些,书中绘制的方法只是一笔带过,感觉球面还是需要挖挖的 而且书中源码绘制部分写的也挺乱的,该抽的我抽了一下,看着好看些
球面的拼接.gif
1.第一关卡:球面的顶点计算
也就是经纬取分割点,再将这些点拼成三角形形成曲面效果 下面应该很形象的说明了渐变的过程
增加切割点数.png
经纬度
代码语言:javascript复制/**
* 初始化顶点坐标数据的方法
*
* @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
代码语言:javascript复制添加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
代码语言:javascript复制uniform mat4 uMVPMatrix;
attribute vec3 aPosition;
varying vec3 vPosition;
void main(){
gl_Position = uMVPMatrix * vec4(aPosition,1);
vPosition = aPosition;
}
2.3:着色器的使用
代码语言:javascript复制//声明句柄
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
就是一个尺寸的伸缩量而已,定义成常量,方便放大与缩小,没别的
UNIT_SIZE.png
普通副本六宇宙之光
代码语言:javascript复制OpenGL ES 中只有三种光:
环境光:光照的作用全体
散射光:单点光源
镜面光:镜面反射
1.第一关卡:环境光
就像太阳光,我们身处的环境,环境中,光照的作用结果一致 修改起来也比较方便,环境光说白了就是对片元颜色的运算而已
0.35 | 0.75 | 1.00 |
---|---|---|
1.1.片元着色器ball.frag
代码语言:javascript复制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
代码语言:javascript复制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.使用:句柄拿到传值而已,也没什么难的
代码语言:javascript复制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)
你应该知道灯在哪了吧,注意看轴色
散射光.png
2.1:顶点着色器:ball.vert
代码语言:javascript复制代码有点复杂,做好心理准备
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
代码语言:javascript复制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:使用
代码语言:javascript复制新添了三个句柄,用法也是写烂了... 这里用一个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着色器
平面光就够喝一壶的了,升级到三维...还是先用着吧,原理等百无聊赖的时候再分析吧
镜面反射.png
3.1:顶点着色器:ball_m.vert
代码语言:javascript复制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
代码语言:javascript复制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.使用
代码语言:javascript复制增加了一个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.第四关卡:三光同时作用
就是综合一下而已...,跟书中小不同,这里我把粗糙度和环境光提出来了 基本上代码里没有什么变化,终点在着色器里
三光.gif
三光.png
代码语言:javascript复制---->[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
中已经简单知道了纹理的贴图 这个副本将来深入了解一下贴图
贴图展示.gif
1.第一关卡:纹理坐标系
纹理坐标系(右侧)是一个二维坐标,方向和Android中的屏幕坐标系一致 书上说贴图的宽高像素数必须是2的n次方,但是我试了不是也可以。为免争议,这里用的是2的n次方
点位坐标与纹理坐标.png
贴图1.png
代码语言:javascript复制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.第二关卡:矩形
先用三点矩形来画,比较形象一些,就是两个三角形拼合
矩形.png
代码语言:javascript复制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.第三关卡:纹理的裁剪与拉伸
剪裁.png
3.1:添加两个系数控制纹理坐标的大小
代码语言:javascript复制---->[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:加载纹理的工具
代码语言:javascript复制其中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
重复
代码语言:javascript复制//设置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:重复模式的枚举
代码语言:javascript复制/**
* 作者:张风捷特烈<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:加载纹理方法的封装
代码语言:javascript复制//---------------纹理加载工具--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;
}
封装重复方式.png
当然这也仅是纹理的简单认识,跟高级的
龙之盛装
副本,敬请期待
普通副本八:黑龙之型 LEVEL1
你以为我的封面图只是吸引眼球?
效果.gif
1.第一关卡:3DMAX与.obj文件
3DMAX大学的时候用过,知道OpenGL ES 可以加载3DMAX的模型,激动之心无法言表 模型自己去网上下,3DMAX装软件我也不废话了,安装教程一大堆
导出obj.png
可见都是点的数据,现在要开始解析数据了,Are you ready?
内容.png
2.第二关卡:加载与解析点:
代码语言:javascript复制
参见《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:绘制无果
代码语言:javascript复制激动人心的时刻到了...点集在手天下我有 然后果然不出所料...没有出现,我就想不会这么简单吧
/**
* 缓冲数据
*/
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违和感十足,坐标系都没了。怎么办?
缩小100倍.png
代码语言:javascript复制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里用个缩放矩阵,截胡不就行了吗?
截胡缩小.png
代码语言:javascript复制然后再移动一下放在中间
---->[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);
移动.png