【openGLES3.0编程指南笔记-9】多重纹理
目录
概述
1. 多重纹理
多重纹理用来组合多个纹理贴图。
void Draw(MYESContext *myesContext)
{
myUserData *userData = (myUserData *)myesContext->userData;
// 两个纹理共用同一组纹理坐标和顶点坐标了
GLfloat vVertices[] = {
-0.5f, 0.5f, 0.0f, // Position 0
0.0f, 0.0f, // TexCoord 0
-0.5f, -0.5f, 0.0f, // Position 1
0.0f, 1.0f, // TexCoord 1
0.5f, -0.5f, 0.0f, // Position 2
1.0f, 1.0f, // TexCoord 2
0.5f, 0.5f, 0.0f, // Position 3
1.0f, 0.0f // TexCoord 3
};
GLushort indices[] = {0, 1, 2, 0, 2, 3};
glViewport(0, 0, myesContext->width, myesContext->height);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(userData->programObject);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), vVertices);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), &vVertices[3]);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
// 激活纹理0:GL_TEXTURE0
glActiveTexture(GL_TEXTURE0);
// 绑定第一个纹理
glBindTexture(GL_TEXTURE_2D, userData->baseMapTexId);
// 将0传给片段着色器,说明是GL_TEXTURE0
glUniform1i(userData->baseMapLoc, 0);
// 激活纹理1:GL_TEXTURE1
glActiveTexture(GL_TEXTURE1);
// 绑定第二个纹理
glBindTexture(GL_TEXTURE_2D, userData->lightMapTexId);
// 将1传给片段着色器,说明是GL_TEXTURE1
glUniform1i(userData->lightMapLoc, 1);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
}
char fShaderStr[] =
"#version 300 es \n"
"precision mediump float; \n"
"in vec2 v_texCoord; \n"
"layout(location = 0) out vec4 outColor; \n"
"uniform sampler2D s_baseMap; \n" // 纹理0
"uniform sampler2D s_lightMap; \n" // 纹理1
"void main() \n"
"{ \n"
" vec4 baseColor; \n"
" vec4 lightColor; \n"
" baseColor = texture(s_baseMap, v_texCoord); \n" // 用同一组纹理坐标
" lightColor = texture(s_lightMap, v_texCoord); \n" // 用同一组纹理坐标
" outColor = baseColor * (lightColor + 0.25); \n" // 两个纹理进行组合操作
"} \n";
2. assets文件的使用
2.1 新建assets文件夹
在app/src/main目录下新建assets文件夹,然后把文件放在这个文件夹中;
可以用7zip解压apk文件,看里面是否有assets文件夹,以及你放进去的文件。
也可以看app/app.iml文件,看assets文件夹是否在文件中有include进去。
2.2 assets文件打开以及使用
char* esLoadTGA(void *ioContext, const char *fileName, int *width, int *height)
{
char *buffer;
TGA_HEADER Header;
int bytesRead;
if (ioContext != NULL) {
// 这个assetManager的值是在android_main函数传入进来的
// myesContext.platformData = (void *)pApp->activity->assetManager;
AAssetManager *assetManager = (AAssetManager *)ioContext;
if (assetManager != nullptr) {
// 1. 打开文件夹;我这里如果只是单纯的打开文件,它会返回null的,不知道是什么原因;所以这里先打开文件夹
AAssetDir *assetDir = AAssetManager_openDir(assetManager, "");
if (assetDir != nullptr) {
const char *filename = (const char *)NULL;
// 2. 获取文件夹里面的文件的文件名
while ((filename = AAssetDir_getNextFileName(assetDir)) != NULL) {
// 3. 然后打开文件
AAsset *asset =
AAssetManager_open(assetManager, filename, AASSET_MODE_BUFFER);
if (asset != nullptr) {
// 4. 如果是你想要打开的文件
if (strcmp(filename, fileName) == 0) {
// 5. 读取tga文件的头
AAsset_read(asset, &Header, sizeof(TGA_HEADER));
*width = Header.Width;
*height = Header.Height;
esLogMessage("size of Header = %d", sizeof(TGA_HEADER));
esLogMessage("idsize = %d,MapType = %d,ImageType = %d,PaletteStart=%d,PaletteSize=%d,x = %d,y = %d,width = %d,height = %d",
Header.IdSize,Header.MapType,Header.ImageType,Header.PaletteStart,Header.PaletteSize, Header.X, Header.Y, *width, Header.Height);
if (Header.ColorDepth == 8 ||
Header.ColorDepth == 24 || Header.ColorDepth == 32) {
esLogMessage("Header color depth");
// 根据图片的长和宽,以及位深,来分配buffer的大小
int bytesToRead = sizeof(char) * (*width)*(*height)*Header.ColorDepth / 8;
buffer = (char *)malloc(bytesToRead);
if (buffer) {
esLogMessage("buffer not null");
// 6. 读取tga文件的数据
bytesRead = AAsset_read(asset, buffer, bytesToRead);
// 最后关闭文件和文件夹
AAsset_close(asset);
AAssetDir_close(assetDir);
// 返回文件数据
return (buffer);
}
}
} else {
esLogMessage("not the file %s", filename);
}
AAsset_close(asset);
} else {
esLogMessage("Failed to open test file");
}
}
AAssetDir_close(assetDir);
} else {
esLogMessage("Failed to open root directory");
}
} else {
esLogMessage("Asset Manager was invalid");
}
}
return (NULL);
}
3. tga文件头格式
typedef struct
{
byte identsize; // size of ID field that follows 18 byte header (0 usually)
byte colourmaptype; // type of colour map 0=none, 1=has palette
byte imagetype; // type of image 0=none,1=indexed,2=rgb,3=grey,+8=rle packed
short colourmapstart; // first colour map entry in palette
short colourmaplength; // number of colours in palette
byte colourmapbits; // number of bits per palette entry 15,16,24,32
short xstart; // image x origin
short ystart; // image y origin
short width; // image width in pixels 图片宽度
short height; // image height in pixels 图片高度
byte bits; // image bits per pixel 8,16,24,32 图片的位深
byte descriptor; // image descriptor bits (vh flip bits)
// pixel data follows header
} TGA_HEADER
源码解析
#include "esUtil.h"
#include <stdlib.h>
typedef struct
{
GLuint programObject;
GLuint baseMapLoc;
GLuint lightMapLoc;
GLuint baseMapTexId;
GLuint lightMapTexId;
} myUserData;
GLuint LoadTexture(void *ioContext, char * fileName)
{
int width, height;
char *buffer = esLoadTGA(ioContext, fileName, &width, &height);
GLuint texId;
if (buffer == NULL) {
esLogMessage("Error loading (%s) image.\n", fileName);
}
glGenTextures(1, &texId);
glBindTexture(GL_TEXTURE_2D, texId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
free(buffer);
return texId;
}
int Init(MYESContext *myesContext)
{
myUserData *userData = (myUserData *)myesContext->userData;
char vShaderStr[] =
"#version 300 es \n"
"layout(location = 0) in vec4 a_position; \n"
"layout(location = 1) in vec2 a_texCoord; \n"
"out vec2 v_texCoord; \n"
"void main() \n"
"{ \n"
" gl_Position = a_position; \n"
" v_texCoord = a_texCoord; \n"
"} \n";
char fShaderStr[] =
"#version 300 es \n"
"precision mediump float; \n"
"in vec2 v_texCoord; \n"
"layout(location = 0) out vec4 outColor; \n"
"uniform sampler2D s_baseMap; \n"
"uniform sampler2D s_lightMap; \n"
"void main() \n"
"{ \n"
" vec4 baseColor; \n"
" vec4 lightColor; \n"
" baseColor = texture(s_baseMap, v_texCoord); \n"
" lightColor = texture(s_lightMap, v_texCoord); \n"
" outColor = baseColor * (lightColor + 0.25); \n"
"} \n";
userData->programObject = myesLoadProgram(vShaderStr, fShaderStr);
userData->baseMapLoc = glGetUniformLocation(userData->programObject, "s_baseMap");
userData->lightMapLoc = glGetUniformLocation(userData->programObject, "s_lightMap");
userData->baseMapTexId = LoadTexture(myesContext->platformData, "basemap.tga");
userData->lightMapTexId = LoadTexture(myesContext->platformData, "lightmap.tga");
if (userData->baseMapTexId == 0 || userData->lightMapTexId == 0) {
return GL_FALSE;
}
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
return GL_TRUE;
}
void Draw(MYESContext *myesContext)
{
myUserData *userData = (myUserData *)myesContext->userData;
GLfloat vVertices[] = {
-0.5f, 0.5f, 0.0f, // Position 0
0.0f, 0.0f, // TexCoord 0
-0.5f, -0.5f, 0.0f, // Position 1
0.0f, 1.0f, // TexCoord 1
0.5f, -0.5f, 0.0f, // Position 2
1.0f, 1.0f, // TexCoord 2
0.5f, 0.5f, 0.0f, // Position 3
1.0f, 0.0f // TexCoord 3
};
GLushort indices[] = {0, 1, 2, 0, 2, 3};
glViewport(0, 0, myesContext->width, myesContext->height);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(userData->programObject);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), vVertices);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), &vVertices[3]);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, userData->baseMapTexId);
glUniform1i(userData->baseMapLoc, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, userData->lightMapTexId);
glUniform1i(userData->lightMapLoc, 1);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
}
void ShutDown(MYESContext *myesContext)
{
myUserData *userData = (myUserData *)myesContext->userData;
glDeleteTextures(1, &userData->baseMapTexId);
glDeleteTextures(1, &userData->lightMapTexId);
glDeleteProgram(userData->programObject);
}
int myesMain(MYESContext *myesContext)
{
myesContext->userData = malloc(sizeof(myUserData));
myesCreateWindow(myesContext, "10_1_multitexture", 320, 240, MY_ES_WINDOW_RGB);
if (!Init(myesContext))
{
return GL_FALSE;
}
esRegisterDrawFunc(myesContext, Draw);
esRegisterShutdownFunc(myesContext, ShutDown);
return GL_TRUE;
}
问题
1. struct对齐问题
如果是按照下面这样定义的话,得到的sizeof(TGA_HEADER)是20个字节,比定义多了2个字节;得到的位深是错的,所以没有读取到图片的数据,所以得到的结果是一张黑黑的图片
typedef struct
{
unsigned char IdSize, MapType, ImageType;
unsigned short PaletteStart, PaletteSize;
unsigned char PaletteEntryDepth;
unsigned short X, Y, Width, Height;
unsigned char ColorDepth, Descriptor;
} TGA_HEADER;
I/esUtil: size of Header = 20
// width和height都是错的
idsize = 0,MapType = 0,ImageType = 2,PaletteStart=0,PaletteSize=0,x = 0,y = 512,width = 512,height = 24
not the file lightmap.tga
Error loading (basemap.tga) image.
I/esUtil: not the file basemap.tga
I/esUtil: size of Header = 20
idsize = 0,MapType = 0,ImageType = 2,PaletteStart=0,PaletteSize=0,x = 0,y = 256,width = 256,height = 24
Error loading (lightmap.tga) image.
但是如果把PaletteEntryDepth去掉的话,得到的sizeof(TGA_HEADER)是18个字节,就正常了;就不知道是什么原因导致的
typedef struct
{
unsigned char IdSize, MapType, ImageType;
unsigned short PaletteStart, PaletteSize;
//unsigned char PaletteEntryDepth;
unsigned short X, Y, Width, Height;
unsigned char ColorDepth, Descriptor;
} TGA_HEADER;
05-15 09:25:54.184 5805 5842 I esUtil : Error loading (lightmap.tga) image.
05-15 09:28:21.042 6360 6416 I esUtil : size of Header = 18
05-15 09:28:21.042 6360 6416 I esUtil : idsize = 0,MapType = 0,ImageType = 2,PaletteStart=0,PaletteSize=0,x = 0,y = 0,width = 512,height = 512
05-15 09:28:21.042 6360 6416 I esUtil : Header color depth
05-15 09:28:21.042 6360 6416 I esUtil : buffer not null
05-15 09:28:21.050 6360 6416 I esUtil : not the file basemap.tga
05-15 09:28:21.050 6360 6416 I esUtil : size of Header = 18
05-15 09:28:21.050 6360 6416 I esUtil : idsize = 0,MapType = 0,ImageType = 2,PaletteStart=0,PaletteSize=0,x = 0,y = 0,width = 256,height = 256
05-15 09:28:21.050 6360 6416 I esUtil : Header color depth
05-15 09:28:21.050 6360 6416 I esUtil : buffer not null
2. assets单独打开文件,会返回null
static esFile *esFileOpen ( void *ioContext, const char *fileName )
{
esFile *pFile = NULL;
if ( ioContext != NULL )
{
AAssetManager *assetManager = ( AAssetManager * ) ioContext;
// 如果不先打开文件夹,就根据文件名打开文件的话,是会返回null的,不知道是什么原因
// pFile = null,最后得到的也是一张黑图
pFile = AAssetManager_open ( assetManager, fileName, AASSET_MODE_BUFFER );
}
return pFile;
}
效果图
basemap.tga图片
lightmap.tga图片
合并之后的效果图:
参考
1. Why does AAssetDir_getNextFileName always return null?
https://stackoverflow.com/questions/55911059/why-does-aassetdir-getnextfilename-always-return-null
2. TGA File Format
https://gshaw.ca/closecombat/formats/tga.html