四、纹理显示图片

第一部分纹理基础

1)基础概念

在 OpenGLES 开发中,纹理除了用于装饰物体表面,还可以用来作为存储数据的容器。

纹理映射:纹理映射就是通过为图元的顶点坐标指定恰当的纹理坐标,通过纹理坐标在纹理图中选定特定的纹理区域,最后通过纹理坐标与顶点的映射关系,将选定的纹理区域映射到指定图元上。
纹理映射也称为纹理贴图,简单地说就是将纹理坐标(纹理坐标系)所指定的纹理区域,映射到顶点坐标(渲染坐标系或OpenGLES 坐标系)对应的区域。

纹理坐标【备注 opengles纹理坐真正的原点为左上角,向下为Y正方向,右为X正方向】

在这里插入图片描述

顶点坐标

在这里插入图片描述

这里在顶点数组编写的时候纹理坐标也需要对应。

绘制三角形:

编写代码的流程:

2)顶点着色器,片段着色器的修改

顶点数组中引入顶点索引位置+对应点的纹理坐标
float vertices[] = {
        //---- 位置 ----    - 纹理坐标 -
        0.5f,  0.5f, 0.0f,   1.0f, 1.0f,   // 右上
        0.5f, -0.5f, 0.0f,   1.0f, 0.0f,   // 右下
        -0.5f, -0.5f, 0.0f,  0.0f, 0.0f,   // 左下
        -0.5f,  0.5f, 0.0f,  0.0f, 1.0f    // 左上
};

顶点着色器

需要引入纹理属性,并传出out给片段着色器

#version 300 es
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec2 aTexCoord;

out vec2 TexCoord;

void main()
{
    gl_Position = vPosition;
    TexCoord = aTexCoord;
}

片段着色器

引入ourTexture 叠加顶点着色器传入的TexCoord组合成片段着色器传出的颜色。

#version 300 es
precision mediump float;
out vec4 fragColor;
in vec2 TexCoord;

//在调用glDrawElements之前绑定纹理了,它会自动把纹理赋值给片段着色器的采样器:
uniform sampler2D ourTexture;//纹理单元

void main()
{
	//texture 是OpenGL ES内置函数,称之为采样器,获取纹理上指定位置的颜色值。
    //ourTexture  绑定定义的纹理
    //TexCoord  由顶点坐标那边传来的纹理坐标
    //最后输出还可以把颜色也叠加上去
    fragColor = texture(ourTexture, TexCoord);
}

3)初始化时准备工作

索引数组

//索引数组 就是画矩形图片时对应的两个三角形坐标
unsigned int indices[] = {
        0, 1, 3, // first triangle
        1, 2, 3  // second triangle
};

绑定纹理相关

//将GL_ELEMENT_ARRAY_BUFFER缓冲区绑定到EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
//将索引坐标绑定到GL_ELEMENT_ARRAY_BUFFER
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

纹理属性绑定

1表示顶点着色器里面location里面的1属性,2表示从顶点数组取两个数据
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float ), (void *)(3*sizeof(float)));//这个与顶点数组的数据内容相关
glEnableVertexAttribArray(1);//启动

创建加载纹理

//创建纹理对象
glGenTextures(1, &texture);
//将纹理绑定到GL_TEXTURE_2D  纹理目标
glBindTexture(GL_TEXTURE_2D, texture);
//为当前绑定的纹理对象设定纹理环绕方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);//重复纹理的填充方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
//为当前绑定的纹理对象设定纹理过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);//缩小时线性插值
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//放到就是线性

绑定纹理数据

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texturewidth, textureheight, 0, GL_RGB, GL_UNSIGNED_BYTE, texturedata);
LOGD("CreateProgram %s", texturedata);
glGenerateMipmap(GL_TEXTURE_2D);//为当前绑定的纹理自动生成所有需要的多级渐远纹理
LOGD("CreateProgram END");

4)绘画纹理

//指定使用的着色器程序
glUseProgram(program);

glBindTexture(GL_TEXTURE_2D, texture);
LOGD("glBindTexture");
//绑定顶点数组
glBindVertexArray(VAO);
//画纹理
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);

第二部分项目工程部分

以三、画三角形的区别为例

Java端没有区别,都是一致的。

C++端存在区别,需引入纹理相关的

1)加载assets资源文件下面的图片资源并解析bmp的图片数据

在ReadFileUtil.h中添加

