X-Git-Url: https://git.lukelau.me/?a=blobdiff_plain;f=clouds.cpp;h=3ed3b10c91cc32a10509483bcb653b6a2f35468f;hb=152db1a5db951f77a55815b0ca7d9c5172fd8dea;hp=7bb51e854e61c39ba8adaf1be51d15525758d70f;hpb=69b74f1d5e67236a5396b75628a6e1be0e501631;p=clouds.git diff --git a/clouds.cpp b/clouds.cpp index 7bb51e8..3ed3b10 100644 --- a/clouds.cpp +++ b/clouds.cpp @@ -1,67 +1,41 @@ #include "debug.hpp" #include "program.hpp" +#include "simulation.h" #include #include +#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]; +enum Mode { + render, + debugContDist, + debugColor, + debugProbExt, + debugProbAct }; +Mode curMode = render; -// calculate continuous distribution -void calcContDist(Clouds *clds); - -float w(int i, int j, int k) { return 1; } +using namespace std; +using namespace glm; -const float metaballR = 1; -float metaballField(float r) { - if (r > metaballR) +const float metaballR = 1.f / 16.f; +inline float metaballField(float r) { + if (r > 1) return 0; - const float a = r / metaballR; + const float a = r / (1); 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; - } - } - } -} +const float normalizationFactor = (748.f / 405.f) * M_PI; void checkError() { if (GLenum e = glGetError()) { @@ -70,53 +44,99 @@ void checkError() { } } -vector bbColors; - -GLuint bbProg; +GLuint bbProg, sunProg; 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 +#define NQ 64 GLuint bbTexIds[NQ]; +// Stores attenuation ratio inside r channel +// Should be highest value at center void precalculateBillboardTextures() { + fprintf(stderr, "Calculating billboard textures...\n"); + glGenTextures(NQ, bbTexIds); + + for (int d = 0; d < NQ; d++) { float data[32 * 32]; + for (int j = 0; j < 32; j++) { + for (int i = 0; i < 32; i++) { // 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)); + float r = distance(vec2(i, j), vec2(16, 16)) / 16; + float density = (float)d / NQ; + data[i + j * 32] = + 1 - (density * 0.7 * (metaballField(r) / normalizationFactor)); + } + } - glGenTextures(NQ, bbTexIds); + mkdir("bbtex", 0777); + char path[32]; + snprintf(path, 32, "bbtex/%i.tga", d); + saveGrayscale(data, 32, 32, path); + + glBindTexture(GL_TEXTURE_2D, bbTexIds[d]); - /* 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(); + + fprintf(stderr, "\r%i out of %i densities calculated%s", d + 1, NQ, + d == NQ - 1 ? "\n" : ""); } } struct Metaball { vec3 pos; - float r; + ivec3 coords; + /** Density */ + float d; + vec4 col; }; -// 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}; +array metaballs; + +const float cloudScale = metaballR; +const float metaballScale = metaballR * 3.f; +Clouds cs; + +void calculateMetaballs() { + stepClouds(&cs); + /* for (int i = 0; i < 256; i++) { */ + /* float x = ((float)rand()/(float)(RAND_MAX) - 0.5) * 2; */ + /* float y = ((float)rand()/(float)(RAND_MAX) - 0.5) * 2; */ + /* float z = ((float)rand()/(float)(RAND_MAX) - 0.5) * 2; */ + /* float r = (float)rand()/(float)(RAND_MAX) * 1; */ + /* Metaball m = {{x,y,z}, r}; */ + /* metaballs.push_back(m); */ + /* } */ + for (int i = 0; i < CLOUD_DIM_X; i++) { + for (int j = 0; j < CLOUD_DIM_Y; j++) { + for (int k = 0; k < CLOUD_DIM_Z; k++) { + Metaball m = { + vec3(i, j, k) * vec3(cloudScale), + {i, j, k} }; + /* m.pos = (m.pos * vec3(2)) - (cloudScale / 2); */ + m.pos -= vec3(CLOUD_DIM_X, CLOUD_DIM_Y, CLOUD_DIM_Z) * cloudScale / 2.f; + m.d = cs.q[i][j][k]; + /* m.d = 0; */ + metaballs[i * CLOUD_DIM_Y * CLOUD_DIM_Z + j * CLOUD_DIM_Z + k] = m; + } + } + } + /* for (int z = 0; z < CLOUD_DIM_Z; z++) */ + /* metaballs[32 * CLOUD_DIM_Y * CLOUD_DIM_Z + 32 * CLOUD_DIM_Z + z].d = 1; */ +} + +vec3 sunPos = {0, 10, 0}, sunDir = {0, -1, 0}; +size_t sunColorIdx = 0; +std::array sunColors = { + vec4(1,1,1,1), + vec4(0.988,0.309,0.677,1) +}; +vec3 camPos = {0, 0, -5}, viewPos = {0, 0, 0}; mat4 proj; // projection matrix mat4 view; // view matrix float znear = 0.001, zfar = 1000; @@ -133,7 +153,7 @@ void setProjectionAndViewUniforms(GLuint progId) { /** Orientates the transformation matrix to face the camera in the view matrix */ -void faceView(mat4 m) { +mat4 faceView(mat4 m) { m[0][0] = view[0][0]; m[0][1] = view[1][0]; m[0][2] = view[2][0]; @@ -143,15 +163,14 @@ void faceView(mat4 m) { m[2][0] = view[0][2]; m[2][1] = view[1][2]; m[2][2] = view[2][2]; + return m; } GLuint attenuationTex; void shadeClouds() { - - bbColors.clear(); - glDisable(GL_DEPTH_TEST); + // shaderOutput * 0 + buffer * shader alpha glBlendFunc(GL_ZERO, GL_SRC_ALPHA); glEnable(GL_BLEND); @@ -159,32 +178,30 @@ void shadeClouds() { 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"); + glUniform1i(glGetUniformLocation(bbProg, "debug"), 0); + /* GLuint pboBuf; */ + /* glGenBuffers(1, &pboBuf); */ + /* glBindBuffer(GL_PIXEL_PACK_BUFFER, pboBuf); */ - GLuint modelLoc = glGetUniformLocation(bbProg, "model"); + /* glViewport(0, 0, shadeWidth, shadeHeight); */ - for (auto k : metaballs) { + + size_t i = 0; + for (auto &k : metaballs) { + fprintf(stderr, "\rShading cloud %lu/%lu...", i++, metaballs.size()); // place the billboard at the center of k - mat4 model = scale(translate(mat4(1), k.pos), vec3(k.r) * 2.f); + mat4 model = translate(mat4(1), k.pos); // rotate the billboard so that its normal is oriented to the sun - faceView(model); + model = faceView(model); + + model = scale(model, vec3(metaballScale)); glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); @@ -195,8 +212,11 @@ void shadeClouds() { // 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); */ + // but glTexEnv is for the old fixed function pipeline -- + // need to just tell our fragment shader then to modulate + int dIdx = k.d * NQ; + glBindTexture(GL_TEXTURE_2D, bbTexIds[dIdx]); + glUniform1i(glGetUniformLocation(bbProg, "modulate"), 1); // Render the billboard. glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); @@ -205,82 +225,126 @@ void shadeClouds() { // 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); + 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)); + // TODO: This is a huge bottleneck + 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; + pixel *= sunColors[sunColorIdx % sunColors.size()]; // Store the color into an array C[k] as the color of the billboard. - fprintf(stderr, "pixel: "); - dump(pixel); - bbColors.push_back(pixel); + k.col = pixel; } + fprintf(stderr, "\n"); - /* saveFBO(); */ + saveFBO(); checkError(); - - /* glDeleteFramebuffers(1, &fbo); */ - /* glBindFramebuffer(GL_FRAMEBUFFER, 0); // render to screen */ + /* glViewport(0, 0, width, height); */ } -void renderObject() {} +void renderObject() { + glDisable(GL_BLEND); + // render the sun + glUseProgram(sunProg); + mat4 model = translate(mat4(1), sunPos); + /* model = lookAt(sunPos, sunPos + sunDir, {0, 1, 0}) * model; */ + model = translate(scale(translate(model, -sunPos), vec3(0.3)), sunPos); + glUniformMatrix4fv(glGetUniformLocation(sunProg, "model"), 1, GL_FALSE, glm::value_ptr(model)); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); +} void renderClouds() { + glUseProgram(bbProg); + // 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); + return distance(camPos, a.pos) > distance(camPos, b.pos); }); + glUniform1i(glGetUniformLocation(bbProg, "debug"), curMode != render); + glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); + // shaderOutput * 1 + buffer * shader alpha glBlendFunc(GL_ONE, GL_SRC_ALPHA); + + /* glBlendColor(1.f,1.f,1.f,1.f); */ + /* glBlendFuncSeparate(GL_ONE, GL_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_SRC_ALPHA); */ + + glActiveTexture(GL_TEXTURE0); + glUniform1i(glGetUniformLocation(bbProg, "tex"), 0); + 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); + mat4 model = translate(mat4(1), k.pos); // Rotate the billboard so that its normal is oriented to the viewpoint. - faceView(model); + model = faceView(model); + + model = scale(model, vec3(metaballScale)); 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}; */ + k.col.w = 1; glUniform4fv(glGetUniformLocation(bbProg, "color"), 1, - glm::value_ptr(bbColors[i])); + glm::value_ptr(k.col)); // Map the billboard texture. - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, bbTexIds[0]); - glUniform1i(glGetUniformLocation(bbProg, "tex"), 0); + int dIdx = k.d * (NQ - 1); + glBindTexture(GL_TEXTURE_2D, bbTexIds[dIdx]); + + // Don't modulate it -- blend it + glUniform1i(glGetUniformLocation(bbProg, "modulate"), 0); + + glUniform1f(glGetUniformLocation(bbProg, "debugColor"), curMode == debugColor); + if (curMode != render) { + float debugVal = 0; + if (curMode == debugContDist) debugVal = k.d; + else if (curMode == debugProbAct) debugVal = cs.p_act[k.coords.x][k.coords.y][k.coords.z]; + else if (curMode == debugProbExt) debugVal = cs.p_ext[k.coords.x][k.coords.y][k.coords.z]; + glUniform1f(glGetUniformLocation(bbProg, "debugVal"), debugVal); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + model = scale(model, vec3(0.2)); + glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); + } // Render the billboard with the blending function. glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); } } +bool needsReshading = true; 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); + if (needsReshading) { + // TODO: find a way to make sure there's no clipping + view = glm::lookAt(sunPos + sunDir * vec3(100.f), sunPos, {0, 0, 1}); + // TODO: calculate bounds so everything is covered + proj = glm::ortho(2.5f, -2.5f, -2.5f, 2.5f, znear, 10000.f); + glUseProgram(bbProg); setProjectionAndViewUniforms(bbProg); glClearColor(1, 1, 1, 1); glClear(GL_COLOR_BUFFER_BIT); shadeClouds(); + needsReshading = false; + } - view = glm::lookAt(viewPos, lookPos, {0, 1, 0}); - proj = glm::perspective(60.f, aspect, znear, zfar); + view = glm::lookAt(camPos, viewPos, {0, 1, 0}); + proj = glm::perspective(45.f, aspect, znear, zfar); + glUseProgram(sunProg); + setProjectionAndViewUniforms(sunProg); + glUseProgram(bbProg); setProjectionAndViewUniforms(bbProg); - glClearColor(0.73,1,1,1); // background color + glClearColor(0.83, 1, 1, 1); // background color glClear(GL_COLOR_BUFFER_BIT); renderObject(); // render things that aren't clouds renderClouds(); @@ -288,6 +352,76 @@ void display() { glutSwapBuffers(); } +bool needsRedisplay = false; +void timer(int _) { + /* calculateMetaballs(); */ + if (needsRedisplay) { + glutPostRedisplay(); + } + needsRedisplay = false; + glutTimerFunc(16, timer, 0); +} + +void keyboard(unsigned char key, int x, int y) { + if (key == ' ') { + calculateMetaballs(); + needsRedisplay = true; + needsReshading = curMode == render; + } + if (key == '0') { + needsReshading = curMode != render; + curMode = render; + needsRedisplay = true; + } + if (key == '1') { + curMode = debugContDist; + needsRedisplay = true; + } + if (key == '2') { + curMode = debugColor; + needsRedisplay = true; + } + if (key == '3') { + curMode = debugProbAct; + needsRedisplay = true; + } + if (key == '4') { + curMode = debugProbExt; + needsRedisplay = true; + } + if (key == 's') { + sunColorIdx++; + needsRedisplay = true; + needsReshading = true; + } +} + +int prevMouseX, prevMouseY; +bool firstMouse = true; +void motion(int x, int y) { + if (firstMouse) { + prevMouseX = x; + prevMouseY = y; + firstMouse = false; + } + float dx = x - prevMouseX, dy = y - prevMouseY; + prevMouseX = x; + prevMouseY = y; + const vec3 origin(0, 0, 0); + const float sensitivity = 0.003f; + auto camMat = translate(mat4(1), origin + camPos); + auto rotation = rotate(rotate(mat4(1), -dx * sensitivity, {0, 1, 0}), + -dy * sensitivity, {1, 0, 0}); + auto rotAroundOrig = camMat * rotation * translate(mat4(1), origin - camPos); + camPos = rotAroundOrig * glm::vec4(camPos, 0); + needsRedisplay = true; +} + +void passiveMotion(int x, int y) { + prevMouseX = x; + prevMouseY = y; +} + int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB | @@ -299,11 +433,14 @@ int main(int argc, char **argv) { glewInit(); Program prog("billboardvert.glsl", "billboardfrag.glsl"); - bbProg = prog.progId; - glUseProgram(bbProg); + Program sProg("sunvert.glsl", "sunfrag.glsl"); + sunProg = sProg.progId; glGenVertexArrays(1, &bbVao); + glUseProgram(sunProg); + glBindVertexArray(bbVao); + glUseProgram(bbProg); glBindVertexArray(bbVao); GLuint vbos[2]; glGenBuffers(2, vbos); @@ -326,11 +463,15 @@ int main(int argc, char **argv) { precalculateBillboardTextures(); + initClouds(&cs); calculateMetaballs(); glGenTextures(1, &attenuationTex); - /* glutTimerFunc(16, timer, 0); */ + glutKeyboardFunc(keyboard); + glutMotionFunc(motion); + glutPassiveMotionFunc(passiveMotion); + glutTimerFunc(16, timer, 0); // set up billboard prog