OpenGL ES渲染图片纹理

1、步骤:

  • 1、编写着色器(顶点着色器和片元着色器)

  • 2、设置顶点、纹理坐标

  • 3、加载着色器

  • 4、创建纹理

  • 5、渲染图片

2、OpenGL ES坐标系

首先介绍一下纹理映射时的坐标系,纹理映射的坐标系和顶点着色器的坐标系是不一样的。

2.1、顶点坐标系

控制窗口坐标

13903214-9c384eafb4b7b922.png
顶点坐标系

2.2、纹理坐标系

控制纹理位置

纹理坐标用浮点数来表示,范围一般从0.0到1.0,左上角坐标为(0.0,0.0),右上角坐标为(1.0,0.0),左下角坐标为(0.0,1.0),右下角坐标为(1.0,1.0),具体如下:


13903214-33080d63708818fa.png
纹理坐标系

3、OpenGL ES绘制四边形

将纹理映射到下边的两个三角形上(也就是一个矩形),需要将纹理坐标指定到正确的顶点上,才能使纹理正确的显示,否则显示出来的纹理会无法显示,或者出现旋转、翻转、错位等情况。规定:图形环绕方向必须一致!

顶点坐标系

13903214-5bd00d3da6f097cc.png

1、GL_TRIANGLES:
v1, v2, v3,
v3, v2, v4,

2、GL_TRIANGLE_STRIP:
偶数:n-1, n-2, n
奇数:n-2, n-1, n

3.1、绘制坐标范围:

float[] vertexData = {
    -1f, -1f,
    1f, -1f,
    -1f, 1f,
    1f, 1f};

3.2、为坐标分配本地内存地址:

    FloatBuffer vertexBuffer;
    vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
                .put(vertexData);
    vertexBuffer.position(0);

3.3、Shader编写

3.3.1、顶点着色器:vertex_shader.glsl

attribute vec4 v_Position;
attribute vec2 f_Position;
varying vec2 ft_Position;
void main() {
    ft_Position = f_Position;
    gl_Position = v_Position;
}

注: attribute 只能在vertex中使用;varying 用于vertex和fragment之间传递值。

3.3.2、片元着色器:fragment_shader.glsl

precision mediump float;
varying vec2 ft_Position;
uniform sampler2D sTexture;
void main() {
    gl_FragColor=texture2D(sTexture, ft_Position);
}

注: uniform 用于在application中向vertex和fragment中传递值。

3.4、OpenGL ES加载Shader

1、创建shader(着色器:顶点或片元)

int shader = GLES20.glCreateShader(shaderType);

2、加载shader源码并编译shader

GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);

3、检查是否编译成功:

GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);

4、创建一个渲染程序:

int program = GLES20.glCreateProgram();

5、将着色器程序添加到渲染程序中:

GLES20.glAttachShader(program, vertexShader);

6、链接源程序:

GLES20.glLinkProgram(program);

前面6个部分可以用一个工具类完成,步骤都是一样的

7、检查链接源程序是否成功

GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);

8、得到着色器中的属性:

int vPosition  = GLES20.glGetAttribLocation(program, "v_Position");

9、使用源程序:

GLES20.glUseProgram(program);

10、使顶点属性数组有效:

GLES20.glEnableVertexAttribArray(vPosition);

11、为顶点属性赋值:

GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8,vertexBuffer);

12、绘制图形:

GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

3.5、OpenGL ES绘制纹理过程

1、加载shader和生成program过程不变
如3.4
2、创建和绑定纹理:

GLES20.glGenTextures(1, textureId, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureid);

3、设置环绕和过滤方式
环绕(超出纹理坐标范围):(s==x t==y GL_REPEAT 重复)

GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);

过滤(纹理像素映射到坐标点):(缩小、放大:GL_LINEAR线性)

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

4、设置图片(bitmap)

GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);

5、绑定顶点坐标和纹理坐标

6、绘制图形

4、实现

4.1、加载shader、创建program(前6步)

