感觉opengl es还是很有用的,这几天一直在潜心研究。让人最琢磨不透的是Openg es中的定点变换流程,我这里先讲解下。 大家觉得好的,就顶一下,觉得不好的也别拍砖,出来混都挺不容易的哈
首先,定点坐标及顶点法线会通过模型视图矩阵从模型坐标系变换到视点坐标系。光照以及用户自定义裁剪会在视点坐标系中完成。接下来投影矩阵会继续将顶点变换到裁剪空间,在这里由若干定点组成的图元会被视锥进行剪裁。剪裁之后,这些定点会经过透视除法被变换到标准化设备坐标系,图元会被光栅化,即转化成像素的形式。在光栅化的过程中,纹理矩阵会被应用到纹理坐标系中以修正纹理映射。最后,视口变换会根据深度值来决定将光栅化后的片元存储到帧缓存中。这样讲起来挺复杂的 ,下面我来画个清晰的图
大家先看看我的实验截图吧
3 小时前 上传下载附件 (22.93 KB)
4 小时前 上传下载附件 (24.55 KB)
这是本人的htc渴望截图,我用的联通3G。有清晰的3G标志。真机比左边的模拟器快多了
下面列出代码来,有本人的详细注释,有不懂的地方可以跟帖询问。附件下有打包的源码。希望看的童鞋们下载吧
MainActivity
private GLSurfaceView mGLSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* 在使用GLSurfaceView时,需要在相应的activity中重载onResume onPause oncreate函数,在onCreate中进行GLSurfaceView
* 设置好具体实现的GLSurfaceView接口,并将View推入前台,由于GlSurfaceView是独立于系统UI县城之外运行的,因此在系统UT线程
* 挂起或者恢复时,需要显示调用GLSurfaceView中的onPause 与onResume来通知底层Opengl ES模块进行相应处理
*/
mGLSurfaceView=new GLSurfaceView(this);
mGLSurfaceView.setRenderer( new CubeRenderer());
setContentView(mGLSurfaceView);
}
@Override
protected void onResume() {
// Ideally a game should implement onResume() and onPause()
// to take appropriate action when the activity looses focus
super.onResume();
mGLSurfaceView.onResume();
}
@Override
protected void onPause() {
// Ideally a game should implement onResume() and onPause()
// to take appropriate action when the activity looses focus
super.onPause();
mGLSurfaceView.onPause();
}
CubeRenderer.java
/**
* 下面的代码中实现了GLSurfaceView的渲染器接口,
* 在函数onSurfaceCreated中处理窗口创建事件,进行一些全局性,一次性的设置
* 在函数onSurfaceChanaged()中设置窗口改变事件,这里一般进行Opengl视口设置,如果相机参数是固定的 也可以在这里设置投影矩阵,
* 在每一帧是调用函数onDrawFrame() 所有回执的操作都应该在这里实现,一般的opengl程序的绘制流程是首先清屏,包括清楚某些缓存,然后设置投影矩阵与
* 模型视图矩阵,最后进行各个物体的绘制。GLSurfaceView还提供其他一些功能,比如重载按键响应,设置egl参数,设置渲染模式等
* @author Administrator
*
*/
public class CubeRenderer implements GLSurfaceView.Renderer {
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
//一般的opengl程序,首先要做的就是清屏
gl.glMatrixMode(GL10.GL_MODELVIEW);//紧着这设置模型视图矩阵
gl.glLoadIdentity();
//视点变换,将相机位置设置为(0.0.3),同时指向(0.0.0)点,竖直向量为(0.1.0)指向正上方
GLU.gluLookAt(gl, 0, 0, 3, 0, 0, 0, 0, 1,0);
//设置模型位置旋转及缩放信息
gl.glTranslatef(0, 0.0f, -1.0f);//将模型位置设置为(0.0.-1)
float angle=30.0f;
gl.glRotatef(angle,0, 1, 0);//绕模型自身y轴旋转30度。
gl.glRotatef(angle,1, 0, 0);//绕模型自身x轴旋转30度。
gl.glScalef(1.2f, 1.2f, 1.2f);//设置三方向的缩放系数
gl.glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
mCube.draw(gl,gl.GL_LINES);//渲染立方体
// mCube.draw(gl,gl.GL_TRIANGLES);//渲染立方体
}
//当屏幕 改变时候,比如手机横着拿,变成竖着拿,先onSurfaceCreated 在两次onSurfaceChanged 。
public void onSurfaceChanged(GL10 gl, int width, int height) {
//设置视口
gl.glViewport(0, 0, width, height);
float ratio = (float) width / height;
gl.glMatrixMode(GL10.GL_PROJECTION);//设置投影矩阵为透视投影
gl.glLoadIdentity();
gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);//正交投影
}
private Cube mCube;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,
GL10.GL_FASTEST);
gl.glClearColor(1,1,1,1);
gl.glDisable(GL10.GL_CULL_FACE);
gl.glShadeModel(GL10.GL_SMOOTH);
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glLineWidth(4.0f);
mCube=new Cube();
}}
Cube.java
这个是封装好的立方体类,支持以线框模式或者实体模式进行绘制。
package com.sdu.advance;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
public class Cube {
/**
* 顶点位置Buffer对象
*/
private FloatBuffer mVertexBuffer;
/**
* 顶点颜色Buffer对象
*/
private FloatBuffer mColorBuffer;
/**
* 实体模式下顶点索引对象
*/
private ByteBuffer mIndexBuffer;
/**
* 线框模式下顶点索引对象
*/
private ByteBuffer mLineIndexBuffer;
public Cube()
{
float one = 1.0f;
/**
* 顶点位置坐标数组
* 立方体有8个顶点
*/
float vertices[] = {
-one, -one, -one,//Vertex 0
one, -one, -one,//1
one, one, -one,//2
-one, one, -one,//3
-one, -one, one,//4
one, -one, one,//5
one, one, one,//6
-one, one, one,//7
};
/**
* 顶点颜色数组
* 分别给8个顶点指定不同的颜色, RGBA 模式
*/
float colors[] = {
0, 0, 0, one,//Vertex 0
one, 0, 0, one,//1
one, one, 0, one,//2
0, one, 0, one,//3
0, 0, one, one,//4
one, 0, one, one,//5
one, one, one, one,//6
0, one, one, one,//7
};
/**
* 实体模式下,立方体有6个面,每个面由两个三角形组成
* 这里分别指定每个面的索引。
*/
byte indices[] = {
0, 4, 5, 0, 5, 1,//Face 0
1, 5, 6, 1, 6, 2,// 1
2, 6, 7, 2, 7, 3,//2
3, 7, 4, 3, 4, 0,//3
4, 7, 6, 4, 6, 5,//4
3, 0, 1, 3, 1, 2//5
};
/**
* 线框模式下,立方体有8个顶点,12条边缘,
* 这里指定画线模式所用到的12条线段
*/
byte lineIndices[] = {
0, 1,//Line 0
0, 3,//1
0, 4,//2
1, 2,//3
1, 5,//4
2, 3,//5
2, 6,//6
3, 7,//7
4, 5,//8
4, 7,//9
5, 6,//10
6, 7//11
};
//根据数组来生成用于直接渲染的java.nio.Buffer
//顶点位置Buffer
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
vbb.order(ByteOrder.nativeOrder());
mVertexBuffer = vbb.asFloatBuffer();
mVertexBuffer.put(vertices);
mVertexBuffer.position(0);
//顶点颜色Buffer
ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
cbb.order(ByteOrder.nativeOrder());
mColorBuffer = cbb.asFloatBuffer();
mColorBuffer.put(colors);
mColorBuffer.position(0);
//实体模式下顶点索引Buffer
mIndexBuffer = ByteBuffer.allocateDirect(indices.length);
mIndexBuffer.put(indices);
mIndexBuffer.position(0);
//线框模式下顶点索引Buffer
mLineIndexBuffer = ByteBuffer.allocateDirect(lineIndices.length);
mLineIndexBuffer.put(lineIndices);
mLineIndexBuffer.position(0);
}
/**
* 根据传入的模式来分别渲染实体模式立方体以及线框模式立方体
* @param gl - OpenGL ES 渲染对象
* @param mode - 渲染模式,GL10.GL_TRIANGLES 表示实体模式,GL10.GL_LINES 表示线框模式
*/
public void draw(GL10 gl, int mode) {
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
if(mode == GL10.GL_TRIANGLES) {
//如果是实体模式,则启用颜色,给每一个顶点指定一个颜色
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
gl.glColorPointer(4, GL10.GL_FLOAT, 0, mColorBuffer);
gl.glDrawElements(GL10.GL_TRIANGLES, 36, GL10.GL_UNSIGNED_BYTE, mIndexBuffer);
}
else if(mode == GL10.GL_LINES) {
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
gl.glDrawElements(GL10.GL_LINES, 24, GL10.GL_UNSIGNED_BYTE, mLineIndexBuffer);
}
}
}
首先,定点坐标及顶点法线会通过模型视图矩阵从模型坐标系变换到视点坐标系。光照以及用户自定义裁剪会在视点坐标系中完成。接下来投影矩阵会继续将顶点变换到裁剪空间,在这里由若干定点组成的图元会被视锥进行剪裁。剪裁之后,这些定点会经过透视除法被变换到标准化设备坐标系,图元会被光栅化,即转化成像素的形式。在光栅化的过程中,纹理矩阵会被应用到纹理坐标系中以修正纹理映射。最后,视口变换会根据深度值来决定将光栅化后的片元存储到帧缓存中。这样讲起来挺复杂的 ,下面我来画个清晰的图
大家先看看我的实验截图吧
3 小时前 上传下载附件 (22.93 KB)
4 小时前 上传下载附件 (24.55 KB)
这是本人的htc渴望截图,我用的联通3G。有清晰的3G标志。真机比左边的模拟器快多了
下面列出代码来,有本人的详细注释,有不懂的地方可以跟帖询问。附件下有打包的源码。希望看的童鞋们下载吧
MainActivity
private GLSurfaceView mGLSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* 在使用GLSurfaceView时,需要在相应的activity中重载onResume onPause oncreate函数,在onCreate中进行GLSurfaceView
* 设置好具体实现的GLSurfaceView接口,并将View推入前台,由于GlSurfaceView是独立于系统UI县城之外运行的,因此在系统UT线程
* 挂起或者恢复时,需要显示调用GLSurfaceView中的onPause 与onResume来通知底层Opengl ES模块进行相应处理
*/
mGLSurfaceView=new GLSurfaceView(this);
mGLSurfaceView.setRenderer( new CubeRenderer());
setContentView(mGLSurfaceView);
}
@Override
protected void onResume() {
// Ideally a game should implement onResume() and onPause()
// to take appropriate action when the activity looses focus
super.onResume();
mGLSurfaceView.onResume();
}
@Override
protected void onPause() {
// Ideally a game should implement onResume() and onPause()
// to take appropriate action when the activity looses focus
super.onPause();
mGLSurfaceView.onPause();
}
CubeRenderer.java
/**
* 下面的代码中实现了GLSurfaceView的渲染器接口,
* 在函数onSurfaceCreated中处理窗口创建事件,进行一些全局性,一次性的设置
* 在函数onSurfaceChanaged()中设置窗口改变事件,这里一般进行Opengl视口设置,如果相机参数是固定的 也可以在这里设置投影矩阵,
* 在每一帧是调用函数onDrawFrame() 所有回执的操作都应该在这里实现,一般的opengl程序的绘制流程是首先清屏,包括清楚某些缓存,然后设置投影矩阵与
* 模型视图矩阵,最后进行各个物体的绘制。GLSurfaceView还提供其他一些功能,比如重载按键响应,设置egl参数,设置渲染模式等
* @author Administrator
*
*/
public class CubeRenderer implements GLSurfaceView.Renderer {
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
//一般的opengl程序,首先要做的就是清屏
gl.glMatrixMode(GL10.GL_MODELVIEW);//紧着这设置模型视图矩阵
gl.glLoadIdentity();
//视点变换,将相机位置设置为(0.0.3),同时指向(0.0.0)点,竖直向量为(0.1.0)指向正上方
GLU.gluLookAt(gl, 0, 0, 3, 0, 0, 0, 0, 1,0);
//设置模型位置旋转及缩放信息
gl.glTranslatef(0, 0.0f, -1.0f);//将模型位置设置为(0.0.-1)
float angle=30.0f;
gl.glRotatef(angle,0, 1, 0);//绕模型自身y轴旋转30度。
gl.glRotatef(angle,1, 0, 0);//绕模型自身x轴旋转30度。
gl.glScalef(1.2f, 1.2f, 1.2f);//设置三方向的缩放系数
gl.glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
mCube.draw(gl,gl.GL_LINES);//渲染立方体
// mCube.draw(gl,gl.GL_TRIANGLES);//渲染立方体
}
//当屏幕 改变时候,比如手机横着拿,变成竖着拿,先onSurfaceCreated 在两次onSurfaceChanged 。
public void onSurfaceChanged(GL10 gl, int width, int height) {
//设置视口
gl.glViewport(0, 0, width, height);
float ratio = (float) width / height;
gl.glMatrixMode(GL10.GL_PROJECTION);//设置投影矩阵为透视投影
gl.glLoadIdentity();
gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);//正交投影
}
private Cube mCube;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,
GL10.GL_FASTEST);
gl.glClearColor(1,1,1,1);
gl.glDisable(GL10.GL_CULL_FACE);
gl.glShadeModel(GL10.GL_SMOOTH);
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glLineWidth(4.0f);
mCube=new Cube();
}}
Cube.java
这个是封装好的立方体类,支持以线框模式或者实体模式进行绘制。
package com.sdu.advance;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
public class Cube {
/**
* 顶点位置Buffer对象
*/
private FloatBuffer mVertexBuffer;
/**
* 顶点颜色Buffer对象
*/
private FloatBuffer mColorBuffer;
/**
* 实体模式下顶点索引对象
*/
private ByteBuffer mIndexBuffer;
/**
* 线框模式下顶点索引对象
*/
private ByteBuffer mLineIndexBuffer;
public Cube()
{
float one = 1.0f;
/**
* 顶点位置坐标数组
* 立方体有8个顶点
*/
float vertices[] = {
-one, -one, -one,//Vertex 0
one, -one, -one,//1
one, one, -one,//2
-one, one, -one,//3
-one, -one, one,//4
one, -one, one,//5
one, one, one,//6
-one, one, one,//7
};
/**
* 顶点颜色数组
* 分别给8个顶点指定不同的颜色, RGBA 模式
*/
float colors[] = {
0, 0, 0, one,//Vertex 0
one, 0, 0, one,//1
one, one, 0, one,//2
0, one, 0, one,//3
0, 0, one, one,//4
one, 0, one, one,//5
one, one, one, one,//6
0, one, one, one,//7
};
/**
* 实体模式下,立方体有6个面,每个面由两个三角形组成
* 这里分别指定每个面的索引。
*/
byte indices[] = {
0, 4, 5, 0, 5, 1,//Face 0
1, 5, 6, 1, 6, 2,// 1
2, 6, 7, 2, 7, 3,//2
3, 7, 4, 3, 4, 0,//3
4, 7, 6, 4, 6, 5,//4
3, 0, 1, 3, 1, 2//5
};
/**
* 线框模式下,立方体有8个顶点,12条边缘,
* 这里指定画线模式所用到的12条线段
*/
byte lineIndices[] = {
0, 1,//Line 0
0, 3,//1
0, 4,//2
1, 2,//3
1, 5,//4
2, 3,//5
2, 6,//6
3, 7,//7
4, 5,//8
4, 7,//9
5, 6,//10
6, 7//11
};
//根据数组来生成用于直接渲染的java.nio.Buffer
//顶点位置Buffer
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
vbb.order(ByteOrder.nativeOrder());
mVertexBuffer = vbb.asFloatBuffer();
mVertexBuffer.put(vertices);
mVertexBuffer.position(0);
//顶点颜色Buffer
ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
cbb.order(ByteOrder.nativeOrder());
mColorBuffer = cbb.asFloatBuffer();
mColorBuffer.put(colors);
mColorBuffer.position(0);
//实体模式下顶点索引Buffer
mIndexBuffer = ByteBuffer.allocateDirect(indices.length);
mIndexBuffer.put(indices);
mIndexBuffer.position(0);
//线框模式下顶点索引Buffer
mLineIndexBuffer = ByteBuffer.allocateDirect(lineIndices.length);
mLineIndexBuffer.put(lineIndices);
mLineIndexBuffer.position(0);
}
/**
* 根据传入的模式来分别渲染实体模式立方体以及线框模式立方体
* @param gl - OpenGL ES 渲染对象
* @param mode - 渲染模式,GL10.GL_TRIANGLES 表示实体模式,GL10.GL_LINES 表示线框模式
*/
public void draw(GL10 gl, int mode) {
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
if(mode == GL10.GL_TRIANGLES) {
//如果是实体模式,则启用颜色,给每一个顶点指定一个颜色
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
gl.glColorPointer(4, GL10.GL_FLOAT, 0, mColorBuffer);
gl.glDrawElements(GL10.GL_TRIANGLES, 36, GL10.GL_UNSIGNED_BYTE, mIndexBuffer);
}
else if(mode == GL10.GL_LINES) {
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
gl.glDrawElements(GL10.GL_LINES, 24, GL10.GL_UNSIGNED_BYTE, mLineIndexBuffer);
}
}
}