第一部分纹理基础
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