unsigned char *DecodeBMP(unsigned char *bmpFileData, int &width, int &height) {
    if (0x4D42 == *((short *) bmpFileData)) { // 数据头是否为0x4D42 判断是否是 24 位的位图,
        // 读格式头
        int pixelDataOffset = *((int *) (bmpFileData + 10));// 取出像素数据在内存块的偏移地址
        width = *((int *) (bmpFileData + 18));
        height = *((int *) (bmpFileData + 22));
        LOGD("pixelDataOffset %d", pixelDataOffset);
        unsigned char *pixelData = bmpFileData + pixelDataOffset;
        // 位图像素数据是 BGR 排布的,所以更换 r b 的位置
        for (int i = 0; i < width * height * 3; i += 3) {
            char temp = pixelData[i];
            pixelData[i] = pixelData[i + 2];
            pixelData[i + 2] = temp;
        }
        return pixelData;
    }
    LOGD("DecodeBMP END");
    return nullptr;
}

unsigned char *ReadBMP(char *bmpPath, int &width, int &height) {

    unsigned char *bmpFileContent = const_cast<unsigned char *>(LoadFileContent(bmpPath));
    if (bmpFileContent == NULL) {
        return 0;
    }
    unsigned char *pixelData = DecodeBMP(bmpFileContent, width, height);
    LOGD("pixelData %d, width = %d, height = %d",pixelData, width, height);
    if (pixelData == NULL) {
        delete[] bmpFileContent;
        return 0;
    }
    LOGD("ReadBMP END");
    return bmpFileContent;
}

2)在Nativity.cpp中调用解析bmp数据并赋值到纹理类中

JNIEXPORT void JNICALL NativeImpl_init(JNIEnv *env, jobject instance)
{
   TextureDemo::GetInstance();
   int width = 0, height = 0;
   unsigned char *img = ReadBMP("awesomeface.bmp", width, height);//解析获取图片数据
   TextureDemo::GetInstance()->getTexturedata(img, width, height);
   TextureDemo::GetInstance()->CreateProgram(
         reinterpret_cast<const char *>(LoadFileContent("vertex.vs")),
         reinterpret_cast<const char *>(LoadFileContent("fragment.fs")));
}

3)纹理实现类

TextureDemo.h

//
// Created by CreatWall_zhouwen on 2023/4/13.
//

#ifndef TWODRAWTEXTURE_TEXTUREDEMO_H
#define TWODRAWTEXTURE_TEXTUREDEMO_H


#include <GLES3/gl3.h>

class TextureDemo {
public:
    TextureDemo(){
        program = 0;
        vertexShaderHandle = 0;
        fragShaderHandle = 0;
    };
    ~TextureDemo(){};
    void CreateProgram(const char *ver, const char *frag);
    void Draw();
    void getTexturedata(unsigned char *data, int width, int height);
    static TextureDemo* GetInstance();
    static void DestroyInstance();
private:
    GLuint program;
    GLuint vertexShaderHandle;
    GLuint fragShaderHandle;
    GLuint VBO, VAO, EBO;
    unsigned int texture;
    unsigned char *texturedata;
    int texturewidth, textureheight;
};



#endif //TWODRAWTEXTURE_TEXTUREDEMO_H

TextureDemo.cpp

//
// Created by CreatWall_zhouwen on 2023/4/13.
//

#include "TextureDemo.h"
#include "Util.h"
TextureDemo* m_pContext = nullptr;
#define TAG "DRAWTEXTURE"


float vertices[] = {
        //---- 位置 ----    - 纹理坐标 -
        0.5f,  0.5f, 0.0f,   1.0f, 1.0f,   // 右上
        0.5f, -0.5f, 0.0f,   1.0f, 0.0f,   // 右下
        -0.5f, -0.5f, 0.0f,  0.0f, 0.0f,   // 左下
        -0.5f,  0.5f, 0.0f,  0.0f, 1.0f    // 左上
};
//索引数组
unsigned int indices[] = {
        0, 1, 3, // first triangle
        1, 2, 3  // second triangle
};

