add particles

This commit is contained in:
Matraf 2022-01-24 18:07:31 +01:00
parent 91ebcef017
commit 782d777654
11 changed files with 510 additions and 558 deletions

View File

@ -1,555 +0,0 @@
#define STB_IMAGE_IMPLEMENTATION
#include "glew.h"
#include "freeglut.h"
#include "glm.hpp"
#include "ext.hpp"
#include <iostream>
#include <cmath>
#include <vector>
#include <random>
#include "Shader_Loader.h"
#include "Render_Utils.h"
#include "Texture.h"
#include "Camera.h"
#include "SOIL/stb_image_aug.h"
GLuint skyboxProgram, skyboxBuffer;
GLuint bubbleProgram;
GLuint programColor;
GLuint programTexture;
GLuint textureSubmarine;
GLuint textureBubble;
GLuint textureFish;
unsigned int cubemapTexture, skyboxVAO;
unsigned int cubeVAO, cubeVBO;
float skyboxVerticeParameter = 50.0f;
float skyboxBoundary = 48.0f;
std::vector<glm::vec3> bubbleArray[300];
float old_x, old_y = -1;
glm::vec3 cursorDiff;
glm::vec3 lightDir = glm::normalize(glm::vec3(0.0f, skyboxVerticeParameter, 0.0f));
glm::vec3 cameraPos = glm::vec3(0, 0, 0);
glm::vec3 oldCameraPos = glm::vec3(0, 0, 5);
glm::vec3 cameraDir; // Wektor "do przodu" kamery
glm::vec3 cameraSide; // Wektor "w bok" kamery
float cameraAngle = 0;
glm::quat rotation = glm::quat(1, 0, 0, 0);
glm::mat4 cameraMatrix, perspectiveMatrix;
Core::Shader_Loader shaderLoader;
Core::RenderContext submarineContext;
Core::RenderContext fishContext;
Core::RenderContext bubbleContext;
std::vector<glm::vec3> fishKeyPoints({
glm::vec3(-18.0f, -10.0f, -10.0f),
glm::vec3(-10.0f, -5.0f, -12.0f),
glm::vec3(8.0f, -3.0f, -3.0f),
glm::vec3(5.0f, 0.0f, 3.0f),
glm::vec3(3.0f, 2.0f, 4.0f),
glm::vec3(8.0f, 5.0f, 9.0f),
glm::vec3(14.0f, 6.0f, 15.0f),
glm::vec3(15.0f, 12.0f, 12.0f),
glm::vec3(10.0f, 17.0f, 15.0f),
glm::vec3(5.0f, 10.0f, 7.0f),
glm::vec3(-1.0f, 4.0f, 8.0f),
glm::vec3(-8.0f, 0.0f, 3.0f),
glm::vec3(-12.0f, -6.0f, -3.0f),
glm::vec3(-15.0f, -8.0f, -6.0f),
glm::vec3(-18.0f, -10.0f, -10.0f),
});
std::vector<glm::quat> keyRotation;
std::vector<Core::Node> fish;
std::string skyboxTextures[6] = {
"models/skybox/right.jpg",
"models/skybox/left.jpg",
"models/skybox/top.jpg",
"models/skybox/bottom.jpg",
"models/skybox/front.jpg",
"models/skybox/back.jpg"
};
float cubeVertices[] = {
// positions // normals
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f
};
float skyboxVertices[] = {
-skyboxVerticeParameter, skyboxVerticeParameter, -skyboxVerticeParameter,
-skyboxVerticeParameter, -skyboxVerticeParameter, -skyboxVerticeParameter,
skyboxVerticeParameter, -skyboxVerticeParameter, -skyboxVerticeParameter,
skyboxVerticeParameter, -skyboxVerticeParameter, -skyboxVerticeParameter,
skyboxVerticeParameter, skyboxVerticeParameter, -skyboxVerticeParameter,
-skyboxVerticeParameter, skyboxVerticeParameter, -skyboxVerticeParameter,
-skyboxVerticeParameter, -skyboxVerticeParameter, skyboxVerticeParameter,
-skyboxVerticeParameter, -skyboxVerticeParameter, -skyboxVerticeParameter,
-skyboxVerticeParameter, skyboxVerticeParameter, -skyboxVerticeParameter,
-skyboxVerticeParameter, skyboxVerticeParameter, -skyboxVerticeParameter,
-skyboxVerticeParameter, skyboxVerticeParameter, skyboxVerticeParameter,
-skyboxVerticeParameter, -skyboxVerticeParameter, skyboxVerticeParameter,
skyboxVerticeParameter, -skyboxVerticeParameter, -skyboxVerticeParameter,
skyboxVerticeParameter, -skyboxVerticeParameter, skyboxVerticeParameter,
skyboxVerticeParameter, skyboxVerticeParameter, skyboxVerticeParameter,
skyboxVerticeParameter, skyboxVerticeParameter, skyboxVerticeParameter,
skyboxVerticeParameter, skyboxVerticeParameter, -skyboxVerticeParameter,
skyboxVerticeParameter, -skyboxVerticeParameter, -skyboxVerticeParameter,
-skyboxVerticeParameter, -skyboxVerticeParameter, skyboxVerticeParameter,
-skyboxVerticeParameter, skyboxVerticeParameter, skyboxVerticeParameter,
skyboxVerticeParameter, skyboxVerticeParameter, skyboxVerticeParameter,
skyboxVerticeParameter, skyboxVerticeParameter, skyboxVerticeParameter,
skyboxVerticeParameter, -skyboxVerticeParameter, skyboxVerticeParameter,
-skyboxVerticeParameter, -skyboxVerticeParameter, skyboxVerticeParameter,
-skyboxVerticeParameter, skyboxVerticeParameter, -skyboxVerticeParameter,
skyboxVerticeParameter, skyboxVerticeParameter, -skyboxVerticeParameter,
skyboxVerticeParameter, skyboxVerticeParameter, skyboxVerticeParameter,
skyboxVerticeParameter, skyboxVerticeParameter, skyboxVerticeParameter,
-skyboxVerticeParameter, skyboxVerticeParameter, skyboxVerticeParameter,
-skyboxVerticeParameter, skyboxVerticeParameter, -skyboxVerticeParameter,
-skyboxVerticeParameter, -skyboxVerticeParameter, -skyboxVerticeParameter,
-skyboxVerticeParameter, -skyboxVerticeParameter, skyboxVerticeParameter,
skyboxVerticeParameter, -skyboxVerticeParameter, -skyboxVerticeParameter,
skyboxVerticeParameter, -skyboxVerticeParameter, -skyboxVerticeParameter,
-skyboxVerticeParameter, -skyboxVerticeParameter, skyboxVerticeParameter,
skyboxVerticeParameter, -skyboxVerticeParameter, skyboxVerticeParameter
};
bool isInBoundaries(glm::vec3 nextPosition) {
return nextPosition.z > -skyboxBoundary && nextPosition.z < skyboxBoundary&& nextPosition.y > -skyboxBoundary &&
nextPosition.y < skyboxBoundary&& nextPosition.x < skyboxBoundary&& nextPosition.x > -skyboxBoundary;
}
std::random_device rd; // obtain a random number from hardware
std::mt19937 gen(rd()); // seed the generator
std::uniform_int_distribution<> distr(-skyboxVerticeParameter, skyboxVerticeParameter); // define the range
std::vector<glm::vec3> genBubbleKeyPoints() {
float random1 = distr(gen);
float random2 = distr(gen);
std::vector<glm::vec3> bubbleKeyPoints({
glm::vec3(random1 , -skyboxVerticeParameter, random2),
glm::vec3(random1 , skyboxVerticeParameter, random2)
}
);
return bubbleKeyPoints;
};
void generateBubbleArray() {
for (int i = 0; i < 300; i++) {
bubbleArray[i] = genBubbleKeyPoints();
}
}
void keyboard(unsigned char key, int x, int y)
{
float angleSpeed = 10.f;
float moveSpeed = 1.0f;
glm::vec3 nextPosition;
switch (key)
{
case 'z': cursorDiff.z -= angleSpeed; break;
case 'x': cursorDiff.z += angleSpeed; break;
case 'w':
nextPosition = cameraPos + (cameraDir * moveSpeed);
if (isInBoundaries(nextPosition)) {
cameraPos = nextPosition;
}
break;
case 's':
nextPosition = cameraPos - (cameraDir * moveSpeed);
if (isInBoundaries(nextPosition)) {
cameraPos = nextPosition;
}
break;
case 'd':
nextPosition = cameraPos + (cameraSide * moveSpeed);
if (isInBoundaries(nextPosition)) {
cameraPos = nextPosition;
}
break;
case 'a':
nextPosition = cameraPos - (cameraSide * moveSpeed);
if (isInBoundaries(nextPosition)) {
cameraPos = nextPosition;
}
break;
}
}
void mouse(int x, int y)
{
if (old_x >= 0) {
cursorDiff.x = x - old_x;
cursorDiff.y = y - old_y;
}
old_x = x;
old_y = y;
if (x < 100 || x > 800 - 100) { //you can use values other than 100 for the screen edges if you like, kind of seems to depend on your mouse sensitivity for what ends up working best
old_x = 800 / 2; //centers the last known position, this way there isn't an odd jump with your cam as it resets
old_y = 800 / 2;
glutWarpPointer(800 / 2, 800 / 2); //centers the cursor
}
else if (y < 100 || y > 800 - 100) {
old_x = 800 / 2;
old_y = 800 / 2;
glutWarpPointer(800 / 2, 800 / 2);
}
}
glm::mat4 createCameraMatrix()
{
glm::quat rotation_x = glm::angleAxis(cursorDiff.y * 0.03f, glm::vec3(1, 0, 0));
cursorDiff.y = 0;
glm::quat rotation_y = glm::angleAxis(cursorDiff.x * 0.03f, glm::vec3(0, 1, 0));
cursorDiff.x = 0;
glm::quat rotation_z = glm::angleAxis(cursorDiff.z * 0.03f, glm::vec3(0, 0, 1));
cursorDiff.z = 0;
glm::quat rotationChange = rotation_x * rotation_y * rotation_z;
rotation = glm::normalize(rotationChange * rotation);
cameraDir = glm::inverse(rotation) * glm::vec3(0, 0, -1);
cameraSide = glm::inverse(rotation) * glm::vec3(1, 0, 0);
return Core::createViewMatrixQuat(cameraPos, rotation);
}
std::vector<glm::vec3> changeKeyPoints(std::vector<glm::vec3> keyPoints, glm::vec3 toChange) {
std::vector<glm::vec3> result;
int size = keyPoints.size();
glm::vec3 change;
for (int i = 0; i < size; i++) {
change.x = keyPoints[i].x + toChange.x;
change.y = keyPoints[i].y + toChange.y;
change.z = keyPoints[i].z + toChange.z;
result.push_back(change);
}
return result;
}
glm::mat4 animationMatrix(float time, glm::vec3 change, std::vector<glm::vec3> keyPoints, glm::vec3 scaleValue, float speed) {
time = time * speed;
std::vector<float> distances;
std::vector<glm::vec3> newKeyPoints = changeKeyPoints(keyPoints, change);
float timeStep = 0;
for (int i = 0; i < keyPoints.size() - 1; i++) {
timeStep += (keyPoints[i] - keyPoints[i + 1]).length();
distances.push_back((keyPoints[i] - keyPoints[i + 1]).length());
}
time = fmod(time, timeStep);
//index of first keyPoint
int index = 0;
while (distances[index] <= time) {
time = time - distances[index];
index += 1;
}
float t = time / distances[index];
int size = keyPoints.size();
int rotationSize = keyRotation.size();
glm::vec3 pos = glm::catmullRom(newKeyPoints[std::max(0, (index - 1) % size)], newKeyPoints[(index) % size], newKeyPoints[(index + 1) % size], newKeyPoints[(index + 2) % size], t);
glm::quat divideByFour = glm::quat(0.25f, 0.25f, 0.25f, 0.25f);
auto a1 = keyRotation[index % rotationSize] * glm::exp(-(glm::log(glm::inverse(keyRotation[index % rotationSize]) * keyRotation[std::max(0, (index - 1) % rotationSize)]) + glm::log(glm::inverse(keyRotation[index % rotationSize]) * keyRotation[(index + 1) % rotationSize])) * divideByFour);
auto a2 = keyRotation[(index + 1) % rotationSize] * glm::exp(-(glm::log(glm::inverse(keyRotation[(index + 1) % rotationSize]) * keyRotation[index % rotationSize]) + glm::log(glm::inverse(keyRotation[(index + 1) % rotationSize]) * keyRotation[(index + 2) % rotationSize])) * divideByFour);
auto animationRotation = glm::squad(keyRotation[index % rotationSize], keyRotation[(index + 1) % rotationSize], a1, a2, t);
glm::mat4 result = glm::translate(pos) * glm::scale(glm::vec3(scaleValue)) * glm::mat4_cast(animationRotation);
return result;
}
void drawObjectTexture(Core::RenderContext context, glm::mat4 modelMatrix, GLuint textureId, GLuint program)
{
glUseProgram(program);
glUniform3f(glGetUniformLocation(program, "lightDir"), lightDir.x, lightDir.y, lightDir.z);
Core::SetActiveTexture(textureId, "textureSampler", program, 0);
glm::mat4 transformation = perspectiveMatrix * cameraMatrix * modelMatrix;
glUniformMatrix4fv(glGetUniformLocation(program, "modelViewProjectionMatrix"), 1, GL_FALSE, (float*)&transformation);
glUniformMatrix4fv(glGetUniformLocation(program, "modelMatrix"), 1, GL_FALSE, (float*)&modelMatrix);
Core::DrawContext(context);
glUseProgram(0);
}
void renderScene()
{
cameraMatrix = createCameraMatrix();
perspectiveMatrix = Core::createPerspectiveMatrix();
glClearColor(0.219f, 0.407f, 0.658f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
float time = glutGet(GLUT_ELAPSED_TIME) / 1000.f;
glUseProgram(skyboxProgram);
glUniform1i(glGetUniformLocation(skyboxProgram, "skybox"), 0);
glm::mat4 transformation = perspectiveMatrix * cameraMatrix;
glUniformMatrix4fv(glGetUniformLocation(skyboxProgram, "projectionViewMatrix"), 1, GL_FALSE, (float*)&transformation);
glBindVertexArray(skyboxVAO);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
glDrawArrays(GL_TRIANGLES, 0, 36);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glm::mat4 submarineInitialTransformation = glm::translate(glm::vec3(0, -0.5, -0.4)) * glm::rotate(glm::radians(180.0f), glm::vec3(0, 1, 0)) * glm::scale(glm::vec3(0.25f));
glm::mat4 submarineModelMatrix = glm::translate(cameraPos + cameraDir) * glm::mat4_cast(glm::inverse(rotation)) * submarineInitialTransformation;
glm::mat4 bubbleInitialTransformation = glm::translate(glm::vec3(0, -0.5, -0.4)) * glm::rotate(glm::radians(180.0f), glm::vec3(0, 1, 0)) * glm::scale(glm::vec3(0.5f));
glm::vec3 change1 = glm::vec3(0, 3, 0);
glm::vec3 change2 = glm::vec3(0, 0, 0);
glm::vec3 change3 = glm::vec3(3, 0, 0);
glm::vec3 change4 = glm::vec3(0, 2, 1);
glm::vec3 change0 = glm::vec3(0, 0, 0);
for (int j = 0; j < 100; j++) {
drawObjectTexture(bubbleContext, animationMatrix(time + j, change0, bubbleArray[j], glm::vec3(0.04f), 0.2f), cubemapTexture, bubbleProgram);
}
for (int i = 0; i < 5; i++) {
if (time > -10) {
drawObjectTexture(fishContext, animationMatrix(time + 15, change1, fishKeyPoints, glm::vec3(0.25f), 1.f), textureFish, programTexture);
drawObjectTexture(fishContext, animationMatrix(time + 15, change2, fishKeyPoints, glm::vec3(0.25f), 1.f), textureFish, programTexture);
drawObjectTexture(fishContext, animationMatrix(time + 15, change3, fishKeyPoints, glm::vec3(0.25f), 1.f), textureFish, programTexture);
drawObjectTexture(fishContext, animationMatrix(time + 15, change4, fishKeyPoints, glm::vec3(0.25f), 1.f), textureFish, programTexture);
time -= 6;
}
}
//drawObjectTexture(bubbleContext, submarineInitialTransformation, cubemapTexture, bubbleProgram);
drawObjectTexture(submarineContext, submarineModelMatrix, textureSubmarine, programTexture);
glutSwapBuffers();
}
void loadModelToContext(std::string path, Core::RenderContext& context)
{
Assimp::Importer import;
const aiScene* scene = import.ReadFile(path, aiProcess_Triangulate | aiProcess_CalcTangentSpace);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
{
std::cout << "ERROR::ASSIMP::" << import.GetErrorString() << std::endl;
return;
}
context.initFromAssimpMesh(scene->mMeshes[0]);
}
unsigned int loadCubemap()
{
unsigned int textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
int width, height, nrChannels;
for (unsigned int i = 0; i < 6; i++)
{
unsigned char* data = stbi_load(skyboxTextures[i].c_str(), &width, &height, &nrChannels, STBI_rgb_alpha);
if (data)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data
);
stbi_image_free(data);
}
else
{
std::cout << stbi_failure_reason() << std::endl;
std::cout << "Cubemap tex failed to load at path: " << skyboxTextures[i] << 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);
return textureID;
}
void initSkybox()
{
glGenVertexArrays(1, &skyboxVAO);
glBindVertexArray(skyboxVAO);
glGenBuffers(1, &skyboxBuffer);
glBindBuffer(GL_ARRAY_BUFFER, skyboxBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices, GL_STATIC_DRAW);
GLuint vPosition = glGetAttribLocation(skyboxProgram, "aPos");
glEnableVertexAttribArray(vPosition);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(skyboxVertices), skyboxVertices);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
}
void initKeyRotation() {
glm::vec3 oldDirection = glm::vec3(0, 0, 1);
glm::quat oldRotationCamera = glm::quat(1, 0, 0, 0);
glm::vec3 direction;
glm::quat rotation;
for (int i = 0; i < fishKeyPoints.size() - 1; i++) {
//3.1
direction = glm::normalize(fishKeyPoints[i + 1] - fishKeyPoints[i]);
//3.2
rotation = glm::normalize(glm::rotationCamera(oldDirection, direction) * oldRotationCamera);
//3.3
keyRotation.push_back(rotation);
//3.4
oldDirection = direction;
oldRotationCamera = rotation;
}
keyRotation.push_back(glm::quat(1, 0, 0, 0));
}
void initCube()
{
glGenVertexArrays(1, &cubeVAO);
glGenBuffers(1, &cubeVBO);
glBindVertexArray(cubeVAO);
glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), &cubeVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
}
void init()
{
glEnable(GL_DEPTH_TEST);
programColor = shaderLoader.CreateProgram((char*)"shaders/shader_color.vert", (char*)"shaders/shader_color.frag");
programTexture = shaderLoader.CreateProgram((char*)"shaders/shader_tex.vert", (char*)"shaders/shader_tex.frag");
skyboxProgram = shaderLoader.CreateProgram((char*)"shaders/skybox.vert", (char*)"shaders/skybox.frag");
bubbleProgram = shaderLoader.CreateProgram((char*)"shaders/bubble.vert", (char*)"shaders/bubble.frag");
cubemapTexture = loadCubemap();
loadModelToContext("models/submarine.obj", submarineContext);
textureSubmarine = Core::LoadTexture("textures/submarine.png");
loadModelToContext("models/fish.obj", fishContext);
textureFish = Core::LoadTexture("textures/fish.png");
initKeyRotation();
loadModelToContext("models/submarine.obj", submarineContext);
textureSubmarine = Core::LoadTexture("textures/submarine.png");
loadModelToContext("models/sphere.obj", bubbleContext);
textureBubble = Core::LoadTexture("textures/bubble.png");
generateBubbleArray();
initCube();
initSkybox();
}
void shutdown()
{
shaderLoader.DeleteProgram(programColor);
shaderLoader.DeleteProgram(programTexture);
shaderLoader.DeleteProgram(skyboxProgram);
shaderLoader.DeleteProgram(bubbleProgram);
}
void idle()
{
glutPostRedisplay();
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(200, 200);
glutInitWindowSize(800, 800);
glutCreateWindow("Water and shit");
glewInit();
init();
glutKeyboardFunc(keyboard);
glutPassiveMotionFunc(mouse);
glutDisplayFunc(renderScene);
glutIdleFunc(idle);
glutSetCursor(GLUT_CURSOR_NONE);
glutMainLoop();
shutdown();
return 0;
}

