Android OpenGL顶点着色器

Android OpenGL顶点着色器

首先申明下,本文为笔者学习《OpenGL ES应用开发实践指南》的笔记,并加入笔者自己的理解和归纳总结。

1、OpenGL坐标

OpenGL会把屏幕映射到[-1, 1]的范围内。

在OpenGL里,只能绘制点、直线以及三角形。三角形一般以逆时针顺序排列顶点。定义一个长方形,可以用两个三角形拼接而成。

2、数据存储

由于OpenGL运行在本地环境,而android运行在Davik虚拟机上,需要把android中的数据复制到本地内存块。
FloatBuffer类可以用来保存顶点数据。

private static final int BYTES_PER_FLOAT = 4;
float[] tableVerticesWithTriangles;

// ByteBuffer.allocateDirect分配一块本地内存,不被垃圾回收
// 每个float类型有4个字节,本地内存的大小是tableVerticesWithTriangles.length * BYTES_PER_FLOAT
// order方法是确保按本地字节序排列保存
// put方法把数据从Davik的内存复制到本地内存
FloatBuffer vertexData = ByteBuffer
		.allocateDirect(tableVerticesWithTriangles.length * BYTES_PER_FLOAT)
		.order(ByteOrder.nativeOrder())
		.asFloatBuffer();
vertexData.put(tableVerticesWithTriangles);

3、着色器文件

(1) 顶点着色器,生成每个顶点的最终位置,针对每个顶点,它都会执行一次。
simple_vertex_shader.glsl文件

attribute vec4 a_Position;

void main()
{
    gl_Position = a_Position;
    gl_PointSize = 10.0;
}
vec4是包含4个分量的向量,可以认为是x,y,z,w,默认情况下,前三个坐标设为0,并把最后一个坐标设为1。
着色器会有几个属性,关键字attribute是把这些属性放进着色器的手段。
main()是着色器的主要入口点,把前面定义过的位置复制到指定的输出变量gl_Position。

(2) 片段着色器,为组成点、线或者三角形的每个片段生成最终的颜色。

OpenGL把每个点、直线及三角形分解成大量的小片段,这些片段类似于屏幕上的像素,每一个都包含单一的纯色。对于每个片段,片段着色器都会被调用一次。
simple_fragment_shader.glsl文件

precision mediump float;

uniform vec4 u_Color;

void main()
{                    	
    gl_FragColor = u_Color;
}
precision指定浮点数据类型精度,lowp、mediump和highp分别对应低精度、中等精度和高精度。
uniform会让每个顶点都使用一个值,u_Color是4个分量的向量,对应红、绿、蓝和透明度。
main()是片段着色器的主要入口点,把uniform定义的颜色复制到指定的输出变量gl_FragColor,作为当前片段的最终颜色。

4、绘制着色器

