Android openGL ES着色,创建顶点shader失败

cxy200927099 2014-08-06 05:45:15
最近在学Android上的OpenGL ES,学到着色这一块,除了个问题,一直没有解决:还请大神帮帮忙:
以下是我的代码:
分为四个部分:主界面MainActivity.java、渲染类Renderer(MyRenderer.java)、画正方形的类Rectangle.java、以及一个做shader操作的类ShaderUtil.java

MainActivity.java:
package com.cxy.picturetransfer;

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;

public class MainActivity extends Activity {

GLSurfaceView glSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
glSurfaceView = new GLSurfaceView(this);
// 设置OpenGLES版本为2.0
glSurfaceView.setEGLContextClientVersion(2);

// 设置渲染器 渲染模式
MyRenderer mRenderer = new MyRenderer(glSurfaceView);
glSurfaceView.setRenderer(mRenderer);
glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);

setContentView(glSurfaceView);
}

}



MyRenderer.java:
package com.cxy.picturetransfer;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;

public class MyRenderer implements GLSurfaceView.Renderer {
private Rectangle rect;

//构找函数
public MyRenderer(GLSurfaceView glView){
Log.v("构造函数先执行\n", "");
//创建三角形对象
rect = new Rectangle(glView);
}

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {//设置屏幕背景色
GLES20.glClearColor(0, 0, 0, 1.0f);
//打开深度检测
GLES20.glEnable(GLES20.GL_DEPTH_TEST);}

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//设置视窗大小及位置
GLES20.glViewport(0, 0, width, height);
//计算GLSurfaceView的宽高比
float ratio = (float)width/height;
/*
* 产生透视矩阵
* 参数介绍 :
* ① 4 * 4 投影矩阵
* ② 投影矩阵的起始位置
* 后面的四个参数分别是 左 右 下 上 的距离
* 最后两个参数是 近视点 和 远视点 距离
*/
Matrix.frustumM(Rectangle.mProjMatrix, 0, -ratio, ratio, -1, 1, 1, 10);
/*
* 设置摄像机参数矩阵
* 参数介绍 :
* 前两个参数是摄像机参数矩阵 和 矩阵数组的起始位置
* 后面三个一组是三个空间坐标 先后依次是 摄像机的位置 看的方向 摄像机上方朝向
*/
Matrix.setLookAtM(Rectangle.mVMatrix, 0, 0f,0f,3f,0f,0f,0f,0f,1.0f,0.0f);
}

@Override
public void onDrawFrame(GL10 gl) {
//清除深度缓冲与颜色缓冲
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
//绘制三角形
rect.drawSelf();
}

//定义一个工具方法,将float[]buffer数据转换为OpenGL ES所需要的FloatBuffer
public FloatBuffer floatBufferUtil(float[] arr){
FloatBuffer mbuffer;
//初始化ByteBuffer,长度为arr.length * 4,因为float占4个字节
ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
//数组排列用nativeOrder
qbb.order(ByteOrder.nativeOrder());

mbuffer = qbb.asFloatBuffer();
mbuffer.put(arr);
mbuffer.position(0);

return mbuffer;
}
//定义一个工具方法,将int[]buffer数据转换为OpenGL ES所需要的IntBuffer
public IntBuffer intBufferUtil(int[] arr){
IntBuffer mbuffer;
//初始化ByteBuffer,长度为arr.length * 4,因为float占4个字节
ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
//数组排列用nativeOrder
qbb.order(ByteOrder.nativeOrder());

mbuffer = qbb.asIntBuffer();
mbuffer.put(arr);
mbuffer.position(0);
return mbuffer;
}
}


...全文
540 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
cxy200927099 2014-08-07
  • 打赏
  • 举报
