OpenGL ES 20 用2D纹理伪造3D效果 Demo

OpenGL ES 20中,使用2D纹理伪造3D效果的Demo

Demo纵览

简介和实现效果

OpenGL ES 20 用2D纹理伪造3D效果 Demo_第1张图片

这个例子的实现是,把2D纹理贴到一个矩形上,然后伪造出3D效果。

里面的树,来源是一张png图。贴在矩形上后,这个矩形要保持一直面对着摄像头。

这个例子来源是《Android3D游戏开发技术宝典》中的第11章的第一节,但是我修改了下包结构。本意能通过这个例子进行更多的拓展。

实现流程:

结构纵览

OpenGL ES 20 用2D纹理伪造3D效果 Demo_第2张图片

类图

OpenGL ES 20 用2D纹理伪造3D效果 Demo_第3张图片

结构说明

Activity和MySurfaceView的关系这里不再说明。如果需要请看上一篇。

这里的代码关系大致是这样:

“MySurfaceView->TreeGroup->Image_2D->TextureRect”

“MySurfaceView->Desert”

TreeGroup是个“树组”,里面定义了不同坐标2D图像作为纹理的“树”。

Image_2D是用x坐标和z坐标形容位置的2D图像类。它主要关心图像的位置和纹理。而图像的长宽是由通过构造函数传入的TextureRect类对象决定。所以TextureRect主要关心图像的长宽。

Desert是“地面”。这个类的说明,请看上一篇关于Triangle的说明。

代码说明

MySurfaceView

public class MySurfaceView extends GLSurfaceView
{
public static final float UNIT_SIZE=1f;
static float direction=0;//视线方向
public static float cx=0;//摄像机x坐标
public static float cz=15;//摄像机z坐标
static final float DEGREE_SPAN=(float)(3.0/180.0f*Math.PI);//摄像机每次转动的角度
//线程循环的标志位
boolean flag=true;
float x;
float y;
float Offset=15;
SceneRenderer mRender;
float preX;
float preY;

@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event)
{
    x = event.getX();
    y = event.getY();
    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
        flag = true;
        new Thread() {
            @Override
            public void run() {
                while (flag) {
                    if (x > 0 && x < WIDTH / 2 && y > 0 && y < HEIGHT / 2) {// 向前
                        Offset = Offset - 0.5f;
                    } else if (x > WIDTH / 2 && x < WIDTH && y > 0 && y < HEIGHT / 2) {// 向后
                        Offset = Offset + 0.5f;
                    } else if (x > 0 && x < WIDTH / 2 && y > HEIGHT / 2 && y < HEIGHT) {
                        direction = direction + DEGREE_SPAN;
                    } else if (x > WIDTH / 2 && x < WIDTH && y > HEIGHT / 2 && y < HEIGHT) {
                        direction = direction - DEGREE_SPAN;
                    }
                    try {
                        Thread.sleep(100);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        break;
    case MotionEvent.ACTION_UP:
        flag = false;
        break;
    }
    //设置新的观察目标点XZ坐标
    cx=(float)(Math.sin(direction)*Offset);//观察目标点x坐标 
    cz=(float)(Math.cos(direction)*Offset);//观察目标点z坐标     

    //计算所有树的朝向
    mRender.group.calculateBillboardDirection();

    //给树按照离视点的距离排序
    Collections.sort(mRender.group.trees);
    //设置新的摄像机位置
    MatrixState.setCamera(cx,0,cz,0,0,0,0,1,0);
    return true;
}

public MySurfaceView(Context context)
{
    super(context);
    this.setEGLContextClientVersion(2); //设置使用OPENGL ES2.0
    mRender = new SceneRenderer();  //创建场景渲染器
    setRenderer(mRender);               //设置渲染器             
    setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);//设置渲染模式为主动渲染 
}

private class SceneRenderer implements GLSurfaceView.Renderer 
{
    int treeTextureId;
    TreeGroup group;
    Desert desert;
    int desertId;

    @Override
    public void onDrawFrame(GL10 gl)
    {
        //清除深度缓冲与颜色缓冲
        GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);

        MatrixState.pushMatrix();
        MatrixState.translate(0, -2, 0);
        desert.drawSelf(desertId);
        MatrixState.popMatrix();

        //开启混合
        GLES20.glEnable(GLES20.GL_BLEND);
        //设置混合因子
        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);

        MatrixState.pushMatrix();
        MatrixState.translate(0, -2, 0);

        group.drawSelf();

        MatrixState.popMatrix();
        //关闭混合
        GLES20.glDisable(GLES20.GL_BLEND);    
    }
    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height)
    {
        //设置视窗大小及位置 
        GLES20.glViewport(0, 0, width, height); 
        //计算GLSurfaceView的宽高比
        float ratio = (float) width / height;
        //调用此方法计算产生透视投影矩阵
        MatrixState.setProjectFrustum(-ratio, ratio, -1, 1, 1, 100);
        //调用此方法产生摄像机9参数位置矩阵
        MatrixState.setCamera(cx,0,cz,0,0,0,0f,1.0f,0.0f);
    }
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config)
    {
        //设置屏幕背景色RGBA
        GLES20.glClearColor(1.0f,1.0f,1.0f,1.0f);
        //打开深度检测
        GLES20.glEnable(GLES20.GL_DEPTH_TEST);
        MatrixState.setInitStack();

        treeTextureId=initTexture(R.drawable.tree);

        desert=new Desert
                (
                    MySurfaceView.this,
                    new float[]
                    {
                        0,0, 0,6, 6,6,
                        6,6, 6,0, 0,0
                    } ,
                    30,
                    20
                ); 
        desertId=initTexture(R.drawable.desert); 

        group=new TreeGroup(MySurfaceView.this,treeTextureId);

        Collections.sort(mRender.group.trees);
    }
}

