OpenGL: Текстурирование с помощью вершинного шейдера

Вопрос или проблема

У меня есть рабочая программа, которая включает 3 сферы: одна текстурирована как солнце, одна с текстурой земли и другая с текстурой луны. Однако эта программа не использует шейдеры. Она использует очень маленький пользовательский класс ‘Vector’, но это единственное, что является кастомным. Все остальные части программы, а именно матрица, используемая для работы, и способ генерации текстур, выполняются через OpenGL и соответствующие библиотеки.

Я хотел бы изменить свой код, чтобы делать текстурирование с помощью вершинного шейдера, чтобы затем передавать значения во фрагментный шейдер для расчётов освещения.

Это раздел моего кода, который в настоящее время генерирует сферы и применяет их текстуры:

#include <GL/glew.h>

#ifdef __APPLE_CC__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

#include <iostream>
#include <sstream>
#include <fstream>
#include <cstring>
#include <cmath>

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

using namespace std;

GLuint loadTexture(const char* path) 
{
    GLuint texture;
    int width, height, nrChannels;
    stbi_set_flip_vertically_on_load(true);
    glGenTextures(1, &texture);

    glBindTexture(GL_TEXTURE_2D, texture);
    glActiveTexture(GL_TEXTURE_2D);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

    unsigned char *data = stbi_load(path, &width, &height, &nrChannels, 0);

    if (data)
    {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    else
    {
        cout << "Не удалось загрузить текстуру" << endl;
    }
    stbi_image_free(data);
    return texture;
}

class Body 
{
    const char* path;
    float r;
    float lum;
    unsigned int texture;
    Vector pos;
    Vector c;
    GLUquadric* quadric;

public:
    Body(const char* imgpath = "maps/Earth.jpg",
         float radius = 1.0,
         float luminosity = 0.0,
         Vector position = Vector(0.0, 0.0, 0.0),
         Vector color = Vector(0.25, 0.25, 0.25)) {
        path = imgpath;
        r = radius;
        lum = luminosity;
        pos = position;
        c = color;
    }

    void render() 
    {
        glPushMatrix();
        glTranslatef(pos.x(), pos.y(), pos.z());

        GLuint texture = loadTexture(path);
        glRotatef(180.0f, 0.0f, 1.0f, 1.0f);
        glRotatef(90.f, 0.0f, 0.0f, 1.0f);

        quadric = gluNewQuadric();
        gluQuadricDrawStyle(quadric, GLU_FILL);
        gluQuadricTexture(quadric, GL_TRUE);
        gluQuadricNormals(quadric, GLU_SMOOTH);
        gluSphere(quadric, r, 40, 40);

        glPopMatrix();
    }

    ~Body() 
    {
        gluDeleteQuadric(quadric);
    }
};

Могу я получить объяснение того, как именно я могу текстурировать сферу в OpenGL с помощью вершинного шейдера?

Ответ или решение

Применение текстурирования сферы в OpenGL с использованием вершинного шейдера

Ваша задача заключается в модернизации существующей программы, использующей OpenGL для текстурирования сферы с помощью вершинного и фрагментного шейдеров. Давайте детально разберёмся, как этого добиться, основываясь на вашем исходном коде.

Шаг 1: Подготовка кода

Перед тем как начать, убедитесь, что у вас установлены необходимые пакеты для работы со шейдерами и для создания контекста OpenGL. Вам нужно будет изменить способ загрузки текстур и рендеринга. Для этого создадим структуру шейдеров.

Шаг 2: Создание шейдеров

Создайте вершинный и фрагментный шейдеры. Вершинный шейдер будет выполнять трансформации вершин и передавать текстурные координаты во фрагментный шейдер.

Vertex Shader (vertex_shader.glsl):

#version 330 core

layout(location = 0) in vec3 position;
layout(location = 1) in vec2 texCoord;

out vec2 TexCoord;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    gl_Position = projection * view * model * vec4(position, 1.0);
    TexCoord = texCoord;
}

Fragment Shader (fragment_shader.glsl):

#version 330 core

out vec4 color;

in vec2 TexCoord;

uniform sampler2D texture1;

void main()
{
    color = texture(texture1, TexCoord);
}

Шаг 3: Загрузка шейдеров

Теперь, добавим функции для компиляции и связывания шейдеров:

GLuint LoadShader(const char *vertex_path, const char *fragment_path)
{
    // Скомпилируйте и создайте шейдеры
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);

    // Загрузите source коды шейдеров
    // (используйте std::ifstream для чтения файлов и glShaderSource для загрузки кода)

    // Компилируйте шейдеры и проверяйте ошибки
    // ...

    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    return shaderProgram;
}

Шаг 4: Геометрия сферы с текстурными координатами

Модифицируйте вашу функцию рендеринга. Сначала необходимо изменить способ создания сферы, чтобы передать текстурные координаты:

  1. Создайте буфер для вершин и буфер для текстурных координат.
  2. Заполните их в цикле, для каждой вершины вычисляя соответствующие текстурные координаты.

Пример кода для генерации текстурных координат:

void generateSphere(float radius, int stacks, int slices)
{
    for (int i = 0; i <= stacks; i++) {
        for (int j = 0; j <= slices; j++) {
            float theta = i * M_PI / stacks;
            float phi = j * 2 * M_PI / slices;

            // Вычисление позиций
            float x = radius * sin(theta) * cos(phi);
            float y = radius * cos(theta);
            float z = radius * sin(theta) * sin(phi);

            // Вычисление текстурных координат
            float s = (float)j / slices;
            float t = (float)i / stacks;

            // Заполнение буфера
            // ...
        }
    }
}

Шаг 5: Отрисовка с использованием шейдеров

При рендеринге объекта, активируйте шейдерную программу, укажите текстуру и передайте матрицы трансформации:

void render() 
{
    glUseProgram(shaderProgram);

    GLuint texture = loadTexture(path);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);

    glUniform1i(glGetUniformLocation(shaderProgram, "texture1"), 0);
    glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model));
    glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "view"), 1, GL_FALSE, glm::value_ptr(view));
    glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, glm::value_ptr(projection));

    // Здесь ваши вызовы для отрисовки вершин
    // ...

    glBindTexture(GL_TEXTURE_2D, 0);
    glUseProgram(0);
}

Заключение

Таким образом, вы сможете полностью перенести текстурирование ваших сфер на шейдерный пайплайн OpenGL. Это позволит вам добавлять более сложные эффекты освещения и модификации текстур в будущем. Убедитесь, что при отладке кода вы отслеживаете ошибки компиляции шейдеров и их связывания. Настройка шейдеров обеспечит более высокую гибкость и расширяемость вашего приложения.

Если у вас возникнут дополнительные вопросы по этой теме, не стесняйтесь обращаться за помощью!

Оцените материал
Добавить комментарий

Капча загружается...