从零开始之OpenGL ES 2.0【4】

这一节开始纹理的学习,在纹理这里遇到了一些坑。不过最终还是解决了。

纹理的学习可以通过绘制一张图片到屏幕上来学习。

开头还是着色器代码

    //顶点着色器代码
    private final String vertex = "" +
            "attribute vec4 vPosition;" +
            "attribute vec2 aCoord;" +
            "uniform mat4 vMatrix;" +
            "varying vec2 vCoord;" +
            "void main(){" +
            "gl_Position=vMatrix*vPosition;"+
            "vCoord=aCoord;" +
            "}";
    //片元着色器代码
    private final String fragment = "" +
            "precision mediump float;" +
            "uniform sampler2D vTexture;" +
            "varying vec2 vCoord;" +
            "void main(){" +
            "gl_FragColor=texture2D(vTexture,vCoord);"+
            "}";

不知道有没有发现和前面代码不太一样,顶点着色器代码变量名修改了一下不影响。片元着色器我们增加了uniform sampler2D vTexture; 使用2D纹理,gl_FragColor=texture2D(vTexture,vCoord);设置前景色为texture2D纹理。再真正开始使用之前我们需要再一次了解Android中的坐标系。如下图


纹理坐标一般用(s,t)表示有时也用(u,v)

顶点坐标为

    //顶点坐标
    private final float[] pos = {
            -1.0f,1.0f,
            1.0f,1.0f,
            -1.0f,-1.0f,
            1.0f,-1.0f
    };

纹理坐标从左下角开始的。

    //纹理坐标
    private final float[] coord = {
            0.0f,0.0f,
            1.0f,0.0f,
            0.0f,1.0f,
            1.0f,1.0f,
    };

这里着重讲一下纹理坐标与顶点坐标的对应关系。


做了一张测试图片如下图


我们上面的顶点坐标与纹理坐标对应关系显示结果和测试图片显示结果如下。


方向是正常的,暂时不要理会拉伸问题。后面会通过矩阵调整。

下面我们调整纹理坐标与顶点坐标对应关系看一下效果。纹理坐标修改如下

//纹理坐标
    private final float[] coord = {
            0.0f,1.0f,
            1.0f,1.0f,
            0.0f,0.0f,
            1.0f,0.0f,
    }; 

让我们看一下效果。


图片颠倒了,到这里应该对纹理坐标与顶点坐标有了一定的认识了。回到我们正题。

使用纹理除了需要创建纹理坐标还需要一个创建纹理的过程。

1、创建纹理获得纹理索引

    a)

int[] texture = new int[1];

    b)

 GLES20.glGenTextures(1, texture, 0);//第一个参数是生成纹理数量因为定义的数组长度为1所以这里也是1.可以根据需要增加。

2、绑定纹理

    a)

GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture[0]);//第一个参数是纹理类型,第二个参数纹理索引

3、设置参数

    a)设置缩放

GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

    b)设置环绕方式

 //设置环绕方向S,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE);
 //设置环绕方向T,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE);

这里借用其他博友一张图

    最后一个参数不同参数不同效果

  • GL_REPEAT:坐标的整数部分被忽略,重复纹理,这是OpenGL纹理默认的处理方式.
  • GL_MIRRORED_REPEAT: 纹理也会被重复,但是当纹理坐标的整数部分是奇数时会使用镜像重复。
  • GL_CLAMP_TO_EDGE: 坐标会被截断到[0,1]之间。结果是坐标值大的被截断到纹理的边缘部分,形成了一个拉伸的边缘(stretched edge pattern)。
  • GL_CLAMP_TO_BORDER: 不在[0,1]范围内的纹理坐标会使用用户指定的边缘颜色。


4、绘制

GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, BitmapFactory.decodeResource(mContext.getResources(), R.drawable.texture_test),0); 

绘制图像纹理我们用到了GLUtils类的texImage2D 方法。

package pers.wtt.opengles20.render;

import android.content.Context;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.opengl.Matrix;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import pers.wtt.opengles20.R;

/**
 * Created by WT on 2018/4/8.
 */
public class DBitmapGLRender implements GLSurfaceView.Renderer {

    private Context mContext;

    public DBitmapGLRender(Context context) {
        this.mContext = context;
    }

    //顶点着色器代码
    private final String vertex = "" +
            "attribute vec4 vPosition;" +
            "attribute vec2 aCoord;" +
            "uniform mat4 vMatrix;" +
            "varying vec2 vCoord;" +
            "void main(){" +
            "gl_Position=vMatrix*vPosition;"+
            "vCoord=aCoord;" +
            "}";

