From 69b74f1d5e67236a5396b75628a6e1be0e501631 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Thu, 9 Apr 2020 15:51:19 +0100 Subject: [PATCH] Initial commit --- .gitignore | 5 + Makefile | 5 + billboardfrag.glsl | 9 ++ billboardvert.glsl | 14 ++ clouds.cpp | 340 +++++++++++++++++++++++++++++++++++++++++++++ compile_flags.txt | 4 + debug.hpp | 41 ++++++ program.cpp | 64 +++++++++ program.hpp | 11 ++ 9 files changed, 493 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 billboardfrag.glsl create mode 100644 billboardvert.glsl create mode 100644 clouds.cpp create mode 100644 compile_flags.txt create mode 100644 debug.hpp create mode 100644 program.cpp create mode 100644 program.hpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dfe6a70 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +*.swp +clouds +fbo.tga +*.dSYM diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7cd3b96 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +clouds: clouds.cpp program.cpp + clang++ -std=c++17 $^ -o $@ \ + -Wall -g \ + -I/usr/local/include -L/usr/local/lib \ + -framework OpenGL -framework glut -lglew diff --git a/billboardfrag.glsl b/billboardfrag.glsl new file mode 100644 index 0000000..840ec89 --- /dev/null +++ b/billboardfrag.glsl @@ -0,0 +1,9 @@ +#version 400 +uniform vec4 color; +uniform sampler2D tex; +in vec2 texCoord; +out vec4 FragColor; +void main() { + FragColor = texture(tex, texCoord).r * color; +} + diff --git a/billboardvert.glsl b/billboardvert.glsl new file mode 100644 index 0000000..e2eb710 --- /dev/null +++ b/billboardvert.glsl @@ -0,0 +1,14 @@ +#version 400 +in vec3 vPosition; + +out vec2 texCoord; +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; +void main() { + gl_Position = projection * view * model * vec4(vPosition, 1); + gl_Position = projection * view * model * vec4(vPosition, 1); + // normalize to within [0,1] + texCoord = (vPosition.xy + 1) / 2.f; +} + diff --git a/clouds.cpp b/clouds.cpp new file mode 100644 index 0000000..7bb51e8 --- /dev/null +++ b/clouds.cpp @@ -0,0 +1,340 @@ +#include "debug.hpp" +#include "program.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + +using namespace std; +using namespace glm; + +static const int CLOUD_DIM = 16; +struct Clouds { + char cld[CLOUD_DIM][CLOUD_DIM][CLOUD_DIM]; + float contDist[CLOUD_DIM][CLOUD_DIM][CLOUD_DIM]; +}; + +// calculate continuous distribution +void calcContDist(Clouds *clds); + +float w(int i, int j, int k) { return 1; } + +const float metaballR = 1; +float metaballField(float r) { + if (r > metaballR) + return 0; + const float a = r / metaballR; + return (-4.f / 9.f * powf(a, 6)) + (17.f / 9.f * powf(a, 4)) - + (22.f / 9.f * powf(a, 2)) + 1; +} + +/* const float normalizationFactor = 748.f / 405.f * M_PI * metaballR; */ + +void calcContDist(Clouds *clds, float t) { + const int i0 = 2, j0 = 2, k0 = 2, t0 = 2; + const float divisor = + 1.f / ((2 * t0 + 1) * (2 * k0 + 1) * (2 * j0 + 1) * (2 * i0 + 1)); + float sum = 0; + for (int i = 0; i < CLOUD_DIM; i++) { + for (int j = 0; j < CLOUD_DIM; j++) { + for (int k = 0; k < CLOUD_DIM; k++) { + + // inner sums + /* for (int tp = -t0, tp < t0; tp++) { */ + for (int ip = -i0; ip < i0; ip++) { + for (int jp = -j0; jp < j0; jp++) { + for (int kp = -k0; kp < k0; kp++) { + + sum += w(ip, jp, kp) * (float)clds->cld[i + ip][j + jp][k + kp]; + } + } + } + /* } */ + + clds->contDist[i][j][k] = sum / divisor; + } + } + } +} + +void checkError() { + if (GLenum e = glGetError()) { + fprintf(stderr, "%s\n", gluErrorString(e)); + abort(); + } +} + +vector bbColors; + +GLuint bbProg; +GLuint bbVao; + +void calculateMetaballs() {} + +// Here we need to generate n_q textures for different densities of metaballs +// These textures then go on the billboards +// The texture stores attenuation ratio? + +#define NQ 1 +GLuint bbTexIds[NQ]; + +void precalculateBillboardTextures() { + float data[32 * 32]; + // TODO: properly calculate this instead of whatever this is + for (int j = 0; j < 32; j++) + for (int i = 0; i < 32; i++) + data[i + j * 32] = fmin(1.f, 0.1f + 2.f * (distance(vec2(i, j), vec2(16, 16)) / 16)); + + glGenTextures(NQ, bbTexIds); + + /* GLuint captureFBO; */ + /* glGenFramebuffers(1, &captureFBO); */ + for (int i = 0; i < NQ; i++) { + glBindTexture(GL_TEXTURE_2D, bbTexIds[i]); + /* glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, 32, 32, 0, GL_RED, */ + /* GL_FLOAT, data); */ + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 32, 32, 0, GL_RED, GL_FLOAT, data); + glGenerateMipmap(GL_TEXTURE_2D); // required, otherwise texture is blank + + checkError(); + } +} + +struct Metaball { + vec3 pos; + float r; +}; +// TODO: why is the x axis flipped?? +/* vector metaballs = {{{-0.5, 0.5, 0.5}, 0.25}, */ +/* {{-0.3, 0.5, 0.3}, 0.25}}; */ +vector metaballs = {{{0, 0, 0.5}, 1.f}, + {{0, 0.3, 0.3}, 0.7f}}; + +vec3 sunPos = {0, 2, 2}, viewPos = {0, 0, 0}, lookPos = {0, 0, 1}; +mat4 proj; // projection matrix +mat4 view; // view matrix +float znear = 0.001, zfar = 1000; +float width = 600, height = 400; +float aspect = width / height; + +void setProjectionAndViewUniforms(GLuint progId) { + GLuint projLoc = glGetUniformLocation(progId, "projection"); + glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(proj)); + + GLuint viewLoc = glGetUniformLocation(progId, "view"); + glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view)); +} + +/** Orientates the transformation matrix to face the camera in the view matrix + */ +void faceView(mat4 m) { + m[0][0] = view[0][0]; + m[0][1] = view[1][0]; + m[0][2] = view[2][0]; + m[1][0] = view[0][1]; + m[1][1] = view[1][1]; + m[1][2] = view[2][1]; + m[2][0] = view[0][2]; + m[2][1] = view[1][2]; + m[2][2] = view[2][2]; +} + +GLuint attenuationTex; + +void shadeClouds() { + + bbColors.clear(); + + glDisable(GL_DEPTH_TEST); + glBlendFunc(GL_ZERO, GL_SRC_ALPHA); + glEnable(GL_BLEND); + + // sort by ascending distance from the sun + sort(metaballs.begin(), metaballs.end(), [](Metaball &a, Metaball &b) { + return distance(sunPos, a.pos) < distance(sunPos, b.pos); + }); + /* GLuint fbo; */ + /* glGenFramebuffers(1, &fbo); */ + /* glBindFramebuffer(GL_FRAMEBUFFER, fbo); */ + /* glBindTexture(GL_TEXTURE_2D, attenuationTex); */ + /* glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, */ + /* GL_UNSIGNED_BYTE, NULL); */ + /* glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, */ + /* attenuationTex, 0); */ + /* assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); */ + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, bbTexIds[0]); + glUniform1i(glGetUniformLocation(bbProg, "tex"), 0); + +/* glClearColor(1, 1, 1, 1); */ +/* glClear(GL_COLOR_BUFFER_BIT); */ + + + GLuint modelLoc = glGetUniformLocation(bbProg, "model"); + + for (auto k : metaballs) { + // place the billboard at the center of k + mat4 model = scale(translate(mat4(1), k.pos), vec3(k.r) * 2.f); + + // rotate the billboard so that its normal is oriented to the sun + faceView(model); + + glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); + + // Set the billboard color as RGBA = (1.0, 1.0, 1.0, 1.0). + vec4 color = {1, 1, 1, 1}; + glUniform4fv(glGetUniformLocation(bbProg, "color"), 1, + glm::value_ptr(color)); + + // Map the billboard texture with GL_MODULATE. + // i.e. multiply rather than add + // TODO: FIX- this crashes with invalid operation + /* glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); */ + + // Render the billboard. + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + + // Read the pixel value corresponding to the center of metaball k. + // 1. First get position in opengl screen space: from [-1,1] + // 2. Normalize to [0,1] + // 3. Multiply by (width * height) + vec2 screenPos = ((vec2(proj * view * model * vec4(0,0,0,1)) + vec2(1)) / vec2(2)) + * vec2(width, height); + vec4 pixel; + glReadPixels(screenPos.x, screenPos.y, 1, 1, GL_RGBA, GL_FLOAT, value_ptr(pixel)); + + // Multiply the pixel value by the sunlight color. + vec4 sunColor = {1, 1, 0.9, 1}; + pixel *= sunColor; + + // Store the color into an array C[k] as the color of the billboard. + fprintf(stderr, "pixel: "); + dump(pixel); + bbColors.push_back(pixel); + } + + /* saveFBO(); */ + checkError(); + + /* glDeleteFramebuffers(1, &fbo); */ + /* glBindFramebuffer(GL_FRAMEBUFFER, 0); // render to screen */ +} + +void renderObject() {} + +void renderClouds() { + // Sort metaballs in descending order from the viewpoint + sort(metaballs.begin(), metaballs.end(), [](Metaball &a, Metaball &b) { + return distance(viewPos, a.pos) > distance(viewPos, b.pos); + }); + + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_SRC_ALPHA); + for (int i = 0; i < metaballs.size(); i++) { + Metaball k = metaballs[i]; + + GLuint modelLoc = glGetUniformLocation(bbProg, "model"); + + // Place the billboard at the center of the corresponding metaball n. + mat4 model = scale(translate(mat4(1), k.pos), vec3(k.r) * 2.f); + // Rotate the billboard so that its normal is oriented to the viewpoint. + faceView(model); + + glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); + + // Set the billboard color as C[n]. + fprintf(stderr, "bbColors[i]: "); + dump(bbColors[i]); + /* bbColors[i] = {0,0,0,0.5}; */ + glUniform4fv(glGetUniformLocation(bbProg, "color"), 1, + glm::value_ptr(bbColors[i])); + + // Map the billboard texture. + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, bbTexIds[0]); + glUniform1i(glGetUniformLocation(bbProg, "tex"), 0); + + // Render the billboard with the blending function. + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + } +} + +void display() { + view = glm::lookAt(sunPos, viewPos, {0, 1, 0}); + proj = glm::ortho(1.f * aspect, -1.f * aspect, -1.f, 1.f, znear, zfar); + setProjectionAndViewUniforms(bbProg); + + glClearColor(1, 1, 1, 1); + glClear(GL_COLOR_BUFFER_BIT); + shadeClouds(); + + view = glm::lookAt(viewPos, lookPos, {0, 1, 0}); + proj = glm::perspective(60.f, aspect, znear, zfar); + setProjectionAndViewUniforms(bbProg); + + glClearColor(0.73,1,1,1); // background color + glClear(GL_COLOR_BUFFER_BIT); + renderObject(); // render things that aren't clouds + renderClouds(); + + glutSwapBuffers(); +} + +int main(int argc, char **argv) { + glutInit(&argc, argv); + glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB | + GLUT_3_2_CORE_PROFILE); + glutInitWindowSize(width, height); + glutCreateWindow("Clouds"); + glutDisplayFunc(display); + + glewInit(); + + Program prog("billboardvert.glsl", "billboardfrag.glsl"); + + bbProg = prog.progId; + glUseProgram(bbProg); + + glGenVertexArrays(1, &bbVao); + glBindVertexArray(bbVao); + GLuint vbos[2]; + glGenBuffers(2, vbos); + + vector poss = {{-1, -1, 0}, {-1, 1, 0}, {1, 1, 0}, {1, -1, 0}}; + vector indices = {2, 1, 0, 3, 2, 0}; + + GLuint posLoc = glGetAttribLocation(bbProg, "vPosition"); + glBindBuffer(GL_ARRAY_BUFFER, vbos[0]); + glBufferData(GL_ARRAY_BUFFER, poss.size() * sizeof(glm::vec3), &poss[0], + GL_STATIC_DRAW); + glEnableVertexAttribArray(posLoc); + glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbos[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), + &indices[0], GL_STATIC_DRAW); + + prog.validate(); + + precalculateBillboardTextures(); + + calculateMetaballs(); + + glGenTextures(1, &attenuationTex); + + /* glutTimerFunc(16, timer, 0); */ + + // set up billboard prog + + glutMainLoop(); + + return 0; +} diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000..7ecd5cf --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1,4 @@ +-Wall +-std=c++17 +-I/usr/local/include/ +-DDEBUG_NODES diff --git a/debug.hpp b/debug.hpp new file mode 100644 index 0000000..debe65e --- /dev/null +++ b/debug.hpp @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include + +inline void dump(glm::mat4 m) { + for (int i = 0; i < 4; i++) + fprintf(stderr, "%f,%f,%f,%f\n", m[i][0], m[i][1], m[i][2], m[i][3]); +} + +inline void dump(glm::vec4 v) { + fprintf(stderr, "{%f,%f,%f,%f}\n", v.x, v.y, v.z, v.w); +} +inline void dump(glm::vec3 v) { + fprintf(stderr, "{%f,%f,%f}\n", v.x, v.y, v.z); +} +inline void dump(glm::vec2 v) { fprintf(stderr, "{%f,%f}\n", v.x, v.y); } + +typedef struct { + char head[12]; + short dx /* Width */, dy /* Height */, head2; + char pic[600 * 400][3]; +} TGA; +char tgahead[12] = {0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +void saveFBO() { + float width = 600, height = 400; + TGA *tga = (TGA *)malloc(sizeof(TGA)); + memcpy(tga->head, tgahead, 12); + tga->dx = width; + tga->dy = height; + tga->head2 = 0x2018; + // TODO: flip -- the image is upside down so far + glReadPixels(0, 0, width, height, GL_BGR, GL_UNSIGNED_BYTE, tga->pic[0]); + FILE *cc = fopen("fbo.tga", "wb"); + fwrite(tga, 1, (18 + 3 * width * height), cc); + fclose(cc); + free(tga); +} diff --git a/program.cpp b/program.cpp new file mode 100644 index 0000000..2275dcb --- /dev/null +++ b/program.cpp @@ -0,0 +1,64 @@ +#include "program.hpp" +#include +#include +#include + +using namespace std; + +void attachShader(GLuint progId, string filePath, GLenum type) { + GLuint shader = glCreateShader(type); + + if (!shader) { + cerr << "error creating shader" << endl; + abort(); + } + + ifstream file(filePath); + stringstream buffer; + buffer << file.rdbuf(); + string str = buffer.str(); + const char* contents = str.c_str(); + + glShaderSource(shader, 1, (const GLchar**)&contents, NULL); + glCompileShader(shader); + GLint success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar log[1024]; + glGetShaderInfoLog(shader, 1024, NULL, log); + fprintf(stderr, "Error compiling %s\n%s\n", filePath.c_str(), log); + exit(1); + } + glAttachShader(progId, shader); +} + +Program::Program(const string vertexShader, const string fragmentShader) { + progId = glCreateProgram(); + + attachShader(progId, vertexShader, GL_VERTEX_SHADER); + attachShader(progId, fragmentShader, GL_FRAGMENT_SHADER); + + glLinkProgram(progId); + GLint success = 0; + glGetProgramiv(progId, GL_LINK_STATUS, &success); + if (!success) { + GLchar log[1024]; + glGetProgramInfoLog(progId, sizeof(log), NULL, log); + fprintf(stderr, "error linking %s and %s\n%s\n", vertexShader.c_str(), fragmentShader.c_str(), log); + exit(1); + } +} + +void Program::validate() const { + glValidateProgram(progId); + + GLint success; + glGetProgramiv(progId, GL_VALIDATE_STATUS, &success); + if (!success) { + GLchar log[1024]; + glGetProgramInfoLog(progId, sizeof(log), NULL, log); + fprintf(stderr, "Error validating: %s\n", log); + exit(1); + } +} + diff --git a/program.hpp b/program.hpp new file mode 100644 index 0000000..3fa8879 --- /dev/null +++ b/program.hpp @@ -0,0 +1,11 @@ +#ifndef PROGRAM_HPP +#define PROGRAM_HPP +#include +#include +class Program { + public: + Program(const std::string vert, const std::string frag); + void validate() const; + GLuint progId; +}; +#endif -- 2.30.2