【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图片

image

lightmap.tga图片

image

合并之后的效果图:

image

参考

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
posted @ 2021-05-15 17:36  pyjetson  阅读(344)  评论(0编辑  收藏  举报