Initial commit
authorLuke Lau <luke_lau@icloud.com>
Thu, 9 Apr 2020 14:51:19 +0000 (15:51 +0100)
committerLuke Lau <luke_lau@icloud.com>
Thu, 9 Apr 2020 14:51:19 +0000 (15:51 +0100)
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
billboardfrag.glsl [new file with mode: 0644]
billboardvert.glsl [new file with mode: 0644]
clouds.cpp [new file with mode: 0644]
compile_flags.txt [new file with mode: 0644]
debug.hpp [new file with mode: 0644]
program.cpp [new file with mode: 0644]
program.hpp [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..dfe6a70
--- /dev/null
@@ -0,0 +1,5 @@
+.DS_Store
+*.swp
+clouds
+fbo.tga
+*.dSYM
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
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 (file)
index 0000000..840ec89
--- /dev/null
@@ -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 (file)
index 0000000..e2eb710
--- /dev/null
@@ -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 (file)
index 0000000..7bb51e8
--- /dev/null
@@ -0,0 +1,340 @@
+#include "debug.hpp"
+#include "program.hpp"
+#include <GL/glew.h>
+#include <GLUT/glut.h>
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <glm/ext.hpp>
+#include <glm/glm.hpp>
+#include <vector>
+
+#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<vec4> 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<Metaball> metaballs = {{{-0.5, 0.5, 0.5}, 0.25}, */
+/*                               {{-0.3, 0.5, 0.3}, 0.25}}; */
+vector<Metaball> 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<vec3> poss = {{-1, -1, 0}, {-1, 1, 0}, {1, 1, 0}, {1, -1, 0}};
+  vector<GLuint> 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 (file)
index 0000000..7ecd5cf
--- /dev/null
@@ -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 (file)
index 0000000..debe65e
--- /dev/null
+++ b/debug.hpp
@@ -0,0 +1,41 @@
+#include <GL/glew.h>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <glm/glm.hpp>
+
+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 (file)
index 0000000..2275dcb
--- /dev/null
@@ -0,0 +1,64 @@
+#include "program.hpp"
+#include <fstream>
+#include <sstream>
+#include <iostream>
+
+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 (file)
index 0000000..3fa8879
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef PROGRAM_HPP
+#define PROGRAM_HPP
+#include <string>
+#include <GL/glew.h>
+class Program {
+       public:
+               Program(const std::string vert, const std::string frag);
+               void validate() const;
+               GLuint progId;
+};
+#endif