Вопрос или проблема
У меня есть рабочая программа, которая включает 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: Геометрия сферы с текстурными координатами
Модифицируйте вашу функцию рендеринга. Сначала необходимо изменить способ создания сферы, чтобы передать текстурные координаты:
- Создайте буфер для вершин и буфер для текстурных координат.
- Заполните их в цикле, для каждой вершины вычисляя соответствующие текстурные координаты.
Пример кода для генерации текстурных координат:
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. Это позволит вам добавлять более сложные эффекты освещения и модификации текстур в будущем. Убедитесь, что при отладке кода вы отслеживаете ошибки компиляции шейдеров и их связывания. Настройка шейдеров обеспечит более высокую гибкость и расширяемость вашего приложения.
Если у вас возникнут дополнительные вопросы по этой теме, не стесняйтесь обращаться за помощью!