回复
引用 2 楼 jiangcaiyang123 的回复:
着色器依赖当前炫染的线程,也就是说不能在其它线程中创建。
你这里所说的 当前的渲染线程是指哪一个线程? 我的问题已经解决了,在一个外国网站上说的,确实是像你说的这样, 我把 MyRenderer.java中的 构造函数中的 rect = new Rectangle(glView);这句代码,放到了OnSurfaceCreate()函数里就没报这个错误了 但是有一点我不理解,就是难道是OnSurfaceCreate()函数自己起了一个线程吗? ???? 构造函数所在的线程,属于 UI线程????
cxy200927099 2014-08-07
  • 打赏
  • 举报
回复
引用 2 楼 jiangcaiyang123 的回复:
着色器依赖当前炫染的线程,也就是说不能在其它线程中创建。
详细一点说吧,该怎么改?
彩阳 2014-08-06
  • 打赏
  • 举报
回复
着色器依赖当前炫染的线程,也就是说不能在其它线程中创建。
cxy200927099 2014-08-06
  • 打赏
  • 举报
回复
由于一个帖子发不完,紧接着上面内容的: Rectangle.java
package com.cxy.picturetransfer;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;

public class Rectangle {

	
	
	/* 绘制正方形的data */
	float[] rectData = new float[] { 
			0.4f, 0.4f, 0.0f, // 右上顶点
			0.4f, -0.4f, 0.0f, // 右下顶点
			-0.4f, 0.4f, 0.0f, // 左上顶点
			-0.4f, -0.4f, 0.0f // 左下顶点
	};

	FloatBuffer rectDataBuffer;

	public static float[] mProjMatrix = new float[16];	//4 * 4 投影矩阵
	public static float[] mVMatrix = new float[16];		//摄影机位置朝向参数矩阵
	public static float[] mMVPMatrix;					//最后起作用的总变换矩阵
	
	int mProgram;										//自定义渲染管线着色程序id
	/*
	 * 下面的三个变量是顶点着色器中定义的三个变量
	 * 其中的总变换矩阵属性 是 一致变量
	 * 顶点位置 和 颜色属性 是 属性变量
	 */
	int muMVPMatrixHandle;								//总变换矩阵的引用
	int maPositionHandle;								//顶点位置属性引用
	int maColorHandle;									//顶点颜色属性引用
	
	String mVertexShader;								//顶点着色器脚本代码
	String mFragmentShader;								//片元着色器脚本代码
	
	//顶点着色器脚本代码
	String mVertexSource = new String(
			"uniform mat4 uMVPMatrix;\n"+ //总变换矩阵
			"attribute vec3 aPosition; \n"+ //顶点位置
			"attribute vec4 aColor;\n"+   //顶点颜色
			"varying  vec4 vColor;\n"+  //用于传递给片元着色器的变量

			"void main()\n"+     
			"{\n"+                            		
			   "gl_Position = uMVPMatrix * vec4(aPosition,1);\n"+ //根据总变换矩阵计算此次绘制此顶点位置
			   "vColor = aColor;\n"+//将接收的颜色传递给片元着色器 
			"}\n"      
	);	
	//片元着色器脚本代码
	String mFragmentSource = new String(
			"precision mediump float;\n"+
			"varying  vec4 vColor;\n"+ //接收从顶点着色器过来的参数

			"void main()\n"+                         
			"{\n"+                       
			   "gl_FragColor = vColor;\n"+//给此片元颜色值
			"}\n"
			);								
	
	/*
	 * 这个变换矩阵 在设置变换 , 位移 , 旋转的时候 将参数设置到这个矩阵中去
	 */
	static float[] mMMatrix = new float[16];			//具体物体的3D变换矩阵, 包括旋转, 平移, 缩放
	
	int vCount = 0;											//顶点数量
	float xAngle = 0;										//绕x轴旋转角度

	/**
	 * 构造方法
	 * 
	 * @param mv
	 *            GLSurfaceView子类对象, 显示3D画面的载体
	 */
	public Rectangle(GLSurfaceView mv) {
		initVertexData();
		initShader(mv);
	}