View File

@ -29,7 +29,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
@ -142,6 +142,7 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\Particle.cpp" />
<ClCompile Include="src\Camera.cpp" />
<ClCompile Include="src\main.cpp" />
<ClCompile Include="src\Render_Utils.cpp" />
@ -153,6 +154,7 @@
<ClCompile Include="src\Texture.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\Particle.h" />
<ClInclude Include="src\Camera.h" />
<ClInclude Include="src\objload.h" />
<ClInclude Include="src\Render_Utils.h" />

View File

@ -42,6 +42,9 @@
<ClCompile Include="src\Render_Utils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Particle.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\Shader_Loader.h">
@ -80,6 +83,9 @@
<ClInclude Include="Textures.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\Particle.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="shaders\skybox.frag" />

View File

@ -0,0 +1,16 @@
#version 330 core
// Interpolated values from the vertex shaders
in vec2 UV;
in vec4 particlecolor;
// Ouput data
out vec4 color;
uniform sampler2D myTextureSampler;
void main(){
// Output color = color of the texture at the specified UV
color = texture( myTextureSampler, UV ) * particlecolor;
}

View File

@ -0,0 +1,33 @@
#version 330 core
// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 squareVertices;
layout(location = 1) in vec4 xyzs; // Position of the center of the particule and size of the square
layout(location = 2) in vec4 color; // Position of the center of the particule and size of the square
// Output data ; will be interpolated for each fragment.
out vec2 UV;
out vec4 particlecolor;
// Values that stay constant for the whole mesh.
uniform vec3 CameraRight_worldspace;
uniform vec3 CameraUp_worldspace;
uniform mat4 VP; // Model-View-Projection matrix, but without the Model (the position is in BillboardPos; the orientation depends on the camera)
void main()
{
float particleSize = xyzs.w; // because we encoded it this way.
vec3 particleCenter_wordspace = xyzs.xyz;
vec3 vertexPosition_worldspace =
particleCenter_wordspace
+ CameraRight_worldspace * squareVertices.x * particleSize
+ CameraUp_worldspace * squareVertices.y * particleSize;
// Output position of the vertex
gl_Position = VP * vec4(vertexPosition_worldspace, 1.0f);
// UV of the vertex. No special space for this one.
UV = squareVertices.xy + vec2(0.5, 0.5);
particlecolor = color;
}

