demo:
http://download.csdn.net/download/keen_zuxwang/10041401
1、创建顶点位置、纹理数组
2、创建、编译、加载shader程序,获得shader中各变量的句柄(如获取纹理采样sampler2D变量的句柄)
3、程序通过program给shader传递各参量,如:顶点位置、纹理坐标,激活、绑定纹理,传递模型/视图/投影矩阵等, 然后通过glDrawArrays()/glDrawElements()绘制图元(片元着色器通过这些参量计算出每个像素的值、然后通过底层EGL 渲染到相应的ANativeWindow)
1)、vertex shader:
attribute vec4 vPosition; //顶点位置
attribute vec2 vCoord; //顶点纹理
uniform mat4 vMatrix; //变化矩阵
//传递量-->fragment shader
varying vec2 varyTexCoord;
varying vec2 varyPostion;
void main()
{
gl_Position = vPosition; //vMatrix*vPosition
varyPostion = vPosition.xy;
varyTexCoord = vCoord;
}
fragment shader:
precision mediump float; // 必须定义float精度(若有使用float 变量,或者定义float变量时指定精度)
uniform sampler2D vTexture; // 2D纹理采样器变量
uniform sampler2D vTexture0;
uniform sampler2D vTexture1;
uniform float mratio; // 融合系数
//定义左上角矩形融合区域,参考顶点位置坐标定义(顶点位置一般范围[-1,1])
const vec2 leftBottom = vec2(-0.99, 0.79);
const vec2 rightTop = vec2(-0.70, 0.99);
varying vec2 varyTexCoord;
varying vec2 varyPostion;
void main() {
//矩形区域内的融合
if (varyPostion.x >= leftBottom.x && varyPostion.x <= rightTop.x
&& varyPostion.y >= leftBottom.y && varyPostion.y <= rightTop.y) {
vec2 tex0 = vec2((varyPostion.x-leftBottom.x)/(rightTop.x-leftBottom.x),
1.0-(varyPostion.y-leftBottom.y)/(rightTop.y-leftBottom.y));
vec4 color = texture2D(vTexture1, tex0);
gl_FragColor = color*color.a + texture2D(vTexture, 1.0-varyTexCoord)*(1.0-color.a); // 以color的透明度color.a 进行mix
}
//矩形区域外的融合
else {
vec4 color1 = texture2D(vTexture, varyTexCoord); //texture2D 纹理采样,获取纹理的像素信息
vec4 color2 = texture2D(vTexture0, varyTexCoord);
gl_FragColor = mix(color1, color2, mratio);
}
}
2)、shader的工具类:
//加载shader
//shader类型: GLES20.GL_VERTEX_SHADER & GLES20.GL_FRAGMENT_SHADER
//sourcesh: shader的脚本字符串资源
public static int loadShader(int shaderType, String source)
{
//创建shader
int shader = GLES20.glCreateShader(shaderType);
//若创建成功则加载shader
if (shader != 0)
{
//加载shader的源代码
GLES20.glShaderSource(shader, source);
//编译shader
GLES20.glCompileShader(shader);
//存放编译成功shader数量的数组
int[] compiled = new int[1];
//获取Shader的编译情况 Get Shader invalidate
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0)
{//若编译失败则显示错误日志并删除此shader
Log.e("ES20_ERROR", "Could not compile shader " + shaderType + ":");
Log.e("ES20_ERROR", GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
//创建shader程序program
public static int createProgram(String vertexSource, String fragmentSource)
{
//加载顶点着色器
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
if (vertexShader == 0) {
return 0;
}
//加载片元着色器
int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
if (pixelShader == 0) {
return 0;
}
int program = GLES20.glCreateProgram(); //创建程序
//若程序创建成功则向程序中加入顶点着色器与片元着色器
if (program != 0)
{
//向程序中加入顶点着色器
GLES20.glAttachShader(program, vertexShader);
checkGlError("glAttachShader");
//向程序中加入片元着色器
GLES20.glAttachShader(program, pixelShader);
checkGlError("glAttachShader");
//链接程序
GLES20.glLinkProgram(program);
//存放链接成功program数量的数组
int[] linkStatus = new int[1];
//获取program的链接情况 Get Program invalidate
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
//若链接失败则报错并删除程序
if (linkStatus[0] != GLES20.GL_TRUE)
{
Log.e("ES20_ERROR", "Could not link program: ");
Log.e("ES20_ERROR", GLES20.glGetProgramInfoLog(program));
GLES20.glDeleteProgram(program);
program = 0;
}
}
return program;
}
//检查错误
public static void checkGlError(String op)
{
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR)
{
Log.e("ES20_ERROR", op + ": glError " + error);
throw new RuntimeException(op + ": glError " + error);
}
}
//从assets加载shader
public static String loadFromAssetsFile(String fname,Resources r)
{
String result=null;
try
{
InputStream in=r.getAssets().open(fname);
int ch=0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while((ch=in.read())!=-1)
{
baos.write(ch);
}
byte[] buff=baos.toByteArray();
baos.close();
in.close();
result=new String(buff,"UTF-8");
result=result.replaceAll("\\r\\n","\n");
}
catch(Exception e)
{
e.printStackTrace();
}
return result;
}
3)、顶点、纹理、矩阵、shader操作类, 该类最终将在GLSurfaceView的GLSurfaceView.Renderer 类的OnDrawFrame()中调用glDrawArrays()/glDrawElements()绘制图元
public class IconObj {
private static final String TAG="Filter";
protected int mProgram;
protected int mHPosition;
protected int mHCoord;
protected int mMVPMatrix;
protected int mHTexture;
protected int mHTexture0;
protected int mHTexture1;
protected int mRatio;
public float ratio=0.5f;
private float[] matrix=new float[16];
private float[] mMatrix=new float[16];
//纹理缓存
protected FloatBuffer mVerBuffer;
//纹理缓存
protected FloatBuffer mTexBuffer;
//顶点绘制索引
protected ShortBuffer drawListBuffer;
//纹理id
private int textureId=-1;
private int textureId0=-1;
private int textureId1=-1;
//顶点位置坐标
private float pos[] = {
-0.9f, 0.9f,
-0.9f, 0.7f,
-0.7f, 0.7f,
-0.7f, 0.9f,
};
//纹理坐标
private float[] coord={
0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f,
};
//顶点位置坐标
private final float[] sPos={
-1.0f,1.0f, //左上角
-1.0f,-1.0f, //左下角
1.0f,1.0f, //右上角
1.0f,-1.0f //右下角
};
//顶点纹理坐标
private final float[] sCoord={
0.0f,0.0f,
0.0f,1.0f,
1.0f,0.0f,
1.0f,1.0f,
};
private static short drawOrder[] = {0, 1, 2, 0, 2, 3};
Context context;
public IconObj(Context context){
this.context=context;
initBuffer();
createProgram();
textureId = initTexture(R.drawable.earth);
textureId0 = initTexture(R.drawable.bg);
textureId1 = initTexture(R.drawable.opengles);
Matrix.setIdentityM(mMatrix, 0);
matrix = flip(mMatrix, true, false);
}
//闀滃儚
public float[] flip(float[] m,boolean x,boolean y){
if(x||y){
Matrix.scaleM(m,0,x?-1:1,y?-1:1,1);
}
return m;
}
protected void initBuffer(){
ByteBuffer a=ByteBuffer.allocateDirect(sPos.length*4);
a.order(ByteOrder.nativeOrder()); //变化成本地字节序
mVerBuffer=a.asFloatBuffer();
mVerBuffer.put(sPos);
mVerBuffer.position(0);
ByteBuffer b=ByteBuffer.allocateDirect(sCoord.length*4);
b.order(ByteOrder.nativeOrder());
mTexBuffer=b.asFloatBuffer();
mTexBuffer.put(sCoord);
mTexBuffer.position(0);
ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
}
protected void createProgram(){
String mVertexShader=ShaderUtil.loadFromAssetsFile("base_vertex.sh", context.getResources());
ShaderUtil.checkGlError("==ss==");
//加载片元着色器的脚本内容
String mFragmentShader=ShaderUtil.loadFromAssetsFile("base_fragment.sh", context.getResources());
//基于顶点着色器与片元着色器创建程序
ShaderUtil.checkGlError("==ss==");
mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
//glGetAttribLocation获取属性句柄(或可称为属性索引),glGetUniformLocation获取着色器中的常量值,它一般存储各种着色器需要的数据,如:转换矩阵、光照参数或者颜色等
mHPosition= GLES20.glGetAttribLocation(mProgram, "vPosition");
mHCoord=GLES20.glGetAttribLocation(mProgram,"vCoord");
mMVPMatrix=GLES20.glGetUniformLocation(mProgram,"vMatrix");
mHTexture=GLES20.glGetUniformLocation(mProgram,"vTexture");
mHTexture0=GLES20.glGetUniformLocation(mProgram,"vTexture0");
mHTexture1=GLES20.glGetUniformLocation(mProgram,"vTexture1");
mRatio = GLES20.glGetUniformLocation(mProgram,"mratio");
}
public int initTexture(int drawableId)
{
//生成纹理ID
int[] textures = new int[1];
GLES20.glGenTextures
(
1, //产生的纹理id的数量
textures, //纹理id的数组
0 //偏移量
);
int textureId = textures[0];
Log.i(TAG, " initTexture textureId = " + textureId);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST); // 纹素放大、缩小设置GL_LINEAR对应线性滤波,GL_NEAREST对应最近邻滤波方式
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE); // 纹理边界处理,当纹理坐标超出[0,1]的范围时该怎么处理,GL_CLAMP_TO_EDGE --- 纹理坐标会被截断到[0,1]之间。坐标值大的被截断到纹理的边缘部分,形成了一个拉伸的边缘
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE);
//加载图片
InputStream is = context.getResources().openRawResource(drawableId);
Bitmap bitmapTmp;
try {
bitmapTmp = BitmapFactory.decodeStream(is);
} finally {
try {
is.close();
}
catch(IOException e) {
e.printStackTrace();
}
}
//实际加载纹理
GLUtils.texImage2D
(
GLES20.GL_TEXTURE_2D, //纹理类型,在OpenGL ES中必须为GL10.GL_TEXTURE_2D
0, //纹理的层次,0表示基本图像层,可以理解为直接贴图
bitmapTmp, //纹理图像
0 //纹理边框尺寸
);
bitmapTmp.recycle(); //纹理加载成功后释放图片
return textureId;
}
public void drawSelf() {
GLES20.glUseProgram(mProgram); // 使用shader program
//顶点属性一般包括位置、颜色、法线、纹理坐标
GLES20.glEnableVertexAttribArray(mHPosition); // 使能相应的顶点位置句柄/索引的顶点属性数组 --- 即为顶点位置属性attribute变量赋值
GLES20.glVertexAttribPointer(mHPosition,2, GLES20.GL_FLOAT, false, 0,mVerBuffer); // 指定(或绑定)该顶点位置句柄关联的顶点属性数组
GLES20.glEnableVertexAttribArray(mHCoord); // 使能相应的纹理坐标句柄的顶点属性数组
GLES20.glVertexAttribPointer(mHCoord, 2, GLES20.GL_FLOAT, false, 0, mTexBuffer); // 指定(或绑定)该纹理坐标句柄关联的顶点属性数组 --- 即为顶点纹理坐标属性attribute变量赋值
//GLES20.glUniformMatrix4fv(mMVPMatrix,1,false,matrix,0);
GLES20.glUniform1f(mRatio, ratio);
//绑定纹理
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
GLES20.glUniform1i(mHTexture, 0);
//绑定纹理
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId0);
GLES20.glUniform1i(mHTexture0, 1);
//绑定纹理
GLES20.glActiveTexture(GLES20.GL_TEXTURE2);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId1);
GLES20.glUniform1i(mHTexture1, 2);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,0,4);
//GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
GLES20.glDisableVertexAttribArray(mHPosition);
GLES20.glDisableVertexAttribArray(mHCoord);
}
}
GLSurfaceView.Renderer 接口实现中进行shader操作类的创建、绘制
SeekBar seekBar = (SeekBar) findViewById(R.id.id_seekBar);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// TODO Auto-generated method stub
if(iconObj != null) {
iconObj.ratio = progress/100.0f;
}
ratio = progress/100.0f;
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
});
IconObj iconObj;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
iconObj = new IconObj(context);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height); //视窗设置
}
@Override
public void onDrawFrame(GL10 gl) {
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); //设置清除颜色
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
//GL_COLOR_BUFFER_BIT 设置窗口颜色
//GL_DEPTH_BUFFER_BIT 设置深度缓存--把所有像素的深度值设置为最大值(一般为远裁剪面)
iconObj.drawSelf();
}