	public void initVertexData() {
		//设置定点数为3
		vCount = 4; 
		rectDataBuffer = floatBufferUtil(rectData);
	}

	/**
	 * 初始化着色器
	 * 
	 * 流程 : ① 从资源中获取顶点 和 片元着色器脚本 ② 根据获取的顶点 片元着色器脚本创建着色程序 ③ 从着色程序中获取顶点位置引用 ,
	 * 顶点颜色引用, 总变换矩阵引用
	 * 
	 * @param mv
	 *            MyTDView对象, 是GLSurfaceView对象
	 */
	public void initShader(GLSurfaceView mv) {
		/*
		 * mVertextShader是顶点着色器脚本代码
		 * 调用工具类方法获取着色器脚本代码, 着色器脚本代码放在assets目录中
		 * 传入的两个参数是 脚本名称 和 应用的资源
		 * 应用资源Resources就是res目录下的那写文件
		 */
		mVertexShader = ShaderUtil.loadFromAssetsFile("vertex.sh", mv.getResources());
		mFragmentShader = ShaderUtil.loadFromAssetsFile("frag.sh", mv.getResources());
		
		/*
		 * 创建着色器程序, 传入顶点着色器脚本 和 片元着色器脚本 注意顺序不要错
		 */
		/*mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);*/
		mProgram = ShaderUtil.createProgram(mVertexSource, mFragmentSource);
		
		/*
		 * 从着色程序中获取 属性变量 顶点坐标(颜色)数据的引用
		 * 其中的"aPosition"是顶点着色器中的顶点位置信息
		 * 其中的"aColor"是顶点着色器的颜色信息
		 */
		maPositionHandle = GLES20.glGetAttribLocation(mProgram, "rectData");
		maColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor");
		
		/*
		 * 从着色程序中获取一致变量  总变换矩阵
		 * uMVPMatrix 是顶点着色器中定义的一致变量
		 */
		muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
		
	}

	/**
	 * 绘制三角形方法
	 * 
	 * 绘制流程 : ① 指定着色程序 ② 设置变换矩阵 ③ 将顶点位置 颜色 数据传进渲染管线 ④ 启动顶点位置 颜色 数据 ⑤ 执行绘制
	 */
	public void drawSelf() {
		//根据着色程序id 指定要使用的着色器
		GLES20.glUseProgram(mProgram);
		/*
		 * 设置旋转变化矩阵 
		 * 参数介绍 : ① 3D变换矩阵 ② 矩阵数组的起始索引 ③旋转的角度 ④⑤⑥
		 */
		Matrix.setRotateM(mMMatrix, 0, 0, 0, 1, 0);
		/*
		 * 设置沿z轴正方向位移
		 * 参数介绍 : ① 变换矩阵 ② 矩阵索引开始位置 ③④⑤设置位移方向z轴
		 */
		Matrix.translateM(mMMatrix, 0, 0, 0, 1);
		/*
		 * 设置绕x轴旋转
		 * 参数介绍 : ① 变换矩阵 ② 索引开始位置 ③ 旋转角度 ④⑤⑥ 设置绕哪个轴旋转
		 */
		Matrix.rotateM(mMMatrix, 0, xAngle, 1, 0, 0);
		/*
		 * 应用投影和视口变换
		 */
		GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, Rectangle.getFianlMatrix(mMMatrix), 0);
		/*
		 * 将顶点位置数据传送进渲染管线, 为画笔指定定点的位置数据
		 */

