Opengl ES2.0 三角形的绘制
先从二维图形的绘制进行Opengl es的学习,本节为3角形的绘制。
1、在绘图前首先要知道Opengl es在android手机系统中的坐标系:
以手机屏幕的中心为原点,x,y的方向如上图所示;z的方向为垂直屏幕的空间,以屏幕向外为正方向。
默认为按逆时针方向绘制。
默认x在屏幕的范围(-1,1),y在屏幕的范围(-1,1)。
2、我们要绘制图形,必须知道图形的顶点坐标
//设置三角形顶点数组,顶点逆时针排版
private float coords[] = {
0.0f, 0.5f, 0.0f,
-0.5f, 0.0f, 0.0f,
0.5f, 0.0f, 0.0f
};
然后把顶点坐标数组缓存到Buffer中:
// 初始化顶点字节缓冲区,用于存放形状的坐标,每个浮点数占用4个字节
ByteBuffer bb = ByteBuffer.allocateDirect(coords.length * 4);
//设置使用设备硬件的原生字节序
bb.order(ByteOrder.nativeOrder());
//从ByteBuffer中创建一个浮点缓冲区
FloatBuffer floatBuffer = bb.asFloatBuffer();
// 把坐标都添加到FloatBuffer中
floatBuffer.put(coords);
//设置buffer从第一个坐标开始读
floatBuffer.position(0);
3、创建并编译顶点、片断shader
vertex shader类型:GLES20.GL_VERTEX_SHADER
fragment shader类型:GLES20.GL_FRAGMENT_SHADER
shader的创建流程如下图:
实现如下:
private static final String vertexShaderCode =上面的shaderCode如果要弄懂,需要去学习GLSL的相关知识,这里不做过多说明。
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = vPosition;" +
"}";
private static final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
// 编译shader代码
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,fragmentShaderCode);
public static int loadShader(int type, String shaderCode){
//vertex shader类型(GLES20.GL_VERTEX_SHADER)或fragment shader类型(GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// 将源码添加到shader并编译它
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
4、创建程序Program
Program的创建流程如下图:
初始化处理如下:
// 创建空的OpenGL ES Program
program = GLES20.glCreateProgram();
// 将vertex shader添加到program
GLES20.glAttachShader(program, vertexShader);
// 将fragment shader添加到program
GLES20.glAttachShader(program, fragmentShader);
// 创建可执行的 OpenGL ES program
GLES20.glLinkProgram(program);
绘图时添加以下代码:
// 添加program到OpenGL ES环境中
GLES20.glUseProgram(program);
5、绘制前还要准备相关的顶点、片断数据:
获取shader的内存位置,把数据写入内存中
// 获取指向vertex shader的成员vPosition的内存地址
positionHandle = GLES20.glGetAttribLocation(program, "vPosition");
// 启用一个指向三角形的顶点数组
GLES20.glEnableVertexAttribArray(positionHandle);
//准备三角形的坐标数据
GLES20.glVertexAttribPointer(positionHandle, 3,
GLES20.GL_FLOAT, false,
3 * 4, vertexBuffer);
// 获取指向fragment shader的成员vColor的内存地址
colorHandle = GLES20.glGetUniformLocation(program, "vColor");
// 上传缓冲,把color写入vColor的内存中
GLES20.glUniform4fv(colorHandle, 1, color, 0);
6、绘制3角形
在GLES20.glDrawArrays(....)的第一个参数为绘制的图元,可以是点、线、面。可以自行配置。我们绘制三角形采用GLES20.GL_TRIANGLES
// glDrawArrays 使用VetexBuffer 来绘制,顶点的顺序由vertexBuffer中的顺序指定。
// GLES20.GL_TRIANGLES 绘制的类型为3角形,从0开始
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, coords.length / 3);
完整的代码如下:
创建一个Triangle类,实现GLSerfaceView.Renderer接口:
public class Triangle implements GLSurfaceView.Renderer
{
private static final String vertexShaderCode =
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = vPosition;" +
"}";
private static final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
//定点的缓存
private FloatBuffer vertexBuffer;
//OpenGL ES Program
private int program;
//设置三角形顶点数组
private float coords[] = { // 默认按逆时针方向绘制
0.0f, 0.5f, 0.0f,
-0.5f, 0.0f, 0.0f,
0.5f, 0.0f, 0.0f
};
// 设置三角形颜色和透明度(r,g,b,a),蓝色不透明
private float color[] = {0.0f, 0.0f, 1.0f, 1.0f};
private int positionHandle;
private int colorHandle;
/**
* 仅调用一次,用于设置view的OpenGLES环境
*/
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
//设置清屏背景色RGBA,白色不透明
GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
// 初始化顶点字节缓冲区
vertexBuffer = BufferUtil.getFloatBuffer(coords);
// 编译shader代码
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,fragmentShaderCode);
// 创建空的OpenGL ES Program
program = GLES20.glCreateProgram();
// 将vertex shader添加到program
GLES20.glAttachShader(program, vertexShader);
// 将fragment shader添加到program
GLES20.glAttachShader(program, fragmentShader);
// 创建可执行的 OpenGL ES program
GLES20.glLinkProgram(program);
}
/**
* 如果view的几何形状发生变化就调用,例如当竖屏变为横屏时
*/
@Override
public void onSurfaceChanged(GL10 gl, int width, int height)
{
//设置视窗大小及位置
GLES20.glViewport(0, 0, width, height);
}
/**
* 每次View被重绘时被调用
*/
@Override
public void onDrawFrame(GL10 gl)
{
//清除深度缓冲与颜色缓冲
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT|GLES20.GL_DEPTH_BUFFER_BIT);
// 添加program到OpenGL ES环境中
GLES20.glUseProgram(program);
// 获取指向vertex shader的成员vPosition的handle
positionHandle = GLES20.glGetAttribLocation(program, "vPosition");
// 启用一个指向三角形的顶点数组的handle
GLES20.glEnableVertexAttribArray(positionHandle);
//准备三角形的坐标数据
GLES20.glVertexAttribPointer(positionHandle, 3,
GLES20.GL_FLOAT, false,
3 * 4, vertexBuffer);
// 获取指向fragment shader的成员vColor的handle
colorHandle = GLES20.glGetUniformLocation(program, "vColor");
// 上传缓冲
GLES20.glUniform4fv(colorHandle, 1, color, 0);
// glDrawArrays 使用VetexBuffer 来绘制,顶点的顺序由vertexBuffer中的顺序指定。
// GLES20.GL_TRIANGLES 绘制的类型为3角形
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, coords.length / 3);
//glDrawElements 可以重新定义顶点的顺序,顶点的顺序由indices Buffer 指定
//GLES20.glDrawElements(GLES20.GL_TRIANGLES,indices.length,GLES20.GL_UNSIGNED_INT,indexBuffer);
// 禁用指向三角形的顶点数组
GLES20.glDisableVertexAttribArray(positionHandle);
}
public static int loadShader(int type, String shaderCode){
//vertex shader类型(GLES20.GL_VERTEX_SHADER)或fragment shader类型(GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// 将源码添加到shader并编译它
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
}
public class BufferUtil
{
public static FloatBuffer getFloatBuffer(float[] coords)
{
// 初始化顶点字节缓冲区,用于存放形状的坐标,每个浮点数占用4个字节
ByteBuffer bb = ByteBuffer.allocateDirect(coords.length * 4);
//设置使用设备硬件的原生字节序
bb.order(ByteOrder.nativeOrder());
//从ByteBuffer中创建一个浮点缓冲区
FloatBuffer floatBuffer = bb.asFloatBuffer();
// 把坐标都添加到FloatBuffer中
floatBuffer.put(coords);
//设置buffer从第一个坐标开始读
floatBuffer.position(0);
return floatBuffer;
}
}
Activity和GLSurfaceView的处理在上一节中有介绍,如果不懂请看上一章节。
工程已上传Github,欢迎下载https://github.com/chyss/OpenglTriangle
运行结果如下: