Глава 3. Hello Windows

Visual Studio, Windows, C++, OpenGL ES, GLFW, GLAD

Запуск OpenGL ES на PC не такой прямолинейный как на Android-е поскольку GL ES предназначен для мобильников, а НЕ для десктопов. Но варианты есть. Издатели OpenGL-а настоятельно рекомендуют использовать window toolkit (GLFW) и OpenGL loading libraries (GLAD). Значит, давайте их сгрузим.

1. Сперва зарезервируем для них место.
В Windows File Explorer-е в нашем корневом C:\CPP создадим под-каталог “p_windows” (“p” is for “platform”). Там мы будем держать windows-specific вещи, применимые ко ВСЕМ проектам.


  1. Download GLFW.

Идем на https://www.glfw.org/

Переходим на Download link который сверху:

Нам нужны 32-bit Windows pre-compiled binaries. Почему 32? Потому что они работают на 32 И на 64-bit Windows тоже, так что выбираем 32 как более “cross-platform-енный”:

ИЛИ: можно сгрузить здесь.

Когда сгрузилось, в Windows File Explorer-е идем в каталог Downloads, разворачиваем glfw-3.3.5.bin.WIN32.zip (right-click на файл -> Open with -> 7-Zip File Manager -> Extract).

ВАЖНО: Поменять destination (Copy to) на C:\CPP\p_windows\

Затем – Ok.

В Windows File Explorer-е идем в каталог C:\CPP\p_windows\ и переименуем извлеченный glfw-3.3.5.bin.WIN32 каталог в glfw335win32.


  1. Download GLAD.

Во-первых – под C:\CPP\p_windows\ добавим новый под-каталог “glad“.

Теперь идем на https://glad.dav1d.de/

  • Language: C/C++,
  • Specification: OpenGL
  • Меняем Profile на Core,
  • Под gles2 выбираем Version 3.2:

Scroll down (прокручиваем вниз).

Оставляем “Generate a loader” отмеченным.

Жмем Generate:

На следующем экране сгружаем “glad.zip”.

ИЛИ: можно сгрузить здесь.

Когда сгрузилось, идем в каталог Downloads и разворачиваем glad.zip в C:\CPP\p_windows\glad.

Теперь наша структура такая:


  1. Create VS project.

На GLFW вебсайте есть хороший пример/обучалка, которым мы и воспользуемся. Этот пример написан под OpenGL ES 2.0. Под 3.2 пришлось его маленько подрихтовать. Для создания окна пример использует GLFW, так что мы создадим не “Windows”, а Console project.

Запускаем Visual Studio, Create a new project, выбираем Windows фильтр, выбираем Windows Desktop Wizard:

Затем – Next.

  • Project name – “p_windows” (“p” for “platform”).
  • Location – C:\CPP\a999hello
  • Place solution and project in the same directory – yes:

Затем – Create.

На следующем экране выбираем “Console Application (.exe)” и “Empty project”:

Затем – Ok.


  1. Source code.

Right-click на “p_windows” project (не на solution), затем Add -> New item.

На <Add New Item> экране выбираем “C++ File (.cpp)”, name – “main.cpp”:

Затем – Add.

Copy-Paste следующий код в main.cpp:

#include <glad/glad.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>

#include "linmath.h"

#include <stdlib.h>
#include <stdio.h>

static const struct
{
    float x, y;
    float r, g, b;
} vertices[3] =
{
    { -0.6f, -0.4f, 1.f, 0.f, 0.f },
    {  0.6f, -0.4f, 0.f, 1.f, 0.f },
    {   0.f,  0.6f, 0.f, 0.f, 1.f }
};

static const char* vertex_shader_text =
"#version 320 es\n"
"precision lowp float;\n"
"uniform mat4 MVP;\n"
"in vec3 vCol;\n"
"in vec2 vPos;\n"
"out vec3 color;\n"
"void main()\n"
"{\n"
"    gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n"
"    color = vCol;\n"
"}\n";

static const char* fragment_shader_text =
"#version 320 es\n"
"precision lowp float;\n"
"in vec3 color;\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
"    FragColor = vec4(color, 1.0);\n"
"}\n";

static void error_callback(int error, const char* description)
{
    fprintf(stderr, "Error: %s\n", description);
}

static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GLFW_TRUE);
}

