working compute shaders!
parent
686c966a77
commit
12b81f1363
|
@ -13,6 +13,25 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This part was made for this assignment and will likely be used in future projects
|
||||||
|
*/
|
||||||
|
class compute_shader {
|
||||||
|
private:
|
||||||
|
GLuint shaderID = 0;
|
||||||
|
GLuint programID = 0;
|
||||||
|
public:
|
||||||
|
explicit compute_shader(const std::string& shader_source, bool loadAsString = true);
|
||||||
|
inline void bind() const {
|
||||||
|
glUseProgram(programID);
|
||||||
|
}
|
||||||
|
inline void execute(int x, int y, int z) const {
|
||||||
|
bind();
|
||||||
|
glDispatchCompute(x, y, z);
|
||||||
|
}
|
||||||
|
~compute_shader();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note: This is taken from my final project,
|
* Note: This is taken from my final project,
|
||||||
* https://github.com/Tri11Paragon/COSC-3P98-Final-Project/blob/main/include/render/gl.h
|
* https://github.com/Tri11Paragon/COSC-3P98-Final-Project/blob/main/include/render/gl.h
|
||||||
|
@ -24,16 +43,16 @@ class shader {
|
||||||
GLint i = -1;
|
GLint i = -1;
|
||||||
};
|
};
|
||||||
// we can have shaders of many types in OpenGL
|
// we can have shaders of many types in OpenGL
|
||||||
unsigned int programID = 0;
|
GLuint programID = 0;
|
||||||
// but we will only make use of these two for now
|
// but we will only make use of these two for now
|
||||||
unsigned int vertexShaderID = 0;
|
GLuint vertexShaderID = 0;
|
||||||
unsigned int fragmentShaderID = 0;
|
GLuint fragmentShaderID = 0;
|
||||||
// while these will remain unused. (Webgl2 apparently doesn't support them despite being based on GL4.3? that's a TODO!)
|
// while these will remain unused. (Webgl2 apparently doesn't support them despite being based on GL4.3? that's a TODO!)
|
||||||
unsigned int geometryShaderID = 0;
|
GLuint geometryShaderID = 0;
|
||||||
// this would be very useful however it is highly unlikely webgl will support it
|
// this would be very useful however it is highly unlikely webgl will support it
|
||||||
// im leaving some of this stuff in here because I might expand the native application to use some of it.
|
// im leaving some of this stuff in here because I might expand the native application to use some of it.
|
||||||
// im trying to keep the web and native versions the same though
|
// im trying to keep the web and native versions the same though
|
||||||
unsigned int tessellationShaderID = 0;
|
GLuint tessellationShaderID = 0;
|
||||||
std::unordered_map<std::string, IntDefaultedToMinusOne> uniformVars;
|
std::unordered_map<std::string, IntDefaultedToMinusOne> uniformVars;
|
||||||
|
|
||||||
static unsigned int createShader(const std::string& source, int type);
|
static unsigned int createShader(const std::string& source, int type);
|
||||||
|
@ -47,18 +66,6 @@ class shader {
|
||||||
uniformVars[name].i = loc;
|
uniformVars[name].i = loc;
|
||||||
return loc;
|
return loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline std::string removeEmptyFirstLines(const std::string& string){
|
|
||||||
auto lines = blt::string::split(string, "\n");
|
|
||||||
std::string new_source_string;
|
|
||||||
for (const auto& line : lines) {
|
|
||||||
if (!line.empty() && !blt::string::contains(line, "\"")) {
|
|
||||||
new_source_string += line;
|
|
||||||
new_source_string += "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new_source_string;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
@ -120,12 +127,6 @@ class shader {
|
||||||
glUseProgram(programID);
|
glUseProgram(programID);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void updateProjectionMatrix(const blt::mat4x4& projectionMatrix);
|
|
||||||
static void updateOrthographicMatrix(const blt::mat4x4& orthoMatrix);
|
|
||||||
static void updateViewMatrix(const blt::mat4x4& viewMatrix);
|
|
||||||
// returns the perspective view matrix which is calculated per frame. (This is for optimization)
|
|
||||||
static const blt::mat4x4& getPVM();
|
|
||||||
|
|
||||||
~shader();
|
~shader();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,10 @@ void init();
|
||||||
|
|
||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
||||||
|
void runPhysicsShader();
|
||||||
|
|
||||||
|
void beginExecution();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif //ASSIGN3_HIGH_PERF_H
|
#endif //ASSIGN3_HIGH_PERF_H
|
||||||
|
|
|
@ -2,13 +2,21 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
std::string shader_frag = R"("
|
std::string shader_frag = R"("
|
||||||
#version 460
|
#version 460
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
in vec2 uv_;
|
in vec2 uv_;
|
||||||
|
in float index;
|
||||||
|
|
||||||
out vec4 out_color;
|
out vec4 out_color;
|
||||||
|
|
||||||
|
uniform mediump sampler2DArray texture_array;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
out_color = vec4(uv_, 0.0, 1.0);
|
//out_color = vec4(uv_, 0.0, 1.0);
|
||||||
|
out_color = texture(texture_array, vec3(uv_, index));
|
||||||
|
|
||||||
|
if (out_color.a < 0.1)
|
||||||
|
discard;
|
||||||
}
|
}
|
||||||
|
|
||||||
")";
|
")";
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#include <string>
|
||||||
|
std::string shader_physics = R"("
|
||||||
|
#version 460
|
||||||
|
|
||||||
|
// execute 1 shader per particle. 128 executions per group
|
||||||
|
layout (local_size_x = 128, local_size_y = 1, local_size_z = 1) in;
|
||||||
|
|
||||||
|
// due to byte alignment, storing pos and dir as vec4 was required anyways.
|
||||||
|
struct particle_t {
|
||||||
|
vec4 pos;
|
||||||
|
vec4 dir;
|
||||||
|
};
|
||||||
|
|
||||||
|
const int offset_size = 8192;
|
||||||
|
const float SPEED = 1.0f;
|
||||||
|
const float SPEED_FACTOR = 25.0f;
|
||||||
|
const float BOUNCE_FACTOR = 0.75f;
|
||||||
|
const float SPREAD = 4.5f;
|
||||||
|
const float particle_lifetime = 10.0f;
|
||||||
|
const vec2 p_min = vec2(-50, -50);
|
||||||
|
const vec2 p_max = vec2(50, 50);
|
||||||
|
const vec3 inital_pos = vec3(0.0f, 1.0f, 0.0f);
|
||||||
|
const vec4 inital_dir = vec4(0.0f, 1.0f, 0.0f, 0.0f);
|
||||||
|
const vec4 GRAVITY = vec4(0.0, -9.8, 0.0, 0.0);
|
||||||
|
|
||||||
|
|
||||||
|
layout (std430, binding=0) buffer particle_buffer {
|
||||||
|
particle_t particles[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout (std430, binding=1) buffer offset_buffer {
|
||||||
|
vec4 offsets[offset_size];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(location=0) uniform float deltaSeconds;
|
||||||
|
|
||||||
|
void resetParticle(uint i, float w) {
|
||||||
|
particles[i].dir = inital_dir * SPEED_FACTOR + offsets[i % offset_size] * SPREAD;
|
||||||
|
particles[i].pos = vec4(inital_pos, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkBounds(vec2 pos) {
|
||||||
|
return pos.x > p_min.x && pos.y > p_min.y && pos.x < p_max.x && pos.y < p_max.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
uint i = gl_GlobalInvocationID.x;
|
||||||
|
|
||||||
|
vec4 pos = particles[i].pos;
|
||||||
|
vec4 dir = particles[i].dir;
|
||||||
|
|
||||||
|
dir.w += deltaSeconds;
|
||||||
|
|
||||||
|
if (dir.w > particle_lifetime) {
|
||||||
|
resetParticle(i, pos.w);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += vec4(dir.xyz * SPEED * deltaSeconds, 0.0);
|
||||||
|
dir += vec4(GRAVITY.xyz * deltaSeconds, 0.0);
|
||||||
|
|
||||||
|
if (pos.y < 0 && checkBounds(pos.xy)) {
|
||||||
|
dir.y = -dir.y * BOUNCE_FACTOR;
|
||||||
|
pos.y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
particles[i].dir = dir;
|
||||||
|
particles[i].pos = pos;
|
||||||
|
|
||||||
|
if (pos.y < -50)
|
||||||
|
resetParticle(i, pos.w);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
")";
|
||||||
|
#endif
|
|
@ -9,12 +9,14 @@ layout (location = 2) in vec4 pos;
|
||||||
layout (location = 3) in vec4 dir;
|
layout (location = 3) in vec4 dir;
|
||||||
|
|
||||||
out vec2 uv_;
|
out vec2 uv_;
|
||||||
|
out float index;
|
||||||
|
|
||||||
uniform mat4 pvm;
|
uniform mat4 pvm;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
// passthough the UV (OpenGL interpolates this per fragment)
|
// passthough the UV (OpenGL interpolates this per fragment)
|
||||||
uv_ = uv;
|
uv_ = uv;
|
||||||
|
index = pos.w;
|
||||||
// offset the vertex by the particle's position
|
// offset the vertex by the particle's position
|
||||||
gl_Position = pvm * vec4(vertex + pos.xyz, 1.0);
|
gl_Position = pvm * vec4(vertex + pos.xyz, 1.0);
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
2
libs/BLT
2
libs/BLT
|
@ -1 +1 @@
|
||||||
Subproject commit 289af1317141c53e04998b32760efced0956db57
|
Subproject commit 08c542658240b60d1221e3580f3c6b4a2f483919
|
|
@ -12,6 +12,18 @@
|
||||||
* https://github.com/Tri11Paragon/COSC-3P98-Final-Project/blob/main/include/render/gl.h
|
* https://github.com/Tri11Paragon/COSC-3P98-Final-Project/blob/main/include/render/gl.h
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static inline std::string removeEmptyFirstLines(const std::string& string){
|
||||||
|
auto lines = blt::string::split(string, "\n");
|
||||||
|
std::string new_source_string;
|
||||||
|
for (const auto& line : lines) {
|
||||||
|
if (!line.empty() && !blt::string::contains(line, "\"")) {
|
||||||
|
new_source_string += line;
|
||||||
|
new_source_string += "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new_source_string;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int shader::createShader(const std::string& source, int type) {
|
unsigned int shader::createShader(const std::string& source, int type) {
|
||||||
const char* shader_code = source.c_str();
|
const char* shader_code = source.c_str();
|
||||||
// creates a Shader
|
// creates a Shader
|
||||||
|
@ -153,4 +165,54 @@ shader::shader(shader&& move) noexcept {
|
||||||
uniformVars.insert(pair);
|
uniformVars.insert(pair);
|
||||||
// by setting the program ID to -1 we tell the shader it has been moved.
|
// by setting the program ID to -1 we tell the shader it has been moved.
|
||||||
move.programID = -1;
|
move.programID = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This part was made for this assignment and will likely be used in future projects
|
||||||
|
*/
|
||||||
|
|
||||||
|
compute_shader::compute_shader(const std::string& shader_source, bool loadAsString) {
|
||||||
|
int status;
|
||||||
|
std::string source;
|
||||||
|
const char* c_source;
|
||||||
|
|
||||||
|
if (!loadAsString)
|
||||||
|
source = blt::fs::loadShaderFile(shader_source);
|
||||||
|
else
|
||||||
|
source = removeEmptyFirstLines(shader_source);
|
||||||
|
|
||||||
|
c_source = source.c_str();
|
||||||
|
|
||||||
|
shaderID = glCreateShader(GL_COMPUTE_SHADER);
|
||||||
|
|
||||||
|
glShaderSource(shaderID, 1, &c_source, NULL);
|
||||||
|
glCompileShader(shaderID);
|
||||||
|
|
||||||
|
glGetShaderiv(shaderID, GL_COMPILE_STATUS, &status);
|
||||||
|
if (!status){
|
||||||
|
int log_length = 0;
|
||||||
|
glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &log_length);
|
||||||
|
blt::scoped_buffer<GLchar> infoLog{static_cast<unsigned long>(log_length + 1)};
|
||||||
|
glGetShaderInfoLog(shaderID, log_length + 1, nullptr, infoLog.buffer);
|
||||||
|
BLT_ERROR("Unable to compile compute shader! (%d)", log_length);
|
||||||
|
BLT_ERROR(infoLog.buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
programID = glCreateProgram();
|
||||||
|
glAttachShader(programID, shaderID);
|
||||||
|
glLinkProgram(programID);
|
||||||
|
|
||||||
|
glGetProgramiv(shaderID, GL_LINK_STATUS, &status);
|
||||||
|
if (!status){
|
||||||
|
int log_length = 0;
|
||||||
|
glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &log_length);
|
||||||
|
blt::scoped_buffer<GLchar> infoLog{static_cast<unsigned long>(log_length + 1)};
|
||||||
|
glGetProgramInfoLog(programID, log_length + 1, nullptr, infoLog.buffer);
|
||||||
|
BLT_ERROR("Unable to link compute shader!");
|
||||||
|
BLT_ERROR(infoLog.buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compute_shader::~compute_shader() {
|
||||||
|
glDeleteShader(shaderID);
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
* Licensed under GNU General Public License V3.0
|
* Licensed under GNU General Public License V3.0
|
||||||
* See LICENSE file for license detail
|
* See LICENSE file for license detail
|
||||||
*/
|
*/
|
||||||
|
#include <locale.h>
|
||||||
#include <high_perf/gl_util.h>
|
#include <high_perf/gl_util.h>
|
||||||
#include <modes/high_perf.h>
|
#include <modes/high_perf.h>
|
||||||
#include <util.h>
|
#include <util.h>
|
||||||
|
@ -10,6 +11,9 @@
|
||||||
#include "blt/std/memory.h"
|
#include "blt/std/memory.h"
|
||||||
#include <shaders/vertex.vert>
|
#include <shaders/vertex.vert>
|
||||||
#include <shaders/fragment.frag>
|
#include <shaders/fragment.frag>
|
||||||
|
#include <shaders/physics.comp>
|
||||||
|
#include <stb_image.h>
|
||||||
|
#include <stb_image_resize.h>
|
||||||
|
|
||||||
//static inline float degreesToRadian(float deg) {
|
//static inline float degreesToRadian(float deg) {
|
||||||
// return deg * (float)M_PI / 180.0f;
|
// return deg * (float)M_PI / 180.0f;
|
||||||
|
@ -32,13 +36,26 @@ void window_resize(int width, int height) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GLuint particleTranslationsVBO;
|
// -------{VBO}-------
|
||||||
|
GLuint particleTranslationsBuffer;
|
||||||
|
GLuint particleOffsetsBuffer;
|
||||||
GLuint verticesVBO;
|
GLuint verticesVBO;
|
||||||
GLuint uvsVBO;
|
GLuint uvsVBO;
|
||||||
GLuint indicesEBO;
|
GLuint indicesEBO;
|
||||||
|
|
||||||
|
// -------{VAO}-------
|
||||||
GLuint particleVAO;
|
GLuint particleVAO;
|
||||||
|
|
||||||
const unsigned int particle_count = 25000000;
|
// -------{Textures}-------
|
||||||
|
GLuint textureArrayID;
|
||||||
|
|
||||||
|
const unsigned int TEXTURE_COUNT = 10;
|
||||||
|
const unsigned int TEXTURE_WIDTH = 512;
|
||||||
|
const unsigned int TEXTURE_HEIGHT = 512;
|
||||||
|
|
||||||
|
// -------{Particles}-------
|
||||||
|
const unsigned int particle_count = 128 * 10000;
|
||||||
|
const unsigned int offset_count = 8192;
|
||||||
|
|
||||||
// generally alignment to multiples of 4 floats helps performance, plus we can use that extra space for info we need.
|
// generally alignment to multiples of 4 floats helps performance, plus we can use that extra space for info we need.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -69,19 +86,43 @@ blt::mat4x4 perspectiveMatrix;
|
||||||
blt::mat4x4 viewMatrix;
|
blt::mat4x4 viewMatrix;
|
||||||
|
|
||||||
shader* instance_shader;
|
shader* instance_shader;
|
||||||
|
compute_shader* physics_shader;
|
||||||
|
|
||||||
void updateView() {
|
void updateView() {
|
||||||
viewMatrix = createViewMatrix();
|
viewMatrix = createViewMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool execute = false;
|
||||||
|
|
||||||
|
void beginExecution() {
|
||||||
|
execute = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void runPhysicsShader(){
|
||||||
|
if (!execute)
|
||||||
|
return;
|
||||||
|
|
||||||
|
physics_shader->bind();
|
||||||
|
glUniform1f(0, (float)((double) getDelta() / 1000000000.0));
|
||||||
|
glBindBuffer(GL_SHADER_STORAGE_BUFFER, particleTranslationsBuffer);
|
||||||
|
physics_shader->execute(particle_count / 128, 1, 1);
|
||||||
|
|
||||||
|
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
void render() {
|
void render() {
|
||||||
updateView();
|
updateView();
|
||||||
perspectiveMatrix = blt::perspective(FOV, (float)WINDOW_WIDTH / (float)WINDOW_HEIGHT, 0.1f, 1000.0f);
|
perspectiveMatrix = blt::perspective(FOV, (float)WINDOW_WIDTH / (float)WINDOW_HEIGHT, 0.1f, 1000.0f);
|
||||||
auto pvm = perspectiveMatrix * viewMatrix;
|
auto pvm = perspectiveMatrix * viewMatrix;
|
||||||
|
|
||||||
|
runPhysicsShader();
|
||||||
|
|
||||||
instance_shader->bind();
|
instance_shader->bind();
|
||||||
instance_shader->setMatrix("pvm", pvm);
|
instance_shader->setMatrix("pvm", pvm);
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY, textureArrayID);
|
||||||
|
|
||||||
glBindVertexArray(particleVAO);
|
glBindVertexArray(particleVAO);
|
||||||
glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0, particle_count);
|
glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0, particle_count);
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
|
@ -89,23 +130,65 @@ void render() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
blt::scoped_buffer<particle_record> translations{particle_count};
|
BLT_DEBUG("High performance subsystem init");
|
||||||
blt::random<float> pos{-50.0, 50.0};
|
setlocale(LC_NUMERIC, "");
|
||||||
|
BLT_INFO("Using %'d particle count", particle_count);
|
||||||
|
BLT_INFO("Loading %d texture(s) of size (%d, %d)", TEXTURE_COUNT, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||||
|
BLT_TRACE("Checking system constants");
|
||||||
|
|
||||||
|
// number of work groups allowed (min: 65535)
|
||||||
|
GLint workGroupCountX;
|
||||||
|
GLint workGroupCountY;
|
||||||
|
GLint workGroupCountZ;
|
||||||
|
// max local size of the work groups (min: 1024, 1024, 64)
|
||||||
|
GLint workGroupSize;
|
||||||
|
// max number of work group invocations (min: 1024)
|
||||||
|
GLint workGroupInvocations;
|
||||||
|
|
||||||
|
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &workGroupCountX);
|
||||||
|
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &workGroupCountY);
|
||||||
|
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, &workGroupCountZ);
|
||||||
|
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &workGroupSize);
|
||||||
|
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS, 0, &workGroupInvocations);
|
||||||
|
|
||||||
|
BLT_INFO("This system's OpenGL supports (%d, %d, %d) work groups", workGroupCountX, workGroupCountY, workGroupCountZ);
|
||||||
|
BLT_INFO("\tLocal group max total size: %d", workGroupSize);
|
||||||
|
BLT_INFO("\tMax work group invocations: %d", workGroupInvocations);
|
||||||
|
blt::scoped_buffer<particle_record> translations{particle_count};
|
||||||
|
blt::scoped_buffer<vec4> offsets{offset_count};
|
||||||
|
blt::random<float> dir{-1, 1};
|
||||||
|
blt::random<float> lifetime{0, 25};
|
||||||
|
|
||||||
|
BLT_TRACE("Creating particles");
|
||||||
for (int i = 0; i < particle_count; i++)
|
for (int i = 0; i < particle_count; i++)
|
||||||
translations[i] = particle_record{vec4{pos.get(), pos.get() / 2, pos.get(), (float)(i % 10)}, vec4{0, 0, 0, 0}};
|
translations[i] = particle_record{vec4{0, 1, 0, (float)(i % 10)}, vec4{0, 1, 0, lifetime.get()}};
|
||||||
|
|
||||||
|
for (int i = 0; i < offset_count; i++) {
|
||||||
|
blt::vec2 v {dir.get(), dir.get()};
|
||||||
|
v = v.normalize();
|
||||||
|
offsets[i] = vec4{v[0], 0, v[1], 0};
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
// Create OpenGL Objects
|
// Create OpenGL Objects
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
BLT_TRACE("Creating OpenGL objects");
|
||||||
// create our VAO
|
// create our VAO
|
||||||
glGenVertexArrays(1, &particleVAO);
|
glGenVertexArrays(1, &particleVAO);
|
||||||
// create our VBOs
|
// create our VBOs
|
||||||
glGenBuffers(1, &particleTranslationsVBO);
|
glGenBuffers(1, &particleTranslationsBuffer);
|
||||||
|
glGenBuffers(1, &particleOffsetsBuffer);
|
||||||
glGenBuffers(1, &verticesVBO);
|
glGenBuffers(1, &verticesVBO);
|
||||||
glGenBuffers(1, &uvsVBO);
|
glGenBuffers(1, &uvsVBO);
|
||||||
glGenBuffers(1, &indicesEBO);
|
glGenBuffers(1, &indicesEBO);
|
||||||
|
// create our texture
|
||||||
|
glGenTextures(1, &textureArrayID);
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// Upload/Assign OpenGL Objects
|
||||||
|
// ----------------------------------
|
||||||
|
|
||||||
|
BLT_TRACE("Uploading VBO data and assigning to VAO");
|
||||||
glBindVertexArray(particleVAO);
|
glBindVertexArray(particleVAO);
|
||||||
|
|
||||||
// bind and upload vertices data to the GPU
|
// bind and upload vertices data to the GPU
|
||||||
|
@ -124,7 +207,7 @@ void init() {
|
||||||
glEnableVertexAttribArray(1);
|
glEnableVertexAttribArray(1);
|
||||||
|
|
||||||
int translations_size = sizeof(particle_record) * particle_count;
|
int translations_size = sizeof(particle_record) * particle_count;
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, particleTranslationsVBO);
|
glBindBuffer(GL_ARRAY_BUFFER, particleTranslationsBuffer);
|
||||||
glBufferData(GL_ARRAY_BUFFER, translations_size, translations.buffer, GL_DYNAMIC_DRAW); // allocate some memory on the GPU
|
glBufferData(GL_ARRAY_BUFFER, translations_size, translations.buffer, GL_DYNAMIC_DRAW); // allocate some memory on the GPU
|
||||||
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(particle_record), (void*) 0);
|
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(particle_record), (void*) 0);
|
||||||
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(particle_record), (void*) offsetof(particle_record, dir));
|
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(particle_record), (void*) offsetof(particle_record, dir));
|
||||||
|
@ -134,18 +217,108 @@ void init() {
|
||||||
glEnableVertexAttribArray(2);
|
glEnableVertexAttribArray(2);
|
||||||
glEnableVertexAttribArray(3);
|
glEnableVertexAttribArray(3);
|
||||||
|
|
||||||
|
// allow the particle buffer to be used in the computer shader!
|
||||||
|
glBindBuffer(GL_SHADER_STORAGE_BUFFER, particleTranslationsBuffer);
|
||||||
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, particleTranslationsBuffer);
|
||||||
|
|
||||||
|
// generating random numbers on the GPU is hard, we can use enough precomputed random offsets to simulate real time randomness
|
||||||
|
glBindBuffer(GL_SHADER_STORAGE_BUFFER, particleOffsetsBuffer);
|
||||||
|
glBufferData(GL_SHADER_STORAGE_BUFFER, offset_count * sizeof(vec4), offsets.buffer, GL_STATIC_DRAW);
|
||||||
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, particleOffsetsBuffer);
|
||||||
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesEBO);
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesEBO);
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(float) * 6, indices, GL_STATIC_DRAW);
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(float) * 6, indices, GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------
|
||||||
|
// Texturing
|
||||||
|
// ----------------------------------
|
||||||
|
BLT_TRACE("Creating array texture");
|
||||||
|
// based on my final project's texture array implementation
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY, textureArrayID);
|
||||||
|
// allocate immutable storage for our textures
|
||||||
|
// we can change what is stored inside the texture, but we cannot change its size
|
||||||
|
// which is why we need to be specific here about the type of data we will be storing.
|
||||||
|
glTexStorage3D(GL_TEXTURE_2D_ARRAY, 4, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, TEXTURE_COUNT);
|
||||||
|
|
||||||
|
std::string texture_locations[TEXTURE_COUNT] = {
|
||||||
|
"wayland.png",
|
||||||
|
"SPONGEBOB_YOUTUBE.jpg",
|
||||||
|
"1618325873904.png",
|
||||||
|
"1665624414712991.jpg",
|
||||||
|
"stonks.png",
|
||||||
|
"yak.png",
|
||||||
|
"penguin.jpg",
|
||||||
|
"fFTkb.png",
|
||||||
|
"depression.png",
|
||||||
|
"1665624414712991.jpg"
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr int channel_count = 4;
|
||||||
|
|
||||||
|
int level = 0;
|
||||||
|
stbi_set_flip_vertically_on_load(true);
|
||||||
|
for (const std::string& texture_loc : texture_locations){
|
||||||
|
// load the texture
|
||||||
|
int width, height, channels;
|
||||||
|
auto* data = stbi_load(
|
||||||
|
(std::string("resources/") += texture_loc).c_str(), &width, &height,
|
||||||
|
&channels, channel_count
|
||||||
|
);
|
||||||
|
auto* resized_data = data;
|
||||||
|
|
||||||
|
// resize if necessary
|
||||||
|
if (width != TEXTURE_WIDTH || height != TEXTURE_HEIGHT){
|
||||||
|
// needs to be malloc since stbi_image_free is just free()
|
||||||
|
auto output_data = (unsigned char*) malloc(
|
||||||
|
TEXTURE_WIDTH * TEXTURE_HEIGHT * channel_count
|
||||||
|
);
|
||||||
|
if (stbir_resize_uint8(
|
||||||
|
// input
|
||||||
|
data, width, height, 0,
|
||||||
|
// output
|
||||||
|
output_data, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0,
|
||||||
|
// channels
|
||||||
|
channel_count
|
||||||
|
)) {
|
||||||
|
BLT_WARN("Error resizing block texture image!");
|
||||||
|
}
|
||||||
|
stbi_image_free(data);
|
||||||
|
resized_data = output_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// upload image to the gpu
|
||||||
|
glTexSubImage3D(
|
||||||
|
GL_TEXTURE_2D_ARRAY, 0, 0, 0, level++, TEXTURE_WIDTH, TEXTURE_HEIGHT, 1,
|
||||||
|
GL_RGBA, GL_UNSIGNED_BYTE, resized_data
|
||||||
|
);
|
||||||
|
|
||||||
|
stbi_image_free(resized_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||||
|
// Anisotropy helps preserve textures at oblique angles
|
||||||
|
float a = 0;
|
||||||
|
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &a);
|
||||||
|
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY_EXT, a);
|
||||||
|
// mipmaps reduce resolution of textures as the distance to them increases
|
||||||
|
glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
|
||||||
|
|
||||||
|
BLT_TRACE("Loading shaders");
|
||||||
|
|
||||||
instance_shader = new shader(shader_vert, shader_frag, "", true);
|
instance_shader = new shader(shader_vert, shader_frag, "", true);
|
||||||
|
physics_shader = new compute_shader(shader_physics);
|
||||||
|
|
||||||
|
BLT_DEBUG("High performance subsystem init complete!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanup() {
|
void cleanup() {
|
||||||
// cleanup opengl resources
|
// cleanup opengl resources
|
||||||
glDeleteVertexArrays(1, &particleVAO);
|
glDeleteVertexArrays(1, &particleVAO);
|
||||||
glDeleteBuffers(1, &particleTranslationsVBO);
|
glDeleteBuffers(1, &particleTranslationsBuffer);
|
||||||
glDeleteBuffers(1, &verticesVBO);
|
glDeleteBuffers(1, &verticesVBO);
|
||||||
glDeleteBuffers(1, &uvsVBO);
|
glDeleteBuffers(1, &uvsVBO);
|
||||||
glDeleteBuffers(1, &indicesEBO);
|
glDeleteBuffers(1, &indicesEBO);
|
||||||
|
|
|
@ -140,6 +140,8 @@ int main(int argc, char** argv) {
|
||||||
fountain->toggleSpray();
|
fountain->toggleSpray();
|
||||||
if (key == 't')
|
if (key == 't')
|
||||||
fountain->toggleTexRandomizer();
|
fountain->toggleTexRandomizer();
|
||||||
|
if (key == 'p')
|
||||||
|
beginExecution();
|
||||||
});
|
});
|
||||||
glutSpecialFunc([](int k, int x, int y) -> void {
|
glutSpecialFunc([](int k, int x, int y) -> void {
|
||||||
cam.specialPress(k);
|
cam.specialPress(k);
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
#include <texture.h>
|
#include <texture.h>
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include <stb_image.h>
|
#include <stb_image.h>
|
||||||
|
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||||
|
#include <stb_image_resize.h>
|
||||||
|
|
||||||
|
|
||||||
texture* loadTexture(const std::string& path) {
|
texture* loadTexture(const std::string& path) {
|
||||||
|
|
Loading…
Reference in New Issue