3 #include "simulation.h"
10 #include <glm/ext.hpp>
11 #include <glm/glm.hpp>
15 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
17 bool debugContDist = false;
22 const float metaballR = 1.5f;
23 inline float metaballField(float r) {
26 const float a = r / metaballR;
27 return (-4.f / 9.f * powf(a, 6)) + (17.f / 9.f * powf(a, 4)) -
28 (22.f / 9.f * powf(a, 2)) + 1;
31 const float normalizationFactor = 748.f / 405.f * M_PI * metaballR;
34 if (GLenum e = glGetError()) {
35 fprintf(stderr, "%s\n", gluErrorString(e));
43 // Here we need to generate n_q textures for different densities of metaballs
44 // These textures then go on the billboards
45 // The texture stores attenuation ratio?
50 // Stores attenuation ratio inside r channel
51 // Should be highest value at center
52 void precalculateBillboardTextures() {
53 fprintf(stderr, "Calculating billboard textures...\n");
54 glGenTextures(NQ, bbTexIds);
56 for (int d = 0; d < NQ; d++) {
58 for (int j = 0; j < 32; j++) {
59 for (int i = 0; i < 32; i++) {
60 // TODO: properly calculate this instead of whatever this is
61 float r = distance(vec2(i, j), vec2(16, 16)) / 16;
62 float density = (float)d / NQ;
64 1 - (density * metaballField(r) / normalizationFactor);
70 snprintf(path, 32, "bbtex/%i.tga", d);
71 saveGrayscale(data, 32, 32, path);
73 glBindTexture(GL_TEXTURE_2D, bbTexIds[d]);
76 glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 32, 32, 0, GL_RED, GL_FLOAT, data);
77 glGenerateMipmap(GL_TEXTURE_2D); // required, otherwise texture is blank
81 fprintf(stderr, "\r%i out of %i densities calculated%s", d + 1, NQ,
82 d == NQ - 1 ? "\n" : "");
88 /** Radius, density */
93 array<Metaball, CLOUD_DIM * CLOUD_DIM * CLOUD_DIM> metaballs;
97 void calculateMetaballs() {
99 /* for (int i = 0; i < 256; i++) { */
100 /* float x = ((float)rand()/(float)(RAND_MAX) - 0.5) * 2; */
101 /* float y = ((float)rand()/(float)(RAND_MAX) - 0.5) * 2; */
102 /* float z = ((float)rand()/(float)(RAND_MAX) - 0.5) * 2; */
103 /* float r = (float)rand()/(float)(RAND_MAX) * 1; */
104 /* Metaball m = {{x,y,z}, r}; */
105 /* metaballs.push_back(m); */
107 for (int i = 0; i < CLOUD_DIM; i++) {
108 for (int j = 0; j < CLOUD_DIM; j++) {
109 for (int k = 0; k < CLOUD_DIM; k++) {
111 {i / (float)CLOUD_DIM, j / (float)CLOUD_DIM, k / (float)CLOUD_DIM},
112 1.f / (float)CLOUD_DIM};
113 m.pos = (m.pos * vec3(2)) - vec3(1);
115 metaballs[i * CLOUD_DIM * CLOUD_DIM + j * CLOUD_DIM + k] = m;
121 vec3 sunPos = {0, 2, 2}, sunDir = {0, -1, -1};
122 vec3 camPos = {0, 0, -5}, viewPos = {0, 0, 0};
123 mat4 proj; // projection matrix
124 mat4 view; // view matrix
125 float znear = 0.001, zfar = 1000;
126 float width = 600, height = 400;
127 float aspect = width / height;
129 void setProjectionAndViewUniforms(GLuint progId) {
130 GLuint projLoc = glGetUniformLocation(progId, "projection");
131 glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(proj));
133 GLuint viewLoc = glGetUniformLocation(progId, "view");
134 glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
137 /** Orientates the transformation matrix to face the camera in the view matrix
139 mat4 faceView(mat4 m) {
140 m[0][0] = view[0][0];
141 m[0][1] = view[1][0];
142 m[0][2] = view[2][0];
143 m[1][0] = view[0][1];
144 m[1][1] = view[1][1];
145 m[1][2] = view[2][1];
146 m[2][0] = view[0][2];
147 m[2][1] = view[1][2];
148 m[2][2] = view[2][2];
152 GLuint attenuationTex;
155 glDisable(GL_DEPTH_TEST);
156 // shaderOutput * 0 + buffer * shader alpha
157 glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
160 // sort by ascending distance from the sun
161 sort(metaballs.begin(), metaballs.end(), [](Metaball &a, Metaball &b) {
162 return distance(sunPos, a.pos) < distance(sunPos, b.pos);
165 glActiveTexture(GL_TEXTURE0);
166 glUniform1i(glGetUniformLocation(bbProg, "tex"), 0);
168 GLuint modelLoc = glGetUniformLocation(bbProg, "model");
170 for (auto &k : metaballs) {
171 // place the billboard at the center of k
172 mat4 model = scale(translate(mat4(1), k.pos), vec3(k.r) * 2.f);
174 // rotate the billboard so that its normal is oriented to the sun
175 model = faceView(model);
177 glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
179 // Set the billboard color as RGBA = (1.0, 1.0, 1.0, 1.0).
180 vec4 color = {1, 1, 1, 1};
181 glUniform4fv(glGetUniformLocation(bbProg, "color"), 1,
182 glm::value_ptr(color));
184 // Map the billboard texture with GL_MODULATE.
185 // i.e. multiply rather than add
186 // but glTexEnv is for the old fixed function pipeline --
187 // need to just tell our fragment shader then to modulate
189 glBindTexture(GL_TEXTURE_2D, bbTexIds[dIdx]);
190 glUniform1i(glGetUniformLocation(bbProg, "modulate"), 1);
192 // Render the billboard.
193 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
195 // Read the pixel value corresponding to the center of metaball k.
196 // 1. First get position in opengl screen space: from [-1,1]
197 // 2. Normalize to [0,1]
198 // 3. Multiply by (width * height)
200 ((vec2(proj * view * model * vec4(0, 0, 0, 1)) + vec2(1)) / vec2(2)) *
203 // TODO: This is a huge bottleneck
204 glReadPixels(screenPos.x, screenPos.y, 1, 1, GL_RGBA, GL_FLOAT,
207 // Multiply the pixel value by the sunlight color.
208 vec4 sunColor = {1, 1, 0.9, 1};
211 // Store the color into an array C[k] as the color of the billboard.
219 void renderObject() {}
221 void renderClouds() {
222 // Sort metaballs in descending order from the viewpoint
223 sort(metaballs.begin(), metaballs.end(), [](Metaball &a, Metaball &b) {
224 return distance(camPos, a.pos) > distance(camPos, b.pos);
227 glUniform1i(glGetUniformLocation(bbProg, "debugContDist"), debugContDist);
229 glDisable(GL_DEPTH_TEST);
231 // shaderOutput * 1 + buffer * shader alpha
232 glBlendFunc(GL_ONE, GL_SRC_ALPHA);
234 glActiveTexture(GL_TEXTURE0);
235 glUniform1i(glGetUniformLocation(bbProg, "tex"), 0);
237 for (int i = 0; i < metaballs.size(); i++) {
238 Metaball k = metaballs[i];
240 GLuint modelLoc = glGetUniformLocation(bbProg, "model");
242 // Place the billboard at the center of the corresponding metaball n.
243 mat4 model = scale(translate(mat4(1), k.pos), vec3(k.r) * 2.f);
244 // Rotate the billboard so that its normal is oriented to the viewpoint.
245 model = faceView(model);
247 glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
249 // Set the billboard color as C[n].
251 glUniform4fv(glGetUniformLocation(bbProg, "color"), 1,
252 glm::value_ptr(k.col));
254 // Map the billboard texture.
256 glBindTexture(GL_TEXTURE_2D, bbTexIds[dIdx]);
258 // Don't modulate it -- blend it
259 glUniform1i(glGetUniformLocation(bbProg, "modulate"), 0);
262 glUniform1f(glGetUniformLocation(bbProg, "density"), k.d);
264 model = scale(model, vec3(0.02));
265 glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
268 // Render the billboard with the blending function.
269 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
273 bool needsReshading = true;
275 if (needsReshading) {
276 // TODO: find a way to make sure there's no clipping
277 view = glm::lookAt(sunPos + sunDir * vec3(20), sunPos, {0, 1, 0});
278 proj = glm::ortho(1.f * aspect, -1.f * aspect, -1.f, 1.f, znear, zfar);
279 setProjectionAndViewUniforms(bbProg);
281 glClearColor(1, 1, 1, 1);
282 glClear(GL_COLOR_BUFFER_BIT);
284 needsReshading = false;
287 view = glm::lookAt(camPos, viewPos, {0, 1, 0});
288 proj = glm::perspective(45.f, aspect, znear, zfar);
289 setProjectionAndViewUniforms(bbProg);
291 glClearColor(0.83, 1, 1, 1); // background color
292 glClear(GL_COLOR_BUFFER_BIT);
293 renderObject(); // render things that aren't clouds
299 bool needsRedisplay = false;
301 /* calculateMetaballs(); */
302 if (needsRedisplay) {
304 needsRedisplay = false;
306 glutTimerFunc(16, timer, 0);
309 void keyboard(unsigned char key, int x, int y) {
311 calculateMetaballs();
312 needsRedisplay = true;
313 needsReshading = true;
316 debugContDist = !debugContDist;
317 needsRedisplay = true;
321 int prevMouseX, prevMouseY;
322 bool firstMouse = true;
323 void motion(int x, int y) {
329 float dx = x - prevMouseX, dy = y - prevMouseY;
332 const vec3 origin(0, 18, 0);
333 const float sensitivity = 0.003f;
334 auto camMat = translate(mat4(1), origin + camPos);
335 auto rotation = rotate(rotate(mat4(1), -dx * sensitivity, {0, 1, 0}),
336 -dy * sensitivity, {1, 0, 0});
337 auto rotAroundOrig = camMat * rotation * translate(mat4(1), origin - camPos);
338 camPos = rotAroundOrig * glm::vec4(camPos, 0);
339 needsRedisplay = true;
342 void passiveMotion(int x, int y) {
347 int main(int argc, char **argv) {
348 glutInit(&argc, argv);
349 glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB |
350 GLUT_3_2_CORE_PROFILE);
351 glutInitWindowSize(width, height);
352 glutCreateWindow("Clouds");
353 glutDisplayFunc(display);
357 Program prog("billboardvert.glsl", "billboardfrag.glsl");
359 bbProg = prog.progId;
360 glUseProgram(bbProg);
362 glGenVertexArrays(1, &bbVao);
363 glBindVertexArray(bbVao);
365 glGenBuffers(2, vbos);
367 vector<vec3> poss = {{-1, -1, 0}, {-1, 1, 0}, {1, 1, 0}, {1, -1, 0}};
368 vector<GLuint> indices = {2, 1, 0, 3, 2, 0};
370 GLuint posLoc = glGetAttribLocation(bbProg, "vPosition");
371 glBindBuffer(GL_ARRAY_BUFFER, vbos[0]);
372 glBufferData(GL_ARRAY_BUFFER, poss.size() * sizeof(glm::vec3), &poss[0],
374 glEnableVertexAttribArray(posLoc);
375 glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
377 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbos[1]);
378 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint),
379 &indices[0], GL_STATIC_DRAW);
383 precalculateBillboardTextures();
386 calculateMetaballs();
388 glGenTextures(1, &attenuationTex);
390 glutKeyboardFunc(keyboard);
391 glutMotionFunc(motion);
392 glutPassiveMotionFunc(passiveMotion);
393 glutTimerFunc(16, timer, 0);
395 // set up billboard prog