Android中使用opengl es2.0基础(2)- 三角形的绘制

时间:2023-01-21 17:49:19

Opengl ES2.0 三角形的绘制

先从二维图形的绘制进行Opengl es的学习,本节为3角形的绘制。

1、在绘图前首先要知道Opengl es在android手机系统中的坐标系:

Android中使用opengl es2.0基础(2)- 三角形的绘制

以手机屏幕的中心为原点,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的创建流程如下图:

            Android中使用opengl es2.0基础(2)- 三角形的绘制

实现如下:

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;" +
"}";
上面的shaderCode如果要弄懂,需要去学习GLSL的相关知识,这里不做过多说明。


// 编译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的创建流程如下图:

Android中使用opengl es2.0基础(2)- 三角形的绘制

初始化处理如下:

// 创建空的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


运行结果如下:

                               Android中使用opengl es2.0基础(2)- 三角形的绘制