OpenGL ES SDK for Android ARM Developer Center
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Heightmap.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2014-2017, ARM Limited and Contributors
2  *
3  * SPDX-License-Identifier: MIT
4  *
5  * Permission is hereby granted, free of charge,
6  * to any person obtaining a copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
14  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
17  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19  */
20 
21 #include "Heightmap.h"
22 #include "Platform.h"
23 #include <cmath>
24 #include <cstdint>
25 #include <cstdlib>
26 #include <cstdio>
27 
28 using namespace MaliSDK;
29 using namespace std;
30 
31 Heightmap::Heightmap(unsigned int size, unsigned int levels)
32  : size(size), levels(levels)
33 {
35  GL_CHECK(glGenTextures(1, &texture));
36  GL_CHECK(glBindTexture(GL_TEXTURE_2D_ARRAY, texture));
37 
38  // Use half-float as we don't need full float precision.
39  // GL_RG16UI would work as well as we don't need texture filtering.
40  // 8-bit does not give sufficient precision except for low-detail heightmaps.
41  // Use two components to allow storing current level's height as well as the height of the next level.
42  GL_CHECK(glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RG16F, size, size, levels));
43 
44  GL_CHECK(glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
45  GL_CHECK(glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
46 
47  // The repeat is crucial here. This allows us to update small sections of the texture when moving the camera.
48  GL_CHECK(glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT));
49  GL_CHECK(glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT));
50 
51  GL_CHECK(glBindTexture(GL_TEXTURE_2D_ARRAY, 0));
53 
54  // Upload to alternating PBOs for better pipelining.
55  GL_CHECK(glGenBuffers(2, pixel_buffer));
57  for (unsigned int i = 0; i < 2; i++)
58  {
59  GL_CHECK(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixel_buffer[i]));
60  pixel_buffer_size = levels * size * size * sizeof(vec2);
61  pixel_buffer_size *= 2; // Double because in worst case we update same region twice.
62  GL_CHECK(glBufferData(GL_PIXEL_UNPACK_BUFFER, pixel_buffer_size, NULL, GL_STREAM_DRAW));
63  }
64  GL_CHECK(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
65 
66  reset();
67 }
68 
70 {
72  level_info.resize(levels);
73  for (unsigned int i = 0; i < levels; i++)
74  level_info[i].cleared = true;
75 }
76 
78 {
79  GL_CHECK(glDeleteTextures(1, &texture));
80  GL_CHECK(glDeleteBuffers(2, pixel_buffer));
81 }
82 
83 // Divides, but always rounds down.
84 // Most compilers round towards 0 for negative numbers (implementation specific).
85 static inline int idiv(int x, int m)
86 {
87  if (x >= 0)
88  return x / m;
89  else
90  return -((-x + m - 1) / m);
91 }
92 
93 // Modulo with wrapping behavior equal to that of GL_REPEAT.
94 static inline int imod(int x, int m)
95 {
96  if (x >= 0)
97  return x % m;
98  else
99  {
100  int ret = m - (-x % m);
101  return ret == m ? 0 : ret;
102  }
103 }
104 
105 static inline double sinc(double v)
106 {
107  if (fabs(v) < 0.0001)
108  return 1.0;
109  else
110  return sin(PI * v) / (PI * v);
111 }
112 
113 // Can really do anything we want, but keep it simple here,
114 // so just generate a bandpass-filtered 2D grid and repeat it infinitely.
115 // This causes a second or two of startup time depending on optimization level and platform.
117 {
118  heightmap_size = 1024;
120 
121  vector<float> orig(heightmap_size * heightmap_size);
122  vector<float> horiz(heightmap_size * heightmap_size);
123 
124  // Create some simple bandpass filters. Modulate up lanczos-windowed sinc low-pass filters.
125 #define FILTER_LEN 65
126 #define FILTER_CENTER ((FILTER_LEN - 1) / 2)
127  float filter[FILTER_LEN] = {0.0f};
128 
129  struct Filter
130  {
131  double amp;
132  double bw;
133  double center;
134  };
135 
136  static const Filter bands[] = {
137  { 8.0, 0.0075, 0.0 },
138  { 0.01, 0.1, 0.1 },
139  { 0.005, 0.2, 0.2 },
140  { 0.0025, 0.4, 0.4 },
141  };
142 
143  for (unsigned int f = 0; f < sizeof(bands) / sizeof(bands[0]); f++)
144  for (int x = 0; x < FILTER_LEN; x++)
145  filter[x] += bands[f].amp * bands[f].bw * sinc(bands[f].bw * (x - FILTER_CENTER)) * sinc((x - FILTER_CENTER) / FILTER_CENTER) * cos(PI * x * bands[f].center);
146 
147  // White noise
148  srand(0);
149  for (unsigned int y = 0; y < heightmap_size; y++)
150  for (unsigned int x = 0; x < heightmap_size; x++)
151  orig[y * heightmap_size + x] = 50.0f * (float(rand()) / RAND_MAX - 0.5f);
152 
153  // Bandpass horizontally
154  for (unsigned int y = 0; y < heightmap_size; y++)
155  {
156  const float *src = &orig[y * heightmap_size];
157  float *dst = &horiz[y * heightmap_size];
158  for (unsigned int x = 0; x < heightmap_size; x++)
159  {
160  float sum = 0.0f;
161  for (unsigned int i = 0; i < FILTER_LEN; i++)
162  sum += src[(x - i) & (heightmap_size - 1)] * filter[i];
163  dst[x] = sum;
164  }
165  }
166 
167  // Bandpass vertically
168  for (unsigned int x = 0; x < heightmap_size; x++)
169  {
170  const float *src = &horiz[x];
171  float *dst = &heightmap[x];
172  for (unsigned int y = 0; y < heightmap_size; y++)
173  {
174  float sum = 0.0f;
175  for (unsigned int i = 0; i < FILTER_LEN; i++)
176  sum += src[((y - i) & (heightmap_size - 1)) * heightmap_size] * filter[i];
177  dst[y * heightmap_size] = sum;
178  }
179  }
180 }
181 
182 // LUT-based approach. In a real application this would likely be way more complicated.
183 // Two common applications are pre-computed terrains and procedural generation.
184 // Sampling like this without appropriate low-pass filtering adds aliasing
185 // which can cause the heightmap to "pop" in as LOD levels decrease.
187 {
188  x &= heightmap_size - 1;
189  y &= heightmap_size - 1;
190  return heightmap[y * heightmap_size + x];
191 }
192 
194 // Compute the height at texel (x, y) for cliplevel.
195 // Also compute the sample for the lower resolution (with simple bilinear).
196 // This avoids an extra texture lookup in vertex shader, avoids complex offsetting and having to use GL_LINEAR.
198 {
199  float height = sample_heightmap(x << level, y << level);
200  float heights[2][2];
201  for (int j = 0; j < 2; j++)
202  for (int i = 0; i < 2; i++)
203  heights[j][i] = sample_heightmap(((x + i) & ~1) << level, ((y + j) & ~1) << level);
204 
205  return vec2(
206  height,
207  (heights[0][0] + heights[0][1] + heights[1][0] + heights[1][1]) * 0.25f);
208 }
210 
212 void Heightmap::update_region(vec2 *buffer, unsigned int& pixel_offset, int tex_x, int tex_y,
213  int width, int height,
214  int start_x, int start_y,
215  int level)
216 {
217  if (width == 0 || height == 0)
218  return;
219 
220  // Here we could either stream a "real" heightmap, or generate it procedurally on the GPU by rendering to these regions.
221 
222  buffer += pixel_offset;
223  for (int y = 0; y < height; y++)
224  for (int x = 0; x < width; x++)
225  buffer[y * width + x] = compute_heightmap(start_x + x, start_y + y, level);
226 
227  UploadInfo info;
228  info.x = tex_x;
229  info.y = tex_y;
230  info.width = width;
231  info.height = height;
232  info.level = level;
233  info.offset = pixel_offset * sizeof(vec2);
234  upload_info.push_back(info);
235 
236  pixel_offset += width * height;
237 }
239 
240 void Heightmap::update_level(vec2 *buffer, unsigned int& pixel_offset, const vec2& offset, unsigned int level)
241 {
242  LevelInfo& info = level_info[level];
243  int start_x = int(offset.c.x) >> level;
244  int start_y = int(offset.c.y) >> level;
245 
246  // Nothing to do for this level.
247  if (start_x == info.x && start_y == info.y && !info.cleared)
248  return;
249 
250  int delta_x = start_x - info.x;
251  int delta_y = start_y - info.y;
252 
253  int old_base_x = idiv(info.x, size) * size;
254  int old_base_y = idiv(info.y, size) * size;
255  int base_x = idiv(start_x, size) * size;
256  int base_y = idiv(start_y, size) * size;
257 
258  // We have suddenly moved to a completely different place in the heightmap, or we need to recompute everything.
259  if (abs(delta_x) >= int(size) || abs(delta_y) >= int(size) || info.cleared)
260  {
261  int wrapped_x = start_x - base_x;
262  int wrapped_y = start_y - base_y;
263 
264  update_region(buffer, pixel_offset,
265  0, 0, wrapped_x, wrapped_y,
266  base_x + size, base_y + size, level);
267 
268  update_region(buffer, pixel_offset,
269  wrapped_x, 0, size - wrapped_x, wrapped_y,
270  start_x, base_y + size, level);
271 
272  update_region(buffer, pixel_offset,
273  0, wrapped_y, wrapped_x, size - wrapped_y,
274  base_x + size, start_y, level);
275 
276  update_region(buffer, pixel_offset,
277  wrapped_x, wrapped_y, size - wrapped_x, size - wrapped_y,
278  start_x, start_y, level);
279 
280  info.cleared = false;
281  }
282  else // Incremental update. Upload what we need.
283  {
284  int old_wrapped_x = info.x - old_base_x;
285  int old_wrapped_y = info.y - old_base_y;
286  int wrapped_x = start_x - base_x;
287  int wrapped_y = start_y - base_y;
288 
289  int wrap_delta_x = wrapped_x - old_wrapped_x;
290  int wrap_delta_y = wrapped_y - old_wrapped_y;
291 
292  // There can be significant overlap between X-delta and Y-delta uploads if deltas are large.
293  // Avoiding this would add even more complexities and is therefore ignored here.
294 
295  // Do this in two steps. First update as we're moving in X, then move in Y.
296  if (wrap_delta_x >= 0 && delta_x >= 0) // One update region for X, simple case. Have to update both Y regions however.
297  {
298  update_region(buffer, pixel_offset,
299  old_wrapped_x, 0, wrap_delta_x, old_wrapped_y,
300  info.x + size, old_base_y + size, level);
301 
302  update_region(buffer, pixel_offset,
303  old_wrapped_x, old_wrapped_y, wrap_delta_x, size - old_wrapped_y,
304  info.x + size, info.y, level);
305  }
306  else if (wrap_delta_x < 0 && delta_x < 0) // One update region for X, simple case. Have to update both Y regions however.
307  {
308  update_region(buffer, pixel_offset,
309  wrapped_x, 0, -wrap_delta_x, old_wrapped_y,
310  start_x, old_base_y + size, level);
311 
312  update_region(buffer, pixel_offset,
313  wrapped_x, old_wrapped_y, -wrap_delta_x, size - old_wrapped_y,
314  start_x, info.y, level);
315  }
316  else if (wrap_delta_x < 0 && delta_x >= 0) // Two update regions in X, and also have to update both Y regions.
317  {
318  update_region(buffer, pixel_offset,
319  0, 0, wrapped_x, old_wrapped_y,
320  base_x + size, old_base_y + size, level);
321 
322  update_region(buffer, pixel_offset,
323  old_wrapped_x, 0, size - old_wrapped_x, old_wrapped_y,
324  base_x + old_wrapped_x, old_base_y + size, level);
325 
326  update_region(buffer, pixel_offset,
327  0, old_wrapped_y, wrapped_x, size - old_wrapped_y,
328  base_x + size, info.y, level);
329 
330  update_region(buffer, pixel_offset,
331  old_wrapped_x, old_wrapped_y, size - old_wrapped_x, size - old_wrapped_y,
332  base_x + old_wrapped_x, info.y, level);
333  }
334  else if (wrap_delta_x >= 0 && delta_x < 0) // Two update regions in X, and also have to update both Y regions.
335  {
336  update_region(buffer, pixel_offset,
337  0, 0, old_wrapped_x, old_wrapped_y,
338  base_x + size, old_base_y + size, level);
339 
340  update_region(buffer, pixel_offset,
341  wrapped_x, 0, size - wrapped_x, old_wrapped_y,
342  start_x, old_base_y + size, level);
343 
344  update_region(buffer, pixel_offset,
345  0, old_wrapped_y, old_wrapped_x, size - old_wrapped_y,
346  base_x + size, info.y, level);
347 
348  update_region(buffer, pixel_offset,
349  wrapped_x, old_wrapped_y, size - wrapped_x, size - old_wrapped_y,
350  start_x, info.y, level);
351  }
352 
353  if (wrap_delta_y >= 0 && delta_y >= 0)
354  {
355  update_region(buffer, pixel_offset,
356  0, old_wrapped_y, wrapped_x, wrap_delta_y,
357  base_x + size, info.y + size, level);
358 
359  update_region(buffer, pixel_offset,
360  wrapped_x, old_wrapped_y, size - wrapped_x, wrap_delta_y,
361  start_x, info.y + size, level);
362  }
363  else if (wrap_delta_y < 0 && delta_y < 0)
364  {
365  update_region(buffer, pixel_offset,
366  0, wrapped_y, wrapped_x, -wrap_delta_y,
367  base_x + size, start_y, level);
368 
369  update_region(buffer, pixel_offset,
370  wrapped_x, wrapped_y, size - wrapped_x, -wrap_delta_y,
371  start_x, start_y, level);
372  }
373  else if (wrap_delta_y < 0 && delta_y >= 0)
374  {
375  update_region(buffer, pixel_offset,
376  0, 0, wrapped_x, wrapped_y,
377  base_x + size, base_y + size, level);
378 
379  update_region(buffer, pixel_offset,
380  0, old_wrapped_y, wrapped_x, size - old_wrapped_y,
381  base_x + size, base_y + old_wrapped_y, level);
382 
383  update_region(buffer, pixel_offset,
384  wrapped_x, 0, size - wrapped_x, wrapped_y,
385  start_x, base_y + size, level);
386 
387  update_region(buffer, pixel_offset,
388  wrapped_x, old_wrapped_y, size - wrapped_x, size - old_wrapped_y,
389  start_x, base_y + old_wrapped_y, level);
390  }
391  else if (wrap_delta_y >= 0 && delta_y < 0)
392  {
393  update_region(buffer, pixel_offset,
394  0, 0, wrapped_x, old_wrapped_y,
395  base_x + size, base_y + size, level);
396 
397  update_region(buffer, pixel_offset,
398  0, wrapped_y, wrapped_x, size - wrapped_y,
399  base_x + size, start_y, level);
400 
401  update_region(buffer, pixel_offset,
402  wrapped_x, 0, size - wrapped_x, old_wrapped_y,
403  start_x, base_y + size, level);
404 
405  update_region(buffer, pixel_offset,
406  wrapped_x, wrapped_y, size - wrapped_x, size - wrapped_y,
407  start_x, start_y, level);
408  }
409  }
410 
411  info.x = start_x;
412  info.y = start_y;
413 }
414 
415 void Heightmap::update_heightmap(const vector<vec2>& level_offsets)
416 {
417  upload_info.clear();
418 
419  GL_CHECK(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixel_buffer[pixel_buffer_index]));
420  pixel_buffer_index ^= 1; // Alternate which PBO we upload to.
421  GL_CHECK(vec2 *buffer = reinterpret_cast<vec2*>(glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0,
422  pixel_buffer_size, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT)));
423  if (!buffer)
424  {
425  LOGE("Failed to map heightmap PBO.\n");
426  return;
427  }
428 
429  unsigned int pixel_offset = 0;
430  for (unsigned int i = 0; i < levels; i++)
431  update_level(buffer, pixel_offset, level_offsets[i], i);
432 
433  GL_CHECK(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));
434 
435  GL_CHECK(glBindTexture(GL_TEXTURE_2D_ARRAY, texture));
436  for (vector<UploadInfo>::const_iterator itr = upload_info.begin(); itr != upload_info.end(); ++itr)
437  {
438  GL_CHECK(glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0,
439  itr->x, itr->y, itr->level,
440  itr->width, itr->height, 1,
441  GL_RG, GL_FLOAT, reinterpret_cast<const GLvoid*>(itr->offset))); // GLES can convert float to half-float here.
442  }
443  GL_CHECK(glBindTexture(GL_TEXTURE_2D_ARRAY, 0));
444  GL_CHECK(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
445 }
const GLfloat * v
Definition: gl2ext.h:2231
Heightmap(unsigned int size, unsigned int levels)
Definition: Heightmap.cpp:31
void update_level(vec2 *buffer, unsigned int &pixel_offset, const vec2 &level_offset, unsigned level)
[Update region]
Definition: Heightmap.cpp:240
#define FILTER_LEN
static double sinc(double v)
Definition: Heightmap.cpp:105
void update_heightmap(const std::vector< vec2 > &level_offsets)
Definition: Heightmap.cpp:415
GLint GLsizei GLsizei height
Definition: gl2ext.h:179
static int idiv(int x, int m)
Definition: Heightmap.cpp:85
void reset()
Definition: Heightmap.cpp:69
Definition: matrix.h:28
unsigned int pixel_buffer_size
Definition: Heightmap.h:42
GLenum GLuint GLintptr offset
Definition: gl2ext.h:629
precision highp int
Definition: hiz_cull.cs:38
GLenum GLenum dst
Definition: gl2ext.h:304
#define PI
Definition: matrix.h:24
void init_heightmap()
Definition: Heightmap.cpp:116
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: gl2ext.h:818
std::vector< UploadInfo > upload_info
Definition: Heightmap.h:63
unsigned int size
Definition: Heightmap.h:43
unsigned int levels
Definition: Heightmap.h:44
static float delta_x
Definition: app.cpp:85
GLfloat GLfloat f
Definition: gl2ext.h:2707
#define GL_CHECK(x)
Definition: AstcTextures.h:59
struct vec2::@27::@30 c
GLsizei levels
Definition: gl2ext.h:1816
GLenum GLuint texture
Definition: gl2ext.h:385
static int imod(int x, int m)
Definition: Heightmap.cpp:94
vec2 compute_heightmap(int x, int y, int level)
[Compute heightmap]
Definition: Heightmap.cpp:197
#define GL_TEXTURE_2D_ARRAY
Definition: gl2ext.h:1612
GLenum GLuint GLintptr GLsizeiptr size
Definition: gl2ext.h:629
static float delta_y
Definition: app.cpp:86
void update_region(vec2 *buffer, unsigned int &pixel_offset, int x, int y, int width, int height, int start_x, int start_y, int level)
[Compute heightmap]
Definition: Heightmap.cpp:212
GLint GLint GLint GLint GLint x
Definition: gl2ext.h:574
float sample_heightmap(int x, int y)
Definition: Heightmap.cpp:186
GLenum GLuint GLint level
Definition: gl2ext.h:385
GLenum GLuint buffer
Definition: gl2ext.h:628
#define GL_MAP_WRITE_BIT
Definition: gl2ext.h:1050
#define LOGE(...)
Definition: AstcTextures.h:30
GLint GLsizei width
Definition: gl2ext.h:179
std::vector< LevelInfo > level_info
Definition: Heightmap.h:52
const GLfloat * m
Definition: gl2ext.h:2508
#define FILTER_CENTER
unsigned int heightmap_size
Definition: Heightmap.h:74
GLuint pixel_buffer[2]
Definition: Heightmap.h:40
GLint y
Definition: gl2ext.h:179
std::vector< float > heightmap
Definition: Heightmap.h:73
GLenum src
Definition: gl2ext.h:304
unsigned int pixel_buffer_index
Definition: Heightmap.h:41