View File

@ -0,0 +1,429 @@
#define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII
#define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII
#define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII
#include "glew.h"
#include "freeglut.h"
#include "glm.hpp"
#include "../glm/gtx/norm.hpp"
#include <algorithm>
#include <iostream>
#include <cmath>
#include <vector>
GLuint billboard_vertex_buffer;
GLuint particles_position_buffer;
GLuint particles_color_buffer;
GLuint particleTexture;
static GLfloat* g_particule_position_size_data;
static GLubyte* g_particule_color_data;
struct ParticleSource {
glm::vec3 pos;
double amount;
float spread;
};
std::vector<ParticleSource> particleSources;
int ParticlesCount = 0;
double lastTime = glutGet(GLUT_ELAPSED_TIME) / 1000.0f;
// CPU representation of a particle
struct Particle {
glm::vec3 pos, speed;
unsigned char r, g, b, a; // Color
float size, angle, weight;
float life; // Remaining life of the particle. if <0 : dead and unused.
float cameradistance; // *Squared* distance to the camera. if dead : -1.0f
bool operator<(const Particle& that) const {
// Sort in reverse order : far particles drawn first.
return this->cameradistance > that.cameradistance;
}
};
void addParticleSource(glm::vec3 pos, double amount, float spread) {
ParticleSource particleSource;
particleSource.pos = pos;
particleSource.amount = amount;
particleSource.spread = spread;
particleSources.push_back(particleSource);
}
const int MaxParticles = 10000;
Particle ParticlesContainer[MaxParticles];
int LastUsedParticle = 0;
// Finds a Particle in ParticlesContainer which isn't used yet.
// (i.e. life < 0);
int FindUnusedParticle() {
for (int i = LastUsedParticle; i < MaxParticles; i++) {
if (ParticlesContainer[i].life < 0) {
LastUsedParticle = i;
return i;
}
}
for (int i = 0; i < LastUsedParticle; i++) {
if (ParticlesContainer[i].life < 0) {
LastUsedParticle = i;
return i;
}
}
return 0; // All particles are taken, override the first one
}
void SortParticles() {
std::sort(&ParticlesContainer[0], &ParticlesContainer[MaxParticles]);
}
GLuint loadDDS(const char* imagepath)
{
unsigned char header[124];
FILE* fp;
/* try to open the file */
fp = fopen(imagepath, "rb");
if (fp == NULL) {
printf("%s could not be opened. Are you in the right directory ? Don't forget to read the FAQ !\n", imagepath); getchar();
return 0;
}
/* verify the type of file */
char filecode[4];
fread(filecode, 1, 4, fp);
if (strncmp(filecode, "DDS ", 4) != 0) {
fclose(fp);
return 0;
}
/* get the surface desc */
fread(&header, 124, 1, fp);
unsigned int height = *(unsigned int*)&(header[8]);
unsigned int width = *(unsigned int*)&(header[12]);
unsigned int linearSize = *(unsigned int*)&(header[16]);
unsigned int mipMapCount = *(unsigned int*)&(header[24]);
unsigned int fourCC = *(unsigned int*)&(header[80]);
unsigned char* buffer;
unsigned int bufsize;
/* how big is it going to be including all mipmaps? */
bufsize = mipMapCount > 1 ? linearSize * 2 : linearSize;
buffer = (unsigned char*)malloc(bufsize * sizeof(unsigned char));
fread(buffer, 1, bufsize, fp);
/* close the file pointer */
fclose(fp);
unsigned int components = (fourCC == FOURCC_DXT1) ? 3 : 4;
unsigned int format;
switch (fourCC)
{
case FOURCC_DXT1:
format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
break;
case FOURCC_DXT3:
format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
break;
case FOURCC_DXT5:
format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
break;
default:
free(buffer);
return 0;
}
// Create one OpenGL texture
GLuint textureID;
glGenTextures(1, &textureID);
// "Bind" the newly created texture : all future texture functions will modify this texture
glBindTexture(GL_TEXTURE_2D, textureID);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
unsigned int blockSize = (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16;
unsigned int offset = 0;
/* load the mipmaps */
for (unsigned int level = 0; level < mipMapCount && (width || height); ++level)
{
unsigned int size = ((width + 3) / 4) * ((height + 3) / 4) * blockSize;
glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height,
0, size, buffer + offset);
offset += size;
width /= 2;
height /= 2;
// Deal with Non-Power-Of-Two textures. This code is not included in the webpage to reduce clutter.
if (width < 1) width = 1;
if (height < 1) height = 1;
}
free(buffer);
return textureID;
}
void initParticles() {
particleTexture = loadDDS("textures/particle_original.DDS");
g_particule_position_size_data = new GLfloat[MaxParticles * 4];
g_particule_color_data = new GLubyte[MaxParticles * 4];
for (int i = 0; i < MaxParticles; i++) {
ParticlesContainer[i].life = -1.0f;
ParticlesContainer[i].cameradistance = -1.0f;
}
// The VBO containing the 4 vertices of the particles.
// Thanks to instancing, they will be shared by all particles.
static const GLfloat g_vertex_buffer_data[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
};
glGenBuffers(1, &billboard_vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, billboard_vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);
// The VBO containing the positions and sizes of the particles
glGenBuffers(1, &particles_position_buffer);
glBindBuffer(GL_ARRAY_BUFFER, particles_position_buffer);
// Initialize with empty (NULL) buffer : it will be updated later, each frame.
glBufferData(GL_ARRAY_BUFFER, MaxParticles * 4 * sizeof(GLfloat), NULL, GL_STREAM_DRAW);
// The VBO containing the colors of the particles
glGenBuffers(1, &particles_color_buffer);
glBindBuffer(GL_ARRAY_BUFFER, particles_color_buffer);
// Initialize with empty (NULL) buffer : it will be updated later, each frame.
glBufferData(GL_ARRAY_BUFFER, MaxParticles * 4 * sizeof(GLubyte), NULL, GL_STREAM_DRAW);
}
void spawnParticles(double deltaTime, glm::vec3 sourcePosition, double amount, float spread) {
// Generate 10 new particule each millisecond,
// but limit this to 16 ms (60 fps), or if you have 1 long frame (1sec),
// newparticles will be huge and the next frame even longer.
int newparticles = (int)(deltaTime * amount);
if (newparticles > (int)(0.016f * amount))
newparticles = (int)(0.016f * amount);
for (int i = 0; i < newparticles; i++) {
int particleIndex = FindUnusedParticle();
ParticlesContainer[particleIndex].life = 20.0f; // This particle will live 5 seconds.
ParticlesContainer[particleIndex].pos = sourcePosition;
glm::vec3 maindir = glm::vec3(0.0f, 0.4f, 0.0f);
// Very bad way to generate a random direction;
// See for instance http://stackoverflow.com/questions/5408276/python-uniform-spherical-distribution instead,
// combined with some user-controlled parameters (main direction, spread, etc)
glm::vec3 randomdir = glm::vec3(
(rand() % 2000 - 1000.0f) / 1000.0f,
(rand() % 2000 - 1000.0f) / 1000.0f,
(rand() % 2000 - 1000.0f) / 1000.0f
);
ParticlesContainer[particleIndex].speed = maindir + randomdir * spread;
// Very bad way to generate a random color
ParticlesContainer[particleIndex].r = 200;
ParticlesContainer[particleIndex].g = 200;
ParticlesContainer[particleIndex].b = 255;
ParticlesContainer[particleIndex].a = (rand() % 256) / 3;
ParticlesContainer[particleIndex].size = (rand() % 1000) / 100000.0f + 0.05f;
}
}
void simulateParticles(glm::vec3 cameraPos, double deltaTime) {
// Simulate all particles
ParticlesCount = 0;
for (int i = 0; i < MaxParticles; i++) {
Particle& p = ParticlesContainer[i]; // shortcut
if (p.life > 0.0f) {
// Decrease life
p.life -= deltaTime;
if (p.life > 0.0f) {
// Simulate simple physics : gravity only, no collisions
// decreased acceleration - bubbles were too fast
p.speed += glm::vec3(0.0f, 1.0f, 0.0f) * (float)deltaTime * 0.5f;
p.pos += p.speed * (float)deltaTime;
p.cameradistance = glm::length2(p.pos - cameraPos);
//ParticlesContainer[i].pos += glm::vec3(0.0f,10.0f, 0.0f) * (float)delta;
// Fill the GPU buffer
g_particule_position_size_data[4 * ParticlesCount + 0] = p.pos.x;
g_particule_position_size_data[4 * ParticlesCount + 1] = p.pos.y;
g_particule_position_size_data[4 * ParticlesCount + 2] = p.pos.z;
g_particule_position_size_data[4 * ParticlesCount + 3] = p.size;
g_particule_color_data[4 * ParticlesCount + 0] = p.r;
g_particule_color_data[4 * ParticlesCount + 1] = p.g;
g_particule_color_data[4 * ParticlesCount + 2] = p.b;
g_particule_color_data[4 * ParticlesCount + 3] = p.a;
}
else {
// Particles that just died will be put at the end of the buffer in SortParticles();
p.cameradistance = -1.0f;
}
ParticlesCount++;
}
}
SortParticles();
// printf("%d ", ParticlesCount);
}
void updateParticles() {
// Update the buffers that OpenGL uses for rendering.
// There are much more sophisticated means to stream data from the CPU to the GPU,
// but this is outside the scope of this tutorial.
// http://www.opengl.org/wiki/Buffer_Object_Streaming
glBindBuffer(GL_ARRAY_BUFFER, particles_position_buffer);
glBufferData(GL_ARRAY_BUFFER, MaxParticles * 4 * sizeof(GLfloat), NULL, GL_STREAM_DRAW); // Buffer orphaning, a common way to improve streaming perf. See above link for details.
glBufferSubData(GL_ARRAY_BUFFER, 0, ParticlesCount * sizeof(GLfloat) * 4, g_particule_position_size_data);
glBindBuffer(GL_ARRAY_BUFFER, particles_color_buffer);
glBufferData(GL_ARRAY_BUFFER, MaxParticles * 4 * sizeof(GLubyte), NULL, GL_STREAM_DRAW); // Buffer orphaning, a common way to improve streaming perf. See above link for details.
glBufferSubData(GL_ARRAY_BUFFER, 0, ParticlesCount * sizeof(GLubyte) * 4, g_particule_color_data);
}
void bindParticles(GLuint programParticles, glm::vec3 cameraSide, glm::vec3 cameraVertical, glm::mat4 cameraMatrix, glm::mat4 perspectiveMatrix) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glUseProgram(programParticles);
// Vertex shader
GLuint CameraRight_worldspace_ID = glGetUniformLocation(programParticles, "CameraRight_worldspace");
GLuint CameraUp_worldspace_ID = glGetUniformLocation(programParticles, "CameraUp_worldspace");
GLuint ViewProjMatrixID = glGetUniformLocation(programParticles, "VP");
// fragment shader
GLuint TextureID = glGetUniformLocation(programParticles, "myTextureSampler");
// Bind our texture in Texture Unit 0
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, particleTexture);
// Set our "myTextureSampler" sampler to user Texture Unit 0
glUniform1i(TextureID, 0);
// Same as the billboards tutorial
glUniform3f(CameraRight_worldspace_ID, cameraSide.x, cameraSide.y, cameraSide.z);
glUniform3f(CameraUp_worldspace_ID, cameraVertical.x, cameraVertical.y, cameraVertical.z);
glm::mat4 transformation = perspectiveMatrix * cameraMatrix;
glUniformMatrix4fv(ViewProjMatrixID, 1, GL_FALSE, (float*)&transformation);
// 1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, billboard_vertex_buffer);
glVertexAttribPointer(
0, // attribute. No particular reason for 0, but must match the layout in the shader.
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// 2nd attribute buffer : positions of particles' centers
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, particles_position_buffer);
glVertexAttribPointer(
1, // attribute. No particular reason for 1, but must match the layout in the shader.
4, // size : x + y + z + size => 4
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// 3rd attribute buffer : particles' colors
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, particles_color_buffer);
glVertexAttribPointer(
2, // attribute. No particular reason for 1, but must match the layout in the shader.
4, // size : r + g + b + a => 4
GL_UNSIGNED_BYTE, // type
GL_TRUE, // normalized? *** YES, this means that the unsigned char[4] will be accessible with a vec4 (floats) in the shader ***
0, // stride
(void*)0 // array buffer offset
);
}
void renderParticles() {
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, ParticlesCount);
// These functions are specific to glDrawArrays*Instanced*.
// The first parameter is the attribute buffer we're talking about.
// The second parameter is the "rate at which generic vertex attributes advance when rendering multiple instances"
// http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribDivisor.xml
glVertexAttribDivisor(0, 0); // particles vertices : always reuse the same 4 vertices -> 0
glVertexAttribDivisor(1, 1); // positions : one per quad (its center) -> 1
glVertexAttribDivisor(2, 1); // color : one per quad -> 1
// Draw the particules !
// This draws many times a small triangle_strip (which looks like a quad).
// This is equivalent to :
// for(i in ParticlesCount) : glDrawArrays(GL_TRIANGLE_STRIP, 0, 4),
// but faster.
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, ParticlesCount);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
}
void handleAllParticleSources(glm::vec3 cameraPos, GLuint programParticles, glm::vec3 cameraSide, glm::vec3 cameraVertical, glm::mat4 cameraMatrix, glm::mat4 perspectiveMatrix) {
double currentTime = glutGet(GLUT_ELAPSED_TIME) / 1000.0f;
double delta = currentTime - lastTime;
lastTime = currentTime;
//std::cout << particleSources.size() << std::endl;
for (ParticleSource particleSource : particleSources)
{
//std::cout << particleSource.pos.x << " " << particleSource.pos.y << "" << particleSource.pos.z << "" << std::endl;
spawnParticles(delta, particleSource.pos, particleSource.amount, particleSource.spread);
}
simulateParticles(cameraPos, delta);
updateParticles();
bindParticles(programParticles, cameraSide, cameraVertical, cameraMatrix, perspectiveMatrix);
renderParticles();
}
void shutdownParticles() {
delete[] g_particule_position_size_data;
glDeleteBuffers(1, &particles_color_buffer);
glDeleteBuffers(1, &particles_position_buffer);
glDeleteBuffers(1, &billboard_vertex_buffer);
}

