start
This commit is contained in:
182
README_WINDOWS.md
Normal file
182
README_WINDOWS.md
Normal 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
34
fragment_shader.glsl
Normal 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
40
model.obj
Normal 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
13
normal_fragment.glsl
Normal 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
BIN
obj_viewer_shader
Executable file
Binary file not shown.
531
obj_viewer_shader.cpp
Normal file
531
obj_viewer_shader.cpp
Normal 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
45
sky.frag
Normal 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
12
sky_vertex.glsl
Normal 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
44
toon_fragment.glsl
Normal 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
19
vertex_shader.glsl
Normal 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
23
wireframe_fragment.glsl
Normal 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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user