void TextureDemo::CreateProgram(const char *ver, const char *frag)
{
    LOGD("CreateProgram Enter");
    //程序中加载编译顶点着色器
    vertexShaderHandle = glCreateShader(GL_VERTEX_SHADER);//创建顶点着色器
    glShaderSource(vertexShaderHandle, 1, &ver, NULL);
    glCompileShader(vertexShaderHandle);
    GLint compiled = 0;
    glGetShaderiv(vertexShaderHandle, GL_COMPILE_STATUS, &compiled);//
    if (!compiled){
        LOGD("glCompileShader vertexShaderHandle error");
        return;
    }

    //程序中加载编译片段着色器
    fragShaderHandle = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragShaderHandle, 1, &frag, NULL);
    glCompileShader(fragShaderHandle);
    glGetShaderiv(fragShaderHandle, GL_COMPILE_STATUS, &compiled);
    if(!compiled){
        LOGD("glCompileShader fragShaderHandle error");
        return;
    }

    //创建程序
    program = glCreateProgram();
    if (program)
    {
        //添加顶点着色器
        GLint AttachStatus = GL_FALSE;
        glAttachShader(program, vertexShaderHandle);
        glGetShaderiv(vertexShaderHandle, GL_ATTACHED_SHADERS, &AttachStatus);
        if(AttachStatus == 1){
            LOGD("glAttachShader vertexShaderHandle error");
            return;
        }

        //添加片段着色器
        glAttachShader(program, fragShaderHandle);
        glGetShaderiv(fragShaderHandle, GL_ATTACHED_SHADERS, &AttachStatus);
        if(AttachStatus == 2){
            LOGD("glAttachShader fragShaderHandle error");
            return;
        }

        //链接
        glLinkProgram(program);
        GLint linkStatus = GL_FALSE;
        glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
        if (linkStatus != GL_TRUE){
            LOGD("glLinkProgram error");
            return;
        }

        //成功连接到程序之后就可以解绑删除
        glDetachShader(program, vertexShaderHandle);
        glDeleteShader(vertexShaderHandle);
        glDetachShader(program, fragShaderHandle);
        glDeleteShader(fragShaderHandle);
        vertexShaderHandle = 0;
        fragShaderHandle = 0;
    }
    LOGD("program success");
    /* -----顶点相关------ */
    //创建顶点数组
    glGenVertexArrays(1, &VAO);
    //创建顶点缓冲区
    glGenBuffers(1, &VBO);
    //绑定顶点数组
    glBindVertexArray(VAO);
    //将顶点缓冲区绑定为GL_ARRAY_BUFFER
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    //将定义的顶点数组绑定到GL_ARRAY_BUFFER这个缓存中,这个缓存对应顶点缓冲区数据
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    /* -----绑定纹理相关------ */
    //将GL_ELEMENT_ARRAY_BUFFER缓冲区绑定到EBO
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    //将索引坐标绑定到GL_ELEMENT_ARRAY_BUFFER
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    //指定配置的顶点属性,第一个index参数对应顶点着色器的location值
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float ), (void *)0);
    //启用顶点属性
    glEnableVertexAttribArray(0);

    //纹理属性绑定
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5*sizeof(float ), (void *)(3*sizeof(float)));
    glEnableVertexAttribArray(1);

    //将顶点数组和顶点缓冲区置空则不让其他修改
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    /* -----创建加载纹理------ */
    //创建纹理
    glGenTextures(1, &texture);
    //将纹理绑定到GL_TEXTURE_2D
    glBindTexture(GL_TEXTURE_2D, texture);
    //为当前绑定的纹理对象设定纹理环绕方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);//重复纹理的填充方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    //为当前绑定的纹理对象设定纹理过滤方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);//缩小时线性插值
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//放到就是线性

    //绑定纹理数据
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texturewidth, textureheight, 0, GL_RGB, GL_UNSIGNED_BYTE, texturedata);
    LOGD("CreateProgram %s", texturedata);
    glGenerateMipmap(GL_TEXTURE_2D);//为当前绑定的纹理自动生成所有需要的多级渐远纹理
    LOGD("CreateProgram END");
}
void TextureDemo::Draw()
{
    glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearColor(0.2f, 0.9f, 0.3f, 1.0f);
    //指定使用的着色器程序
    glUseProgram(program);

    glBindTexture(GL_TEXTURE_2D, texture);
    LOGD("glBindTexture");
    //绑定顶点数组
    glBindVertexArray(VAO);
    //画纹理
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);

}
TextureDemo* TextureDemo::GetInstance()
{
    if (m_pContext == nullptr)
    {
        m_pContext = new TextureDemo();
    }
    return m_pContext;
}
void TextureDemo::DestroyInstance()
{
    if (m_pContext)
    {
        delete m_pContext;
        m_pContext = nullptr;
    }
}

void TextureDemo::getTexturedata(unsigned char *data, int width, int height)
{
    texturedata = data;
    texturewidth = width;
    textureheight = height;
}

安卓配置部分与三、画三角形一致。

引入资源与三、画三角形一致,只是多出一个解析bmp图片数据的接口

参考链接

OpenGL ES 3.0 开发之纹理贴图 https://blog.csdn.net/qq_39792615/article/details/113394702

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值