(1) 加载着色器代码
protected String readShaderFromRaw(int resId) {
	BufferedReader br = null;
	StringBuffer stringBuffer = new StringBuffer();
	try {
		br = new BufferedReader(new InputStreamReader(
					getResources().openRawResource(resId)));
		String line = null;
		while ((line = br.readLine()) != null) {
			stringBuffer.append(line + "\n");
		}
	} catch (IOException e) {
	} finally {
		if (br != null) {
			try {
				br.close();
			} catch (IOException e) {
			}
		}
	}
	return stringBuffer.toString();
}
(2) 编译着色器代码
protected int compileShader(int type, String shaderCode) {
	// 创建一个新的着色器对象
	int shaderObjectId = GLES20.glCreateShader(type);
	if (shaderObjectId == 0) {
		// 创建失败
		return 0;
	}

	// 上传和编译着色器代码
	GLES20.glShaderSource(shaderObjectId, shaderCode);
	GLES20.glCompileShader(shaderObjectId);

	// 获取编译状态
	int[] compileStatus = new int[1];
	GLES20.glGetShaderiv(shaderObjectId, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
	// 获取着色器信息日志
	LogUtil.log("OpenGL", GLES20.glGetShaderInfoLog(shaderObjectId));

	if (compileStatus[0] == 0) {
		// 如果失败,删除着色器对象
		GLES20.glDeleteShader(shaderObjectId);
		return 0;
	}

	return shaderObjectId;
}
(3) 链接程序对象
protected int linkProgram(int vertexShaderId, int fragmentShaderId) {
	// 创建一个新的程序对象
	int programId = GLES20.glCreateProgram();
	if (programId == 0) {
		return 0;
	}

	// 新建程序对象附上着色器,并链接程序
	GLES20.glAttachShader(programId, vertexShaderId);
	GLES20.glAttachShader(programId, fragmentShaderId);
	GLES20.glLinkProgram(programId);

	// 获取链接状态
	int[] linkStatus = new int[1];
	GLES20.glGetProgramiv(programId, GLES20.GL_LINK_STATUS, linkStatus, 0);
	// 获取着程序链接信息日志
	LogUtil.log("OpenGL", GLES20.glGetProgramInfoLog(programId));

	if (linkStatus[0] == 0) {
		// 如果链接失败,删除程序对象
		GLES20.glDeleteProgram(programId);
		return 0;
	}

	return programId;
}
(4) 验证程序对象
protected boolean validateProgram(int programId) {
	// 验证程序,只在开发阶段需要
	GLES20.glValidateProgram(programId);
	LogUtil.log("OpenGL", GLES20.glGetProgramInfoLog(programId));

	int[] validateStatus = new int[1];
	GLES20.glGetProgramiv(programId, GLES20.GL_VALIDATE_STATUS, validateStatus, 0);

	return validateStatus[0] != 0;
}
(5) 使用程序对象
protected int useProgram(int vertexShaderResId, int fragmentShaderResId) {
	String vertexShaderCode = readShaderFromRaw(vertexShaderResId);
	String fragmentShaderCode = readShaderFromRaw(fragmentShaderResId);

	int vertexShaderId = compileShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
	int fragmentShaderId = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

	int programId = linkProgram(vertexShaderId, fragmentShaderId);
	validateProgram(programId);

	GLES20.glUseProgram(programId);

	return programId;
} 
(6) 获取Uniform位置
private static final String U_COLOR = "u_Color";
uColorLocation = GLES20.glGetUniformLocation(mProgramId, U_COLOR);
(7) 获取Attribute位置
private static final String A_POSITION = "a_Position";
aPositionLocation = GLES20.glGetAttribLocation(mProgramId, A_POSITION);
(8) 关联顶点数据
vertexData.position(0);
GLES20.glVertexAttribPointer(aPositionLocation, POSITION_COMPONENT_COUNT,
		GLES20.GL_FLOAT, false, 0, vertexData);
glVertexAttribPointer(int indx, int size, int type, boolean normalized,
                int stride, java.nio.Buffer ptr) 
  • index是属性位置
  • size是对每个属性的计数
  • type是数据的类型
  • normalized只对整型数据有意义
  • stride只有当一个数组存储多于一个属性时,指定位移
  • ptr是缓存数据
(9) Renderer对象
private class OpenGLVertexShaderRender implements GLSurfaceView.Renderer {
	private static final String U_COLOR = "u_Color";
	private static final String A_POSITION = "a_Position";
	private static final int POSITION_COMPONENT_COUNT = 2;
	private static final int BYTES_PER_FLOAT = 4;
	private FloatBuffer vertexData;

	private int mProgramId;
	private int uColorLocation;
	private int aPositionLocation;

	OpenGLVertexShaderRender() {
		float[] tableVerticesWithTriangles = {
			// 左上角
			-0.5f, -0.5f,
			0.5f,  0.5f,
			-0.5f,  0.5f,

			// 右下角
			-0.5f, -0.5f,
			0.5f, -0.5f,
			0.5f,  0.5f,

			// 直线
			-0.5f, 0f,
			0.5f, 0f,

			// 点
			0f, -0.25f,
			0f,  0.25f
		};

		// ByteBuffer.allocateDirect分配一块本地内存,不被垃圾回收,保存到FloatBuffer中
		vertexData = ByteBuffer
				.allocateDirect(tableVerticesWithTriangles.length * BYTES_PER_FLOAT)
				.order(ByteOrder.nativeOrder())
				.asFloatBuffer();
		vertexData.put(tableVerticesWithTriangles);
	}
	@Override
	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

		mProgramId = useProgram(R.raw.simple_vertex_shader, R.raw.simple_fragment_shader);

		// 获取Uniform的位置
		uColorLocation = GLES20.glGetUniformLocation(mProgramId, U_COLOR);
		// 获取Attribute位置
		aPositionLocation = GLES20.glGetAttribLocation(mProgramId, A_POSITION);

		vertexData.position(0);
		// 关联顶点数据
		GLES20.glVertexAttribPointer(aPositionLocation, POSITION_COMPONENT_COUNT,
				GLES20.GL_FLOAT, false, 0, vertexData);
		GLES20.glEnableVertexAttribArray(aPositionLocation);
	}

	@Override
	public void onSurfaceChanged(GL10 gl, int width, int height) {
		GLES20.glViewport(0, 0, width, height);
	}

	@Override
	public void onDrawFrame(GL10 gl) {
		GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

		// 绘制三角形,白色背景
		GLES20.glUniform4f(uColorLocation, 1, 1, 1, 1);
		GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 6);

		// 绘制直线,红色背景
		GLES20.glUniform4f(uColorLocation, 1, 0, 0, 1);
		GLES20.glDrawArrays(GLES20.GL_LINES, 6, 2);

		// 绘制点,蓝色背景
		GLES20.glUniform4f(uColorLocation, 0, 0, 1, 1);
		GLES20.glDrawArrays(GLES20.GL_POINTS, 8, 1);

		// 绘制点,红色背景
		GLES20.glUniform4f(uColorLocation, 1, 0, 0, 1);
		GLES20.glDrawArrays(GLES20.GL_POINTS, 9, 1);
	}

}
显示如下


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值