Android Opengles2.0 多纹理融合

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();
    }

demo 效果:
Android Opengles2.0 多纹理融合_第1张图片

Android Opengles2.0 多纹理融合_第2张图片

Android Opengles2.0 多纹理融合_第3张图片

你可能感兴趣的:(android,opengl,es)