3 #include "simulation.h"
10 #include <glm/ext.hpp>
11 #include <glm/glm.hpp>
15 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
24 Mode curMode = render;
29 const float metaballR = 1.f / 16.f;
30 inline float metaballField(float r) {
33 const float a = r / (1);
34 return (-4.f / 9.f * powf(a, 6)) + (17.f / 9.f * powf(a, 4)) -
35 (22.f / 9.f * powf(a, 2)) + 1;
38 const float normalizationFactor = (748.f / 405.f) * M_PI;
41 if (GLenum e = glGetError()) {
42 fprintf(stderr, "%s\n", gluErrorString(e));
47 GLuint bbProg, sunProg;
50 // Here we need to generate n_q textures for different densities of metaballs
51 // These textures then go on the billboards
52 // The texture stores attenuation ratio?
57 // Stores attenuation ratio inside r channel
58 // Should be highest value at center
59 void precalculateBillboardTextures() {
60 fprintf(stderr, "Calculating billboard textures...\n");
61 glGenTextures(NQ, bbTexIds);
63 for (int d = 0; d < NQ; d++) {
65 for (int j = 0; j < 32; j++) {
66 for (int i = 0; i < 32; i++) {
67 // TODO: properly calculate this instead of whatever this is
68 float r = distance(vec2(i, j), vec2(16, 16)) / 16;
69 float density = (float)d / NQ;
71 1 - (density * 0.7 * (metaballField(r) / normalizationFactor));
77 snprintf(path, 32, "bbtex/%i.tga", d);
78 saveGrayscale(data, 32, 32, path);
80 glBindTexture(GL_TEXTURE_2D, bbTexIds[d]);
82 glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 32, 32, 0, GL_RED, GL_FLOAT, data);
83 glGenerateMipmap(GL_TEXTURE_2D); // required, otherwise texture is blank
86 fprintf(stderr, "\r%i out of %i densities calculated%s", d + 1, NQ,
87 d == NQ - 1 ? "\n" : "");
99 array<Metaball, CLOUD_DIM_X * CLOUD_DIM_Y * CLOUD_DIM_Z> metaballs;
101 const float cloudScale = metaballR;
102 const float metaballScale = metaballR * 3.f;
105 void calculateMetaballs() {
107 /* for (int i = 0; i < 256; i++) { */
108 /* float x = ((float)rand()/(float)(RAND_MAX) - 0.5) * 2; */
109 /* float y = ((float)rand()/(float)(RAND_MAX) - 0.5) * 2; */
110 /* float z = ((float)rand()/(float)(RAND_MAX) - 0.5) * 2; */
111 /* float r = (float)rand()/(float)(RAND_MAX) * 1; */
112 /* Metaball m = {{x,y,z}, r}; */
113 /* metaballs.push_back(m); */
115 for (int i = 0; i < CLOUD_DIM_X; i++) {
116 for (int j = 0; j < CLOUD_DIM_Y; j++) {
117 for (int k = 0; k < CLOUD_DIM_Z; k++) {
119 vec3(i, j, k) * vec3(cloudScale),
121 /* m.pos = (m.pos * vec3(2)) - (cloudScale / 2); */
122 m.pos -= vec3(CLOUD_DIM_X, CLOUD_DIM_Y, CLOUD_DIM_Z) * cloudScale / 2.f;
125 metaballs[i * CLOUD_DIM_Y * CLOUD_DIM_Z + j * CLOUD_DIM_Z + k] = m;
129 /* for (int z = 0; z < CLOUD_DIM_Z; z++) */
130 /* metaballs[32 * CLOUD_DIM_Y * CLOUD_DIM_Z + 32 * CLOUD_DIM_Z + z].d = 1; */
133 vec3 sunPos = {0, 10, 0}, sunDir = {0, -1, 0};
134 size_t sunColorIdx = 0;
135 std::array<vec4, 2> sunColors = {
137 vec4(0.988,0.309,0.677,1)
139 vec3 camPos = {0, 0, -5}, viewPos = {0, 0, 0};
140 mat4 proj; // projection matrix
141 mat4 view; // view matrix
142 float znear = 0.001, zfar = 1000;
143 float width = 600, height = 400;
144 float aspect = width / height;
146 void setProjectionAndViewUniforms(GLuint progId) {
147 GLuint projLoc = glGetUniformLocation(progId, "projection");
148 glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(proj));
150 GLuint viewLoc = glGetUniformLocation(progId, "view");
151 glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
154 /** Orientates the transformation matrix to face the camera in the view matrix
156 mat4 faceView(mat4 m) {
157 m[0][0] = view[0][0];
158 m[0][1] = view[1][0];
159 m[0][2] = view[2][0];
160 m[1][0] = view[0][1];
161 m[1][1] = view[1][1];
162 m[1][2] = view[2][1];
163 m[2][0] = view[0][2];
164 m[2][1] = view[1][2];
165 m[2][2] = view[2][2];
169 GLuint attenuationTex;
172 glDisable(GL_DEPTH_TEST);
173 // shaderOutput * 0 + buffer * shader alpha
174 glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
177 // sort by ascending distance from the sun
178 sort(metaballs.begin(), metaballs.end(), [](Metaball &a, Metaball &b) {
179 return distance(sunPos, a.pos) < distance(sunPos, b.pos);
182 glActiveTexture(GL_TEXTURE0);
183 glUniform1i(glGetUniformLocation(bbProg, "tex"), 0);
185 GLuint modelLoc = glGetUniformLocation(bbProg, "model");
186 glUniform1i(glGetUniformLocation(bbProg, "debug"), 0);
189 /* glGenBuffers(1, &pboBuf); */
190 /* glBindBuffer(GL_PIXEL_PACK_BUFFER, pboBuf); */
192 /* glViewport(0, 0, shadeWidth, shadeHeight); */
196 for (auto &k : metaballs) {
197 fprintf(stderr, "\rShading cloud %lu/%lu...", i++, metaballs.size());
198 // place the billboard at the center of k
199 mat4 model = translate(mat4(1), k.pos);
201 // rotate the billboard so that its normal is oriented to the sun
202 model = faceView(model);
204 model = scale(model, vec3(metaballScale));
206 glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
208 // Set the billboard color as RGBA = (1.0, 1.0, 1.0, 1.0).
209 vec4 color = {1, 1, 1, 1};
210 glUniform4fv(glGetUniformLocation(bbProg, "color"), 1,
211 glm::value_ptr(color));
213 // Map the billboard texture with GL_MODULATE.
214 // i.e. multiply rather than add
215 // but glTexEnv is for the old fixed function pipeline --
216 // need to just tell our fragment shader then to modulate
218 glBindTexture(GL_TEXTURE_2D, bbTexIds[dIdx]);
219 glUniform1i(glGetUniformLocation(bbProg, "modulate"), 1);
221 // Render the billboard.
222 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
224 // Read the pixel value corresponding to the center of metaball k.
225 // 1. First get position in opengl screen space: from [-1,1]
226 // 2. Normalize to [0,1]
227 // 3. Multiply by (width * height)
229 ((vec2(proj * view * model * vec4(0, 0, 0, 1)) + vec2(1)) / vec2(2)) *
232 // TODO: This is a huge bottleneck
233 glReadPixels(screenPos.x, screenPos.y, 1, 1, GL_RGBA, GL_FLOAT,
236 // Multiply the pixel value by the sunlight color.
237 pixel *= sunColors[sunColorIdx % sunColors.size()];
239 // Store the color into an array C[k] as the color of the billboard.
242 fprintf(stderr, "\n");
246 /* glViewport(0, 0, width, height); */
249 void renderObject() {
252 glUseProgram(sunProg);
253 mat4 model = translate(mat4(1), sunPos);
254 /* model = lookAt(sunPos, sunPos + sunDir, {0, 1, 0}) * model; */
255 model = translate(scale(translate(model, -sunPos), vec3(0.3)), sunPos);
256 glUniformMatrix4fv(glGetUniformLocation(sunProg, "model"), 1, GL_FALSE, glm::value_ptr(model));
257 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
260 void renderClouds() {
261 glUseProgram(bbProg);
263 // Sort metaballs in descending order from the viewpoint
264 sort(metaballs.begin(), metaballs.end(), [](Metaball &a, Metaball &b) {
265 return distance(camPos, a.pos) > distance(camPos, b.pos);
268 glUniform1i(glGetUniformLocation(bbProg, "debug"), curMode != render);
270 glDisable(GL_DEPTH_TEST);
272 // shaderOutput * 1 + buffer * shader alpha
273 glBlendFunc(GL_ONE, GL_SRC_ALPHA);
275 /* glBlendColor(1.f,1.f,1.f,1.f); */
276 /* glBlendFuncSeparate(GL_ONE, GL_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_SRC_ALPHA); */
278 glActiveTexture(GL_TEXTURE0);
279 glUniform1i(glGetUniformLocation(bbProg, "tex"), 0);
281 for (int i = 0; i < metaballs.size(); i++) {
282 Metaball k = metaballs[i];
284 GLuint modelLoc = glGetUniformLocation(bbProg, "model");
286 // Place the billboard at the center of the corresponding metaball n.
287 mat4 model = translate(mat4(1), k.pos);
288 // Rotate the billboard so that its normal is oriented to the viewpoint.
289 model = faceView(model);
291 model = scale(model, vec3(metaballScale));
293 glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
295 // Set the billboard color as C[n].
297 glUniform4fv(glGetUniformLocation(bbProg, "color"), 1,
298 glm::value_ptr(k.col));
300 // Map the billboard texture.
301 int dIdx = k.d * (NQ - 1);
302 glBindTexture(GL_TEXTURE_2D, bbTexIds[dIdx]);
304 // Don't modulate it -- blend it
305 glUniform1i(glGetUniformLocation(bbProg, "modulate"), 0);
307 glUniform1f(glGetUniformLocation(bbProg, "debugColor"), curMode == debugColor);
308 if (curMode != render) {
310 if (curMode == debugContDist) debugVal = k.d;
311 else if (curMode == debugProbAct) debugVal = cs.p_act[k.coords.x][k.coords.y][k.coords.z];
312 else if (curMode == debugProbExt) debugVal = cs.p_ext[k.coords.x][k.coords.y][k.coords.z];
313 glUniform1f(glGetUniformLocation(bbProg, "debugVal"), debugVal);
314 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
315 model = scale(model, vec3(0.2));
316 glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
319 // Render the billboard with the blending function.
320 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
324 bool needsReshading = true;
326 if (needsReshading) {
327 // TODO: find a way to make sure there's no clipping
328 view = glm::lookAt(sunPos + sunDir * vec3(100.f), sunPos, {0, 0, 1});
329 // TODO: calculate bounds so everything is covered
330 proj = glm::ortho(2.5f, -2.5f, -2.5f, 2.5f, znear, 10000.f);
331 glUseProgram(bbProg);
332 setProjectionAndViewUniforms(bbProg);
334 glClearColor(1, 1, 1, 1);
335 glClear(GL_COLOR_BUFFER_BIT);
337 needsReshading = false;
340 view = glm::lookAt(camPos, viewPos, {0, 1, 0});
341 proj = glm::perspective(45.f, aspect, znear, zfar);
342 glUseProgram(sunProg);
343 setProjectionAndViewUniforms(sunProg);
344 glUseProgram(bbProg);
345 setProjectionAndViewUniforms(bbProg);
347 glClearColor(0.83, 1, 1, 1); // background color
348 glClear(GL_COLOR_BUFFER_BIT);
349 renderObject(); // render things that aren't clouds
355 bool needsRedisplay = false;
357 /* calculateMetaballs(); */
358 if (needsRedisplay) {
361 needsRedisplay = false;
362 glutTimerFunc(16, timer, 0);
365 void keyboard(unsigned char key, int x, int y) {
367 calculateMetaballs();
368 needsRedisplay = true;
369 needsReshading = curMode == render;
372 needsReshading = curMode != render;
374 needsRedisplay = true;
377 curMode = debugContDist;
378 needsRedisplay = true;
381 curMode = debugColor;
382 needsRedisplay = true;
385 curMode = debugProbAct;
386 needsRedisplay = true;
389 curMode = debugProbExt;
390 needsRedisplay = true;
394 needsRedisplay = true;
395 needsReshading = true;
399 int prevMouseX, prevMouseY;
400 bool firstMouse = true;
401 void motion(int x, int y) {
407 float dx = x - prevMouseX, dy = y - prevMouseY;
410 const vec3 origin(0, 0, 0);
411 const float sensitivity = 0.003f;
412 auto camMat = translate(mat4(1), origin + camPos);
413 auto rotation = rotate(rotate(mat4(1), -dx * sensitivity, {0, 1, 0}),
414 -dy * sensitivity, {1, 0, 0});
415 auto rotAroundOrig = camMat * rotation * translate(mat4(1), origin - camPos);
416 camPos = rotAroundOrig * glm::vec4(camPos, 0);
417 needsRedisplay = true;
420 void passiveMotion(int x, int y) {
425 int main(int argc, char **argv) {
426 glutInit(&argc, argv);
427 glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB |
428 GLUT_3_2_CORE_PROFILE);
429 glutInitWindowSize(width, height);
430 glutCreateWindow("Clouds");
431 glutDisplayFunc(display);
435 Program prog("billboardvert.glsl", "billboardfrag.glsl");
436 bbProg = prog.progId;
437 Program sProg("sunvert.glsl", "sunfrag.glsl");
438 sunProg = sProg.progId;
440 glGenVertexArrays(1, &bbVao);
441 glUseProgram(sunProg);
442 glBindVertexArray(bbVao);
443 glUseProgram(bbProg);
444 glBindVertexArray(bbVao);
446 glGenBuffers(2, vbos);
448 vector<vec3> poss = {{-1, -1, 0}, {-1, 1, 0}, {1, 1, 0}, {1, -1, 0}};
449 vector<GLuint> indices = {2, 1, 0, 3, 2, 0};
451 GLuint posLoc = glGetAttribLocation(bbProg, "vPosition");
452 glBindBuffer(GL_ARRAY_BUFFER, vbos[0]);
453 glBufferData(GL_ARRAY_BUFFER, poss.size() * sizeof(glm::vec3), &poss[0],
455 glEnableVertexAttribArray(posLoc);
456 glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
458 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbos[1]);
459 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint),
460 &indices[0], GL_STATIC_DRAW);
464 precalculateBillboardTextures();
467 calculateMetaballs();
469 glGenTextures(1, &attenuationTex);
471 glutKeyboardFunc(keyboard);
472 glutMotionFunc(motion);
473 glutPassiveMotionFunc(passiveMotion);
474 glutTimerFunc(16, timer, 0);
476 // set up billboard prog