X-Git-Url: http://git.lukelau.me/?a=blobdiff_plain;f=clouds.cpp;h=f6c0083837e15611cc628e5316e3c55fd63cd4d5;hb=724a80cbecb587d1d069f7999758181b10d92f73;hp=baabcba703d4aca0e44e80f142e7eb97a82ed1c7;hpb=92594780db1f9bb51b97e68457c526fa4287a29e;p=clouds.git diff --git a/clouds.cpp b/clouds.cpp index baabcba..f6c0083 100644 --- a/clouds.cpp +++ b/clouds.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -14,27 +15,22 @@ #pragma clang diagnostic ignored "-Wdeprecated-declarations" -enum Mode { - render, - debugContDist, - debugProbExt, - debugProbAct -}; +enum Mode { render, debugContDist, debugColor, debugProbExt, debugProbAct }; Mode curMode = render; using namespace std; using namespace glm; -const float metaballR = 1.5f; +const float metaballR = 1.f / 16.f; inline float metaballField(float r) { - if (r > metaballR) + 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; +const float normalizationFactor = (748.f / 405.f) * M_PI; void checkError() { if (GLenum e = glGetError()) { @@ -43,7 +39,7 @@ void checkError() { } } -GLuint bbProg; +GLuint bbProg, sunProg; GLuint bbVao; // Here we need to generate n_q textures for different densities of metaballs @@ -67,7 +63,7 @@ void precalculateBillboardTextures() { float r = distance(vec2(i, j), vec2(16, 16)) / 16; float density = (float)d / NQ; data[i + j * 32] = - 1 - (density * metaballField(r) / normalizationFactor); + 1 - (3 * density * 0.7 * (metaballField(r) / normalizationFactor)); } } @@ -77,13 +73,10 @@ void precalculateBillboardTextures() { saveGrayscale(data, 32, 32, path); glBindTexture(GL_TEXTURE_2D, bbTexIds[d]); - checkError(); 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" : ""); } @@ -92,13 +85,15 @@ void precalculateBillboardTextures() { struct Metaball { vec3 pos; ivec3 coords; - /** Radius, density */ - float r, d; + /** Density */ + float d; vec4 col; }; -array metaballs; +array metaballs; +const float cloudScale = metaballR; +const float metaballScale = metaballR * 3.f; Clouds cs; void calculateMetaballs() { @@ -111,27 +106,35 @@ void calculateMetaballs() { /* Metaball m = {{x,y,z}, r}; */ /* metaballs.push_back(m); */ /* } */ - for (int i = 0; i < CLOUD_DIM; i++) { - for (int j = 0; j < CLOUD_DIM; j++) { - for (int k = 0; k < CLOUD_DIM; k++) { - Metaball m = { - {i / (float)CLOUD_DIM, j / (float)CLOUD_DIM, k / (float)CLOUD_DIM}, - {i, j, k}, - 1.f / (float)CLOUD_DIM}; - m.pos = (m.pos * vec3(2)) - vec3(1); + 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]; - metaballs[i * CLOUD_DIM * CLOUD_DIM + j * CLOUD_DIM + k] = m; + /* 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, 2, 2}, sunDir = {0, -1, -1}; -vec3 camPos = {0, 0, -5}, viewPos = {0, 0, 0}; +vec3 sunPos = {0, 10, 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 width = 512, height = 512; float aspect = width / height; void setProjectionAndViewUniforms(GLuint progId) { @@ -157,7 +160,11 @@ mat4 faceView(mat4 m) { return m; } -GLuint attenuationTex; +#ifdef PBO +const int numPbos = 64; +GLuint pboBufs[numPbos]; +GLbyte sink[1000 * 1000 * 4]; +#endif void shadeClouds() { glDisable(GL_DEPTH_TEST); @@ -174,14 +181,26 @@ void shadeClouds() { glUniform1i(glGetUniformLocation(bbProg, "tex"), 0); GLuint modelLoc = glGetUniformLocation(bbProg, "model"); + glUniform1i(glGetUniformLocation(bbProg, "debug"), 0); + + /* 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 metaball %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 model = faceView(model); + model = scale(model, vec3(metaballScale)); + glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); // Set the billboard color as RGBA = (1.0, 1.0, 1.0, 1.0). @@ -204,29 +223,67 @@ 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); vec4 pixel; + +#ifndef PBO // 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); +#else + + glBindBuffer(GL_PIXEL_PACK_BUFFER, pboBufs[pboIdx]); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, + NULL); + + glBindBuffer(GL_PIXEL_PACK_BUFFER, pboBufs[(pboIdx + 1) % numPbos]); + GLuint offset = screenPos.x * 4 + screenPos.y * (int)width * 4; + ivec4 *src = (ivec4 *)glMapBufferRange(GL_PIXEL_PACK_BUFFER, offset, 4, GL_MAP_READ_BIT); + checkError(); + if (src) { + ivec4 t = src[(int)screenPos.x + (int)screenPos.y * (int)width]; + pixel = vec4(t) / vec4(255.f); + } + glUnmapBuffer(GL_PIXEL_PACK_BUFFER); + + pboIdx = (pboIdx + 1) % numPbos; + + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); +#endif // Multiply the pixel value by the sunlight color. - vec4 sunColor = {1, 1, 0.9, 1}; - pixel *= sunColor; + pixel *= envColors[envColorIdx][0]; // Store the color into an array C[k] as the color of the billboard. k.col = pixel; } + fprintf(stderr, "\n"); + + auto elapsed = std::chrono::system_clock::now() - begin_time; + double elapsed_seconds = std::chrono::duration_cast >(elapsed).count(); + fprintf(stderr, "time with pbo: %f\n", elapsed_seconds); saveFBO(); checkError(); + /* 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(camPos, a.pos) > distance(camPos, b.pos); @@ -239,6 +296,10 @@ void renderClouds() { // 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); @@ -248,10 +309,12 @@ void renderClouds() { 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. model = faceView(model); + model = scale(model, vec3(metaballScale)); + glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); // Set the billboard color as C[n]. @@ -260,20 +323,25 @@ void renderClouds() { glm::value_ptr(k.col)); // Map the billboard texture. - int dIdx = k.d * NQ; + 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]; + 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); - glDisable(GL_BLEND); - model = scale(model, vec3(0.02)); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + model = scale(model, vec3(0.2)); glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); } @@ -286,8 +354,10 @@ bool needsReshading = true; void display() { if (needsReshading) { // TODO: find a way to make sure there's no clipping - view = glm::lookAt(sunPos + sunDir * vec3(20), sunPos, {0, 1, 0}); - proj = glm::ortho(1.f * aspect, -1.f * aspect, -1.f, 1.f, znear, zfar); + 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); @@ -298,9 +368,14 @@ void display() { 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.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(); @@ -322,9 +397,10 @@ void keyboard(unsigned char key, int x, int y) { if (key == ' ') { calculateMetaballs(); needsRedisplay = true; - needsReshading = true; + needsReshading = curMode == render; } if (key == '0') { + needsReshading = curMode != render; curMode = render; needsRedisplay = true; } @@ -333,13 +409,22 @@ void keyboard(unsigned char key, int x, int y) { needsRedisplay = true; } if (key == '2') { - curMode = debugProbAct; + curMode = debugColor; needsRedisplay = true; } if (key == '3') { + curMode = debugProbAct; + needsRedisplay = true; + } + if (key == '4') { curMode = debugProbExt; needsRedisplay = true; } + if (key == 's') { + envColorIdx = (envColorIdx + 1) % envColors.size(); + needsRedisplay = true; + needsReshading = true; + } } int prevMouseX, prevMouseY; @@ -353,7 +438,7 @@ void motion(int x, int y) { float dx = x - prevMouseX, dy = y - prevMouseY; prevMouseX = x; prevMouseY = y; - const vec3 origin(0, 18, 0); + 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}), @@ -370,7 +455,7 @@ void passiveMotion(int x, int y) { 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"); @@ -379,11 +464,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); @@ -409,14 +497,21 @@ 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, width * height * 4, NULL, + GL_DYNAMIC_READ); + } + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); +#endif glutMainLoop();