//生成纹理的id
public int initTexture(int drawableId)
{
    //生成纹理ID
    int[] textures = new int[1];
    GLES20.glGenTextures
    (
            1,          //产生的纹理id的数量
            textures,   //纹理id的数组
            0           //偏移量
    );      
    int textureId=textures[0];    
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.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_REPEAT);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_REPEAT);

    //通过输入流加载图片
    InputStream is = this.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;
}

}

请注意代码中的onDrawFrame方法,只有调用group.drawSelf()方法时,前后才有开启混合的代码。而在绘制desert即地面时,并不开启混合的代码。

TreeGroup

public class TreeGroup
{
TextureRect rect;
public List<Image_2D> trees=new ArrayList<Image_2D>();

public TreeGroup(MySurfaceView mv,int texId)
{
    rect=new TextureRect(mv, 3, 5, 0);
    trees.add(new Image_2D(0,0,0,rect,texId));
    trees.add(new Image_2D(8*UNIT_SIZE,0,0,rect,texId));
    trees.add(new Image_2D(5.7f*UNIT_SIZE,5.7f*UNIT_SIZE,0,rect,texId));
    trees.add(new Image_2D(0,-8*UNIT_SIZE,0,rect,texId));
    trees.add(new Image_2D(-5.7f*UNIT_SIZE,5.7f*UNIT_SIZE,0,rect,texId));
    trees.add(new Image_2D(-8*UNIT_SIZE,0,0,rect,texId));
    trees.add(new Image_2D(-5.7f*UNIT_SIZE,-5.7f*UNIT_SIZE,0,rect,texId));
    trees.add(new Image_2D(0,8*UNIT_SIZE,0,rect,texId));
    trees.add(new Image_2D(5.7f*UNIT_SIZE,-5.7f*UNIT_SIZE,0,rect,texId));
}
public void calculateBillboardDirection()
{
    //计算列表中每个树木的朝向
    for(int i=0;i<trees.size();i++)
    {
        trees.get(i).calculateBillboardDirection();
    }
}

public void drawSelf()
{//绘制列表中的每个树木
    for(int i=0;i<trees.size();i++)
    {
        trees.get(i).drawSelf();
    }
}
}

注意里面所有树都是用同一个TextureRect对象的,因为他们的宽高都相同。这个类主要是为了绘制树木时比较方便。

Image_2D

//单个的图片类
public class Image_2D implements Comparable<Image_2D>
{
public float x;
public float z;
public float yAngle;
TextureRect rect;
public int texId;

public Image_2D(float x, float z, float yAngle, TextureRect rect,int texId) {
    this.x = x;
    this.z = z;
    this.yAngle = yAngle;
    this.rect = rect;
    this.texId=texId;
}

public void drawSelf() {
    MatrixState.pushMatrix();
    MatrixState.translate(x, 0, z);
    MatrixState.rotate(yAngle, 0, 1, 0);
    rect.drawSelf(texId);
    MatrixState.popMatrix();
}

// 把图片朝向摄像机位置
public void calculateBillboardDirection() {
    float xspan = x - cx;
    float zspan = z - cz;

    if (zspan <= 0) {
        yAngle = (float) Math.toDegrees(Math.atan(xspan / zspan));
    } else {
        yAngle = 180 + (float) Math.toDegrees(Math.atan(xspan / zspan));
    }
}

@Override
public int compareTo(Image_2D another) {
    // 重写的比较两个树木离摄像机距离的方法
    float xs = x - cx;
    float zs = z - cz;

    float xo = another.x - cx;
    float zo = another.z - cz;

    float disA = (float) Math.sqrt(xs * xs + zs * zs);
    float disB = (float) Math.sqrt(xo * xo + zo * zo);

    return ((disA - disB) == 0) ? 0 : ((disA - disB) > 0) ? -1 : 1;
}
}

