Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the antispam-bee domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /home/ruwritingagame/public_html/wp-includes/functions.php on line 6121
Глава 11. Текстуры – Игра в Написание Игры

Глава 11. Текстуры

1. Для начала нужно загрузить текстуру в программу. Какая-нибудь image-loading библиотечка, поддерживающая несколько популярных форматов кажется неплохим решением. Мне понравилась stb_image.h от Шона Барретта, особенно то, что это 1 h-файл, а не lib или dll.

Скачать можно здесь.


Сохраним ее в C:\CPP\engine folder (там же где и linmath.h ранее).


2. Потом нужна сама картинка.

Создадим для нее каталог: \dt под C:\CPP\a999hello (этот каталог должен принадлежать проекту, а НЕ движку (engine)).

ВАЖНО: размеры картинки ДОЛЖНЫ быть степенями двойки.
У нижеприведенной sample_img.png размер 512×256:

Сгрузить можно здесь.


Сохраним ее в C:\CPP\a999hello\dt


Windows

3. Запускаем Visual Studio. Открываем C:\CPP\a999hello\p_windows\p_windows.sln


4. Добавим новый каталог /dt в Post-Build xcopy инструкции:

Открываем p_windows project Properties, All Configurations / x86, Build events -> Post-Build Event -> Command Line -> Edit.

Добавляем новую строку

xcopy "..\dt\*.*" "$(TargetDir)dt\" /E /R /D /y

так что теперь у нас 2 xcopy команды.

Ok, Apply, Ok.


5. Откроем TheGame.cpp. Нужно поменять кое-что:

  • Нужна ссылка на stb_image.h.
  • Немного другой набор переменных.
  • Новая структура вертексов: 3D координаты + 2D координаты текстуры вместо просто координат.
  • Понадобятся 4 вертекса вместо трех и примитив GL_TRIANGLE_STRIP вместо GL_TRIANGLES.
  • Еще нужны чтение изображения и загрузка его в текстуру.
  • И передача id текстуры и текстурные коодинаты по каждому вертексу в новую шейдер-программу.

ЗаменимTheGame.cpp код на:

#include "TheGame.h"
#include "platform.h"
#include "linmath.h"
#include "utils.h"
#include "Shader.h"

extern std::string filesRoot;

#define STB_IMAGE_IMPLEMENTATION  //required by stb_image.h
#include "stb_image.h"

static const struct
{
    float x, y, z, tu, tv;
} vertices[4] =
{
    { -0.5f,  0.5f, 0.f, 0.f, 0.f }, //top-left
    { -0.5f, -0.5f, 0.f, 0.f, 1.f }, //bottom-left
    {  0.5f,  0.5f, 0.f, 1.f, 0.f }, //top-right
    {  0.5f, -0.5f, 0.f, 1.f, 1.f }  //bottom-right
};
unsigned int vao_buffer, vertex_buffer, shaderProgramId, textureId;
int umvp_location, apos_location, atuv_location;
float angle_z = 0;