    //片元着色器代码
    private final String fragment = "" +
            "precision mediump float;" +
            "uniform sampler2D vTexture;" +
            "varying vec2 vCoord;" +
            "void main(){" +
            "gl_FragColor=texture2D(vTexture,vCoord);"+
            "}";

    //顶点坐标
    private final float[] pos = {
            -1.0f,1.0f,
            1.0f,1.0f,
            -1.0f,-1.0f,
            1.0f,-1.0f
    };

    //纹理坐标
    private final float[] coord = {
            0.0f,0.0f,
            1.0f,0.0f,
            0.0f,1.0f,
            1.0f,1.0f,
    };

    //GL程序
    int program;
    //定点坐标Buffer
    FloatBuffer vertexBuffer;
    FloatBuffer coordBuffer;
    float[] mMVPMatrix;

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //将背景设置为灰色,这里只是设置,并没有立即生效
        GLES20.glClearColor(0.5f,0.5f,0.5f,1.0f);
        //创建一个定点坐标Buffer,一个float为4字节所以这里需要
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(pos.length*4);
        byteBuffer.order(ByteOrder.nativeOrder());
        vertexBuffer  = byteBuffer.asFloatBuffer();
        vertexBuffer .put(pos);
        vertexBuffer .position(0);

        ByteBuffer cbyteBuffer = ByteBuffer.allocateDirect(coord.length*4);
        cbyteBuffer.order(ByteOrder.nativeOrder());
        coordBuffer  = cbyteBuffer.asFloatBuffer();
        coordBuffer .put(coord);
        coordBuffer .position(0);

        //装载顶点着色器和片元着色器,从source
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertex);
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragment);
        //创建Opengl程序,获取程序句柄,为了方便onDrawFrame方法使用所以声明为成员变量
        program = GLES20.glCreateProgram();
        //激活着色器
        GLES20.glAttachShader(program, vertexShader);
        GLES20.glAttachShader(program, fragmentShader);
        //链接程序
        GLES20.glLinkProgram(program);
    }

    /**
     * 装载着色器从资源代码,需要检测是否生成成功,暂时不检测
     * @param type 着色器类型
     * @param source 着色器代码源
     * @return 返回着色器句柄
     */
    private int loadShader(int type, String source) {
        int shader = 0;
        shader = GLES20.glCreateShader(type);
        GLES20.glShaderSource(shader, source);
        GLES20.glCompileShader(shader);
        return shader;
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {

        //计算屏幕宽高比
        float ratio = (float)width/height;
        //储存投影矩阵
        float[] mPMatrix = new float[16];
        //储存相机位置矩阵
        float[] mVMatrix = new float[16];
        //最终得到的矩阵
        mMVPMatrix = new float[16];
        //透视矩阵
        //存储生成矩阵元素的float[]类型数组
        //填充起始偏移量
        //near面的left,right,bottom,top
        //near面,far面与视点的距离
        Matrix.orthoM(mPMatrix, 0, -ratio, ratio, -1,1,3, 6);
        //存储生成矩阵元素的float[]类型数组
        //填充起始偏移量
        //摄像机位置X,Y,Z坐标
        //观察目标X,Y,Z坐标
        //up向量在X,Y,Z上的分量,也就是相机上方朝向,upY=1朝向手机上方,upX=1朝向手机右侧,upZ=1朝向与手机屏幕垂直
        Matrix.setLookAtM(mVMatrix, 0, 0,0,6, 0,0,0,0,1,0);
        //以上两个方法只能得到矩阵并不能使其生效
        //下面通过矩阵计算得到最终想要的矩阵
        //存放结果的总变换矩阵
        //结果矩阵偏移量
        //左矩阵
        //左矩阵偏移量
        //右矩阵
        //右矩阵偏移量
        Matrix.multiplyMM(mMVPMatrix, 0,mPMatrix, 0, mVMatrix, 0);

        //当大小改变时重置视区大小
        GLES20.glViewport(0,0, width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        //清空缓冲区,与  GLES20.glClearColor(0.5f,0.5f,0.5f,1.0f);对应
        GLES20.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);

        //使用OpenGL程序
        GLES20.glUseProgram(program);
        int vMatrix = GLES20.glGetUniformLocation(program, "vMatrix");
        GLES20.glUniformMatrix4fv(vMatrix, 1 ,false, mMVPMatrix, 0);
        //获取顶点着色器变量vPosition
        int vPositionHandler = GLES20.glGetAttribLocation(program, "vPosition");
        //允许使用顶点坐标数组
        GLES20.glEnableVertexAttribArray(vPositionHandler);
        //第一个参数顶点属性的索引值
        // 第二个参数顶点属性的组件数量。必须为1、2、3或者4,如position是由3个(x,y,z)组成,而颜色是4个(r,g,b,a))
        // 第三个参数数组中每个组件的数据类型
        // 第四个参数指定当被访问时,固定点数据值是否应该被归一化(GL_TRUE)或者直接转换为固定点值(GL_FALSE)
        // 第五个参数指定连续顶点属性之间的偏移量,这里由于是三个点 每个点4字节(float) 所以就是 3*4
        // 第六个参数前面的顶点坐标数组
        GLES20.glVertexAttribPointer(vPositionHandler, 2, GLES20.GL_FLOAT, false, 8, vertexBuffer);
        int aCoordHandler = GLES20.glGetAttribLocation(program, "aCoord");
        GLES20.glEnableVertexAttribArray(aCoordHandler);
        GLES20.glVertexAttribPointer(aCoordHandler, 2, GLES20.GL_FLOAT, false, 8, coordBuffer);
        int vTexture = GLES20.glGetUniformLocation(program, "vTexture");
        GLES20.glUniform1i(vTexture, 0);
        texture();
        //获取片元着色器变量vColor
        //int vColor = GLES20.glGetUniformLocation(program, "vColor");
        //GLES20.glUniform4fv(vColor, 1, colors, 0);
        //三角形绘制方式
        //顶点的数量
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
        //禁止使用顶点坐标数组
        GLES20.glDisableVertexAttribArray(vPositionHandler);
    }


    int[] texture = new int[1];
    private void texture(){
        //第一个参数是生成纹理数量因为定义的数组长度为1所以这里也是1.可以根据需要增加。
        GLES20.glGenTextures(1, texture, 0);
        //
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture[0]);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        //设置环绕方向S,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE);
        //设置环绕方向T,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE);
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, BitmapFactory.decodeResource(mContext.getResources(), R.drawable.texture_test),0);
    }

}