如上所说,这个类关心的是图像的x轴和z轴的位置和图像的纹理。这里的yAngle是图像在y轴的旋转角度。calculateBillboardDirection()方法保证它一直面对着摄像机。

TextureRect

public class TextureRect  
{

int mProgram;//自定义渲染管线程序id
int muMVPMatrixHandle;//总变换矩阵引用id
int maPositionHandle; //顶点位置属性引用id  
int maTexCoorHandle; //顶点纹理坐标属性引用id  

FloatBuffer   mVertexBuffer;//顶点坐标数据缓冲
FloatBuffer   mTexCoorBuffer;//顶点纹理坐标数据缓冲
int vCount=0;   

int width=1;
int height=1;
float bottom=0;

public TextureRect(GLSurfaceView mv,int width,int height,float bottom)
{
    this.width=width;
    this.height=height*2;
    this.bottom=bottom;
    initVertexData();
    initShader(mv);
}
//初始化顶点数据的方法
public void initVertexData()
{
    vCount=6;
    float vertices[]=new float[]
    {
        -UNIT_SIZE*width,bottom,0,
        UNIT_SIZE*width,bottom,0,
        UNIT_SIZE*width,UNIT_SIZE*height+bottom,0,

        UNIT_SIZE*width,UNIT_SIZE*height+bottom,0,
        -UNIT_SIZE*width,UNIT_SIZE*height+bottom,0,
        -UNIT_SIZE*width,bottom,0,
    };
    //创建顶点坐标数据缓冲
    ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
    vbb.order(ByteOrder.nativeOrder());//设置字节顺序
    mVertexBuffer = vbb.asFloatBuffer();//转换为Float型缓冲
    mVertexBuffer.put(vertices);//向缓冲区中放入顶点坐标数据
    mVertexBuffer.position(0);//设置缓冲区起始位置

    float[] texcoor=new float[]
    {
        0,1,    1,1,   
        1,0,    1,0,   
        0,0,    0,1
    };
    ByteBuffer tbb = ByteBuffer.allocateDirect(texcoor.length*4);
    tbb.order(ByteOrder.nativeOrder());//设置字节顺序
    mTexCoorBuffer = tbb.asFloatBuffer();//转换为Float型缓冲
    mTexCoorBuffer.put(texcoor);//向缓冲区中放入顶点坐标数据
    mTexCoorBuffer.position(0);//设置缓冲区起始位置        
}
//初始化shader
public void initShader(GLSurfaceView mv)
{
    //加载顶点着色器的脚本内容
    String mVertexShader=ShaderUtil.loadFromAssetsFile("vertex.sh", mv.getResources());
    //加载片元着色器的脚本内容
    String mFragmentShader=ShaderUtil.loadFromAssetsFile("frag.sh", mv.getResources());  
    //基于顶点着色器与片元着色器创建程序
    mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
    //获取程序中顶点位置属性引用id  
    maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
    //获取程序中顶点纹理坐标属性引用id  
    maTexCoorHandle= GLES20.glGetAttribLocation(mProgram, "aTexCoor");
    //获取程序中总变换矩阵引用id
    muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");  
}
//主绘制方法
public void drawSelf(int texId)    
{
    //指定使用某套shader程序
    GLES20.glUseProgram(mProgram); 
    //将最终变换矩阵传入shader程序
    GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, MatrixState.getFinalMatrix(), 0); 
    //传送顶点位置数据
    GLES20.glVertexAttribPointer  
    (
        maPositionHandle,   
        3, 
        GLES20.GL_FLOAT, 
        false,
        3*4,   
        mVertexBuffer
    );       
    //传送顶点纹理坐标数据
    GLES20.glVertexAttribPointer  
    (
        maTexCoorHandle, 
        2, 
        GLES20.GL_FLOAT, 
        false,
        2*4,   
        mTexCoorBuffer
    );   
    //允许顶点位置、纹理坐标数据数组
    GLES20.glEnableVertexAttribArray(maPositionHandle);  
    GLES20.glEnableVertexAttribArray(maTexCoorHandle);  