int main(void)
{
    GLFWwindow* window;
    unsigned int vao_buffer, vertex_buffer, vertex_shader, fragment_shader, program;
    int mvp_location, vpos_location, vcol_location;
    float angle_z = 0;

    glfwSetErrorCallback(error_callback);

    if (!glfwInit())
        exit(EXIT_FAILURE);

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);

    window = glfwCreateWindow(640, 480, "OurProject", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        exit(EXIT_FAILURE);
    }

    glfwSetKeyCallback(window, key_callback);

    glfwMakeContextCurrent(window);
    gladLoadGLES2Loader((GLADloadproc)glfwGetProcAddress); //gladLoadGL(glfwGetProcAddress);
    glfwSwapInterval(1);

    // NOTE: OpenGL error checks have been omitted for brevity

    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);

    vertex_shader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL);
    glCompileShader(vertex_shader);

    fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL);
    glCompileShader(fragment_shader);

    program = glCreateProgram();
    glAttachShader(program, vertex_shader);
    glAttachShader(program, fragment_shader);
    glLinkProgram(program);

    mvp_location = glGetUniformLocation(program, "MVP");
    vpos_location = glGetAttribLocation(program, "vPos");
    vcol_location = glGetAttribLocation(program, "vCol");

    glEnableVertexAttribArray(vpos_location);
    glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE,
        sizeof(vertices[0]), (void*)0);
    glEnableVertexAttribArray(vcol_location);
    glVertexAttribPointer(vcol_location, 3, GL_FLOAT, GL_FALSE,
        sizeof(vertices[0]), (void*)(sizeof(float) * 2));

    while (!glfwWindowShouldClose(window))
    {
        float ratio;
        int width, height;
        mat4x4 m, p, mvp;

        glfwGetFramebufferSize(window, &width, &height);
        ratio = width / (float)height;

        glViewport(0, 0, width, height);
        glClear(GL_COLOR_BUFFER_BIT);

        mat4x4_identity(m);
        mat4x4_rotate_Z(m, m, (float)glfwGetTime());
        mat4x4_ortho(p, -ratio, ratio, -1.f, 1.f, 1.f, -1.f);
        mat4x4_mul(mvp, p, m);

        glUseProgram(program);
        glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*)mvp);
        glDrawArrays(GL_TRIANGLES, 0, 3);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwDestroyWindow(window);

    glfwTerminate();
    exit(EXIT_SUCCESS);
}

  • ES 3.2-related изменения отмечены.

6. Еще в этом примере используется “linmath.h” – библиотечка для линейной алгебры.

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

Поскольку мы будем использовать ее и в других проектах, расположим ее вне a999hello каталога.

В Windows File Explorer-е создадим новый под-каталог: C:\CPP\engine\

Копируем сгруженный linmath.h файл из каталога Downloads в

C:\CPP\engine\

В Visual Studio right-click на p_windows project -> Add -> New Filter.

Name – xEngine

  • “Filter” – это типа виртуального под-каталога в VS проекте.
  • “x” перед “Engine” – просто чтоб он был под “Source Files” (он будет “под” когда вернемся в VS в следующий раз).

Теперь – right-click на xEngine -> Add -> Existing Item

C:\CPP\engine\linmath.h


7. Также в проект нужно добавить файл glad.c (из GLAD каталога).

Right-click на “Source Files” -> Add -> Existing item

C:\CPP\p_windows\glad\src\glad.c

Add.


  1. Теперь надо задать ссылки на сгруженные библиотеки.

Right-click на “p_windows” project -> Properties.

ВАЖНО: Поменять Configuration с “Debug” на “All configurations”. Поменять platform на Win32 (x64 работать не будет из-за GLFW32):

Теперь идем в Configuration properties -> C++ -> General и открываем Additional Include Directories -> Edit:

В Edit window добавляем новую строку:

и идем на C:\CPP\p_windows\glad\include, Select Folder

Теперь добавляем еще одну строку и идем на

C:\CPP\p_windows\glfw335win32\include, Select Folder.

Добавляем еще строку и идем на

C:\CPP\engine, Select Folder.

Теперь у нас:

Затем – Ok.


9. Теперь переходим в Configuration properties -> Linker -> General и открываем Additional Library Directories -> Edit.

Добавляем новую строку и идем на C:\CPP\p_windows\glfw335win32\lib-vc2022

Select Folder, Ok.


10. Теперь переходим в Configuration properties -> Linker -> Input и открываем Additional Dependencies -> Edit.

Copy-Paste следующую строку (3 библиотеки):

opengl32.lib; glfw3.lib; glfw3dll.lib

Ok, Apply, Ok.


11. Еще: Мы же не хотим, чтобы наш екзешник назывался “p_windows.exe” (как это задано названием проекта). Название можно поменять в project properties.

Right-click на p_windows project -> Properties -> Configuration Properties -> General, выбираем Target name.

Чтобы изменить – жмем стрелку вниз, и Edit.

Поменяем имя на “OurProject”.

Затем – Ok, Apply, Ok.


12. Теперь запуск (зеленая стрелка).

  • Проверьте что выбранная платформа (в верхнем меню) x86 (32 бита, поскольку x64 работать не будет из-за GLFW32).

Работает:

Пример работает, но у нас ДВА окна: Console window И Application window. Причина в том, что мы не хотели создавать окно по-Windows-овски, и поэтому выбрали “Console application” и создали окно через GLFW.


Как убрать окно Консоли:

13. Вообще-то, иметь Console window совсем даже неплохо, поскольку оно может принимать printf команды (OpenGL окно – НЕ может), что бесценно для разработки и отладки, так что мы лучше оставим окно Консоли в Debug конфигурации как есть, но уберем его из Release.

Right-click на “p_windows” project, открываем Properties.

Меняем Configuration на Release.

Идем в Configuration Properties -> Linker -> System и меняем SubSystem с Console на Windows

Apply, Ok.

Теперь, если попробовать скомпилировать (в Release конфигурации), то получим ошибку:

Error LNK2001 unresolved external symbol _WinMain@16

Это потому что теперь линкер ищет входную точку WinMain(), а не main().

Если мы хотим оставить main() (а мы хотим), то надо переустановить входную точку на mainCRTStartup (это в Advanced Linker-е).

Снова открываем project Properties, Release configuration.

Идем в Configuration Properties -> Linker -> Advanced. Открываем Entry Point -> Edit. Меняем на mainCRTStartup

Ok, Apply, Ok.

Теперь Debug конфигурация работает с Console window, Release – без.


Leave a Reply

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