Я работаю над проектом OpenGL. В этом проекте я хотел бы смоделировать 3D-игру в шахматы на С++. Конечно, части должны быть подвижными по желанию. Шахматная доска и фигуры уже отображаются с включенными текстурами.
Я работал над преобразованием частей. В моем классе моделей все важные параметры, такие как вершины, индексы, числовые вершины, числовые индексы и т. д., считываются из соответствующей модели. Это происходит в конструкторе этого класса.
Я также создаю единичную матрицу (glm::mat4(1.0f)) для каждой модели в конструкторе класса модели.
После того, как все параметры прочитаны, я помещаю их в vector<Mesh*>.
В классе сетки вершины и индексы сначала загружаются в буфер вершин/индексов в конструкторе.
Расположение юниформ-переменной в вершинном шейдере для матрицы моей модели также вычисляется в конструкторе.
В функции рендеринга класса сетки буфер вершин/индексный буфер привязан, и установлены все юниформы. В конце функции я вызываю glDrawElements.
В вершинном шейдере я умножаю матрицу модели, viewProjectionMatrix и vec4(a_position, 1.0f) на gl_Position. Если единичная матрица в конструкторе класса модели — glm::mat4(1.0f), модель отображается нормально.
Но если я трансформирую, масштабирую или поворачиваю матрицу в конструкторе для целей тестирования, больше ничего не отображается. Я сделал ошибку в том, как я это делаю, или я на правильном пути?
#pragma once
#include <vector>
#include <fstream>
#include "libs/glm/glm.hpp"
#include "shader.h"
#include "vertexbuffer.h"
#include "indexbuffer.h"
#include "mesh.h"
#include <cassert>
class Model {
public:
Model(const char* filename, Shader* shader) {
this->shader = shader;
modelmatrix = glm::mat4(1.0f);
//modelmatrix = glm::scale(modelmatrix, glm::vec3(10));
std::ifstream input = std::ifstream(filename, std::ios::in | std::ios::binary);
uint64 numMeshes;
uint64 numMaterials;
input.read((char*)&numMaterials, sizeof(uint64));
for (uint64 i = 0; i < numMaterials; i++) {
Material material = {};
input.read((char*)&material, sizeof(BMFMaterial));
uint64 diffuseMapNameLenght = 0;
input.read((char*)&diffuseMapNameLenght, sizeof(uint64));
std::string diffuseMapName(diffuseMapNameLenght, '\0');
input.read((char*)&diffuseMapName[0], diffuseMapNameLenght);
uint64 normalMapNameLenght = 0;
input.read((char*)&normalMapNameLenght, sizeof(uint64));
std::string normalMapName(normalMapNameLenght, '\0');
input.read((char*)&normalMapName[0], normalMapNameLenght);
if (diffuseMapNameLenght > 0) {
std::cout << filename << " successfully loaded" << std::endl;
}
else {
std::cout << filename << " failed loading" << std::endl;
}
int32 textureWidth = 0;
int32 textureHeight = 0;
int32 bitsPerPixel = 0;
glGenTextures(2, &material.diffuseMap);
stbi_set_flip_vertically_on_load(true);
auto textureBuffer = stbi_load(diffuseMapName.c_str(), &textureWidth, &textureHeight, &bitsPerPixel, 4); //Map geladen und in Buffer drinnen
glBindTexture(GL_TEXTURE_2D, material.diffuseMap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureBuffer); //Texture in Grafikkarte geladen
if (textureBuffer) {
stbi_image_free(textureBuffer);
}
auto textureBuffer2 = stbi_load(normalMapName.c_str(), &textureWidth, &textureHeight, &bitsPerPixel, 4); //Map geladen und in Buffer drinnen
glBindTexture(GL_TEXTURE_2D, material.normalMap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureBuffer2); //Texture in Grafikkarte geladen
if (textureBuffer2) {
stbi_image_free(textureBuffer2);
}
glBindTexture(GL_TEXTURE_2D, 0);
materials.push_back(material);
}
input.read((char*)&numMeshes, sizeof(uint64));
for (uint64 i = 0; i < numMeshes; i++) {
input.read((char*)&materialIndex, sizeof(uint64));
input.read((char*)&numVertices, sizeof(uint64));
input.read((char*)&numIndices, sizeof(uint64));
for (uint64 i = 0; i < numVertices; i++) {
Vertex vertex;
input.read((char*)&vertex.position.x, sizeof(float));
input.read((char*)&vertex.position.y, sizeof(float));
input.read((char*)&vertex.position.z, sizeof(float));
input.read((char*)&vertex.normal.x, sizeof(float));
input.read((char*)&vertex.normal.y, sizeof(float));
input.read((char*)&vertex.normal.z, sizeof(float));
input.read((char*)&vertex.textureCoord.x, sizeof(float));
input.read((char*)&vertex.textureCoord.y, sizeof(float));
vertices.push_back(vertex);
}
for (uint64 i = 0; i < numIndices; i++) {
uint32 index;
input.read((char*)&index, sizeof(uint32));
indices.push_back(index);
}
Mesh* mesh = new Mesh(vertices, numVertices, indices, numIndices, materials[materialIndex], shader, modelmatrix); //Mesh laden
meshes.push_back(mesh);
}
}
void render() {
for (Mesh* mesh : meshes) {
mesh->render();
}
}
~Model() {
for (Mesh* mesh : meshes) {
delete mesh;
}
}
private:
std::vector<Mesh*> meshes;
std::vector<Material> materials;
glm::mat4 modelmatrix;
std::vector<Vertex> vertices;
uint64 numVertices = 0;
std::vector<uint32> indices;
uint64 numIndices = 0;
uint64 materialIndex = 0;
Shader* shader;
};
#pragma once
#include <iostream>
#include <vector>
#include <fstream>
#include "libs/glm/mat4x4.hpp"
#include "libs/glm/glm.hpp"
#include "shader.h"
#include "vertexbuffer.h"
#include "indexbuffer.h"
#include "libs/stb_image.h"
struct BMFMaterial {
glm::vec3 diffuse;
glm::vec3 specular;
glm::vec3 emissive;
float shininess;
};
struct Material {
BMFMaterial material;
GLuint diffuseMap;
GLuint normalMap;
};
class Mesh {
public:
Mesh(std::vector<Vertex>& vertices, uint64 numVertices, std::vector<uint32>&indices, uint64 numIndices, Material material, Shader* shader, glm::mat4 modelmatrix) {
this->material = material;
this->shader = shader;
this->numIndices = numIndices;
this->modelmatrix = modelmatrix;
vertexBuffer = new VertexBuffer(vertices.data(), numVertices);
indexBuffer = new IndexBuffer(indices.data(), numIndices, sizeof(indices[0]));
diffuseLocation = glGetUniformLocation(shader->getShaderID(), "u_material.diffuse");
specularLocation = glGetUniformLocation(shader->getShaderID(), "u_material.specular");
emissiveLocation = glGetUniformLocation(shader->getShaderID(), "u_material.emissive");
shininessLocation = glGetUniformLocation(shader->getShaderID(), "u_material.shininess");
diffuseMapLocation = glGetUniformLocation(shader->getShaderID(), "u_diffuse_map");
modelmatrixlocation = glGetUniformLocation(shader->getShaderID(), "u_modelmatrix");
}
~Mesh() {
delete vertexBuffer;
delete indexBuffer;
}
inline void render() {
std::cout << glm::to_string(modelmatrix) << std::endl;
vertexBuffer->bind();
indexBuffer->bind();
glUniformMatrix4fv(modelmatrixlocation, 1, GL_FALSE, &modelmatrix[0][0]);
glUniform3fv(diffuseLocation, 1, (float*)&material.material.diffuse[0]);
glUniform3fv(specularLocation, 1, (float*)&material.material.specular[0]);
glUniform3fv(emissiveLocation, 1, (float*)&material.material.emissive[0]);
glUniform1f(shininessLocation, material.material.shininess);
glBindTexture(GL_TEXTURE_2D, material.diffuseMap);
glUniform1i(diffuseMapLocation, 0);
glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_INT, 0);
}
private:
VertexBuffer* vertexBuffer;
IndexBuffer* indexBuffer;
Shader* shader;
Material material;
uint64 numIndices = 0;
glm::mat4 modelmatrix;
int diffuseLocation;
int specularLocation;
int emissiveLocation;
int shininessLocation;
int diffuseMapLocation;
int modelmatrixlocation;
};
while (!close) { //GAMELOOP
camera.update();
//model = glm::rotate(model, glm::radians(rotation = 1.0f), glm::vec3(1.0f, 0.0f, 0.0f));
modelViewProj = camera.getViewProj();//*model //Pro Frame jedes mal neu berechnen
glm::mat4 modelView = camera.getView(); //*model
glm::mat4 invModelView = glm::transpose(glm::inverse(modelView));
glm::vec4 sunDirectionCamera = glm::transpose(glm::inverse(camera.getView())) * glm::vec4(sunDirection, 1.0f); //sunDirection ist abhänging von Kamera, weil Licht wird im viewspace berechnet
glUniform3fv(directionLocationDirection, 1, (float*)&sunDirectionCamera[0]); //aktivieren
glm::mat4 pointLightMatrix = glm::mat4(1.0f); //Für Matrixmultiplikation weil der Punkt nicht immer am gleichen Punkt bleiben soll
pointLightPosition = pointLightPosition * pointLightMatrix; //Position ändert sich, rotiert nun um y-Achse
glm::vec3 transformedPointLightPosition = (glm::vec3)(camera.getView() * pointLightPosition); //Transformierte Position im Viewspace
glUniform3fv(positionLocationPoint, 1, (float*)&transformedPointLightPosition[0]);
glUniformMatrix4fv(modelViewProjMatrixLocation, 1, GL_FALSE, &modelViewProj[0][0]); //Matrix setzen/aktivieren
glUniformMatrix4fv(modelViewLocation, 1, GL_FALSE, &modelView[0][0]);
glUniformMatrix4fv(invModelViewLocation, 1, GL_FALSE, &invModelView[0][0]);
//Brett und Figuren rendern/zeichnen
whitepawnA2.render();
board.render();
/*
whitebishopright.render();
whitebishopleft.render();
whiteking.render();
whiteknightleft.render();
whiteknightright.render();
whiterookleft.render();
whiterookright.render();
whitequeen.render();
whitepawnH2.render();
whitepawnG2.render();
whitepawnF2.render();
whitepawnE2.render();
whitepawnD2.render();
whitepawnC2.render();
whitepawnB2.render();
blackknightleft.render();
blackknightright.render();
blackking.render();
blackbishopleft.render();
blackbishopright.render();
blackqueen.render();
blackrookleft.render();
blackrookright.render();
blackpawnA.render();
blackpawnB.render();
blackpawnC.render();
blackpawnD.render();
blackpawnE.render();
blackpawnF.render();
blackpawnG.render();
blackpawnH.render();
*/
SDL_GL_SwapWindow(window); //double buffer aktivieren
//FPS COUNTER
uint64 endCounter = SDL_GetPerformanceCounter();
uint64 counterElapsed = endCounter - lastCounter;
delta = ((float32)counterElapsed) / (float32)perfCounterFrequency; //Zeit die seit letztem Frame vergangen ist
uint32 FPS = (uint32)((float32)perfCounterFrequency / (float32)counterElapsed);
//std::cout << FPS << std::endl;
lastCounter = endCounter;
}
return 0;
}
#include "shader.h"
#include <fstream>
#include <iostream>
Shader::Shader(const char* vertexShaderFilename, const char* fragmentShaderFilename) {
shaderID = createShader(vertexShaderFilename, fragmentShaderFilename);
}
Shader::~Shader() {
glDeleteProgram(shaderID);
}
void Shader::bind() {
glUseProgram(shaderID);
}
void Shader::unbind() {
glUseProgram(0);
}
GLuint Shader::getShaderID() {
return shaderID;
}
GLuint Shader::compile(std::string shaderSource, GLenum type) { //Shadersourcecode so kompiliert werden
GLuint id = glCreateShader(type);
const char* src = shaderSource.c_str();
glShaderSource(id, 1, &src, 0);
glCompileShader(id);
int result;
glGetShaderiv(id, GL_COMPILE_STATUS, &result);
if (result != GL_TRUE) {
int length = 0;
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
char* message = new char[length];
glGetShaderInfoLog(id, length, &length, message);
std::cout << "Shader compilation error: " << message << std::endl;
delete[] message;
return 0;
}
return id;
}
std::string Shader::parse(const char* filename) {
FILE* file;
#ifdef _WIN32
if (fopen_s(&file, filename, "rb") != 0) {
std::cout << "File " << filename << " not found" << std::endl;
return "";
}
#else
file = fopen(filename, "rb");
if (file == nullptr) {
std::cout << "File " << filename << " not found" << std::endl;
return "";
}
#endif
std::string content; //Dateininhalt
fseek(file, 0, SEEK_END); //ans ende der datei gehen
size_t filesize = ftell(file); //wie weit ist man im file
rewind(file); //wieder zurückgehen
content.resize(filesize); //file resizen damit man nicht zu wenig oder zu viel speicher reserviert
fread(&content[0], 1, filesize, file); //&content[0] gibt adresse auf speicherbereich - 1 ganze filesize soll gelesen werden -
fclose(file); //file wieder schließen
return content; //code returnen
}
GLuint Shader::createShader(const char* vertexShaderFilename, const char* fragmentShaderFilename) {
std::string vertexShaderSource = parse(vertexShaderFilename); //Sourcecode speichern
std::string fragmentShaderSource = parse(fragmentShaderFilename);
GLuint program = glCreateProgram(); //program erstellen
GLuint vs = compile(vertexShaderSource, GL_VERTEX_SHADER); //shader erstellen
GLuint fs = compile(fragmentShaderSource, GL_FRAGMENT_SHADER);
glAttachShader(program, vs); //shader wird program hinzugefügt
glAttachShader(program, fs);
glLinkProgram(program); //executable erstellt und läuft auf vertex
//Solange program gelinked ist, kann alles wieder deattached und gelöscht werden - spart speicherplatz
#ifdef _RELEASE
glDetachShader(progam, vs);
glDetachShader(program, fs);
glDeleteShader(vs);
glDeleteShader(fs);
#endif
return program;
}
//Vertexshader soll Position von Vertex berechnen
#version 330 core
//INPUTS IN DEN SHADER
layout(location = 0) in vec3 a_position; //layout location 0 -> 1 Attribut
layout(location = 1) in vec3 a_normal;
layout(location = 2) in vec2 a_tex_coord;
//OUTPUTS
out vec3 v_normal;
out vec3 v_position;
out vec2 v_tex_coord;
uniform mat4 u_modelmatrix;
uniform mat4 u_modelView;
uniform mat4 u_modelViewProj;
uniform mat4 u_invModelView;
void main()
{
gl_Position = u_modelmatrix * u_modelViewProj * vec4(a_position, 1.0f); //vec4 für Matrixmultiplikationen - Position in positionsvariable - 1.0f -> 4 Koordinate
v_normal = mat3(u_invModelView) * a_normal; //Normalevktor berechnen
v_position = vec3(u_modelView * vec4(a_position, 1.0f)); //Position des Fragments berechnen
v_tex_coord = a_tex_coord;
}
Я написал текст на немецком и только что перевел на английский :)
Для любого будущего читателя:
Правильнее будет вопрос.
Почему мои объекты исчезли после применения трансформации?
После моего первоначального предположения, которое вы можете увидеть ниже, OP опубликовал более подробный код, и в конце концов я обнаружил ошибку в программе шейдера.
Умножение матрицы было в неправильном порядке:
// OP's original code
gl_Position = u_modelmatrix * u_modelViewProj * vec4(a_position, 1.0f);
// Good order
gl_Position = projection * view * model * vec4(aPos, 1.0);
Обратный порядок также может быть хорошим в зависимости от порядка матрицы
Хороший туториал про трансформации и системы координат
Оригинальный ответ
Вы не предоставили каждую часть своего кода, поэтому я могу только догадываться. Я предполагаю, что в вашем классе шейдеров вы создаете шейдерную программу, а затем правильно выполняете компоновку.
Но чтобы обновить униформы в шейдере, вы должны сообщить OpenGL, какую программу шейдера использовать, вызвав:
glUseProgram(shader->getShaderID());
//and then
glUniformMatrix4fv(...);
...
Обычно это делается в функции render() (особенно важно, если вы используете несколько шейдеров), и я не вижу этого в вашем коде.
Может потребоваться дополнительный код, чтобы найти проблему, если она связана с чем-то другим (где вы вызываете функцию render() и что там происходит? как выглядит ваша шейдерная программа?).
Спасибо за помощь! Я вызываю функцию render() в своем основном игровом цикле. В игровом цикле сначала вычисляются ViewProjectionMatrix и View matrix. Затем происходит расчет освещения. Затем я загружаю матрицы в шейдер, и модели рендерятся. Я включил туда gameloop и свою шейдерную программу. Спасибо.
Проверяя ваш шейдер, умножение матриц может быть в неправильном порядке. Лучше всего, насколько я знаю, порядок должен быть gl_Position = projection * view * model * vec4(aPos, 1.0);
, и это может вызвать проблемы, если вы поменяете его местами.
Если это вызывает проблему, вы можете найти гораздо более подробное дополнительное объяснение здесь: stackoverflow.com/questions/17717600/…
А также здесь: stackoverflow.com/questions/23521089/…
Ты прав. Умножение матриц было в неправильном порядке. Я даже не знал, что есть определенный порядок, которому ты должен следовать. Спасибо большое!!
Почему этот вопрос выглядит так, как будто он был создан ИИ?