    //绑定纹理
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);

    //绘制纹理矩形
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount); 
}

}

这个类与着色器代码绑定,是绘制纹理矩形的代码。它关心矩形的长宽。所以这里面定义着矩形的顶点坐标。至于矩形的位置,它在Image_2D中通过矩阵变换而得到正确的位置。

传入的参数是,矩形宽,高,以及矩阵的底部在z轴上的坐标。

Desert

public class Desert 
{   
int mProgram;//自定义渲染管线程序id
int muMVPMatrixHandle;//总变换矩阵引用id
int maPositionHandle; //顶点位置属性引用id  
int maTexCoorHandle; //顶点纹理坐标属性引用id  
String mVertexShader;//顶点着色器
String mFragmentShader;//片元着色器
static float[] mMMatrix = new float[16];//具体物体的移动旋转矩阵

FloatBuffer   mVertexBuffer;//顶点坐标数据缓冲
FloatBuffer   mTexCoorBuffer;//顶点纹理坐标数据缓冲
int vCount=0;   

public Desert(MySurfaceView mv,float[] texCoor,int width,int height)
{       
    //初始化顶点坐标与着色数据
    initVertexData(texCoor,width,height);
    //初始化shader        
    initShader(mv);
}

//初始化顶点坐标与着色数据的方法
public void initVertexData(float[] texCoor,int width,int height)
{
    vCount=6;
    float vertices[]=new float[]
    {
        -UNIT_SIZE*width,0,-UNIT_SIZE*height,
        UNIT_SIZE*width,0,-UNIT_SIZE*height,
        UNIT_SIZE*width,0,UNIT_SIZE*height,

        -UNIT_SIZE*width,0,-UNIT_SIZE*height,
        UNIT_SIZE*width,0,UNIT_SIZE*height,
        -UNIT_SIZE*width,0,UNIT_SIZE*height
    };
    //创建顶点坐标数据缓冲
    ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
    vbb.order(ByteOrder.nativeOrder());//设置字节顺序
    mVertexBuffer = vbb.asFloatBuffer();//转换为Float型缓冲
    mVertexBuffer.put(vertices);//向缓冲区中放入顶点坐标数据
    mVertexBuffer.position(0);//设置缓冲区起始位置

    //创建顶点纹理坐标数据缓冲
    ByteBuffer cbb = ByteBuffer.allocateDirect(texCoor.length*4);
    cbb.order(ByteOrder.nativeOrder());//设置字节顺序
    mTexCoorBuffer = cbb.asFloatBuffer();//转换为Float型缓冲
    mTexCoorBuffer.put(texCoor);//向缓冲区中放入顶点着色数据
    mTexCoorBuffer.position(0);//设置缓冲区起始位置
}

//初始化shader
public void initShader(MySurfaceView mv)
{
    //加载顶点着色器的脚本内容
    mVertexShader=ShaderUtil.loadFromAssetsFile("vertex.sh", mv.getResources());
    //加载片元着色器的脚本内容
    mFragmentShader=ShaderUtil.loadFromAssetsFile("frag.sh", mv.getResources());  
    //基于顶点着色器与片元着色器创建程序
    mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
    //获取程序中顶点位置属性引用id  
    maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
    //获取程序中顶点纹理坐标属性引用id  
    maTexCoorHandle= GLES20.glGetAttribLocation(mProgram, "aTexCoor");
    //获取程序中总变换矩阵引用id
    muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");  
}

public void drawSelf(int texId)
{        
     //指定使用某套shader程序
     GLES20.glUseProgram(mProgram); 
     //将最终变换矩阵传入shader程序
     GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, MatrixState.getFinalMatrix(), 0); 
     //传送顶点位置数据
     GLES20.glVertexAttribPointer  
     (
            maPositionHandle,   
            3, 
            GLES20.GL_FLOAT, 
            false,
            3*4,   
            mVertexBuffer
     );       
     //传送顶点纹理坐标数据
     GLES20.glVertexAttribPointer  
     (
            maTexCoorHandle, 
            2, 
            GLES20.GL_FLOAT, 
            false,
            2*4,   
            mTexCoorBuffer
     );   
     //允许顶点位置、纹理坐标数据数组
     GLES20.glEnableVertexAttribArray(maPositionHandle);  
     GLES20.glEnableVertexAttribArray(maTexCoorHandle);  
     //绑定纹理
     GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
     GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);
     //绘制纹理矩形
     GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount); 
}
}

