✨ Add skybox rendering with cubemap textures; integrate into engine
This commit is contained in:
parent
652577d0e6
commit
11fde0d25a
14
include/CubemapTexture.h
Normal file
14
include/CubemapTexture.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class CubemapTexture
|
||||
{
|
||||
public:
|
||||
CubemapTexture(const std::vector<std::string>& faces);
|
||||
void bind();
|
||||
|
||||
private:
|
||||
unsigned int textureID;
|
||||
};
|
||||
20
include/Skybox.h
Normal file
20
include/Skybox.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
namespace djalim {
|
||||
class ShaderProgram;
|
||||
}
|
||||
|
||||
#include "CubemapTexture.h"
|
||||
#include "Camera.h"
|
||||
|
||||
class Skybox
|
||||
{
|
||||
public:
|
||||
Skybox();
|
||||
void load();
|
||||
void draw(djalim::ShaderProgram* shader, Camera& camera, const glm::mat4& projection);
|
||||
|
||||
private:
|
||||
unsigned int skyboxVAO, skyboxVBO;
|
||||
CubemapTexture* cubemapTexture;
|
||||
};
|
||||
@ -10,6 +10,7 @@
|
||||
#include <string>
|
||||
#include "Mesh3D.h"
|
||||
#include "Lighting.h"
|
||||
#include "Skybox.h"
|
||||
#include <GL/glew.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <glm/glm.hpp>
|
||||
@ -41,15 +42,18 @@ namespace djalim {
|
||||
int windowHeight;
|
||||
|
||||
const double ZOOM_SENSITIVITY = -3.0;
|
||||
const float MOVE_SPEED = 5.0; // units per second
|
||||
const float MOVE_SPEED = 20.0; // units per second
|
||||
const float MOUSE_SENSITIVITY = 0.1f;
|
||||
|
||||
double previousSeconds = 0.0;
|
||||
double elapsedSeconds;
|
||||
int frameCount = 0;
|
||||
ShaderProgram shaderProgram;
|
||||
FPSCamera Camera = FPSCamera(glm::vec3(0.0f, 2.0f, 10.0f));
|
||||
ShaderProgram skyboxShaderProgram;
|
||||
FPSCamera Camera = FPSCamera();
|
||||
glm::mat4 projection;
|
||||
Lighting lighting;
|
||||
Skybox* skybox;
|
||||
|
||||
Objects3D objects;
|
||||
|
||||
@ -61,8 +65,8 @@ namespace djalim {
|
||||
void onUpdate();
|
||||
void loadHints();
|
||||
void cameraUpdate();
|
||||
void createObject(std::string name, std::string textures, std::string filepath);
|
||||
void createObject(std::string name, std::string textures, std::string filepath, glm::vec3 position, glm::vec3 rotation, glm::vec3 scale);
|
||||
void createObject(std::string name, std::string textures, std::string filepath,bool flip = false);
|
||||
void createObject(std::string name, std::string textures, std::string filepath, glm::vec3 position, glm::vec3 rotation, glm::vec3 scale,bool flip = false);
|
||||
void loadCallbacks();
|
||||
void showFps();
|
||||
void onCreate();
|
||||
|
||||
39
src/core/CubemapTexture.cpp
Normal file
39
src/core/CubemapTexture.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
#include "CubemapTexture.h"
|
||||
#include "engine.h"
|
||||
#include "stb_image/stb_image.h"
|
||||
#include <iostream>
|
||||
|
||||
CubemapTexture::CubemapTexture(const std::vector<std::string>& faces)
|
||||
{
|
||||
glGenTextures(1, &textureID);
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
|
||||
|
||||
int width, height, nrChannels;
|
||||
for (unsigned int i = 0; i < faces.size(); i++)
|
||||
{
|
||||
unsigned char *data = stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0);
|
||||
if (data)
|
||||
{
|
||||
std::cout << "Cubemap texture loaded: " << faces[i] << " width: " << width << " height: " << height << " nrChannels: " << nrChannels << std::endl;
|
||||
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
|
||||
0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data
|
||||
);
|
||||
stbi_image_free(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Cubemap texture failed to load at path: " << faces[i] << " reason: " << stbi_failure_reason() << std::endl;
|
||||
stbi_image_free(data);
|
||||
}
|
||||
}
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
void CubemapTexture::bind()
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
|
||||
}
|
||||
@ -1,13 +1,15 @@
|
||||
#include "Lighting.h"
|
||||
#include "ShaderProgram.h"
|
||||
#include <glm/fwd.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
Lighting::Lighting() {
|
||||
// Directional light
|
||||
sunLight.direction = glm::vec3(0.0f, -0.9f, -0.17f);
|
||||
sunLight.ambient = glm::vec3(0.1f, 0.1f, 0.1f);
|
||||
sunLight.diffuse = glm::vec3(0.1f, 0.1f, 0.1f);
|
||||
sunLight.specular = glm::vec3(0.1f, 0.1f, 0.1f);
|
||||
glm::vec3 power = glm::vec3(1.0f, 1.0f, 1.0f);
|
||||
sunLight.direction = glm::normalize(glm::vec3(-0.3f, -1.0f, -0.2f)); // léger angle
|
||||
sunLight.ambient = glm::vec3(0.25f, 0.22f, 0.18f); // lumière ambiante chaude
|
||||
sunLight.diffuse = glm::vec3(0.9f, 0.85f, 0.8f); // lumière du soleil douce
|
||||
sunLight.specular = glm::vec3(0.3f, 0.25f, 0.2f); // reflets légers et chauds
|
||||
|
||||
// Point Light 1
|
||||
pointLights[0].position = glm::vec3(-5.0f, 3.8f, 0.0f);
|
||||
|
||||
90
src/core/Skybox.cpp
Normal file
90
src/core/Skybox.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
#include "Skybox.h"
|
||||
#include "ShaderProgram.h"
|
||||
#include "engine.h"
|
||||
|
||||
Skybox::Skybox() { }
|
||||
|
||||
void Skybox::load()
|
||||
{
|
||||
|
||||
stbi_set_flip_vertically_on_load(false);
|
||||
std::vector<std::string> faces
|
||||
{
|
||||
"../assets/textures/skybox/right.jpg",
|
||||
"../assets/textures/skybox/left.jpg",
|
||||
"../assets/textures/skybox/top.jpg",
|
||||
"../assets/textures/skybox/bottom.jpg",
|
||||
"../assets/textures/skybox/front.jpg",
|
||||
"../assets/textures/skybox/back.jpg"
|
||||
};
|
||||
cubemapTexture = new CubemapTexture(faces);
|
||||
|
||||
float skyboxVertices[] = {
|
||||
// positions
|
||||
-1.0f, 1.0f, -1.0f,
|
||||
-1.0f, -1.0f, -1.0f,
|
||||
1.0f, -1.0f, -1.0f,
|
||||
1.0f, -1.0f, -1.0f,
|
||||
1.0f, 1.0f, -1.0f,
|
||||
-1.0f, 1.0f, -1.0f,
|
||||
|
||||
-1.0f, -1.0f, 1.0f,
|
||||
-1.0f, -1.0f, -1.0f,
|
||||
-1.0f, 1.0f, -1.0f,
|
||||
-1.0f, 1.0f, -1.0f,
|
||||
-1.0f, 1.0f, 1.0f,
|
||||
-1.0f, -1.0f, 1.0f,
|
||||
|
||||
1.0f, -1.0f, -1.0f,
|
||||
1.0f, -1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, -1.0f,
|
||||
1.0f, -1.0f, -1.0f,
|
||||
|
||||
-1.0f, -1.0f, 1.0f,
|
||||
-1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f,
|
||||
1.0f, -1.0f, 1.0f,
|
||||
-1.0f, -1.0f, 1.0f,
|
||||
|
||||
-1.0f, 1.0f, -1.0f,
|
||||
1.0f, 1.0f, -1.0f,
|
||||
1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f,
|
||||
-1.0f, 1.0f, 1.0f,
|
||||
-1.0f, 1.0f, -1.0f,
|
||||
|
||||
-1.0f, -1.0f, -1.0f,
|
||||
-1.0f, -1.0f, 1.0f,
|
||||
1.0f, -1.0f, -1.0f,
|
||||
1.0f, -1.0f, -1.0f,
|
||||
-1.0f, -1.0f, 1.0f,
|
||||
1.0f, -1.0f, 1.0f
|
||||
};
|
||||
|
||||
glGenVertexArrays(1, &skyboxVAO);
|
||||
glGenBuffers(1, &skyboxVBO);
|
||||
glBindVertexArray(skyboxVAO);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices, GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
|
||||
stbi_set_flip_vertically_on_load(true);
|
||||
}
|
||||
|
||||
void Skybox::draw(djalim::ShaderProgram* shader, Camera& camera, const glm::mat4& projection)
|
||||
{
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
shader->use();
|
||||
glm::mat4 view = glm::mat4(glm::mat3(camera.getViewMatrix()));
|
||||
shader->setUniform("view", view);
|
||||
shader->setUniform("projection", projection);
|
||||
|
||||
glBindVertexArray(skyboxVAO);
|
||||
cubemapTexture->bind();
|
||||
glDrawArrays(GL_TRIANGLES, 0, 36);
|
||||
glBindVertexArray(0);
|
||||
glDepthFunc(GL_LESS);
|
||||
}
|
||||
@ -4,57 +4,58 @@ extern bool gWireframe;
|
||||
|
||||
// TODO Try to make callbacks more flexible
|
||||
// there should not be a single function with 100+ if elses 💀
|
||||
void mainCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
|
||||
{
|
||||
//TODO Extract this in a exitCallBack Function
|
||||
void mainCallback(GLFWwindow *window, int key, int scancode, int action,
|
||||
int mods) {
|
||||
auto *engine =
|
||||
static_cast<djalim::OpenGlEngine *>(glfwGetWindowUserPointer(window));
|
||||
|
||||
// Quit
|
||||
if ((key == GLFW_KEY_ESCAPE || key == GLFW_KEY_Q) && action == GLFW_PRESS) {
|
||||
glfwSetWindowShouldClose(window, GL_TRUE);
|
||||
}
|
||||
// end todo
|
||||
|
||||
//TODO Extract this in a wireframeCallBack Function
|
||||
if (key == GLFW_KEY_W && action == GLFW_PRESS)
|
||||
// Toggle wireframe
|
||||
if (key == GLFW_KEY_E && action == GLFW_PRESS)
|
||||
gWireframe = !gWireframe;
|
||||
if (gWireframe)
|
||||
|
||||
glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
else
|
||||
glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
|
||||
// end todo
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
|
||||
// Toggle spot light
|
||||
if (key == GLFW_KEY_F && action == GLFW_PRESS) {
|
||||
engine->lighting.spotLight.on = !engine->lighting.spotLight.on;
|
||||
}
|
||||
}
|
||||
|
||||
void glfw_onFramebufferSize(GLFWwindow* window, int width, int height)
|
||||
{
|
||||
auto* engine = static_cast<djalim::OpenGlEngine*>(glfwGetWindowUserPointer(window));
|
||||
void glfw_onFramebufferSize(GLFWwindow *window, int width, int height) {
|
||||
auto *engine =
|
||||
static_cast<djalim::OpenGlEngine *>(glfwGetWindowUserPointer(window));
|
||||
|
||||
engine->windowWidth = width;
|
||||
engine->windowHeight = height;
|
||||
|
||||
|
||||
// Define the viewport dimensions
|
||||
int w, h;
|
||||
glfwGetFramebufferSize( engine->window, &w, &h); // For retina display
|
||||
glViewport(0, 0, w, h);
|
||||
// glViewport(0, 0, gWindowWidth, gWindowHeight);
|
||||
engine->windowWidth = width;
|
||||
engine->windowHeight = height;
|
||||
|
||||
// Define the viewport dimensions
|
||||
int w, h;
|
||||
glfwGetFramebufferSize(engine->window, &w, &h); // For retina display
|
||||
glViewport(0, 0, w, h);
|
||||
// glViewport(0, 0, gWindowWidth, gWindowHeight);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Called by GLFW when the mouse wheel is rotated
|
||||
//-----------------------------------------------------------------------------
|
||||
void glfw_onMouseScroll(GLFWwindow* window, double deltaX, double deltaY)
|
||||
{
|
||||
auto* engine = static_cast<djalim::OpenGlEngine*>(glfwGetWindowUserPointer(window));
|
||||
double fov = engine->Camera.getFOV() + deltaY * engine->ZOOM_SENSITIVITY;
|
||||
void glfw_onMouseScroll(GLFWwindow *window, double deltaX, double deltaY) {
|
||||
auto *engine =
|
||||
static_cast<djalim::OpenGlEngine *>(glfwGetWindowUserPointer(window));
|
||||
double fov = engine->Camera.getFOV() + deltaY * engine->ZOOM_SENSITIVITY;
|
||||
|
||||
fov = glm::clamp(fov, 1.0, 120.0);
|
||||
fov = glm::clamp(fov, 1.0, 120.0);
|
||||
|
||||
engine->Camera.setFOV((float)fov);
|
||||
engine->Camera.setFOV((float)fov);
|
||||
}
|
||||
|
||||
|
||||
void djalim::OpenGlEngine::loadCallbacks(){
|
||||
void djalim::OpenGlEngine::loadCallbacks() {
|
||||
glfwSetKeyCallback(window, mainCallback);
|
||||
glfwSetFramebufferSizeCallback(window, glfw_onFramebufferSize);
|
||||
glfwSetScrollCallback(window, glfw_onMouseScroll);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#include "engine.h"
|
||||
#include "Lighting.h"
|
||||
#include "Mesh3D.h"
|
||||
#include "Skybox.h"
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <cstdlib>
|
||||
#include <glm/ext/vector_float3.hpp>
|
||||
@ -51,21 +52,28 @@ djalim::OpenGlEngine::OpenGlEngine(const char* title, int width, int height) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
loaded = skyboxShaderProgram.loadShaders("../assets/shaders/skybox.vert", "../assets/shaders/skybox.frag");
|
||||
if (!loaded) {
|
||||
std::cerr << "Failed to load skybox shaders correctly" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
onCreate();
|
||||
|
||||
skybox = new Skybox();
|
||||
|
||||
}
|
||||
|
||||
void djalim::OpenGlEngine::createObject(std::string name, std::string textures, std::string filepath, glm::vec3 position, glm::vec3 rotation, glm::vec3 scale){
|
||||
createObject(name,textures,filepath);
|
||||
void djalim::OpenGlEngine::createObject(std::string name, std::string textures, std::string filepath, glm::vec3 position, glm::vec3 rotation, glm::vec3 scale, bool flip){
|
||||
createObject(name,textures,filepath,flip);
|
||||
(objects[name])->position = position;
|
||||
(objects[name])->rotation = rotation;
|
||||
(objects[name])->scale = scale;
|
||||
}
|
||||
|
||||
void djalim::OpenGlEngine::createObject(std::string name, std::string textures, std::string filepath){
|
||||
void djalim::OpenGlEngine::createObject(std::string name, std::string textures, std::string filepath,bool flip){
|
||||
|
||||
// Load the mesh from the file
|
||||
bool flip = (name == "mimikyu");
|
||||
objects[name] = std::make_unique<Mesh3D>(Mesh3D(filepath, flip));
|
||||
|
||||
bool textureLoaded = objects[name]->texture.loadTexture(textures);
|
||||
@ -105,12 +113,15 @@ void djalim::OpenGlEngine::createObject(std::string name, std::string textures,
|
||||
}
|
||||
|
||||
djalim::OpenGlEngine::~OpenGlEngine() {
|
||||
delete skybox;
|
||||
onDestroy();
|
||||
glfwDestroyWindow(window);
|
||||
glfwTerminate();
|
||||
}
|
||||
|
||||
void djalim::OpenGlEngine::start(){
|
||||
skybox->load();
|
||||
projection = glm::perspective(glm::radians(Camera.getFOV()), (float)windowWidth / (float)windowHeight, 0.1f, 2000.0f);
|
||||
// Main loop
|
||||
|
||||
double currentSeconds;
|
||||
@ -125,6 +136,7 @@ void djalim::OpenGlEngine::start(){
|
||||
//glClearColor(0, 0.5, 0.5, 1.0f);
|
||||
|
||||
onUpdate();
|
||||
skybox->draw(&skyboxShaderProgram, Camera, projection);
|
||||
cameraUpdate();
|
||||
|
||||
glfwSwapBuffers(this->window);
|
||||
@ -170,6 +182,9 @@ void djalim::OpenGlEngine::cameraUpdate(){
|
||||
// Clamp mouse cursor to center of screen
|
||||
glfwSetCursorPos(window, windowWidth / 2.0, windowHeight / 2.0);
|
||||
|
||||
//std::cout << "Camera position: " << Camera.getPosition().x << ", " << Camera.getPosition().y << ", " << Camera.getPosition().z << std::endl;
|
||||
//std::cout << "Camera orientation: " << Camera.getLook().x << ", " << Camera.getLook().y << ", " << Camera.getLook().z << std::endl;
|
||||
|
||||
// Camera FPS movement
|
||||
|
||||
// Forward/backward
|
||||
|
||||
@ -1,28 +1,43 @@
|
||||
#include "Camera.h"
|
||||
#include "engine.h"
|
||||
#include "Texture2D.h"
|
||||
#include <glm/fwd.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
void djalim::OpenGlEngine::onCreate(){
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
stbi_set_flip_vertically_on_load(true);
|
||||
|
||||
shaderProgram.loadShaders("../assets/shaders/lighting.vert", "../assets/shaders/lighting.frag");
|
||||
lighting = Lighting();
|
||||
|
||||
createObject("mimikyu",
|
||||
"../assets/textures/crate.jpg",
|
||||
"../assets/models/destiny island.obj",
|
||||
createObject("island",
|
||||
"../assets/textures/FloatingIsland.png",
|
||||
"../assets/models/FloatingIsland.obj",
|
||||
glm::vec3(1.0,1.0,1.0),
|
||||
glm::vec3(0.0,0.0,0.0),
|
||||
glm::vec3(100.0,100.0,100.0)
|
||||
);
|
||||
|
||||
createObject("tower",
|
||||
"../assets/textures/prof.png",
|
||||
"../assets/models/castle.obj",
|
||||
glm::vec3(1.0,65.0,1.0),
|
||||
glm::vec3(0.0,0.0,0.0),
|
||||
glm::vec3(0.0,0.0,0.0),
|
||||
glm::vec3(.01,.01,.01)
|
||||
glm::vec3(4,4,4)
|
||||
);
|
||||
createObject("cube",
|
||||
"../assets/textures/crate.jpg",
|
||||
"../assets/models/crate.obj",
|
||||
|
||||
createObject("tree",
|
||||
"../assets/textures/tree.jpg",
|
||||
"../assets/models/tree.obj",
|
||||
glm::vec3(1.0,1.0,1.0),
|
||||
glm::vec3(0.0,1.0,0.0),
|
||||
glm::vec3(0.0,0.0,0.0),
|
||||
glm::vec3(1.0,1.0,1.0)
|
||||
);
|
||||
);
|
||||
|
||||
Camera.setPosition(glm::vec3(104.0,119.0,73.0));
|
||||
|
||||
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
|
||||
}
|
||||
|
||||
@ -9,22 +9,18 @@ void djalim::OpenGlEngine::onUpdate(){
|
||||
shaderProgram.use();
|
||||
shaderProgram.setUniform("material.diffuseMap", 0);
|
||||
|
||||
glm::mat4 model(1.0), view(1.0), projection(1.0);
|
||||
glm::mat4 model(1.0), view(1.0);
|
||||
|
||||
// Create the View matrix
|
||||
view = Camera.getViewMatrix();
|
||||
|
||||
// Create the projection matrix
|
||||
projection = glm::perspective(glm::radians(Camera.getFOV()), (float)windowWidth / (float)windowHeight, 0.1f, 200.0f);
|
||||
|
||||
// Matrice de Projection
|
||||
shaderProgram.setUniform("projection", projection);
|
||||
shaderProgram.setUniform("view", view);
|
||||
|
||||
lighting.update(shaderProgram, Camera);
|
||||
|
||||
draw((objects["cube"]).get());
|
||||
draw((objects["mimikyu"]).get());
|
||||
|
||||
|
||||
for (auto const& [key, val] : objects) {
|
||||
draw(val.get());
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user