		GLES20.glVertexAttribPointer(
				maPositionHandle, 
				3, 
				GLES20.GL_FLOAT, 
				false, 
				3 * 4, 
				rectDataBuffer
		);
		/*
		 * 将顶点颜色数据传送进渲染管线, 为画笔指定定点的颜色数据
		 
		GLES20.glVertexAttribPointer(
				maColorHandle, 
				4, 
				GLES20.GL_FLOAT, 
				false, 
				4 * 4, 
				mColorBuffer
		);*/
		//启用顶点位置数据
		GLES20.glEnableVertexAttribArray(maPositionHandle);
		//启用顶点颜色数据
		GLES20.glEnableVertexAttribArray(maColorHandle);
		//执行绘制
		GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vCount);
	}

	/**
	 * 计算最终投影的矩阵
	 * 
	 * @param spec
	 * @return
	 */
	public static float[] getFianlMatrix(float[] spec) {
		mMVPMatrix = new float[16];
		/*
		 * 计算矩阵变换投影
		 * 
		 * 参数介绍 : ① 总变换矩阵 ② 总变换矩阵起始索引 ③ 摄像机位置朝向矩阵 ④ 摄像机朝向矩阵起始索引 ⑤ 投影变换矩阵 ⑥
		 * 投影变换矩阵起始索引
		 */
		Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, spec, 0);
		Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
		return mMVPMatrix;
	}

	// 定义一个工具方法,将float[]buffer数据转换为OpenGL ES所需要的FloatBuffer
	public FloatBuffer floatBufferUtil(float[] arr) {
		FloatBuffer mbuffer;
		// 初始化ByteBuffer,长度为arr.length * 4,因为float占4个字节
		ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
		// 数组排列用nativeOrder
		qbb.order(ByteOrder.nativeOrder());

		mbuffer = qbb.asFloatBuffer();
		mbuffer.put(arr);
		mbuffer.position(0);

		return mbuffer;
	}

}
ShaderUtil.java
package com.cxy.picturetransfer;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;

import android.content.res.Resources;
import android.opengl.GLES20;
import android.util.Log;

/*
 * 这个工具类用来加载定点着色器与片元着色器
 */
public class ShaderUtil {
	
	/**
	 * 加载着色器方法
	 * 
	 * 流程 : 
	 * 
	 * ① 创建着色器
	 * ② 加载着色器脚本
	 * ③ 编译着色器
	 * ④ 获取着色器编译结果
	 * 
	 * @param shaderType 着色器类型,顶点着色器(GLES20.GL_FRAGMENT_SHADER), 片元着色器(GLES20.GL_FRAGMENT_SHADER)
	 * @param source 着色脚本字符串
	 * @return 返回的是着色器的引用, 返回值可以代表加载的着色器
	 */
	public static int loadShader(int shaderType , String source){
		//1.创建一个着色器, 并记录所创建的着色器的id, 如果id==0, 那么创建失败
		int shader = GLES20.glCreateShader(shaderType);
		if(shader != 0){
			//2.如果着色器创建成功, 为创建的着色器加载脚本代码
			GLES20.glShaderSource(shader, source);
			//3.编译已经加载脚本代码的着色器
			GLES20.glCompileShader(shader);
			int[] compiled = new int[1];
			//4.获取着色器的编译情况, 如果结果为0, 说明编译失败
			GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
			if(compiled[0] == 0){
				 Log.e("ES20_ERROR", "Could not compile shader " + shaderType + ":");
	             Log.e("ES20_ERROR", GLES20.glGetShaderInfoLog(shader));
	             //编译失败的话, 删除着色器, 并显示log
	             GLES20.glDeleteShader(shader);
	             shader = 0;
			}
		}
		else{
			Log.e("ES20_ERROR", "Could Create shader " + shaderType + ":"+
					"Error:"+ shader);
		}
		return shader;
	}
	
	/**
	 * 检查每一步的操作是否正确
	 * 
	 * 使用GLES20.glGetError()方法可以获取错误代码, 如果错误代码为0, 那么就没有错误
	 * 
	 * @param op 具体执行的方法名, 比如执行向着色程序中加入着色器, 
	 * 		使glAttachShader()方法, 那么这个参数就是"glAttachShader"
	 */
	public static void checkGLError(String op){
		int error;
		//错误代码不为0, 就打印错误日志, 并抛出异常
		while( (error = GLES20.glGetError()) != GLES20.GL_NO_ERROR ){
			 Log.e("ES20_ERROR", op + ": glError " + error);
	         throw new RuntimeException(op + ": glError " + error);
		}
	}
	