import android.content.Context;
import android.opengl.GLES20;
import android.util.Log;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class JfShaderUtil {
    /**
     * 得到shade代码
     * @param context
     * @param rawId
     * @return
     */
    public static String getRawResource(Context context,int rawId){
        InputStream inputStream = context.getResources().openRawResource(rawId);
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        StringBuffer sb = new StringBuffer();
        String line;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line).append("\n");
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }


    /**
     * 加载着色器源码
     * @param shadeType 顶点、片元
     * @param source
     * @return
     */
    public static int loadShader(int shadeType,String source){
        //1、创建shader(着色器:顶点或片元)
        int shader = GLES20.glCreateShader(shadeType);
        if (shader != 0) {
            //2、加载shader源码并编译shader
            GLES20.glShaderSource(shader, source);
            GLES20.glCompileShader(shader);

            //3、检查是否编译成功
            int[] compile = new int[1];
            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS,compile,0);

            if (compile[0] != GLES20.GL_TRUE) {
                Log.e("zjf","编译失败");
                GLES20.glDeleteShader(shader);
                shader = 0;
            }
            return shader;
        } else {
            return 0;
        }
    }


    /**
     * 创建渲染程序
     * @param vertexSource
     * @param fragmentSoruce
     * @return
     */
    public static int createProgram(String vertexSource, String fragmentSoruce){
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,vertexSource);
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,fragmentSoruce);

        if (vertexShader != 0 && fragmentShader != 0) {
            //4、创建一个渲染程序
            int program = GLES20.glCreateProgram();
            //5、将着色器程序添加到渲染程序中
            GLES20.glAttachShader(program,vertexShader);
            GLES20.glAttachShader(program,fragmentShader);
            //6、链接源程序
            GLES20.glLinkProgram(program);
            return program;
        }
        return 0;
    }
}

4.2、渲染纹理

public class JfTextureRender implements JfEGLSurfaceView.JfGLRender {
    private Context context;

    //顶点坐标
    private float[] vertexData = {
            -1f,-1f,
            1f,-1f,
            -1f,1f,
            1f,1f
    };
    private FloatBuffer vertexBuffer;

    //纹理坐标
    private float[] fragmentData = {
            0f,1f,
            1f,1f,
            0f,0f,
            1f,0f
    };
    private FloatBuffer fragmentBuffer;


    private int program;//程序
    private int vPosition;//顶点着色器属性
    private int fPosition;//片元着色器属性
    private int textureId;
    private int sampler;

    public JfTextureRender(Context context){
        this.context = context;
        vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
                .put(vertexData);
        vertexBuffer.position(0);

        fragmentBuffer = ByteBuffer.allocateDirect(fragmentData.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
                .put(fragmentData);
        fragmentBuffer.position(0);
    }
    @Override
    public void onSurfaceCreated() {
        //获取源码
        String vertexSource = JfShaderUtil.getRawResource(context,R.raw.vertex_shader);
        String fragmentSource = JfShaderUtil.getRawResource(context,R.raw.fragment_shader);
        //前6步完成
        program = JfShaderUtil.createProgram(vertexSource,fragmentSource);

        //得到着色器中的属性
        vPosition = GLES20.glGetAttribLocation(program,"v_Position");
        fPosition = GLES20.glGetAttribLocation(program,"f_Position");
        sampler = GLES20.glGetUniformLocation(program,"sTexture");
        //开始绘制纹理,只绘制一个图片纹理
        int[] textureIds = new int[1];

        GLES20.glGenTextures(1,textureIds,0);//创建纹理
        textureId = textureIds[0];

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,textureId);//绑定纹理

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glUniform1i(sampler,0);

        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);

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

        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),R.mipmap.fengjing);
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D,0,bitmap,0);
        bitmap.recycle();
        bitmap = null;
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,0);//解绑纹理
    }

    @Override
    public void onSurfaceChanged(int width, int height) {
        GLES20.glViewport(0,0,width,height);
    }

    @Override
    public void onDrawFrame() {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glClearColor(1f,0f,0f,1f);

        //使用源程序
        GLES20.glUseProgram(program);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,textureId);//再次绑定纹理

        //使顶点属性数组有效
        GLES20.glEnableVertexAttribArray(vPosition);
        //为顶点属性赋值
        GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8,
                vertexBuffer);
        GLES20.glEnableVertexAttribArray(fPosition);
        GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8,
                fragmentBuffer);

        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,0);//再次解绑纹理
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_33872566/article/details/86984928