int TheGame::getReady() {
    bExitGame = false;

    glGenVertexArrays(1, &vao_buffer);
    glBindVertexArray(vao_buffer);

    glGenBuffers(1, &vertex_buffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    shaderProgramId = Shader::linkShaderProgram((filesRoot + "/dt/shaders/flat_tex_v.txt").c_str(), (filesRoot + "/dt/shaders/flat_tex_f.txt").c_str());

    umvp_location = glGetUniformLocation(shaderProgramId, "uMVP");

    apos_location = glGetAttribLocation(shaderProgramId, "aPos");
    glEnableVertexAttribArray(apos_location);
    glVertexAttribPointer(apos_location, 3, GL_FLOAT, GL_FALSE,
        sizeof(vertices[0]), (void*)0);

    atuv_location = glGetAttribLocation(shaderProgramId, "aTuv");
    glEnableVertexAttribArray(atuv_location);
    glVertexAttribPointer(atuv_location, 2, GL_FLOAT, GL_FALSE,
        sizeof(vertices[0]), (void*)(sizeof(float) * 3));

    // loading an image
    int imgWidth, imgHeight, nrChannels;
    unsigned char* imgData = stbi_load((filesRoot + "/dt/sample_img.png").c_str(),
        &imgWidth, &imgHeight, &nrChannels, 4); //"4"-convert to 4 channels -RGBA
    if (imgData == NULL) {
        mylog("ERROR loading image\n");
    }
    // generate texture
    unsigned int textureId;
    glGenTextures(1, &textureId);
    glBindTexture(GL_TEXTURE_2D, textureId);
    // set the texture wrapping/filtering options (on the currently bound texture object)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // attach/load image data
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imgWidth, imgHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, imgData);
    glGenerateMipmap(GL_TEXTURE_2D);
    // release image data
    stbi_image_free(imgData);

    //pass textureId to shader program
    glActiveTexture(GL_TEXTURE0); // activate the texture unit first before binding texture
    glBindTexture(GL_TEXTURE_2D, textureId);

    return 1;
}
int TheGame::drawFrame() {
    myPollEvents();

    mat4x4 m, p, mvp;

    //glClearColor(0.0, 0.0, 0.5, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    angle_z += 0.01f;
    mat4x4_identity(m);
    mat4x4_rotate_Z(m, m, angle_z);
    mat4x4_scale_aniso(m, m, 2.0, 1.0, 1.0);
    mat4x4_ortho(p, -screenRatio, screenRatio, -1.f, 1.f, 1.f, -1.f);
    mat4x4_mul(mvp, p, m);

    glUseProgram(shaderProgramId);
    glUniformMatrix4fv(umvp_location, 1, GL_FALSE, (const GLfloat*)mvp);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    mySwapBuffers();
    return 1;
}
int TheGame::cleanUp() {
    return 1;
}
int TheGame::onScreenResize(int width, int height) {
    if (screenSize[0] == width && screenSize[1] == height)
        return 0;
    screenSize[0] = width;
    screenSize[1] = height;
    screenRatio = (float)width / (float)height;
    glViewport(0, 0, width, height);
    mylog(" screen size %d x %d\n", width, height);
    return 1;
}
int TheGame::run() {
    getReady();
    while (!bExitGame) {
        drawFrame();
    }
    cleanUp();
    return 1;
}


  1. И, конечно, новые шейдеры:

Vertex shader:

#version 320 es
precision lowp float;
uniform mat4 uMVP; // transform matrix (Model-View-Projection)
in vec3 aPos; //attribute position (3D coordinates)
in vec2 aTuv; //attribute TUV (texture coordinates)
out vec2 vTuv; //varying TUV (pass to fragment shader)
void main(){
  gl_Position = uMVP * vec4(aPos, 1.0);
  vTuv = aTuv;
}

Копируем этот код в Текстовый редактор и сохраняем его как txt файл в

C:\CPP\engine\dt\shaders\flat_tex_v.txt

Fragment shader:

#version 320 es
precision lowp float;
uniform sampler2D uTex0;  //texture id
in vec2 vTuv; //varying TUV (passed from vertex shader)
out vec4 FragColor; // output pixel color
void main(){
  FragColor = texture(uTex0, vTuv);
}

Копируем этот код в Текстовый редактор и сохраняем его как txt файл в

C:\CPP\engine\dt\shaders\flat_tex_f.txt


7. Компиляция и запуск. Результат:


Теперь – на

Андроиде

На этот раз ВСЕ программные изменения были на cross-platform-енной стороне, так что здесь даже менять ничего не нужно… Кроме stb_image.h и xcopy post-build инструкции.

8. Пере-запускаем Visual Studio. Открываем C:\CPP\a999hello\p_android\p_android.sln.


9. Идем в p_android.NativeActivity project Properties, All Configurations / ARM64, Build Events -> Post-Build Event -> Command Line -> Edit

Добавляем новую строку:

xcopy "..\..\dt\*.*" "..\$(RootNamespace).Packaging\assets\dt\" /E /R /D /y

Ok, Apply, Ok.


9. Теперь, right-click на xEngine, Add -> Existing Item,

C:\CPP\engine\stb_image.h

Add.


10. Включаем Android, разблокируем, подключаем, разрешаем.

Re-build и запуск.

Отлично, как и ожидалось.


Кстати, если Вы – новичок в программировании, то теперь Вы можете скромно упомянуть в своем резюме, что в совершенстве владеете следующими технологиями:

  • C++
  • graphics
  • 3D
  • Visual Studio
  • Cross-platform
  • Android
  • Windows
  • OpenGL ES
  • GLSL

Ну, “в совершенстве” – пока, может, и преувеличение, но все остальное – чистая правда.


Leave a Reply

Your email address will not be published. Required fields are marked *