	/**
	 * 创建着色程序
	 * 
	 * ① 加载顶点着色器
	 * ② 加载片元着色器
	 * ③ 创建着色程序
	 * ④ 向着色程序中加入顶点着色器
	 * ⑤ 向着色程序中加入片元着色器
	 * ⑥ 链接程序
	 * ⑦ 获取链接程序结果
	 * 
	 * @param vertexSource		定点着色器脚本字符串
	 * @param fragmentSource	片元着色器脚本字符串
	 * @return
	 */
	public static int createProgram(String vertexSource , String fragmentSource){
		//1. 加载顶点着色器, 返回0说明加载失败
		int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
		if(vertexShader == 0)
		{
			Log.e("ES20_ERROR", "加载顶点着色器失败");			
			return 0;
		}
		//2. 加载片元着色器, 返回0说明加载失败
		int fragShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
		if(fragShader == 0)
		{
			Log.e("ES20_ERROR", "加载片元着色器失败");
			return 0;
		}
		//3. 创建着色程序, 返回0说明创建失败
		int program = GLES20.glCreateProgram();
		if(program != 0){
			//4. 向着色程序中加入顶点着色器
			GLES20.glAttachShader(program, vertexShader);
			//检查glAttachShader操作有没有失败
			checkGLError("glAttachShader");
			//5. 向着色程序中加入片元着色器
			GLES20.glAttachShader(program, fragShader);
			//检查glAttachShader操作有没有失败
			checkGLError("glAttachShader");
			
			//6. 链接程序
			GLES20.glLinkProgram(program);
			int[] linkStatus = new int[1];
			//获取链接程序结果
			GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
			if(linkStatus[0] != GLES20.GL_TRUE){
				Log.e("ES20.ERROR", "链接程序失败 : ");
				Log.e("ES20.ERROR", GLES20.glGetProgramInfoLog(program));
				//如果链接程序失败删除程序
				GLES20.glDeleteProgram(program);
				program = 0;
			}			
		}
		else{
			Log.e("ES20_ERROR", "glCreateProgram Failed: "+program);
		}
	
		return program;
	}
	
	/**
	 * 从assets中加载着色脚本
	 * 
	 * ① 打开assets目录中的文件输入流
	 * ② 创建带缓冲区的输出流
	 * ③ 逐个字节读取文件数据, 放入缓冲区
	 * ④ 将缓冲区中的数据转为字符串
	 * 
	 * @param fileName assets目录中的着色脚本文件名
	 * @param resources	应用的资源
	 * @return
	 */
	public static String loadFromAssetsFile(String fileName, Resources resources){
		String result = null;
		try {
			//1. 打开assets目录中读取文件的输入流, 相当于创建了一个文件的字节输入流
			InputStream is = resources.getAssets().open(fileName);
			int ch = 0;
			//2. 创建一个带缓冲区的输出流, 每次读取一个字节, 注意这里字节读取用的是int类型
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			//3. 逐个字节读取数据, 并将读取的数据放入缓冲器中
			while((ch = is.read()) != -1){
				baos.write(ch);
			}
			//4. 将缓冲区中的数据转为字节数组, 并将字节数组转换为字符串
			byte[] buffer = baos.toByteArray();
			baos.close();
			is.close();
			result = new String(buffer, "UTF-8");
			result = result.replaceAll("\\r\\n", "\n");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}
	
}
程序运行结果如下图:

8,303

社区成员

发帖
与我相关
我的任务
社区描述
游戏开发相关内容讨论专区
社区管理员
  • 游戏开发
  • 呆呆敲代码的小Y
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