OpenGL ES 20中,使用2D纹理伪造3D效果的Demo
Demo纵览
简介和实现效果
这个例子的实现是,把2D纹理贴到一个矩形上,然后伪造出3D效果。
里面的树,来源是一张png图。贴在矩形上后,这个矩形要保持一直面对着摄像头。
这个例子来源是《Android3D游戏开发技术宝典》中的第11章的第一节,但是我修改了下包结构。本意能通过这个例子进行更多的拓展。
实现流程:
结构纵览
类图
结构说明
Activity和MySurfaceView的关系这里不再说明。如果需要请看上一篇。
这里的代码关系大致是这样:
“MySurfaceView->TreeGroup->Image_2D->TextureRect”
“MySurfaceView->Desert”
TreeGroup是个“树组”,里面定义了不同坐标2D图像作为纹理的“树”。
Image_2D是用x坐标和z坐标形容位置的2D图像类。它主要关心图像的位置和纹理。而图像的长宽是由通过构造函数传入的TextureRect类对象决定。所以TextureRect主要关心图像的长宽。
Desert是“地面”。这个类的说明,请看上一篇关于Triangle的说明。
代码说明
MySurfaceView
public class MySurfaceView extends GLSurfaceView
{
public static final float UNIT_SIZE=1f;
static float direction=0;//视线方向
public static float cx=0;//摄像机x坐标
public static float cz=15;//摄像机z坐标
static final float DEGREE_SPAN=(float)(3.0/180.0f*Math.PI);//摄像机每次转动的角度
//线程循环的标志位
boolean flag=true;
float x;
float y;
float Offset=15;
SceneRenderer mRender;
float preX;
float preY;
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event)
{
x = event.getX();
y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
flag = true;
new Thread() {
@Override
public void run() {
while (flag) {
if (x > 0 && x < WIDTH / 2 && y > 0 && y < HEIGHT / 2) {// 向前
Offset = Offset - 0.5f;
} else if (x > WIDTH / 2 && x < WIDTH && y > 0 && y < HEIGHT / 2) {// 向后
Offset = Offset + 0.5f;
} else if (x > 0 && x < WIDTH / 2 && y > HEIGHT / 2 && y < HEIGHT) {
direction = direction + DEGREE_SPAN;
} else if (x > WIDTH / 2 && x < WIDTH && y > HEIGHT / 2 && y < HEIGHT) {
direction = direction - DEGREE_SPAN;
}
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}.start();
break;
case MotionEvent.ACTION_UP:
flag = false;
break;
}
//设置新的观察目标点XZ坐标
cx=(float)(Math.sin(direction)*Offset);//观察目标点x坐标
cz=(float)(Math.cos(direction)*Offset);//观察目标点z坐标
//计算所有树的朝向
mRender.group.calculateBillboardDirection();
//给树按照离视点的距离排序
Collections.sort(mRender.group.trees);
//设置新的摄像机位置
MatrixState.setCamera(cx,0,cz,0,0,0,0,1,0);
return true;
}
public MySurfaceView(Context context)
{
super(context);
this.setEGLContextClientVersion(2); //设置使用OPENGL ES2.0
mRender = new SceneRenderer(); //创建场景渲染器
setRenderer(mRender); //设置渲染器
setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);//设置渲染模式为主动渲染
}
private class SceneRenderer implements GLSurfaceView.Renderer
{
int treeTextureId;
TreeGroup group;
Desert desert;
int desertId;
@Override
public void onDrawFrame(GL10 gl)
{
//清除深度缓冲与颜色缓冲
GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
MatrixState.pushMatrix();
MatrixState.translate(0, -2, 0);
desert.drawSelf(desertId);
MatrixState.popMatrix();
//开启混合
GLES20.glEnable(GLES20.GL_BLEND);
//设置混合因子
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
MatrixState.pushMatrix();
MatrixState.translate(0, -2, 0);
group.drawSelf();
MatrixState.popMatrix();
//关闭混合
GLES20.glDisable(GLES20.GL_BLEND);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height)
{
//设置视窗大小及位置
GLES20.glViewport(0, 0, width, height);
//计算GLSurfaceView的宽高比
float ratio = (float) width / height;
//调用此方法计算