技术标签: 3D图形 android 纹理贴图 opengl es android图形
最近看了《疯狂android讲义》的图形相关的内容,结合自己的理解,整理了一下。
下图是做出来的3D纹理贴图效果,手指在屏幕滑动时,图片可以随之转动。
要实现一个纹理贴图,很简单,大致需要五步:
1、gl.glEnable(GL10.GL_TEXTURE_2D) 启用2D纹理功能;
2、gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY) 启用纹理坐标数组;
3、gl.glBindTexture(GL10.GL_TEXTURE_2D,texture) 绑定纹理;
4、GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0) 根据位图生成纹理;
5、gl.glTexCoordPointer(2, GL10.GL_FLOAT,0, bufferUtil(cubeTextures)) 设置纹理坐标;
下面是一个完整的例子:
public class MainActivity extends AppCompatActivity implements GestureDetector.OnGestureListener{
private static final String TAG = "MainActivity";
GestureDetector gestureDetector;
// 定义旋转角度
private float anglex = 0f;
private float angley = 0f;
static final float ROTATE_FACTOR = 60;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GLSurfaceView glSurfaceView = new GLSurfaceView(this);
MyRenderOne myRender = new MyRenderOne(this);
glSurfaceView.setRenderer(myRender);
setContentView(glSurfaceView);
gestureDetector = new GestureDetector(this, this);
}
@Override
public boolean onDown(MotionEvent e) {
return false;
}
@Override
public void onShowPress(MotionEvent e) { }
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) { }
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
velocityX = velocityX > 4000 ? 4000 : velocityX;
velocityX = velocityX < -4000 ? -4000 : velocityX;
velocityY = velocityY > 4000 ? 4000 : velocityY;
velocityY = velocityY < -4000 ? -4000 : velocityY;
// 根据横向上的速度计算沿Y轴旋转的角度
angley += velocityX * ROTATE_FACTOR / 4000;
// 根据纵向上的速度计算沿X轴旋转的角度
anglex += velocityY * ROTATE_FACTOR / 4000;
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 将该Activity上的触碰事件交给GestureDetector处理
return gestureDetector.onTouchEvent(event);
}
public class MyRenderOne implements GLSurfaceView.Renderer {
// 立方体的顶点座标(一共是36个顶点,组成12个三角形)
private float[] cubeVertices = { -0.4f, -0.4f, -0.4f, -0.4f, 0.4f,
-0.4f, 0.4f, 0.4f, -0.4f, 0.4f, 0.4f, -0.4f, 0.4f, -0.4f, -0.4f,
-0.4f, -0.4f, -0.4f, -0.4f, -0.4f, 0.4f, 0.4f, -0.4f, 0.4f, 0.4f,
0.4f, 0.4f, 0.4f, 0.4f, 0.4f, -0.4f, 0.4f, 0.4f, -0.4f, -0.4f,
0.4f, -0.4f, -0.4f, -0.4f, 0.4f, -0.4f, -0.4f, 0.4f, -0.4f, 0.4f,
0.4f, -0.4f, 0.4f, -0.4f, -0.4f, 0.4f, -0.4f, -0.4f, -0.4f, 0.4f,
-0.4f, -0.4f, 0.4f, 0.4f, -0.4f, 0.4f, 0.4f, 0.4f, 0.4f, 0.4f,
0.4f, 0.4f, -0.4f, 0.4f, 0.4f, -0.4f, -0.4f, 0.4f, 0.4f, -0.4f,
-0.4f, 0.4f, -0.4f, -0.4f, 0.4f, 0.4f, -0.4f, 0.4f, 0.4f, 0.4f,
0.4f, 0.4f, 0.4f, 0.4f, -0.4f, -0.4f, 0.4f, -0.4f, -0.4f, -0.4f,
-0.4f, -0.4f, -0.4f, 0.4f, -0.4f, -0.4f, 0.4f, -0.4f, 0.4f, 0.4f,
-0.4f, 0.4f, -0.4f, };
// 定义立方体所需要的6个面(一共是12个三角形所需的顶点)
private byte[] cubeFacets = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, };
// 定义纹理贴图的72个座标数据
private float[] cubeTextures = { 1.0000f, 1.0000f, 1.0000f, 0.0000f,
0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, 1.0000f,
1.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f,
1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, 0.0000f,
1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.0000f,
0.0000f, 0.0000f, 0.0000f, 1.0000f, 0.0000f, 1.0000f, 1.0000f,
1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f,
0.0000f, 1.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f,
0.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f,
0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f,
0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f };
private Context context;
private int texture;
public MyRenderOne(Context con)
{
this.context = con;
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glDisable(GL10.GL_DITHER); //关闭抗抖动
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); //设置系统对透视进行修正
gl.glClearColor(0, 0, 0, 0); //黑色背景
gl.glShadeModel(GL10.GL_SMOOTH); //设置平滑模式
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glDepthFunc(GL10.GL_LEQUAL); //设置深度测试(opengl es会跟踪z轴的深度,以避免后面的图像挡住前面的图像)的类型
gl.glEnable(GL10.GL_TEXTURE_2D); //开启2D纹理贴图
loadTexture(gl);
}
private void loadTexture(GL10 gl) {
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.aa);
int textures[] = new int[1];
gl.glGenTextures(1,textures,0); //创建纹理
texture = textures[0];
gl.glBindTexture(GL10.GL_TEXTURE_2D, texture); //将纹理绑定到目标
// 设置纹理被缩小(距离视点很远时被缩小)时候的滤波方式
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
// 设置纹理被放大(距离视点很近时被放大)时候的滤波方式
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
// 设置在横向、纵向上都是平铺纹理
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
gl.glTexParameterf(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_WRAP_T,GL10.GL_REPEAT);
// 加载位图生成纹理
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
} finally {
if (bitmap != null){
bitmap.recycle();
}
}
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
Log.i(TAG, "onSurfaceChanged enter...");
gl.glViewport(0, 0, width, height); //设置视窗的大小及位置
gl.glMatrixMode(GL10.GL_PROJECTION); //设置为投影矩阵
gl.glLoadIdentity(); // 初始化单位矩阵
float ratio = (float) width / height;
gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); // 设置透视视窗的空间大小。
}
@Override
public void onDrawFrame(GL10 gl) {
// 清除屏幕缓存和深度缓存
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
//开启顶点设置和纹理设置功能
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glMatrixMode(GL10.GL_MODELVIEW); //设置为模型矩阵
Log.i(TAG, "onDrawFrame enter...");
gl.glLoadIdentity();
gl.glTranslatef(0f, 0.0f, -2.0f); // 把绘图中心移入屏幕2个单位
gl.glRotatef(angley, 0, 1, 0); // 旋转图形
gl.glRotatef(anglex, 1, 0, 0);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, bufferUtil(cubeVertices)); //设置立方体的顶点
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, bufferUtil(cubeTextures)); //设置纹理顶点
//gl.glBindTexture(GL10.GL_TEXTURE_2D, texture); //将纹理绑定到目标(执行纹理贴图)
//绘制立方体
gl.glDrawElements(GL10.GL_TRIANGLES, cubeFacets.length, GL10.GL_UNSIGNED_BYTE, bufferUtil2(cubeFacets));
gl.glFinish(); //绘制完成
// 禁用顶点和纹理设置
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
/*
* OpenGL 是一个非常底层的画图接口,它所使用的缓冲区存储结构是和我们的 java 程序中不相同的。
* Java 是大端字节序(BigEdian),而 OpenGL 所需要的数据是小端字节序(LittleEdian)。
* 所以,我们在将 Java 的缓冲区转化为 OpenGL 可用的缓冲区时需要作一些工作。建立buff的方法如下
* */
public Buffer bufferUtil(float []arr){
FloatBuffer mBuffer ;
//先初始化buffer,数组的长度*4,因为一个float占4个字节
ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
//数组排列用nativeOrder
qbb.order(ByteOrder.nativeOrder());
mBuffer = qbb.asFloatBuffer();
mBuffer.put(arr);
mBuffer.position(0);
return mBuffer;
}
public Buffer bufferUtil1(int []arr){
IntBuffer mBuffer ;
//先初始化buffer,数组的长度*4,因为一个int占4个字节
ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
//数组排列用nativeOrder
qbb.order(ByteOrder.nativeOrder());
mBuffer = qbb.asIntBuffer();
mBuffer.put(arr);
mBuffer.position(0);
return mBuffer;
}
public Buffer bufferUtil2(byte []arr){
//先初始化buffer
ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length);
//数组排列用nativeOrder
qbb.order(ByteOrder.nativeOrder());
qbb.put(arr);
qbb.position(0);
return qbb;
}
}
}
其中有几点需要说明下:
1、启动2D纹理功能用的是gl.glEnable(),而不是gl.glEnableClientState();
2、gl.glDrawElements()的第一个参数指定绘制的模块,可以是:GL10.GL_TRIANGLES(绘制三角形)和GL10.GL_TRIANGLE_STRIP(用多个三角形绘制多边形),第三个参数是坐标值的类型,如果是float则为GL10.GL_FLOAT,如果是int则为GL10.GL_FIXED,如果是byte则为GL10.GL_UNSIGNED_BYTE;
3、gl.glDrawElements()最后一个参数是坐标数组,如果用不好就会报错:java.lang.IllegalArgumentException
文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib
文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang
文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些
文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器
文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距
文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器
文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn
文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios
文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql
文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...
文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120
文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数