mirror of
https://github.com/JoeyDeVries/LearnOpenGL.git
synced 2026-01-02 04:37:54 +08:00
Code re-work instancing.
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
#version 330 core
|
||||
in vec2 TexCoords;
|
||||
out vec4 color;
|
||||
out vec4 FragColor;
|
||||
|
||||
uniform sampler2D texture_diffuse1;
|
||||
in vec3 fColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = texture(texture_diffuse1, TexCoords);
|
||||
FragColor = vec4(fColor, 1.0);
|
||||
}
|
||||
@@ -1,15 +1,12 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec3 position;
|
||||
layout (location = 2) in vec2 texCoords;
|
||||
layout (location = 3) in mat4 instanceMatrix;
|
||||
layout (location = 0) in vec2 aPos;
|
||||
layout (location = 1) in vec3 aColor;
|
||||
layout (location = 2) in vec2 aOffset;
|
||||
|
||||
out vec2 TexCoords;
|
||||
|
||||
uniform mat4 projection;
|
||||
uniform mat4 view;
|
||||
out vec3 fColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = projection * view * instanceMatrix * vec4(position, 1.0f);
|
||||
TexCoords = texCoords;
|
||||
fColor = aColor;
|
||||
gl_Position = vec4(aPos + aOffset, 0.0, 1.0);
|
||||
}
|
||||
@@ -1,248 +1,142 @@
|
||||
// GLEW
|
||||
#define GLEW_STATIC
|
||||
#include <GL/glew.h>
|
||||
|
||||
// GLFW
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
// GL includes
|
||||
#include <learnopengl/shader.h>
|
||||
#include <learnopengl/camera.h>
|
||||
#include <learnopengl/model.h>
|
||||
|
||||
// GLM Mathemtics
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include <learnopengl/filesystem.h>
|
||||
#include <learnopengl/shader.h>
|
||||
|
||||
// Properties
|
||||
GLuint screenWidth = 800, screenHeight = 600;
|
||||
#include <iostream>
|
||||
|
||||
// Function prototypes
|
||||
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
|
||||
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
|
||||
void Do_Movement();
|
||||
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
|
||||
|
||||
// Camera
|
||||
Camera camera(glm::vec3(0.0f, 0.0f, 155.0f));
|
||||
bool keys[1024];
|
||||
GLfloat lastX = 400, lastY = 300;
|
||||
bool firstMouse = true;
|
||||
// settings
|
||||
const unsigned int SCR_WIDTH = 1280;
|
||||
const unsigned int SCR_HEIGHT = 720;
|
||||
|
||||
GLfloat deltaTime = 0.0f;
|
||||
GLfloat lastFrame = 0.0f;
|
||||
|
||||
// The MAIN function, from here we start our application and run our Game loop
|
||||
int main()
|
||||
{
|
||||
// Init GLFW
|
||||
// glfw: initialize and configure
|
||||
// ------------------------------
|
||||
glfwInit();
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
|
||||
|
||||
GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr); // Windowed
|
||||
// glfw window creation
|
||||
// --------------------
|
||||
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
|
||||
glfwMakeContextCurrent(window);
|
||||
if (window == NULL)
|
||||
{
|
||||
std::cout << "Failed to create GLFW window" << std::endl;
|
||||
glfwTerminate();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set the required callback functions
|
||||
glfwSetKeyCallback(window, key_callback);
|
||||
glfwSetCursorPosCallback(window, mouse_callback);
|
||||
// glad: load all OpenGL function pointers
|
||||
// ---------------------------------------
|
||||
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
|
||||
{
|
||||
std::cout << "Failed to initialize GLAD" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Options
|
||||
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||
|
||||
// Initialize GLEW to setup the OpenGL Function pointers
|
||||
glewExperimental = GL_TRUE;
|
||||
glewInit();
|
||||
|
||||
// Define the viewport dimensions
|
||||
glViewport(0, 0, screenWidth, screenHeight);
|
||||
|
||||
// Setup OpenGL options
|
||||
// configure global opengl state
|
||||
// -----------------------------
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
// Setup and compile our shaders
|
||||
Shader planetShader("planet.vs", "planet.frag");
|
||||
Shader instanceShader("instanced_asteroids.vs", "instanced_asteroids.frag");
|
||||
// build and compile shaders
|
||||
// -------------------------
|
||||
Shader shader("10.1.instancing.vs", "10.1.instancing.fs");
|
||||
|
||||
// Load models
|
||||
Model rock(FileSystem::getPath("resources/objects/rock/rock.obj").c_str());
|
||||
Model planet(FileSystem::getPath("resources/objects/planet/planet.obj").c_str());
|
||||
|
||||
// Set projection matrix
|
||||
glm::mat4 projection = glm::perspective(45.0f, (GLfloat)screenWidth/(GLfloat)screenHeight, 1.0f, 10000.0f);
|
||||
planetShader.Use();
|
||||
glUniformMatrix4fv(glGetUniformLocation(planetShader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
|
||||
// Also of instance shader
|
||||
instanceShader.Use();
|
||||
glUniformMatrix4fv(glGetUniformLocation(instanceShader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
|
||||
|
||||
// Generate a large list of semi-random model transformation matrices
|
||||
GLuint amount = 100000;
|
||||
glm::mat4* modelMatrices;
|
||||
modelMatrices = new glm::mat4[amount];
|
||||
srand(glfwGetTime()); // initialize random seed
|
||||
GLfloat radius = 150.0f;
|
||||
GLfloat offset = 25.0f;
|
||||
for(GLuint i = 0; i < amount; i++)
|
||||
// generate a list of 100 quad locations/translation-vectors
|
||||
// ---------------------------------------------------------
|
||||
glm::vec2 translations[100];
|
||||
int index = 0;
|
||||
float offset = 0.1f;
|
||||
for (int y = -10; y < 10; y += 2)
|
||||
{
|
||||
glm::mat4 model;
|
||||
// 1. Translation: Randomly displace along circle with radius 'radius' in range [-offset, offset]
|
||||
GLfloat angle = (GLfloat)i / (GLfloat)amount * 360.0f;
|
||||
GLfloat displacement = (rand() % (GLint)(2 * offset * 100)) / 100.0f - offset;
|
||||
GLfloat x = sin(angle) * radius + displacement;
|
||||
displacement = (rand() % (GLint)(2 * offset * 100)) / 100.0f - offset;
|
||||
GLfloat y = -2.5f + displacement * 0.4f; // Keep height of asteroid field smaller compared to width of x and z
|
||||
displacement = (rand() % (GLint)(2 * offset * 100)) / 100.0f - offset;
|
||||
GLfloat z = cos(angle) * radius + displacement;
|
||||
model = glm::translate(model, glm::vec3(x, y, z));
|
||||
|
||||
// 2. Scale: Scale between 0.05 and 0.25f
|
||||
GLfloat scale = (rand() % 20) / 100.0f + 0.05;
|
||||
model = glm::scale(model, glm::vec3(scale));
|
||||
|
||||
// 3. Rotation: add random rotation around a (semi)randomly picked rotation axis vector
|
||||
GLfloat rotAngle = (rand() % 360);
|
||||
model = glm::rotate(model, rotAngle, glm::vec3(0.4f, 0.6f, 0.8f));
|
||||
|
||||
// 4. Now add to list of matrices
|
||||
modelMatrices[i] = model;
|
||||
for (int x = -10; x < 10; x += 2)
|
||||
{
|
||||
glm::vec2 translation;
|
||||
translation.x = (float)x / 10.0f + offset;
|
||||
translation.y = (float)y / 10.0f + offset;
|
||||
translations[index++] = translation;
|
||||
}
|
||||
}
|
||||
|
||||
// forward declare the buffer
|
||||
GLuint buffer;
|
||||
glGenBuffers(1, &buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, amount * sizeof(glm::mat4), &modelMatrices[0], GL_STATIC_DRAW);
|
||||
// store instance data in an array buffer
|
||||
// --------------------------------------
|
||||
unsigned int instanceVBO;
|
||||
glGenBuffers(1, &instanceVBO);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec2) * 100, &translations[0], GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
// Set transformation matrices as an instance vertex attribute (with divisor 1)
|
||||
// NOTE: We're cheating a little by taking the, now publicly declared, VAO of the model's mesh(es) and adding new vertexAttribPointers
|
||||
// Normally you'd want to do this in a more organized fashion, but for learning purposes this will do.
|
||||
for(GLuint i = 0; i < rock.meshes.size(); i++)
|
||||
// set up vertex data (and buffer(s)) and configure vertex attributes
|
||||
// ------------------------------------------------------------------
|
||||
float quadVertices[] = {
|
||||
// positions // colors
|
||||
-0.05f, 0.05f, 1.0f, 0.0f, 0.0f,
|
||||
0.05f, -0.05f, 0.0f, 1.0f, 0.0f,
|
||||
-0.05f, -0.05f, 0.0f, 0.0f, 1.0f,
|
||||
|
||||
-0.05f, 0.05f, 1.0f, 0.0f, 0.0f,
|
||||
0.05f, -0.05f, 0.0f, 1.0f, 0.0f,
|
||||
0.05f, 0.05f, 0.0f, 1.0f, 1.0f
|
||||
};
|
||||
unsigned int quadVAO, quadVBO;
|
||||
glGenVertexArrays(1, &quadVAO);
|
||||
glGenBuffers(1, &quadVBO);
|
||||
glBindVertexArray(quadVAO);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(2 * sizeof(float)));
|
||||
// also set instance data
|
||||
glEnableVertexAttribArray(2);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO); // this attribute comes from a different vertex buffer
|
||||
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glVertexAttribDivisor(2, 1); // tell OpenGL this is an instanced vertex attribute.
|
||||
|
||||
|
||||
// render loop
|
||||
// -----------
|
||||
while (!glfwWindowShouldClose(window))
|
||||
{
|
||||
GLuint VAO = rock.meshes[i].VAO;
|
||||
glBindVertexArray(VAO);
|
||||
// Set attribute pointers for matrix (4 times vec4)
|
||||
glEnableVertexAttribArray(3);
|
||||
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (GLvoid*)0);
|
||||
glEnableVertexAttribArray(4);
|
||||
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (GLvoid*)(sizeof(glm::vec4)));
|
||||
glEnableVertexAttribArray(5);
|
||||
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (GLvoid*)(2 * sizeof(glm::vec4)));
|
||||
glEnableVertexAttribArray(6);
|
||||
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (GLvoid*)(3 * sizeof(glm::vec4)));
|
||||
|
||||
glVertexAttribDivisor(3, 1);
|
||||
glVertexAttribDivisor(4, 1);
|
||||
glVertexAttribDivisor(5, 1);
|
||||
glVertexAttribDivisor(6, 1);
|
||||
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
// Game loop
|
||||
while(!glfwWindowShouldClose(window))
|
||||
{
|
||||
// Set frame time
|
||||
GLfloat currentFrame = glfwGetTime();
|
||||
deltaTime = currentFrame - lastFrame;
|
||||
lastFrame = currentFrame;
|
||||
|
||||
// Check and call events
|
||||
glfwPollEvents();
|
||||
Do_Movement();
|
||||
|
||||
// Clear buffers
|
||||
glClearColor(0.03f, 0.03f, 0.03f, 1.0f);
|
||||
// render
|
||||
// ------
|
||||
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Add transformation matrices
|
||||
planetShader.Use();
|
||||
glUniformMatrix4fv(glGetUniformLocation(planetShader.Program, "view"), 1, GL_FALSE, glm::value_ptr(camera.GetViewMatrix()));
|
||||
instanceShader.Use();
|
||||
glUniformMatrix4fv(glGetUniformLocation(instanceShader.Program, "view"), 1, GL_FALSE, glm::value_ptr(camera.GetViewMatrix()));
|
||||
// draw 100 instanced quads
|
||||
shader.use();
|
||||
glBindVertexArray(quadVAO);
|
||||
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 100); // 100 triangles of 6 vertices each
|
||||
glBindVertexArray(0);
|
||||
|
||||
// Draw Planet
|
||||
planetShader.Use();
|
||||
glm::mat4 model;
|
||||
model = glm::translate(model, glm::vec3(0.0f, -5.0f, 0.0f));
|
||||
model = glm::scale(model, glm::vec3(4.0f, 4.0f, 4.0f));
|
||||
glUniformMatrix4fv(glGetUniformLocation(planetShader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model));
|
||||
planet.Draw(planetShader);
|
||||
|
||||
// Draw meteorites
|
||||
instanceShader.Use();
|
||||
// NB: This could all be implemented as a method within the Model class, perhaps "DrawInstanced(const GLuint amount)"
|
||||
glActiveTexture(GL_TEXTURE0); // Activate proper texture unit before binding
|
||||
glUniform1i(glGetUniformLocation(instanceShader.Program, "texture_diffuse1"), 0); // Now set the sampler to the correct texture unit
|
||||
glBindTexture(GL_TEXTURE_2D, rock.textures_loaded[0].id); // Note we also made the textures_loaded vector public (instead of private) from the model class.
|
||||
for(GLuint i = 0; i < rock.meshes.size(); i++)
|
||||
{
|
||||
glBindVertexArray(rock.meshes[i].VAO);
|
||||
glDrawElementsInstanced(GL_TRIANGLES, rock.meshes[i].indices.size(), GL_UNSIGNED_INT, 0, amount);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
// reset our texture binding
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// Swap the buffers
|
||||
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
|
||||
// -------------------------------------------------------------------------------
|
||||
glfwSwapBuffers(window);
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
delete[] modelMatrices;
|
||||
// optional: de-allocate all resources once they've outlived their purpose:
|
||||
// ------------------------------------------------------------------------
|
||||
glDeleteVertexArrays(1, &quadVAO);
|
||||
glDeleteBuffers(1, &quadVBO);
|
||||
|
||||
glfwTerminate();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#pragma region "User input"
|
||||
|
||||
// Moves/alters the camera positions based on user input
|
||||
void Do_Movement()
|
||||
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
|
||||
{
|
||||
// Camera controls
|
||||
if(keys[GLFW_KEY_W])
|
||||
camera.ProcessKeyboard(FORWARD, deltaTime);
|
||||
if(keys[GLFW_KEY_S])
|
||||
camera.ProcessKeyboard(BACKWARD, deltaTime);
|
||||
if(keys[GLFW_KEY_A])
|
||||
camera.ProcessKeyboard(LEFT, deltaTime);
|
||||
if(keys[GLFW_KEY_D])
|
||||
camera.ProcessKeyboard(RIGHT, deltaTime);
|
||||
}
|
||||
|
||||
// Is called whenever a key is pressed/released via GLFW
|
||||
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
|
||||
{
|
||||
if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
|
||||
glfwSetWindowShouldClose(window, GL_TRUE);
|
||||
|
||||
if(action == GLFW_PRESS)
|
||||
keys[key] = true;
|
||||
else if(action == GLFW_RELEASE)
|
||||
keys[key] = false;
|
||||
}
|
||||
|
||||
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
|
||||
{
|
||||
if(firstMouse)
|
||||
{
|
||||
lastX = xpos;
|
||||
lastY = ypos;
|
||||
firstMouse = false;
|
||||
}
|
||||
|
||||
GLfloat xoffset = xpos - lastX;
|
||||
GLfloat yoffset = lastY - ypos;
|
||||
|
||||
lastX = xpos;
|
||||
lastY = ypos;
|
||||
|
||||
camera.ProcessMouseMovement(xoffset, yoffset);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
// make sure the viewport matches the new window dimensions; note that width and
|
||||
// height will be significantly larger than specified on retina displays.
|
||||
glViewport(0, 0, width, height);
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
|
||||
in vec2 TexCoords;
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D texture_diffuse1;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = texture(texture_diffuse1, TexCoords);
|
||||
FragColor = texture(texture_diffuse1, TexCoords);
|
||||
}
|
||||
15
src/4.advanced_opengl/10.2.asteroids/10.2.instancing.vs
Normal file
15
src/4.advanced_opengl/10.2.asteroids/10.2.instancing.vs
Normal file
@@ -0,0 +1,15 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 2) in vec2 aTexCoords;
|
||||
|
||||
out vec2 TexCoords;
|
||||
|
||||
uniform mat4 projection;
|
||||
uniform mat4 view;
|
||||
uniform mat4 model;
|
||||
|
||||
void main()
|
||||
{
|
||||
TexCoords = aTexCoords;
|
||||
gl_Position = projection * view * model * vec4(aPos, 1.0f);
|
||||
}
|
||||
218
src/4.advanced_opengl/10.2.asteroids/asteroids.cpp
Normal file
218
src/4.advanced_opengl/10.2.asteroids/asteroids.cpp
Normal file
@@ -0,0 +1,218 @@
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <stb_image.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include <learnopengl/filesystem.h>
|
||||
#include <learnopengl/shader.h>
|
||||
#include <learnopengl/camera.h>
|
||||
#include <learnopengl/model.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
|
||||
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
|
||||
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
|
||||
void processInput(GLFWwindow *window);
|
||||
|
||||
// settings
|
||||
const unsigned int SCR_WIDTH = 1280;
|
||||
const unsigned int SCR_HEIGHT = 720;
|
||||
|
||||
// camera
|
||||
Camera camera(glm::vec3(0.0f, 0.0f, 55.0f));
|
||||
float lastX = (float)SCR_WIDTH / 2.0;
|
||||
float lastY = (float)SCR_HEIGHT / 2.0;
|
||||
bool firstMouse = true;
|
||||
|
||||
// timing
|
||||
float deltaTime = 0.0f;
|
||||
float lastFrame = 0.0f;
|
||||
|
||||
int main()
|
||||
{
|
||||
// glfw: initialize and configure
|
||||
// ------------------------------
|
||||
glfwInit();
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
|
||||
// glfw window creation
|
||||
// --------------------
|
||||
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
|
||||
glfwMakeContextCurrent(window);
|
||||
if (window == NULL)
|
||||
{
|
||||
std::cout << "Failed to create GLFW window" << std::endl;
|
||||
glfwTerminate();
|
||||
return -1;
|
||||
}
|
||||
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
|
||||
glfwSetCursorPosCallback(window, mouse_callback);
|
||||
glfwSetScrollCallback(window, scroll_callback);
|
||||
|
||||
// tell GLFW to capture our mouse
|
||||
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||
|
||||
// glad: load all OpenGL function pointers
|
||||
// ---------------------------------------
|
||||
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
|
||||
{
|
||||
std::cout << "Failed to initialize GLAD" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// configure global opengl state
|
||||
// -----------------------------
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
// build and compile shaders
|
||||
// -------------------------
|
||||
Shader shader("10.2.instancing.vs", "10.2.instancing.fs");
|
||||
|
||||
// load models
|
||||
// -----------
|
||||
Model rock(FileSystem::getPath("resources/objects/rock/rock.obj"));
|
||||
Model planet(FileSystem::getPath("resources/objects/planet/planet.obj"));
|
||||
|
||||
// generate a large list of semi-random model transformation matrices
|
||||
// ------------------------------------------------------------------
|
||||
unsigned int amount = 1000;
|
||||
glm::mat4* modelMatrices;
|
||||
modelMatrices = new glm::mat4[amount];
|
||||
srand(glfwGetTime()); // initialize random seed
|
||||
float radius = 50.0;
|
||||
float offset = 2.5f;
|
||||
for (unsigned int i = 0; i < amount; i++)
|
||||
{
|
||||
glm::mat4 model;
|
||||
// 1. translation: displace along circle with 'radius' in range [-offset, offset]
|
||||
float angle = (float)i / (float)amount * 360.0f;
|
||||
float displacement = (rand() % (int)(2 * offset * 100)) / 100.0f - offset;
|
||||
float x = sin(angle) * radius + displacement;
|
||||
displacement = (rand() % (int)(2 * offset * 100)) / 100.0f - offset;
|
||||
float y = displacement * 0.4f; // keep height of asteroid field smaller compared to width of x and z
|
||||
displacement = (rand() % (int)(2 * offset * 100)) / 100.0f - offset;
|
||||
float z = cos(angle) * radius + displacement;
|
||||
model = glm::translate(model, glm::vec3(x, y, z));
|
||||
|
||||
// 2. scale: Scale between 0.05 and 0.25f
|
||||
float scale = (rand() % 20) / 100.0f + 0.05;
|
||||
model = glm::scale(model, glm::vec3(scale));
|
||||
|
||||
// 3. rotation: add random rotation around a (semi)randomly picked rotation axis vector
|
||||
float rotAngle = (rand() % 360);
|
||||
model = glm::rotate(model, rotAngle, glm::vec3(0.4f, 0.6f, 0.8f));
|
||||
|
||||
// 4. now add to list of matrices
|
||||
modelMatrices[i] = model;
|
||||
}
|
||||
|
||||
// render loop
|
||||
// -----------
|
||||
while (!glfwWindowShouldClose(window))
|
||||
{
|
||||
// per-frame time logic
|
||||
// --------------------
|
||||
float currentFrame = glfwGetTime();
|
||||
deltaTime = currentFrame - lastFrame;
|
||||
lastFrame = currentFrame;
|
||||
|
||||
// input
|
||||
// -----
|
||||
processInput(window);
|
||||
|
||||
// render
|
||||
// ------
|
||||
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// configure transformation matrices
|
||||
glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 1000.0f);
|
||||
glm::mat4 view = camera.GetViewMatrix();;
|
||||
shader.use();
|
||||
shader.setMat4("projection", projection);
|
||||
shader.setMat4("view", view);
|
||||
|
||||
// draw planet
|
||||
glm::mat4 model;
|
||||
model = glm::translate(model, glm::vec3(0.0f, -3.0f, 0.0f));
|
||||
model = glm::scale(model, glm::vec3(4.0f, 4.0f, 4.0f));
|
||||
shader.setMat4("model", model);
|
||||
planet.Draw(shader);
|
||||
|
||||
// draw meteorites
|
||||
for (unsigned int i = 0; i < amount; i++)
|
||||
{
|
||||
shader.setMat4("model", modelMatrices[i]);
|
||||
rock.Draw(shader);
|
||||
}
|
||||
|
||||
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
|
||||
// -------------------------------------------------------------------------------
|
||||
glfwSwapBuffers(window);
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
glfwTerminate();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
|
||||
// ---------------------------------------------------------------------------------------------------------
|
||||
void processInput(GLFWwindow *window)
|
||||
{
|
||||
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
|
||||
glfwSetWindowShouldClose(window, true);
|
||||
|
||||
float cameraSpeed = 2.5 * deltaTime;
|
||||
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
|
||||
camera.ProcessKeyboard(FORWARD, deltaTime);
|
||||
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
|
||||
camera.ProcessKeyboard(BACKWARD, deltaTime);
|
||||
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
|
||||
camera.ProcessKeyboard(LEFT, deltaTime);
|
||||
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
|
||||
camera.ProcessKeyboard(RIGHT, deltaTime);
|
||||
}
|
||||
|
||||
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
|
||||
{
|
||||
// make sure the viewport matches the new window dimensions; note that width and
|
||||
// height will be significantly larger than specified on retina displays.
|
||||
glViewport(0, 0, width, height);
|
||||
}
|
||||
|
||||
|
||||
// glfw: whenever the mouse moves, this callback is called
|
||||
// -------------------------------------------------------
|
||||
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
|
||||
{
|
||||
if (firstMouse)
|
||||
{
|
||||
lastX = xpos;
|
||||
lastY = ypos;
|
||||
firstMouse = false;
|
||||
}
|
||||
|
||||
float xoffset = xpos - lastX;
|
||||
float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
|
||||
|
||||
lastX = xpos;
|
||||
lastY = ypos;
|
||||
|
||||
camera.ProcessMouseMovement(xoffset, yoffset);
|
||||
}
|
||||
|
||||
// glfw: whenever the mouse scroll wheel scrolls, this callback is called
|
||||
// ----------------------------------------------------------------------
|
||||
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
|
||||
{
|
||||
camera.ProcessMouseScroll(yoffset);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec3 position;
|
||||
layout (location = 2) in vec2 texCoords;
|
||||
layout (location = 3) in mat4 instanceMatrix;
|
||||
|
||||
out vec2 TexCoords;
|
||||
|
||||
uniform mat4 projection;
|
||||
uniform mat4 view;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = projection * view * instanceMatrix * vec4(position, 1.0f);
|
||||
TexCoords = texCoords;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec3 position;
|
||||
layout (location = 2) in vec2 texCoords;
|
||||
|
||||
out vec2 TexCoords;
|
||||
|
||||
uniform mat4 projection;
|
||||
uniform mat4 view;
|
||||
uniform mat4 model;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = projection * view * model * vec4(position, 1.0f);
|
||||
TexCoords = texCoords;
|
||||
}
|
||||
@@ -1,248 +0,0 @@
|
||||
// GLEW
|
||||
#define GLEW_STATIC
|
||||
#include <GL/glew.h>
|
||||
|
||||
// GLFW
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
// GL includes
|
||||
#include <learnopengl/shader.h>
|
||||
#include <learnopengl/camera.h>
|
||||
#include <learnopengl/model.h>
|
||||
|
||||
// GLM Mathemtics
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include <learnopengl/filesystem.h>
|
||||
|
||||
// Properties
|
||||
GLuint screenWidth = 800, screenHeight = 600;
|
||||
|
||||
// Function prototypes
|
||||
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
|
||||
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
|
||||
void Do_Movement();
|
||||
|
||||
// Camera
|
||||
Camera camera(glm::vec3(0.0f, 0.0f, 155.0f));
|
||||
bool keys[1024];
|
||||
GLfloat lastX = 400, lastY = 300;
|
||||
bool firstMouse = true;
|
||||
|
||||
GLfloat deltaTime = 0.0f;
|
||||
GLfloat lastFrame = 0.0f;
|
||||
|
||||
// The MAIN function, from here we start our application and run our Game loop
|
||||
int main()
|
||||
{
|
||||
// Init GLFW
|
||||
glfwInit();
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
|
||||
|
||||
GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr); // Windowed
|
||||
glfwMakeContextCurrent(window);
|
||||
|
||||
// Set the required callback functions
|
||||
glfwSetKeyCallback(window, key_callback);
|
||||
glfwSetCursorPosCallback(window, mouse_callback);
|
||||
|
||||
// Options
|
||||
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||
|
||||
// Initialize GLEW to setup the OpenGL Function pointers
|
||||
glewExperimental = GL_TRUE;
|
||||
glewInit();
|
||||
|
||||
// Define the viewport dimensions
|
||||
glViewport(0, 0, screenWidth, screenHeight);
|
||||
|
||||
// Setup OpenGL options
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
// Setup and compile our shaders
|
||||
Shader planetShader("planet.vs", "planet.frag");
|
||||
Shader instanceShader("instanced_asteroids.vs", "instanced_asteroids.frag");
|
||||
|
||||
// Load models
|
||||
Model rock(FileSystem::getPath("resources/objects/rock/rock.obj").c_str());
|
||||
Model planet(FileSystem::getPath("resources/objects/planet/planet.obj").c_str());
|
||||
|
||||
// Set projection matrix
|
||||
glm::mat4 projection = glm::perspective(45.0f, (GLfloat)screenWidth/(GLfloat)screenHeight, 1.0f, 10000.0f);
|
||||
planetShader.Use();
|
||||
glUniformMatrix4fv(glGetUniformLocation(planetShader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
|
||||
// Also of instance shader
|
||||
instanceShader.Use();
|
||||
glUniformMatrix4fv(glGetUniformLocation(instanceShader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
|
||||
|
||||
// Generate a large list of semi-random model transformation matrices
|
||||
GLuint amount = 100000;
|
||||
glm::mat4* modelMatrices;
|
||||
modelMatrices = new glm::mat4[amount];
|
||||
srand(glfwGetTime()); // initialize random seed
|
||||
GLfloat radius = 150.0f;
|
||||
GLfloat offset = 25.0f;
|
||||
for(GLuint i = 0; i < amount; i++)
|
||||
{
|
||||
glm::mat4 model;
|
||||
// 1. Translation: Randomly displace along circle with radius 'radius' in range [-offset, offset]
|
||||
GLfloat angle = (GLfloat)i / (GLfloat)amount * 360.0f;
|
||||
GLfloat displacement = (rand() % (GLint)(2 * offset * 100)) / 100.0f - offset;
|
||||
GLfloat x = sin(angle) * radius + displacement;
|
||||
displacement = (rand() % (GLint)(2 * offset * 100)) / 100.0f - offset;
|
||||
GLfloat y = -2.5f + displacement * 0.4f; // Keep height of asteroid field smaller compared to width of x and z
|
||||
displacement = (rand() % (GLint)(2 * offset * 100)) / 100.0f - offset;
|
||||
GLfloat z = cos(angle) * radius + displacement;
|
||||
model = glm::translate(model, glm::vec3(x, y, z));
|
||||
|
||||
// 2. Scale: Scale between 0.05 and 0.25f
|
||||
GLfloat scale = (rand() % 20) / 100.0f + 0.05;
|
||||
model = glm::scale(model, glm::vec3(scale));
|
||||
|
||||
// 3. Rotation: add random rotation around a (semi)randomly picked rotation axis vector
|
||||
GLfloat rotAngle = (rand() % 360);
|
||||
model = glm::rotate(model, rotAngle, glm::vec3(0.4f, 0.6f, 0.8f));
|
||||
|
||||
// 4. Now add to list of matrices
|
||||
modelMatrices[i] = model;
|
||||
}
|
||||
|
||||
// forward declare the buffer
|
||||
GLuint buffer;
|
||||
glGenBuffers(1, &buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, amount * sizeof(glm::mat4), &modelMatrices[0], GL_STATIC_DRAW);
|
||||
|
||||
// Set transformation matrices as an instance vertex attribute (with divisor 1)
|
||||
// NOTE: We're cheating a little by taking the, now publicly declared, VAO of the model's mesh(es) and adding new vertexAttribPointers
|
||||
// Normally you'd want to do this in a more organized fashion, but for learning purposes this will do.
|
||||
for(GLuint i = 0; i < rock.meshes.size(); i++)
|
||||
{
|
||||
GLuint VAO = rock.meshes[i].VAO;
|
||||
glBindVertexArray(VAO);
|
||||
// Set attribute pointers for matrix (4 times vec4)
|
||||
glEnableVertexAttribArray(3);
|
||||
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (GLvoid*)0);
|
||||
glEnableVertexAttribArray(4);
|
||||
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (GLvoid*)(sizeof(glm::vec4)));
|
||||
glEnableVertexAttribArray(5);
|
||||
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (GLvoid*)(2 * sizeof(glm::vec4)));
|
||||
glEnableVertexAttribArray(6);
|
||||
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (GLvoid*)(3 * sizeof(glm::vec4)));
|
||||
|
||||
glVertexAttribDivisor(3, 1);
|
||||
glVertexAttribDivisor(4, 1);
|
||||
glVertexAttribDivisor(5, 1);
|
||||
glVertexAttribDivisor(6, 1);
|
||||
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
// Game loop
|
||||
while(!glfwWindowShouldClose(window))
|
||||
{
|
||||
// Set frame time
|
||||
GLfloat currentFrame = glfwGetTime();
|
||||
deltaTime = currentFrame - lastFrame;
|
||||
lastFrame = currentFrame;
|
||||
|
||||
// Check and call events
|
||||
glfwPollEvents();
|
||||
Do_Movement();
|
||||
|
||||
// Clear buffers
|
||||
glClearColor(0.03f, 0.03f, 0.03f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Add transformation matrices
|
||||
planetShader.Use();
|
||||
glUniformMatrix4fv(glGetUniformLocation(planetShader.Program, "view"), 1, GL_FALSE, glm::value_ptr(camera.GetViewMatrix()));
|
||||
instanceShader.Use();
|
||||
glUniformMatrix4fv(glGetUniformLocation(instanceShader.Program, "view"), 1, GL_FALSE, glm::value_ptr(camera.GetViewMatrix()));
|
||||
|
||||
// Draw Planet
|
||||
planetShader.Use();
|
||||
glm::mat4 model;
|
||||
model = glm::translate(model, glm::vec3(0.0f, -5.0f, 0.0f));
|
||||
model = glm::scale(model, glm::vec3(4.0f, 4.0f, 4.0f));
|
||||
glUniformMatrix4fv(glGetUniformLocation(planetShader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model));
|
||||
planet.Draw(planetShader);
|
||||
|
||||
// Draw meteorites
|
||||
instanceShader.Use();
|
||||
// NB: This could all be implemented as a method within the Model class, perhaps "DrawInstanced(const GLuint amount)"
|
||||
glActiveTexture(GL_TEXTURE0); // Activate proper texture unit before binding
|
||||
glUniform1i(glGetUniformLocation(instanceShader.Program, "texture_diffuse1"), 0); // Now set the sampler to the correct texture unit
|
||||
glBindTexture(GL_TEXTURE_2D, rock.textures_loaded[0].id); // Note we also made the textures_loaded vector public (instead of private) from the model class.
|
||||
for(GLuint i = 0; i < rock.meshes.size(); i++)
|
||||
{
|
||||
glBindVertexArray(rock.meshes[i].VAO);
|
||||
glDrawElementsInstanced(GL_TRIANGLES, rock.meshes[i].indices.size(), GL_UNSIGNED_INT, 0, amount);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
// reset our texture binding
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// Swap the buffers
|
||||
glfwSwapBuffers(window);
|
||||
}
|
||||
|
||||
delete[] modelMatrices;
|
||||
|
||||
glfwTerminate();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#pragma region "User input"
|
||||
|
||||
// Moves/alters the camera positions based on user input
|
||||
void Do_Movement()
|
||||
{
|
||||
// Camera controls
|
||||
if(keys[GLFW_KEY_W])
|
||||
camera.ProcessKeyboard(FORWARD, deltaTime);
|
||||
if(keys[GLFW_KEY_S])
|
||||
camera.ProcessKeyboard(BACKWARD, deltaTime);
|
||||
if(keys[GLFW_KEY_A])
|
||||
camera.ProcessKeyboard(LEFT, deltaTime);
|
||||
if(keys[GLFW_KEY_D])
|
||||
camera.ProcessKeyboard(RIGHT, deltaTime);
|
||||
}
|
||||
|
||||
// Is called whenever a key is pressed/released via GLFW
|
||||
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
|
||||
{
|
||||
if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
|
||||
glfwSetWindowShouldClose(window, GL_TRUE);
|
||||
|
||||
if(action == GLFW_PRESS)
|
||||
keys[key] = true;
|
||||
else if(action == GLFW_RELEASE)
|
||||
keys[key] = false;
|
||||
}
|
||||
|
||||
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
|
||||
{
|
||||
if(firstMouse)
|
||||
{
|
||||
lastX = xpos;
|
||||
lastY = ypos;
|
||||
firstMouse = false;
|
||||
}
|
||||
|
||||
GLfloat xoffset = xpos - lastX;
|
||||
GLfloat yoffset = lastY - ypos;
|
||||
|
||||
lastX = xpos;
|
||||
lastY = ypos;
|
||||
|
||||
camera.ProcessMouseMovement(xoffset, yoffset);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
@@ -1,10 +1,11 @@
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
|
||||
in vec2 TexCoords;
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D texture_diffuse1;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = texture(texture_diffuse1, TexCoords);
|
||||
FragColor = texture(texture_diffuse1, TexCoords);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 2) in vec2 aTexCoords;
|
||||
layout (location = 3) in mat4 aInstanceMatrix;
|
||||
|
||||
out vec2 TexCoords;
|
||||
|
||||
uniform mat4 projection;
|
||||
uniform mat4 view;
|
||||
|
||||
void main()
|
||||
{
|
||||
TexCoords = aTexCoords;
|
||||
gl_Position = projection * view * aInstanceMatrix * vec4(aPos, 1.0f);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
|
||||
in vec2 TexCoords;
|
||||
|
||||
uniform sampler2D texture_diffuse1;
|
||||
|
||||
void main()
|
||||
{
|
||||
FragColor = texture(texture_diffuse1, TexCoords);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#version 330 core
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 2) in vec2 aTexCoords;
|
||||
|
||||
out vec2 TexCoords;
|
||||
|
||||
uniform mat4 projection;
|
||||
uniform mat4 view;
|
||||
uniform mat4 model;
|
||||
|
||||
void main()
|
||||
{
|
||||
TexCoords = aTexCoords;
|
||||
gl_Position = projection * view * model * vec4(aPos, 1.0f);
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <stb_image.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include <learnopengl/filesystem.h>
|
||||
#include <learnopengl/shader.h>
|
||||
#include <learnopengl/camera.h>
|
||||
#include <learnopengl/model.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
|
||||
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
|
||||
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
|
||||
void processInput(GLFWwindow *window);
|
||||
|
||||
// settings
|
||||
const unsigned int SCR_WIDTH = 1280;
|
||||
const unsigned int SCR_HEIGHT = 720;
|
||||
|
||||
// camera
|
||||
Camera camera(glm::vec3(0.0f, 0.0f, 155.0f));
|
||||
float lastX = (float)SCR_WIDTH / 2.0;
|
||||
float lastY = (float)SCR_HEIGHT / 2.0;
|
||||
bool firstMouse = true;
|
||||
|
||||
// timing
|
||||
float deltaTime = 0.0f;
|
||||
float lastFrame = 0.0f;
|
||||
|
||||
int main()
|
||||
{
|
||||
// glfw: initialize and configure
|
||||
// ------------------------------
|
||||
glfwInit();
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
|
||||
// glfw window creation
|
||||
// --------------------
|
||||
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
|
||||
glfwMakeContextCurrent(window);
|
||||
if (window == NULL)
|
||||
{
|
||||
std::cout << "Failed to create GLFW window" << std::endl;
|
||||
glfwTerminate();
|
||||
return -1;
|
||||
}
|
||||
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
|
||||
glfwSetCursorPosCallback(window, mouse_callback);
|
||||
glfwSetScrollCallback(window, scroll_callback);
|
||||
|
||||
// tell GLFW to capture our mouse
|
||||
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||
|
||||
// glad: load all OpenGL function pointers
|
||||
// ---------------------------------------
|
||||
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
|
||||
{
|
||||
std::cout << "Failed to initialize GLAD" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// configure global opengl state
|
||||
// -----------------------------
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
// build and compile shaders
|
||||
// -------------------------
|
||||
Shader asteroidShader("10.3.asteroids.vs", "10.3.asteroids.fs");
|
||||
Shader planetShader("10.3.planet.vs", "10.3.planet.fs");
|
||||
|
||||
// load models
|
||||
// -----------
|
||||
Model rock(FileSystem::getPath("resources/objects/rock/rock.obj"));
|
||||
Model planet(FileSystem::getPath("resources/objects/planet/planet.obj"));
|
||||
|
||||
// generate a large list of semi-random model transformation matrices
|
||||
// ------------------------------------------------------------------
|
||||
unsigned int amount = 100000;
|
||||
glm::mat4* modelMatrices;
|
||||
modelMatrices = new glm::mat4[amount];
|
||||
srand(glfwGetTime()); // initialize random seed
|
||||
float radius = 150.0;
|
||||
float offset = 25.0f;
|
||||
for (unsigned int i = 0; i < amount; i++)
|
||||
{
|
||||
glm::mat4 model;
|
||||
// 1. translation: displace along circle with 'radius' in range [-offset, offset]
|
||||
float angle = (float)i / (float)amount * 360.0f;
|
||||
float displacement = (rand() % (int)(2 * offset * 100)) / 100.0f - offset;
|
||||
float x = sin(angle) * radius + displacement;
|
||||
displacement = (rand() % (int)(2 * offset * 100)) / 100.0f - offset;
|
||||
float y = displacement * 0.4f; // keep height of asteroid field smaller compared to width of x and z
|
||||
displacement = (rand() % (int)(2 * offset * 100)) / 100.0f - offset;
|
||||
float z = cos(angle) * radius + displacement;
|
||||
model = glm::translate(model, glm::vec3(x, y, z));
|
||||
|
||||
// 2. scale: Scale between 0.05 and 0.25f
|
||||
float scale = (rand() % 20) / 100.0f + 0.05;
|
||||
model = glm::scale(model, glm::vec3(scale));
|
||||
|
||||
// 3. rotation: add random rotation around a (semi)randomly picked rotation axis vector
|
||||
float rotAngle = (rand() % 360);
|
||||
model = glm::rotate(model, rotAngle, glm::vec3(0.4f, 0.6f, 0.8f));
|
||||
|
||||
// 4. now add to list of matrices
|
||||
modelMatrices[i] = model;
|
||||
}
|
||||
|
||||
// configure instanced array
|
||||
// -------------------------
|
||||
unsigned int buffer;
|
||||
glGenBuffers(1, &buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
glBufferData(GL_ARRAY_BUFFER, amount * sizeof(glm::mat4), &modelMatrices[0], GL_STATIC_DRAW);
|
||||
|
||||
// Set transformation matrices as an instance vertex attribute (with divisor 1)
|
||||
// note: we're cheating a little by taking the, now publicly declared, VAO of the model's mesh(es) and adding new vertexAttribPointers
|
||||
// normally you'd want to do this in a more organized fashion, but for learning purposes this will do.
|
||||
// -----------------------------------------------------------------------------------------------------------------------------------
|
||||
for (unsigned int i = 0; i < rock.meshes.size(); i++)
|
||||
{
|
||||
unsigned int VAO = rock.meshes[i].VAO;
|
||||
glBindVertexArray(VAO);
|
||||
// Set attribute pointers for matrix (4 times vec4)
|
||||
glEnableVertexAttribArray(3);
|
||||
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)0);
|
||||
glEnableVertexAttribArray(4);
|
||||
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(glm::vec4)));
|
||||
glEnableVertexAttribArray(5);
|
||||
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(2 * sizeof(glm::vec4)));
|
||||
glEnableVertexAttribArray(6);
|
||||
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(3 * sizeof(glm::vec4)));
|
||||
|
||||
glVertexAttribDivisor(3, 1);
|
||||
glVertexAttribDivisor(4, 1);
|
||||
glVertexAttribDivisor(5, 1);
|
||||
glVertexAttribDivisor(6, 1);
|
||||
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
// render loop
|
||||
// -----------
|
||||
while (!glfwWindowShouldClose(window))
|
||||
{
|
||||
// per-frame time logic
|
||||
// --------------------
|
||||
float currentFrame = glfwGetTime();
|
||||
deltaTime = currentFrame - lastFrame;
|
||||
lastFrame = currentFrame;
|
||||
|
||||
// input
|
||||
// -----
|
||||
processInput(window);
|
||||
|
||||
// render
|
||||
// ------
|
||||
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// configure transformation matrices
|
||||
glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 1000.0f);
|
||||
glm::mat4 view = camera.GetViewMatrix();
|
||||
asteroidShader.use();
|
||||
asteroidShader.setMat4("projection", projection);
|
||||
asteroidShader.setMat4("view", view);
|
||||
planetShader.use();
|
||||
planetShader.setMat4("projection", projection);
|
||||
planetShader.setMat4("view", view);
|
||||
|
||||
// draw planet
|
||||
glm::mat4 model;
|
||||
model = glm::translate(model, glm::vec3(0.0f, -3.0f, 0.0f));
|
||||
model = glm::scale(model, glm::vec3(4.0f, 4.0f, 4.0f));
|
||||
planetShader.setMat4("model", model);
|
||||
planet.Draw(planetShader);
|
||||
|
||||
// draw meteorites
|
||||
asteroidShader.use();
|
||||
asteroidShader.setInt("texture_diffuse1", 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, rock.textures_loaded[0].id); // note: we also made the textures_loaded vector public (instead of private) from the model class.
|
||||
for (unsigned int i = 0; i < rock.meshes.size(); i++)
|
||||
{
|
||||
glBindVertexArray(rock.meshes[i].VAO);
|
||||
glDrawElementsInstanced(GL_TRIANGLES, rock.meshes[i].indices.size(), GL_UNSIGNED_INT, 0, amount);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
|
||||
// -------------------------------------------------------------------------------
|
||||
glfwSwapBuffers(window);
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
glfwTerminate();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
|
||||
// ---------------------------------------------------------------------------------------------------------
|
||||
void processInput(GLFWwindow *window)
|
||||
{
|
||||
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
|
||||
glfwSetWindowShouldClose(window, true);
|
||||
|
||||
float cameraSpeed = 2.5 * deltaTime;
|
||||
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
|
||||
camera.ProcessKeyboard(FORWARD, deltaTime);
|
||||
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
|
||||
camera.ProcessKeyboard(BACKWARD, deltaTime);
|
||||
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
|
||||
camera.ProcessKeyboard(LEFT, deltaTime);
|
||||
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
|
||||
camera.ProcessKeyboard(RIGHT, deltaTime);
|
||||
}
|
||||
|
||||
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
|
||||
{
|
||||
// make sure the viewport matches the new window dimensions; note that width and
|
||||
// height will be significantly larger than specified on retina displays.
|
||||
glViewport(0, 0, width, height);
|
||||
}
|
||||
|
||||
|
||||
// glfw: whenever the mouse moves, this callback is called
|
||||
// -------------------------------------------------------
|
||||
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
|
||||
{
|
||||
if (firstMouse)
|
||||
{
|
||||
lastX = xpos;
|
||||
lastY = ypos;
|
||||
firstMouse = false;
|
||||
}
|
||||
|
||||
float xoffset = xpos - lastX;
|
||||
float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
|
||||
|
||||
lastX = xpos;
|
||||
lastY = ypos;
|
||||
|
||||
camera.ProcessMouseMovement(xoffset, yoffset);
|
||||
}
|
||||
|
||||
// glfw: whenever the mouse scroll wheel scrolls, this callback is called
|
||||
// ----------------------------------------------------------------------
|
||||
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
|
||||
{
|
||||
camera.ProcessMouseScroll(yoffset);
|
||||
}
|
||||
Reference in New Issue
Block a user