#include #include #include #define STB_IMAGE_IMPLEMENTATION #include #include #include "Camera.h" #include "Model.h" #define WINDOW_WIDTH 800 #define WINDOW_HEIGHT 600 #define WINDOW_CAPTION "OPENGL notes on rekovalev.site" // Функция-callback для изменения размеров буфера кадра в случае изменения размеров поверхности окна void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height); } #include #include // Функция чтения шейдера из файла std::string readFile(const char* filename) { std::string text; std::ifstream file(filename, std::ios::in); // Открываем файл на чтение // Если файл доступен и успешно открыт if (file.is_open()) { std::stringstream sstr; // Буфер для чтения sstr << file.rdbuf(); // Считываем файл text = sstr.str(); // Преобразуем буфер к строке file.close(); // Закрываем файл } return text; } // Функция для загрузки шейдеров // Принимает два адреса вершинного и фрагментного шейдеров // Возвращает дескриптор шейдерной программы GLuint LoadShaders(const char *vertex_file, const char *fragment_file) { // Создание дескрипторов вершинного и фрагментного шейдеров GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER); GLuint fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); // Переменные под результат компиляции GLint result = GL_FALSE; int infoLogLength; // Считываем текст вершинного шейдера std::string code = readFile(vertex_file); const char* pointer = code.c_str(); // Преобразование к указателю на const char, так как функция принимает массив си-строк // Компиляция кода вершинного шейдера glShaderSource(vertexShaderID, 1, &pointer, NULL); glCompileShader(vertexShaderID); // Проверка результата компиляции glGetShaderiv(vertexShaderID, GL_COMPILE_STATUS, &result); glGetShaderiv(vertexShaderID, GL_INFO_LOG_LENGTH, &infoLogLength); if (infoLogLength > 0) { char* errorMessage = new char[infoLogLength + 1]; glGetShaderInfoLog(vertexShaderID, infoLogLength, NULL, errorMessage); std::cout << errorMessage; delete errorMessage; } // Считываем текст вершинного шейдера code = readFile(fragment_file); pointer = code.c_str(); // Преобразование к указателю на const char, так как функция принимает массив си-строк // Компиляция кода фрагментного шейдера glShaderSource(fragmentShaderID, 1, &pointer, NULL); glCompileShader(fragmentShaderID); // Проверка результата компиляции glGetShaderiv(fragmentShaderID, GL_COMPILE_STATUS, &result); glGetShaderiv(fragmentShaderID, GL_INFO_LOG_LENGTH, &infoLogLength); if (infoLogLength > 0) { char* errorMessage = new char[infoLogLength + 1]; glGetShaderInfoLog(fragmentShaderID, infoLogLength, NULL, errorMessage); std::cout << errorMessage; delete errorMessage; } // Привязка скомпилированных шейдеров GLuint programID = glCreateProgram(); glAttachShader(programID, vertexShaderID); glAttachShader(programID, fragmentShaderID); glLinkProgram(programID); // Проверка программы glGetProgramiv(programID, GL_LINK_STATUS, &result); glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &infoLogLength); if (infoLogLength > 0) { char* errorMessage = new char[infoLogLength + 1]; glGetProgramInfoLog(programID, infoLogLength, NULL, errorMessage); std::cout << errorMessage; delete errorMessage; } // Освобождение дескрипторов шейдеров glDeleteShader(vertexShaderID); glDeleteShader(fragmentShaderID); return programID; } bool firstMouse = true; float lastX, lastY; void mouse_callback(GLFWwindow* window, double xpos, double ypos) { if (firstMouse) { lastX = xpos; lastY = ypos; firstMouse = false; } glm::vec2 offset(xpos - lastX, lastY - ypos); lastX = xpos; lastY = ypos; Camera::current().rotate(offset); } int main(void) { GLFWwindow* window; // Указатель на окно GLFW3 // Инициализация GLFW3 if (!glfwInit()) { std::cout << "GLFW init error\n"; return -1; } // Завершение работы с GLFW3 перед выходом atexit(glfwTerminate); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Мажорная версия спецификаций OpenGL glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); // Минорная версия спецификаций OpenGL glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Контекст OpenGL, который поддерживает только основные функции // Создание окна GLFW3 с заданными шириной, высотой и заголовком окна window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_CAPTION, NULL, NULL); if (!window) { std::cout << "GLFW create window error\n"; return -1; } // Установка основного контекста окна glfwMakeContextCurrent(window); // Установка callback-функции для изменения размеров окна и буфера кадра glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); glfwSwapInterval(1); // Вертикальная синхронизация // Установка callback-функции для мыши и камеры glfwSetCursorPosCallback(window, mouse_callback); // Загрузка функций OpenGL с помощью GLAD if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "GLAD load GL error\n"; return -1; } // Компиляция шейдеров GLuint shaderProgram = LoadShaders("shaders/shader.vert", "shaders/shader.frag"); // Активация шейдера glUseProgram(shaderProgram); // Вершины прямоугольника glm::vec3 verticies[] = { {-0.5f, -0.5f, 0.0f} , { 0.5f, -0.5f, 0.0f} , { 0.5f, 0.5f, 0.0f} , {-0.5f, 0.5f, 0.0f} }; // Модель прямоугольника Model rectangle; // Загрузка вершин модели rectangle.load_verteces(verticies, sizeof(verticies)/sizeof(glm::vec3)); // индексы вершин GLuint indices[] = {0, 1, 2, 2, 3, 0}; // Загрузка индексов модели rectangle.load_indices(indices, sizeof(indices)); // Текстурные координаты glm::vec2 texCoords[] = { {0.0f, 0.0f} , {1.0f, 0.0f} , {1.0f, 1.0f} , {0.0f, 1.0f} }; // Загрузка текстурных координат модели rectangle.load_texCoords(texCoords, sizeof(texCoords)/sizeof(glm::vec2)); // Зададим горизонтальное положение перед камерой rectangle.e_position().y = -1; rectangle.e_position().z = 3; rectangle.e_rotation() = {0.707f, 0.707f, 0.0f, 0.0f}; rectangle.e_scale() = glm::vec3(3); // Работа с текстурой GLuint texture; // Дескриптор текстуры glGenTextures(1, &texture); // Генерация одной текстуры glBindTexture(GL_TEXTURE_2D, texture); // Привязка текстуры как активной int width, height, channels; // Ширина, высота и цветовые каналы текстуры unsigned char* image = stbi_load("../resources/textures/grass.png", &width, &height, &channels, STBI_default); // Загрузка в оперативную память изображения // Загрузка данных с учетом прозрачности if (channels == 3) // RGB glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); else if (channels == 4) // RGBA glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image); glGenerateMipmap(GL_TEXTURE_2D); // Генерация мипмапа для активной текстуры glBindTexture(GL_TEXTURE_2D, 0); // Отвязка активной текстуры stbi_image_free(image); // Освобождение оперативной памяти // Установка цвета очистки буфера цвета glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Расположение Uniform-переменной GLuint vp_uniform = glGetUniformLocation(shaderProgram, "vp"); GLuint model_uniform = glGetUniformLocation(shaderProgram, "model"); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // Использование уменьшенных версий mipmap // Пока не произойдет событие запроса закрытия окна while(!glfwWindowShouldClose(window)) { // Загрузим матрицу проекции*вида glUniformMatrix4fv(vp_uniform, 1, GL_FALSE, &Camera::current().getVP()[0][0]); // Очистка буфера цвета glClear(GL_COLOR_BUFFER_BIT); // Тут производится рендер glBindTexture(GL_TEXTURE_2D, texture); // Привязка текстуры как активной rectangle.render(model_uniform); // Представление содержимого буфера цепочки показа на окно glfwSwapBuffers(window); // Обработка системных событий glfwPollEvents(); } // Удаление текстуры glDeleteTextures(1, &texture); // Удаление шейдерной программы glDeleteProgram(shaderProgram); }