剩下两个util包内的类和着色器代码.sh文件

MatrixState

//存储系统矩阵状态的类
public class MatrixState 
{
private static float[] mProjMatrix = new float[16];//4x4矩阵 投影用
private static float[] mVMatrix = new float[16];//摄像机位置朝向9参数矩阵   
private static float[] currMatrix;//当前变换矩阵
public static float[] lightLocation=new float[]{0,0,0};//定位光光源位置
public static FloatBuffer cameraFB;    
public static FloatBuffer lightPositionFB;

public static Stack<float[]> mStack=new Stack<float[]>();//保护变换矩阵的栈

public static void setInitStack()//获取不变换初始矩阵
{
    currMatrix=new float[16];
    Matrix.setRotateM(currMatrix, 0, 0, 1, 0, 0);
}

public static void pushMatrix()//保护变换矩阵
{
    mStack.push(currMatrix.clone());
}

public static void popMatrix()//恢复变换矩阵
{
    currMatrix=mStack.pop();
}

public static void translate(float x,float y,float z)//设置沿xyz轴移动
{
    Matrix.translateM(currMatrix, 0, x, y, z);
}

public static void rotate(float angle,float x,float y,float z)//设置绕xyz轴移动
{
    Matrix.rotateM(currMatrix,0,angle,x,y,z);
}

//设置摄像机
public static void setCamera
(
        float cx,   //摄像机位置x
        float cy,   //摄像机位置y
        float cz,   //摄像机位置z
        float tx,   //摄像机目标点x
        float ty,   //摄像机目标点y
        float tz,   //摄像机目标点z
        float upx,  //摄像机UP向量X分量
        float upy,  //摄像机UP向量Y分量
        float upz   //摄像机UP向量Z分量        
)
{
    Matrix.setLookAtM
    (
            mVMatrix, 
            0, 
            cx,
            cy,
            cz,
            tx,
            ty,
            tz,
            upx,
            upy,
            upz
    );

    float[] cameraLocation=new float[3];//摄像机位置
    cameraLocation[0]=cx;
    cameraLocation[1]=cy;
    cameraLocation[2]=cz;

    ByteBuffer llbb = ByteBuffer.allocateDirect(3*4);
    llbb.order(ByteOrder.nativeOrder());//设置字节顺序
    cameraFB=llbb.asFloatBuffer();
    cameraFB.put(cameraLocation);
    cameraFB.position(0);  
}

//设置透视投影参数
public static void setProjectFrustum
(
    float left,     //near面的left
    float right,    //near面的right
    float bottom,   //near面的bottom
    float top,      //near面的top
    float near,     //near面距离
    float far       //far面距离
)
{
    Matrix.frustumM(mProjMatrix, 0, left, right, bottom, top, near, far);       
}

//设置正交投影参数
public static void setProjectOrtho
(
    float left,     //near面的left
    float right,    //near面的right
    float bottom,   //near面的bottom
    float top,      //near面的top
    float near,     //near面距离
    float far       //far面距离
)
{       
    Matrix.orthoM(mProjMatrix, 0, left, right, bottom, top, near, far);
}   

//获取具体物体的总变换矩阵
public static float[] getFinalMatrix()
{
    float[] mMVPMatrix=new float[16];
    Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, currMatrix, 0);
    Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);        
    return mMVPMatrix;
}

//获取具体物体的变换矩阵
public static float[] getMMatrix()
{       
    return currMatrix;
}

//设置灯光位置的方法
public static void setLightLocation(float x,float y,float z)
{
    lightLocation[0]=x;
    lightLocation[1]=y;
    lightLocation[2]=z;
    ByteBuffer llbb = ByteBuffer.allocateDirect(3*4);
    llbb.order(ByteOrder.nativeOrder());//设置字节顺序
    lightPositionFB=llbb.asFloatBuffer();
    lightPositionFB.put(lightLocation);
    lightPositionFB.position(0);
}


public static void scale(float x,float y,float z){
    Matrix.scaleM(currMatrix, 0, x, y, z);
}
}

ShaderUtil

//加载顶点Shader与片元Shader的工具类
public class ShaderUtil {
// 加载制定shader的方法
public static int loadShader(
        int shaderType, // shader的类型
        String source // shader的脚本字符串
) {
    // 创建一个新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的编译情况
        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程序的方法
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的链接情况
        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);
    }
}

// 从sh脚本中加载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;
}
}

Demo下载地址

https://github.com/henryjaxliukun/OpenGLES20Demo/tree/master/GL2DDemo1

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