X-Git-Url: https://git.lukelau.me/?p=clouds.git;a=blobdiff_plain;f=clouds.cpp;h=b8ea3f0071cb0f0e5ce34bdfd48e44b17151edbd;hp=3ed3b10c91cc32a10509483bcb653b6a2f35468f;hb=HEAD;hpb=152db1a5db951f77a55815b0ca7d9c5172fd8dea diff --git a/clouds.cpp b/clouds.cpp index 3ed3b10..b8ea3f0 100644 --- a/clouds.cpp +++ b/clouds.cpp @@ -1,9 +1,10 @@ +#include "GL/glew.h" #include "debug.hpp" #include "program.hpp" -#include "simulation.h" -#include +#include "simulation.hpp" #include #include +#include #include #include #include @@ -14,13 +15,7 @@ #pragma clang diagnostic ignored "-Wdeprecated-declarations" -enum Mode { - render, - debugContDist, - debugColor, - debugProbExt, - debugProbAct -}; +enum Mode { render, debugContDist, debugColor, debugProbExt, debugProbAct }; Mode curMode = render; using namespace std; @@ -68,7 +63,8 @@ void precalculateBillboardTextures() { 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)); + 1 - + fmin(1, (3 * density * (metaballField(r) / normalizationFactor))); } } @@ -82,7 +78,6 @@ void precalculateBillboardTextures() { glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 32, 32, 0, GL_RED, GL_FLOAT, data); glGenerateMipmap(GL_TEXTURE_2D); // required, otherwise texture is blank - fprintf(stderr, "\r%i out of %i densities calculated%s", d + 1, NQ, d == NQ - 1 ? "\n" : ""); } @@ -99,7 +94,7 @@ struct Metaball { array metaballs; const float cloudScale = metaballR; -const float metaballScale = metaballR * 3.f; +const float metaballScale = metaballR * 1.5f; Clouds cs; void calculateMetaballs() { @@ -115,9 +110,7 @@ void calculateMetaballs() { 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} }; + 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]; @@ -127,21 +120,23 @@ void calculateMetaballs() { } } /* for (int z = 0; z < CLOUD_DIM_Z; z++) */ - /* metaballs[32 * CLOUD_DIM_Y * CLOUD_DIM_Z + 32 * CLOUD_DIM_Z + z].d = 1; */ + /* 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}; +vec3 sunPos = {0, 5, 0}, sunDir = {0, -1, 0}; +size_t envColorIdx = 0; +// First color is sun color, second is sky color +std::array, 3> envColors{ + {{vec4(1, 1, 1, 1), vec4(0.9, 1, 1, 1)}, + {vec4(0.939, 0.632, 0.815, 1), vec4(0.9, 1, 1, 1)}, + {vec4(0.999, 0.999, 0.519, 1), vec4(0.981, 0.667, 0.118, 1)}}}; +vec3 camPos = {0, 0, -3}, viewPos = {0, 0, 0}; mat4 proj; // projection matrix mat4 view; // view matrix float znear = 0.001, zfar = 1000; -float width = 600, height = 400; -float aspect = width / height; +// for performance with glReadPixels these should be powers of 2! +float width = 1200, height = 800; void setProjectionAndViewUniforms(GLuint progId) { GLuint projLoc = glGetUniformLocation(progId, "projection"); @@ -166,9 +161,37 @@ mat4 faceView(mat4 m) { return m; } -GLuint attenuationTex; +#define PBO + +/* const int shadeWidth = 256, shadeHeight = 256; */ +const int shadeWidth = 256, shadeHeight = 256; + +#ifdef PBO +const int numPbos = 512; +GLuint pboBufs[numPbos]; +GLbyte sink[shadeWidth * shadeHeight * 4]; + +void inline mapPixelRead(int pboBuf, int metaball) { + glBindBuffer(GL_PIXEL_PACK_BUFFER, pboBufs[pboBuf]); + GLubyte *src = + (GLubyte *)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, + 4 * sizeof(GLubyte), GL_MAP_READ_BIT); + vec4 pixel = vec4(src[0], src[1], src[2], src[3]) / vec4(255.f); + glUnmapBuffer(GL_PIXEL_PACK_BUFFER); + + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + + // Multiply the pixel value by the sunlight color. + pixel *= envColors[envColorIdx][0]; + + // Store the color for the previous set of pixels + metaballs[metaball].col = pixel; +} + +#endif void shadeClouds() { + glDisable(GL_DEPTH_TEST); // shaderOutput * 0 + buffer * shader alpha glBlendFunc(GL_ZERO, GL_SRC_ALPHA); @@ -185,16 +208,16 @@ void shadeClouds() { GLuint modelLoc = glGetUniformLocation(bbProg, "model"); glUniform1i(glGetUniformLocation(bbProg, "debug"), 0); - /* GLuint pboBuf; */ - /* glGenBuffers(1, &pboBuf); */ - /* glBindBuffer(GL_PIXEL_PACK_BUFFER, pboBuf); */ - - /* glViewport(0, 0, shadeWidth, shadeHeight); */ + glViewport(0, 0, shadeWidth, shadeHeight); +#ifdef PBO + int pboIdx = 0; +#endif + auto begin_time = std::chrono::system_clock::now(); size_t i = 0; for (auto &k : metaballs) { - fprintf(stderr, "\rShading cloud %lu/%lu...", i++, metaballs.size()); + /* fprintf(stderr, "\rShading metaball %lu/%lu...", i, metaballs.size()); */ // place the billboard at the center of k mat4 model = translate(mat4(1), k.pos); @@ -225,25 +248,58 @@ 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 = + ivec2 screenPos = ((vec2(proj * view * model * vec4(0, 0, 0, 1)) + vec2(1)) / vec2(2)) * - vec2(width, height); + vec2(shadeWidth, shadeHeight); + +#ifndef PBO vec4 pixel; // TODO: This is a huge bottleneck - glReadPixels(screenPos.x, screenPos.y, 1, 1, GL_RGBA, GL_FLOAT, - value_ptr(pixel)); + glReadPixels(screenPos.x, screenPos.y, 1, 1, GL_RGBA, GL_FLOAT, &pixel); // Multiply the pixel value by the sunlight color. - pixel *= sunColors[sunColorIdx % sunColors.size()]; + pixel *= envColors[envColorIdx][0]; // Store the color into an array C[k] as the color of the billboard. k.col = pixel; +#else + + glBindBuffer(GL_PIXEL_PACK_BUFFER, pboBufs[pboIdx]); + // It would be nice if this worked. But it doesn't + // macOS driver does this synchronously + /* glReadPixels(screenPos.x, screenPos.y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, + */ + /* NULL); */ + glReadPixels(screenPos.x, screenPos.y, 64, 64, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + int nextPbo = (pboIdx + 1) % numPbos; + if (i >= numPbos - 1) { + // start mapping the read values back + mapPixelRead(nextPbo, i - numPbos + 1); } - fprintf(stderr, "\n"); + pboIdx = nextPbo; +#endif + + i++; + } + /* fprintf(stderr, "\n"); */ + +#ifdef PBO + // sink remaining reads + for (int i = 0; i < numPbos; i++) { + mapPixelRead(i, metaballs.size() - numPbos + i); + } +#endif + + auto elapsed = std::chrono::system_clock::now() - begin_time; + double elapsed_seconds = + std::chrono::duration_cast>(elapsed) + .count(); + fprintf(stderr, "Time taken to shade: %fs\n", elapsed_seconds); saveFBO(); checkError(); - /* glViewport(0, 0, width, height); */ + glViewport(0, 0, width, height); } void renderObject() { @@ -251,9 +307,10 @@ void renderObject() { // render the sun glUseProgram(sunProg); mat4 model = translate(mat4(1), sunPos); - /* model = lookAt(sunPos, sunPos + sunDir, {0, 1, 0}) * model; */ + 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)); + glUniformMatrix4fv(glGetUniformLocation(sunProg, "model"), 1, GL_FALSE, + glm::value_ptr(model)); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); } @@ -273,7 +330,8 @@ void renderClouds() { 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); */ + /* glBlendFuncSeparate(GL_ONE, GL_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_SRC_ALPHA); + */ glActiveTexture(GL_TEXTURE0); glUniform1i(glGetUniformLocation(bbProg, "tex"), 0); @@ -304,15 +362,19 @@ void renderClouds() { // Don't modulate it -- blend it glUniform1i(glGetUniformLocation(bbProg, "modulate"), 0); - glUniform1f(glGetUniformLocation(bbProg, "debugColor"), curMode == debugColor); + 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]; + if (curMode == debugContDist) + debugVal = k.d; + else if (curMode == debugProbAct) + debugVal = cs.p_act[k.coords.x][k.coords.y][k.coords.z] / P_ACT; + else if (curMode == debugProbExt) + debugVal = cs.p_ext[k.coords.x][k.coords.y][k.coords.z] / P_EXT; glUniform1f(glGetUniformLocation(bbProg, "debugVal"), debugVal); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - model = scale(model, vec3(0.2)); + model = scale(model, vec3(0.1)); glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); } @@ -321,9 +383,10 @@ void renderClouds() { } } -bool needsReshading = true; +bool curBeenShaded = false; + void display() { - if (needsReshading) { + if (!curBeenShaded && (curMode == render || curMode == debugColor)) { // 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 @@ -334,17 +397,20 @@ void display() { glClearColor(1, 1, 1, 1); glClear(GL_COLOR_BUFFER_BIT); shadeClouds(); - needsReshading = false; + curBeenShaded = true; } view = glm::lookAt(camPos, viewPos, {0, 1, 0}); + const float aspect = width / height; proj = glm::perspective(45.f, aspect, znear, zfar); glUseProgram(sunProg); setProjectionAndViewUniforms(sunProg); glUseProgram(bbProg); setProjectionAndViewUniforms(bbProg); - glClearColor(0.83, 1, 1, 1); // background color + vec4 skyColor = envColors[envColorIdx][1]; + glClearColor(skyColor.r, skyColor.g, skyColor.b, + skyColor.a); // background color glClear(GL_COLOR_BUFFER_BIT); renderObject(); // render things that aren't clouds renderClouds(); @@ -354,7 +420,6 @@ void display() { bool needsRedisplay = false; void timer(int _) { - /* calculateMetaballs(); */ if (needsRedisplay) { glutPostRedisplay(); } @@ -366,10 +431,9 @@ void keyboard(unsigned char key, int x, int y) { if (key == ' ') { calculateMetaballs(); needsRedisplay = true; - needsReshading = curMode == render; + curBeenShaded = false; } if (key == '0') { - needsReshading = curMode != render; curMode = render; needsRedisplay = true; } @@ -390,9 +454,9 @@ void keyboard(unsigned char key, int x, int y) { needsRedisplay = true; } if (key == 's') { - sunColorIdx++; + envColorIdx = (envColorIdx + 1) % envColors.size(); needsRedisplay = true; - needsReshading = true; + curBeenShaded = false; } } @@ -422,13 +486,23 @@ void passiveMotion(int x, int y) { prevMouseY = y; } +void reshape(int w, int h) { + width = w; + height = h; +} + int main(int argc, char **argv) { glutInit(&argc, argv); - glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB | + glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA | GLUT_ALPHA | GLUT_3_2_CORE_PROFILE); glutInitWindowSize(width, height); glutCreateWindow("Clouds"); glutDisplayFunc(display); + glutReshapeFunc(reshape); + glutKeyboardFunc(keyboard); + glutMotionFunc(motion); + glutPassiveMotionFunc(passiveMotion); + glutTimerFunc(16, timer, 0); glewInit(); @@ -466,14 +540,16 @@ int main(int argc, char **argv) { initClouds(&cs); calculateMetaballs(); - glGenTextures(1, &attenuationTex); - - glutKeyboardFunc(keyboard); - glutMotionFunc(motion); - glutPassiveMotionFunc(passiveMotion); - glutTimerFunc(16, timer, 0); - - // set up billboard prog +#ifdef PBO + // setup PBOs for buffering readPixels + glGenBuffers(numPbos, pboBufs); + for (int i = 0; i < numPbos; i++) { + glBindBuffer(GL_PIXEL_PACK_BUFFER, pboBufs[i]); + glBufferData(GL_PIXEL_PACK_BUFFER, 64 * 64 * 4, NULL, + GL_DYNAMIC_READ); + } + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); +#endif glutMainLoop();