View File

@ -0,0 +1,8 @@
#pragma once
#include "glm.hpp"
#include "freeglut.h"
void initParticles();
void handleAllParticleSources(glm::vec3 cameraPos, GLuint programParticles, glm::vec3 cameraSide, glm::vec3 cameraVertical, glm::mat4 cameraMatrix, glm::mat4 perspectiveMatrix);
void shutdownParticles();
void addParticleSource(glm::vec3 pos, double amount, float spread);

View File

@ -12,10 +12,12 @@
#include "Texture.h"
#include "Camera.h"
#include "SOIL/stb_image_aug.h"
#include "Particle.h"
GLuint skyboxProgram, skyboxBuffer;
GLuint bubbleProgram;
GLuint programTexture;
GLuint particlesProgram;
GLuint textureSubmarine;
GLuint textureBubble;
@ -36,6 +38,7 @@ glm::vec3 oldCameraPos = glm::vec3(0, 0, 5);
glm::vec3 cameraDir; // Wektor "do przodu" kamery
glm::vec3 cameraSide; // Wektor "w bok" kamery
glm::vec3 cameraVertical;
float cameraAngle = 0;
glm::quat rotation = glm::quat(1, 0, 0, 0);
@ -231,6 +234,8 @@ void keyboard(unsigned char key, int x, int y)
cameraPos = nextPosition;
}
break;
case 'q': cameraPos += cameraVertical * moveSpeed; break;
case 'e': cameraPos -= cameraVertical * moveSpeed; break;
}
}
@ -269,6 +274,7 @@ glm::mat4 createCameraMatrix()
cameraDir = glm::inverse(rotation) * glm::vec3(0, 0, -1);
cameraSide = glm::inverse(rotation) * glm::vec3(1, 0, 0);
cameraVertical = glm::inverse(rotation) * glm::vec3(0, 1, 0);
return Core::createViewMatrixQuat(cameraPos, rotation);
}
@ -364,7 +370,7 @@ void renderScene()
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
addParticleSource(glm::vec3(cameraPos.x, cameraPos.y - 0.5, cameraPos.z -0.4), 100.0f, 1.5f);
glm::mat4 submarineInitialTransformation = glm::translate(glm::vec3(0, -0.5, -0.4)) * glm::rotate(glm::radians(180.0f), glm::vec3(0, 1, 0)) * glm::scale(glm::vec3(0.25f));
glm::mat4 submarineModelMatrix = glm::translate(cameraPos + cameraDir) * glm::mat4_cast(glm::inverse(rotation)) * submarineInitialTransformation;
@ -393,6 +399,7 @@ void renderScene()
}
//drawObjectTexture(bubbleContext, submarineInitialTransformation, cubemapTexture, bubbleProgram);
drawObjectTexture(submarineContext, submarineModelMatrix, textureSubmarine, programTexture);
handleAllParticleSources(cameraPos, particlesProgram, cameraSide, cameraVertical, cameraMatrix, perspectiveMatrix);
glutSwapBuffers();
}
@ -497,6 +504,7 @@ void init()
programTexture = shaderLoader.CreateProgram((char*)"shaders/shader_tex.vert", (char*)"shaders/shader_tex.frag");
skyboxProgram = shaderLoader.CreateProgram((char*)"shaders/skybox.vert", (char*)"shaders/skybox.frag");
bubbleProgram = shaderLoader.CreateProgram((char*)"shaders/bubble.vert", (char*)"shaders/bubble.frag");
particlesProgram = shaderLoader.CreateProgram((char*)"shaders/particles.vert", (char*)"shaders/particles.frag");
cubemapTexture = loadCubemap();
loadModelToContext("models/submarine.obj", submarineContext);
@ -513,6 +521,10 @@ void init()
generateBubbleArray();
initCube();
initSkybox();
initParticles();
//addParticleSource(glm::vec3(0, 0, 0), 100.0f, 1.5f);
//addParticleSource(glm::vec3(0, 0, 0), 100.0f, 0.3f);
//addParticleSource(glm::vec3(0, 0, 0), 100.0f, 1.5f);
}
@ -521,7 +533,8 @@ void shutdown()
shaderLoader.DeleteProgram(programTexture);
shaderLoader.DeleteProgram(skyboxProgram);
shaderLoader.DeleteProgram(bubbleProgram);
shaderLoader.DeleteProgram(particlesProgram);
shutdownParticles();
}
void idle()

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.