图片拉伸问题解决方法,

        //计算屏幕宽高比
        float ratio = (float)width/height;
        Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.texture_test);
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();
        float ratioB = (float)w/h;

        if(width>height){
            if(ratioB>ratio){
                Matrix.orthoM(mPMatrix, 0, -ratio*ratioB, ratio*ratioB,-1,1,3, 6);
            }else{
                Matrix.orthoM(mPMatrix, 0, -ratioB/ratio, ratioB/ratio, -1,1,3, 6);
            }
        }else{
            if(ratioB>ratio){
                Matrix.orthoM(mPMatrix, 0, -1,1, -1/ratioB*ratio, 1/ratioB*ratio,3, 6);
            }else{
                Matrix.orthoM(mPMatrix, 0, -1,1, -ratioB/ratio, ratioB/ratio,3, 6);
            }
        } 

下一篇

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenGL ES 2.0是一种图形编程接口,用于开发移动设备上的2D和3D图形应用程序。由于其高效性、跨平台性和灵活性,它成为许多手机游戏和应用程序的首选。 要下载OpenGL ES 2.0,首先需要考虑的是您使用的平台。通常,OpenGL ES 2.0可以在Android、iOS和Windows等操作系统上使用。 对于Android设备,您可以在Google Play商店上搜索“OpenGL ES 2.0”并下载适用于您的设备的OpenGL ES 2.0开发工具包(SDK)。您还可以通过在计算机上安装Android开发套件(SDK)并使用Android Studio或Eclipse等IDE来开发OpenGL ES 2.0应用程序。 对于iOS设备,OpenGL ES 2.0通常已经预装在iOS系统中,无需单独下载。您可以使用Xcode IDE来开发在iOS设备上运行的OpenGL ES 2.0应用程序。 对于Windows操作系统,您可以通过访问Khronos Group的官方网站来下载OpenGL ES 2.0的开发工具包(SDK)。您可以选择从官方提供的软件包中选择适用于您的Windows版本。 除了操作系统特定的下载之外,您还可以在各种开发者论坛、网站和教程上找到OpenGL ES 2.0的相关示例代码和资源。这些资源可以帮助您更好地了解和使用OpenGL ES 2.0来开发高质量的图形应用程序。 总之,要下载OpenGL ES 2.0,您需要根据您使用的平台选择适当的开发工具包或SDK。随后,您可以使用IDE和其他资源开始在您的设备上开发出色的OpenGL ES 2.0应用程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值