多重纹理用来组合多个纹理贴图。
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";
在app/src/main目录下新建assets文件夹,然后把文件放在这个文件夹中;
可以用7zip解压apk文件,看里面是否有assets文件夹,以及你放进去的文件。
也可以看app/app.iml文件,看assets文件夹是否在文件中有include进去。
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); }
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; }
如果是按照下面这样定义的话,得到的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
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