This commit is contained in:
root
2025-11-19 20:11:18 +05:00
commit ec9e591040
11 changed files with 943 additions and 0 deletions

182
README_WINDOWS.md Normal file
View File

@@ -0,0 +1,182 @@
# OBJ Viewer с Шейдерами
Программа для просмотра 3D моделей в формате OBJ с поддержкой различных шейдеров и интерактивного управления.
## Возможности
- Загрузка и отображение 3D моделей в формате OBJ
- 5 встроенных шейдеров с переключением в реальном времени
- Интерактивное управление камерой (вращение, масштабирование)
- Визуализация неба с настраиваемыми параметрами
- Каркасный режим (wireframe)
- Вывод цветовых значений в консоль
## Шейдеры
1. **Phong** - классическое освещение с ambient, diffuse и specular компонентами
2. **Toon** - мультяшный стиль с дискретными уровнями освещения
3. **Normal** - визуализация нормалей как RGB цветов
4. **Wireframe** - зеленоватый каркасный стиль
5. **Sky** - визуализация неба с параметрами высоты, угла солнца и интенсивности
## Установка для Windows
### Требования
- MinGW-w64 или Visual Studio
- GLEW
- GLUT (freeglut)
- GLM
### Установка зависимостей (MinGW)
1. Скачайте и установите [MinGW-w64](https://www.mingw-w64.org/)
2. Скачайте библиотеки:
- [GLEW](http://glew.sourceforge.net/) - OpenGL Extension Wrangler
- [freeglut](http://freeglut.sourceforge.net/) - GLUT для Windows
- [GLM](https://github.com/g-truc/glm) - OpenGL Mathematics (header-only)
3. Распакуйте библиотеки и скопируйте файлы:
```
GLEW:
- glew32.dll -> C:\Windows\System32\
- glew32.lib -> C:\mingw64\lib\
- include\GL\* -> C:\mingw64\include\GL\
freeglut:
- freeglut.dll -> C:\Windows\System32\
- freeglut.lib -> C:\mingw64\lib\
- include\GL\* -> C:\mingw64\include\GL\
GLM:
- glm\* -> C:\mingw64\include\glm\
```
### Компиляция
Откройте командную строку (cmd) в папке с проектом и выполните:
```bash
g++ obj_viewer_shader.cpp -o obj_viewer_shader.exe -lopengl32 -lglu32 -lfreeglut -lglew32 -std=c++11
```
Или используйте MinGW-w64:
```bash
g++ obj_viewer_shader.cpp -o obj_viewer_shader.exe -lglu32 -lglew32 -lfreeglut -lopengl32 -std=c++11
```
### Запуск
```bash
obj_viewer_shader.exe model.obj
```
Или просто запустите без параметров - программа попросит ввести путь к файлу:
```bash
obj_viewer_shader.exe
```
## Управление
### Мышь
- **Левая кнопка** - вращение модели
- **Правая кнопка** - приближение/отдаление
### Клавиатура
#### Переключение шейдеров
- **1** - Phong (классическое освещение)
- **2** - Toon (мультяшный стиль)
- **3** - Normal (нормали)
- **4** - Wireframe
- **5** - Sky (небо)
#### Параметры неба (для шейдера Sky)
- **h/H** - уменьшить/увеличить высоту (±500м)
- **e/E** - уменьшить/увеличить угол солнца (±0.05 рад)
- **i/I** - уменьшить/увеличить интенсивность света (±0.05)
- **P** - вывести текущий цвет неба в консоль
#### Прочее
- **W** - включить/выключить каркасный режим
- **ESC** - выход из программы
## Структура проекта
```
project/
├── obj_viewer_shader.cpp # Основной файл программы
├── vertex_shader.glsl # Вершинный шейдер (общий)
├── fragment_shader.glsl # Phong шейдер
├── toon_fragment.glsl # Toon шейдер
├── normal_fragment.glsl # Normal шейдер
├── wireframe_fragment.glsl # Wireframe шейдер
├── sky_vertex.glsl # Вершинный шейдер для неба
├── sky.frag # Фрагментный шейдер неба
└── model.obj # Ваша 3D модель
```
## Формат OBJ
Программа поддерживает стандартные OBJ файлы с:
- Вершинами (v)
- Нормалями (vn)
- Текстурными координатами (vt)
- Гранями (f) - треугольники и квады
## Пример вывода цвета неба
```
=== ЦВЕТ НЕБА ===
Параметры:
height: 5000
sunElevation: 0.5
sunIntensity: 0.8
Результат (RGB):
0.51 0.64 0.82
```
## Создание своих шейдеров
Чтобы добавить собственный шейдер:
1. Создайте файл фрагментного шейдера (например, `my_shader.glsl`)
2. Откройте `obj_viewer_shader.cpp`
3. Найдите функцию `init()` и добавьте в массив `shaders`:
```cpp
{"Мой шейдер", "vertex_shader.glsl", "my_shader.glsl"}
```
4. Перекомпилируйте программу
## Troubleshooting
### Ошибка "не удалось найти DLL"
Убедитесь, что `glew32.dll` и `freeglut.dll` находятся в `C:\Windows\System32\` или в папке с программой.
### Ошибка компиляции "cannot find -lglew32"
Проверьте, что библиотеки установлены в `C:\mingw64\lib\`
### Черный экран
Проверьте, что файлы шейдеров (`.glsl`, `.frag`) находятся в той же папке, что и исполняемый файл.
### Модель не загружается
- Проверьте путь к OBJ файлу
- Убедитесь, что файл в правильном формате OBJ
- Используйте относительные пути или полные пути с двойными слешами: `C:\\path\\to\\model.obj`
## Системные требования
- Windows 7/8/10/11
- OpenGL 3.3+
- Видеокарта с поддержкой шейдеров GLSL 330
## Лицензия
Свободное использование в образовательных целях.
## Автор
Создано для работы с 3D моделями и изучения шейдеров GLSL.

34
fragment_shader.glsl Normal file
View File

@@ -0,0 +1,34 @@
#version 330 core
out vec4 FragColor;
in vec3 FragPos;
in vec3 Normal;
uniform vec3 lightPos;
uniform vec3 viewPos;
uniform vec3 lightColor;
uniform vec3 objectColor;
void main()
{
// Ambient
float ambientStrength = 0.3;
vec3 ambient = ambientStrength * lightColor;
// Diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;
// Specular
float specularStrength = 0.5;
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;
vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);
}

40
model.obj Normal file
View File

@@ -0,0 +1,40 @@
# Blender 4.4.3
# www.blender.org
mtllib Без имени.mtl
o Cube
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
vn -0.0000 1.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -1.0000 -0.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vt 0.625000 0.500000
vt 0.875000 0.500000
vt 0.875000 0.750000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.125000 0.500000
vt 0.375000 0.500000
vt 0.125000 0.750000
s 0
usemtl Material
f 1/1/1 5/2/1 7/3/1 3/4/1
f 4/5/2 3/4/2 7/6/2 8/7/2
f 8/8/3 7/9/3 5/10/3 6/11/3
f 6/12/4 2/13/4 4/5/4 8/14/4
f 2/13/5 1/1/5 3/4/5 4/5/5
f 6/11/6 5/10/6 1/1/6 2/13/6

13
normal_fragment.glsl Normal file
View File

@@ -0,0 +1,13 @@
#version 330 core
out vec4 FragColor;
in vec3 FragPos;
in vec3 Normal;
void main()
{
// Отображение нормалей как цветов
vec3 normalColor = normalize(Normal) * 0.5 + 0.5;
FragColor = vec4(normalColor, 1.0);
}

BIN
obj_viewer_shader Executable file

Binary file not shown.

531
obj_viewer_shader.cpp Normal file
View File

@@ -0,0 +1,531 @@
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <cmath>
#include <GL/glew.h>
#include <GL/glut.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
struct Vec3 {
float x, y, z;
Vec3(float x = 0, float y = 0, float z = 0) : x(x), y(y), z(z) {}
};
struct Face {
std::vector<int> vertices;
std::vector<int> textures;
std::vector<int> normals;
};
class OBJModel {
public:
std::vector<Vec3> vertices;
std::vector<Vec3> normals;
std::vector<Vec3> texcoords;
std::vector<Face> faces;
bool loaded;
GLuint VAO, VBO, NBO;
std::vector<float> vertexData;
std::vector<float> normalData;
OBJModel() : loaded(false), VAO(0), VBO(0), NBO(0) {}
bool loadOBJ(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
std::cout << "Модель '" << filename << "' не найдена!" << std::endl;
return false;
}
std::string line;
while (std::getline(file, line)) {
std::istringstream iss(line);
std::string prefix;
iss >> prefix;
if (prefix == "v") {
float x, y, z;
iss >> x >> y >> z;
vertices.push_back(Vec3(x, y, z));
}
else if (prefix == "vn") {
float x, y, z;
iss >> x >> y >> z;
normals.push_back(Vec3(x, y, z));
}
else if (prefix == "vt") {
float u, v;
iss >> u >> v;
texcoords.push_back(Vec3(u, v, 0));
}
else if (prefix == "f") {
Face face;
std::string vertex;
while (iss >> vertex) {
std::istringstream vss(vertex);
std::string index;
int idx = 0;
while (std::getline(vss, index, '/')) {
if (!index.empty()) {
int i = std::stoi(index) - 1;
if (idx == 0) face.vertices.push_back(i);
else if (idx == 1) face.textures.push_back(i);
else if (idx == 2) face.normals.push_back(i);
}
idx++;
}
}
faces.push_back(face);
}
}
file.close();
loaded = true;
std::cout << "Модель загружена успешно!" << std::endl;
std::cout << "Вершин: " << vertices.size() << std::endl;
std::cout << "Граней: " << faces.size() << std::endl;
setupBuffers();
return true;
}
void setupBuffers() {
// Преобразуем грани в массив вершин (триангуляция)
for (const auto& face : faces) {
// Для граней с 3+ вершинами делаем триангуляцию веером
for (size_t i = 1; i < face.vertices.size() - 1; ++i) {
// Первая вершина треугольника (общая для всех)
int v0 = face.vertices[0];
int v1 = face.vertices[i];
int v2 = face.vertices[i + 1];
// Добавляем вершины треугольника
for (int vIdx : {v0, v1, v2}) {
if (vIdx < vertices.size()) {
vertexData.push_back(vertices[vIdx].x);
vertexData.push_back(vertices[vIdx].y);
vertexData.push_back(vertices[vIdx].z);
}
}
// Добавляем нормали для треугольника
std::vector<size_t> indices = {0, i, i + 1};
for (size_t j : indices) {
if (j < face.normals.size() && face.normals[j] < normals.size()) {
normalData.push_back(normals[face.normals[j]].x);
normalData.push_back(normals[face.normals[j]].y);
normalData.push_back(normals[face.normals[j]].z);
} else {
// Если нормалей нет, вычисляем плоскую нормаль
normalData.push_back(0.0f);
normalData.push_back(0.0f);
normalData.push_back(1.0f);
}
}
}
}
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &NBO);
glBindVertexArray(VAO);
// Вершины
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(float),
vertexData.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// Нормали
glBindBuffer(GL_ARRAY_BUFFER, NBO);
glBufferData(GL_ARRAY_BUFFER, normalData.size() * sizeof(float),
normalData.data(), GL_STATIC_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glBindVertexArray(0);
}
void render() {
if (!loaded || VAO == 0) return;
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, vertexData.size() / 3);
glBindVertexArray(0);
}
~OBJModel() {
if (VAO) glDeleteVertexArrays(1, &VAO);
if (VBO) glDeleteBuffers(1, &VBO);
if (NBO) glDeleteBuffers(1, &NBO);
}
};
// Функция для загрузки шейдера
std::string loadShaderSource(const std::string& filepath) {
std::ifstream file(filepath);
if (!file.is_open()) {
std::cerr << "Не удалось открыть файл шейдера: " << filepath << std::endl;
return "";
}
std::stringstream buffer;
buffer << file.rdbuf();
return buffer.str();
}
// Компиляция шейдера
GLuint compileShader(GLenum type, const std::string& source) {
GLuint shader = glCreateShader(type);
const char* src = source.c_str();
glShaderSource(shader, 1, &src, nullptr);
glCompileShader(shader);
GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
char infoLog[512];
glGetShaderInfoLog(shader, 512, nullptr, infoLog);
std::cerr << "Ошибка компиляции шейдера:\n" << infoLog << std::endl;
return 0;
}
return shader;
}
// Создание шейдерной программы
GLuint createShaderProgram(const std::string& vertexPath, const std::string& fragmentPath) {
std::string vertexSource = loadShaderSource(vertexPath);
std::string fragmentSource = loadShaderSource(fragmentPath);
if (vertexSource.empty() || fragmentSource.empty()) {
return 0;
}
GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexSource);
GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentSource);
if (!vertexShader || !fragmentShader) {
return 0;
}
GLuint program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
GLint success;
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success) {
char infoLog[512];
glGetProgramInfoLog(program, 512, nullptr, infoLog);
std::cerr << "Ошибка линковки шейдерной программы:\n" << infoLog << std::endl;
return 0;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return program;
}
// Глобальные переменные
OBJModel model;
GLuint shaderProgram;
std::vector<GLuint> shaderPrograms;
std::vector<std::string> shaderNames;
int currentShader = 0;
float rotationX = 0.0f;
float rotationY = 0.0f;
float zoom = 5.0f;
int lastMouseX = 0;
int lastMouseY = 0;
bool mouseLeftDown = false;
bool mouseRightDown = false;
bool showWireframe = false;
// Параметры для шейдера неба
float skyHeight = 5000.0f;
float sunElevation = 0.5f;
float sunIntensity = 0.8f;
void display() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Используем текущий шейдер
GLuint activeShader = shaderPrograms[currentShader];
glUseProgram(activeShader);
// Матрицы трансформации
glm::mat4 model_matrix = glm::mat4(1.0f);
model_matrix = glm::rotate(model_matrix, glm::radians(rotationX), glm::vec3(1.0f, 0.0f, 0.0f));
model_matrix = glm::rotate(model_matrix, glm::radians(rotationY), glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -zoom));
glm::mat4 projection = glm::perspective(glm::radians(45.0f), 800.0f / 600.0f, 0.1f, 100.0f);
// Передаем матрицы в шейдер
glUniformMatrix4fv(glGetUniformLocation(activeShader, "model"), 1, GL_FALSE, glm::value_ptr(model_matrix));
glUniformMatrix4fv(glGetUniformLocation(activeShader, "view"), 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(glGetUniformLocation(activeShader, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
// Параметры освещения (не все шейдеры их используют, но это не проблема)
glm::vec3 lightPos(5.0f, 5.0f, 5.0f);
glm::vec3 viewPos(0.0f, 0.0f, zoom);
glm::vec3 lightColor(1.0f, 1.0f, 1.0f);
glm::vec3 objectColor(0.8f, 0.8f, 0.8f);
glUniform3fv(glGetUniformLocation(activeShader, "lightPos"), 1, glm::value_ptr(lightPos));
glUniform3fv(glGetUniformLocation(activeShader, "viewPos"), 1, glm::value_ptr(viewPos));
glUniform3fv(glGetUniformLocation(activeShader, "lightColor"), 1, glm::value_ptr(lightColor));
glUniform3fv(glGetUniformLocation(activeShader, "objectColor"), 1, glm::value_ptr(objectColor));
// Параметры для шейдера неба
glUniform1f(glGetUniformLocation(activeShader, "height"), skyHeight);
glUniform1f(glGetUniformLocation(activeShader, "sunElevation"), sunElevation);
glUniform1f(glGetUniformLocation(activeShader, "sunIntensity"), sunIntensity);
// Wireframe режим
if (showWireframe) {
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
} else {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
model.render();
glutSwapBuffers();
}
void reshape(int w, int h) {
glViewport(0, 0, w, h);
}
void mouse(int button, int state, int x, int y) {
if (button == GLUT_LEFT_BUTTON) {
mouseLeftDown = (state == GLUT_DOWN);
lastMouseX = x;
lastMouseY = y;
}
else if (button == GLUT_RIGHT_BUTTON) {
mouseRightDown = (state == GLUT_DOWN);
lastMouseY = y;
}
}
void mouseMotion(int x, int y) {
if (mouseLeftDown) {
rotationY += (x - lastMouseX) * 0.5f;
rotationX += (y - lastMouseY) * 0.5f;
lastMouseX = x;
lastMouseY = y;
glutPostRedisplay();
}
else if (mouseRightDown) {
zoom += (y - lastMouseY) * 0.05f;
if (zoom < 1.0f) zoom = 1.0f;
lastMouseY = y;
glutPostRedisplay();
}
}
void printSkyColor() {
// Константы - цвета неба
glm::vec3 C_strato = glm::vec3(0x05, 0x0F, 0x1F) / 255.0f;
glm::vec3 C_sea = glm::vec3(0x87, 0xBD, 0xFF) / 255.0f;
glm::vec3 C_warm = glm::vec3(0xFF, 0xF2, 0xD9) / 255.0f;
glm::vec3 C_sunset = glm::vec3(0xFF, 0x8C, 0x4D) / 255.0f;
// 1. Зависимость от высоты
float H = 8000.0f;
float t = glm::clamp(expf(-skyHeight / H), 0.0f, 1.0f);
glm::vec3 C_base = glm::mix(C_strato, C_sea, t);
// 2. Параметры солнца
float d = glm::smoothstep(0.0f, 0.6f, sunElevation);
float s = glm::clamp(sunIntensity, 0.0f, 1.0f);
// 3. Тёплая добавка
glm::vec3 C_tint = glm::mix(C_sunset, C_warm, d);
float alpha = 0.6f * s * (1.0f - d) * t;
// 4. Итоговый цвет
glm::vec3 C_lit = C_base * (0.55f + 0.45f * s * d) + alpha * C_tint;
glm::vec3 C_out = glm::clamp(C_lit, 0.0f, 1.0f);
std::cout << "\n=== ЦВЕТ НЕБА ===" << std::endl;
std::cout << "Параметры:" << std::endl;
std::cout << " height: " << skyHeight << std::endl;
std::cout << " sunElevation: " << sunElevation << std::endl;
std::cout << " sunIntensity: " << sunIntensity << std::endl;
std::cout << "Результат (RGB):" << std::endl;
printf(" %.2f %.2f %.2f\n", C_out.r, C_out.g, C_out.b);
}
void keyboard(unsigned char key, int x, int y) {
bool needUpdate = false;
if (key == 27) { // ESC
exit(0);
}
else if (key == '1' || key == '2' || key == '3' || key == '4' || key == '5') {
int shader = key - '1';
if (shader < shaderPrograms.size()) {
currentShader = shader;
std::cout << "Активирован шейдер: " << shaderNames[currentShader] << std::endl;
if (currentShader == 4) { // Sky shader
printSkyColor();
}
glutPostRedisplay();
}
}
else if (key == 'w' || key == 'W') {
showWireframe = !showWireframe;
std::cout << "Wireframe: " << (showWireframe ? "ВКЛ" : "ВЫКЛ") << std::endl;
glutPostRedisplay();
}
else if (key == 'p' || key == 'P') {
printSkyColor();
}
// Управление высотой
else if (key == 'h') {
skyHeight -= 500.0f;
if (skyHeight < 0) skyHeight = 0;
needUpdate = true;
}
else if (key == 'H') {
skyHeight += 500.0f;
needUpdate = true;
}
// Управление углом солнца
else if (key == 'e') {
sunElevation -= 0.05f;
if (sunElevation < 0) sunElevation = 0;
needUpdate = true;
}
else if (key == 'E') {
sunElevation += 0.05f;
needUpdate = true;
}
// Управление интенсивностью
else if (key == 'i') {
sunIntensity -= 0.05f;
if (sunIntensity < 0) sunIntensity = 0;
needUpdate = true;
}
else if (key == 'I') {
sunIntensity += 0.05f;
if (sunIntensity > 1.0f) sunIntensity = 1.0f;
needUpdate = true;
}
if (needUpdate) {
printSkyColor();
glutPostRedisplay();
}
}
void init() {
glewExperimental = GL_TRUE;
GLenum err = glewInit();
if (err != GLEW_OK) {
std::cerr << "Ошибка инициализации GLEW: " << glewGetErrorString(err) << std::endl;
exit(1);
}
glEnable(GL_DEPTH_TEST);
glClearColor(0.1f, 0.1f, 0.15f, 1.0f);
// Загрузка всех шейдеров
struct ShaderPair {
std::string name;
std::string vertex;
std::string fragment;
};
std::vector<ShaderPair> shaders = {
{"Phong (освещение)", "vertex_shader.glsl", "fragment_shader.glsl"},
{"Toon (мультяшный)", "vertex_shader.glsl", "toon_fragment.glsl"},
{"Normal (нормали)", "vertex_shader.glsl", "normal_fragment.glsl"},
{"Wireframe Style", "vertex_shader.glsl", "wireframe_fragment.glsl"},
{"Sky (небо)", "sky_vertex.glsl", "sky.frag"}
};
for (const auto& shader : shaders) {
GLuint program = createShaderProgram(shader.vertex, shader.fragment);
if (program) {
shaderPrograms.push_back(program);
shaderNames.push_back(shader.name);
std::cout << "✓ Загружен: " << shader.name << std::endl;
} else {
std::cout << "✗ Ошибка: " << shader.name << std::endl;
}
}
if (shaderPrograms.empty()) {
std::cerr << "Не удалось загрузить ни один шейдер!" << std::endl;
exit(1);
}
std::cout << "\nДоступно шейдеров: " << shaderPrograms.size() << std::endl;
}
int main(int argc, char** argv) {
std::string filename;
if (argc > 1) {
filename = argv[1];
} else {
std::cout << "Введите путь к OBJ файлу: ";
std::getline(std::cin, filename);
}
// Инициализация GLUT
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(800, 600);
glutCreateWindow("OBJ Viewer с шейдерами - Используйте мышь для вращения");
init();
// Загрузка модели
if (!model.loadOBJ(filename)) {
std::cout << "Не удалось загрузить модель." << std::endl;
return 1;
}
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMouseFunc(mouse);
glutMotionFunc(mouseMotion);
glutKeyboardFunc(keyboard);
std::cout << "\n=== УПРАВЛЕНИЕ ===" << std::endl;
std::cout << "Мышь:" << std::endl;
std::cout << " - Левая кнопка: вращение модели" << std::endl;
std::cout << " - Правая кнопка: приближение/отдаление" << std::endl;
std::cout << "\nШейдеры:" << std::endl;
std::cout << " - 1: Phong (классическое освещение)" << std::endl;
std::cout << " - 2: Toon (мультяшный)" << std::endl;
std::cout << " - 3: Normal (нормали)" << std::endl;
std::cout << " - 4: Wireframe" << std::endl;
std::cout << " - 5: Sky (небо)" << std::endl;
std::cout << "\nПараметры неба (шейдер Sky):" << std::endl;
std::cout << " - h/H: уменьшить/увеличить высоту" << std::endl;
std::cout << " - e/E: уменьшить/увеличить угол солнца" << std::endl;
std::cout << " - i/I: уменьшить/увеличить интенсивность" << std::endl;
std::cout << " - P: вывести цвет неба в консоль" << std::endl;
std::cout << "\nПрочее:" << std::endl;
std::cout << " - W: каркасный режим" << std::endl;
std::cout << " - ESC: выход" << std::endl;
std::cout << "\nТекущий шейдер: " << shaderNames[currentShader] << std::endl;
glutMainLoop();
return 0;
}

45
sky.frag Normal file
View File

@@ -0,0 +1,45 @@
#version 330 core
out vec4 FragColor;
in vec3 FragPos;
in vec3 Normal;
uniform float height;
uniform float sunElevation;
uniform float sunIntensity;
void main()
{
// Константы - цвета неба
vec3 C_strato = vec3(0x05, 0x0F, 0x1F) / 255.0; // #050F1F
vec3 C_sea = vec3(0x87, 0xBD, 0xFF) / 255.0; // #87BDFF
vec3 C_warm = vec3(0xFF, 0xF2, 0xD9) / 255.0; // #FFF2D9
vec3 C_sunset = vec3(0xFF, 0x8C, 0x4D) / 255.0; // #FF8C4D
// 1. Зависимость цвета от высоты наблюдателя
float H = 8000.0;
float t = clamp(exp(-height / H), 0.0, 1.0);
// C_base - линейная интерполяция от C_strato до C_sea с параметром t
vec3 C_base = mix(C_strato, C_sea, t);
// 2. Параметры положения Солнца
float d = smoothstep(0.0, 0.6, sunElevation);
float s = clamp(sunIntensity, 0.0, 1.0);
// 3. Тёплая добавка при низком Солнце
// C_tint - линейная интерполяция от C_sunset до C_warm с параметром d
vec3 C_tint = mix(C_sunset, C_warm, d);
// Коэффициент тёплой добавки
float alpha = 0.6 * s * (1.0 - d) * t;
// 4. Итоговый цвет неба
vec3 C_lit = C_base * (0.55 + 0.45 * s * d) + alpha * C_tint;
// Финальный цвет с ограничением
vec3 C_out = clamp(C_lit, 0.0, 1.0);
FragColor = vec4(C_out, 1.0);
}

12
sky_vertex.glsl Normal file
View File

@@ -0,0 +1,12 @@
#version 330 core
layout(location = 0) in vec3 aPos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
}

44
toon_fragment.glsl Normal file
View File

@@ -0,0 +1,44 @@
#version 330 core
out vec4 FragColor;
in vec3 FragPos;
in vec3 Normal;
uniform vec3 lightPos;
uniform vec3 viewPos;
void main()
{
// Toon shader (мультяшный стиль)
vec3 lightColor = vec3(1.0, 1.0, 1.0);
vec3 objectColor = vec3(0.3, 0.6, 0.9);
// Ambient
float ambientStrength = 0.3;
vec3 ambient = ambientStrength * lightColor;
// Diffuse с дискретными уровнями
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
// Квантуем освещение на 4 уровня
if (diff > 0.95) diff = 1.0;
else if (diff > 0.5) diff = 0.7;
else if (diff > 0.25) diff = 0.4;
else diff = 0.2;
vec3 diffuse = diff * lightColor;
// Specular с четким бликом
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = vec3(0.0);
if (spec > 0.5) specular = vec3(1.0); // Четкий блик
vec3 result = (ambient + diffuse + specular * 0.3) * objectColor;
FragColor = vec4(result, 1.0);
}

19
vertex_shader.glsl Normal file
View File

@@ -0,0 +1,19 @@
#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aNormal;
out vec3 FragPos;
out vec3 Normal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = mat3(transpose(inverse(model))) * aNormal;
gl_Position = projection * view * vec4(FragPos, 1.0);
}

23
wireframe_fragment.glsl Normal file
View File

@@ -0,0 +1,23 @@
#version 330 core
out vec4 FragColor;
in vec3 FragPos;
in vec3 Normal;
uniform vec3 lightPos;
uniform vec3 viewPos;
void main()
{
// Wireframe стиль
vec3 lightColor = vec3(0.0, 1.0, 0.5);
vec3 objectColor = vec3(0.1, 0.1, 0.1);
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 result = objectColor + lightColor * diff * 0.3;
FragColor = vec4(result, 1.0);
}