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

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