/* generate_env_map3.c - Don Yang (uguu.org) 09/26/09 */ #include #include #include #include #define TEXTURE_SIZE 512 #define TEXTURE_GRADIENT_SCALE 1.0 #define CLOUD_DIVISION_COUNT 10 #define CLOUD_SIZE (1 << CLOUD_DIVISION_COUNT) #define CLOUD_SPIKE_FACTOR 3 #define CLOUD_SPIKE_STEP (CLOUD_SIZE / (1 << CLOUD_SPIKE_FACTOR)) #define CLOUD_SPIKE_COUNT (4 << CLOUD_SPIKE_FACTOR) #define CLOUD_INIT_RANGE 0.2 #define CLOUD_INIT_OFFSET 0.1 #define CLOUD_ROUGHNESS 0.9 #define RandomNumber() (((double)rand()) / (double)RAND_MAX) /* Tile image for cloud texture */ static double EnvCloud[CLOUD_SIZE + 1][CLOUD_SIZE + 1]; /* Environment map texture image */ static unsigned char TextureImage[TEXTURE_SIZE][TEXTURE_SIZE][3]; /* Update pixel in cloud image */ static void SetCloudPixel(int x, int y, int lock_edges, double value) { if( lock_edges == 0 || (x != 0 && x != CLOUD_SIZE && y != 0 && y != CLOUD_SIZE) ) { EnvCloud[y][x] = value; } } /* Subdivide & interpolate cloud image recursively */ static void SubDivideCloud(int step, double roughness, int lock_edges) { int x, y, cx, cy, nx, ny; for(; step > 1; step /= 2) { for(y = 0; y < CLOUD_SIZE; y = ny) { cy = y + step / 2; ny = y + step; for(x = 0; x < CLOUD_SIZE; x = nx) { cx = x + step / 2; nx = x + step; /* Add center point */ SetCloudPixel(cx, cy, lock_edges, (EnvCloud[y][x] + EnvCloud[y][nx] + EnvCloud[ny][x] + EnvCloud[ny][nx]) / 4.0); /* Add edge points */ SetCloudPixel(cx, y, lock_edges, (EnvCloud[y][x] + EnvCloud[y][nx] + EnvCloud[cy][cx]) / 3.0); SetCloudPixel(x, cy, lock_edges, (EnvCloud[y][x] + EnvCloud[cy][cx] + EnvCloud[ny][x]) / 3.0); SetCloudPixel(nx, cy, lock_edges, (EnvCloud[y][nx] + EnvCloud[cy][cx] + EnvCloud[ny][nx]) / 3.0); SetCloudPixel(cx, ny, lock_edges, (EnvCloud[cy][cx] + EnvCloud[ny][x] + EnvCloud[ny][nx]) / 3.0); /* Display newly added points */ SetCloudPixel(cx, y, lock_edges, EnvCloud[y][cx] + (RandomNumber() - 0.5) * roughness); SetCloudPixel(x, cy, lock_edges, EnvCloud[cy][x] + (RandomNumber() - 0.5) * roughness); SetCloudPixel(cx, cy, lock_edges, EnvCloud[cy][cx] + (RandomNumber() - 0.5) * roughness); SetCloudPixel(nx, cy, lock_edges, EnvCloud[cy][nx] + (RandomNumber() - 0.5) * roughness); SetCloudPixel(cx, ny, lock_edges, EnvCloud[ny][cx] + (RandomNumber() - 0.5) * roughness); } } roughness *= 0.5; } } /* Generate cloud tile image */ static void GenerateCloudImage(void) { int i, x, y; /* First pass: generate base image */ for(y = 0; y <= CLOUD_SIZE; y += CLOUD_SPIKE_STEP) { for(x = 0; x <= CLOUD_SIZE; x += CLOUD_SPIKE_STEP) EnvCloud[y][x] = 0.0; } for(i = 0; i < CLOUD_SPIKE_COUNT; i++) { x = (int)(RandomNumber() * CLOUD_SIZE / CLOUD_SPIKE_STEP) * CLOUD_SPIKE_STEP; y = (int)(RandomNumber() * CLOUD_SIZE / CLOUD_SPIKE_STEP) * CLOUD_SPIKE_STEP; EnvCloud[y][x] = CLOUD_INIT_OFFSET + CLOUD_INIT_RANGE * RandomNumber(); } SubDivideCloud(CLOUD_SPIKE_STEP, CLOUD_ROUGHNESS / (1 << CLOUD_SPIKE_FACTOR), 0); /* Second pass: tile image */ for(i = 0; i <= CLOUD_SIZE; i++) { EnvCloud[CLOUD_SIZE][i] = EnvCloud[0][i]; EnvCloud[i][CLOUD_SIZE] = EnvCloud[i][0]; } SubDivideCloud(CLOUD_SIZE, CLOUD_ROUGHNESS, 1); /* Third pass: scale image */ for(y = 0; y < CLOUD_SIZE; y++) { for(x = 0; x < CLOUD_SIZE; x++) EnvCloud[y][x] *= 255.0; } } /* Convert value from one range to another */ static double ConvertRange(double input0, double input1, double output0, double output1, double x) { return ((output1 - output0) * (x - input0) / (input1 - input0)) + output0; } /* Convert value from one range to another, keeping only the remainder for out of bound values. */ static int ModulusRange(double input0, double input1, int output, double x) { int sx = (int)(output * (x - input0) / (input1 - input0)); if( sx < 0 ) return output - ((-sx) % output); return sx % output; } /* Set pixel from a gradient of 2 colors */ static void SetGradient(const double *color0, const double *color1, double value, unsigned char *pixels) { int i; for(i = 0; i < 3; i++) { pixels[i] = (unsigned char)ConvertRange(0.0, 1.0, color0[i], color1[i], value); } } /* Set pixel color based on gradient position in [-1, 1] */ static void SetTexturePixel(double y, unsigned char *pixels) { static const double top[3] = {107.0, 192.0, 229.0}; static const double middle[3] = {240.0, 250.0, 250.0}; static const double bottom[3] = {0.0, 128.0, 64.0}; static const double t1 = 0.40; static const double t2 = 0.39; int i; if( y > t1 ) { SetGradient(middle, top, ConvertRange(t1, 1.0, 0.0, 1.0, y), pixels); } else if( y > t2 ) { for(i = 0; i < 3; i++) pixels[i] = (unsigned char)middle[i]; } else { SetGradient(bottom, middle, ConvertRange(-1.0, t2, 0.0, 1.0, y), pixels); } } /* Generate gradient based texture image, then modulate top with cloud image */ static void GenerateTextureMap(void) { double dx, dy, r2, ly, tx, ty, p; int x, y, i, m; for(y = 0; y < TEXTURE_SIZE; y++) { dy = ConvertRange(0.0, (double)(TEXTURE_SIZE-1), 1.0, -1.0, (double)y); for(x = 0; x < TEXTURE_SIZE; x++) { dx = ConvertRange(0.0, (double)(TEXTURE_SIZE-1), -1.0, 1.0, (double)x); r2 = dx * dx + dy * dy; ly = TEXTURE_GRADIENT_SCALE * sqrt((1 - dx * dx) / (1 + TEXTURE_GRADIENT_SCALE * TEXTURE_GRADIENT_SCALE)); if( dy >= ly ) { /* Sky */ SetTexturePixel(1.0, TextureImage[y][x]); } else if( dy <= -ly ) { /* Ground */ SetTexturePixel(-1.0, TextureImage[y][x]); } else { /* Bits in between */ if( r2 < 1.0 ) { ty = dy / (TEXTURE_GRADIENT_SCALE * sqrt(1 - r2)); if( ty >= -1.0 && ty <= 1.0 ) SetTexturePixel(ty, TextureImage[y][x]); } } /* Modulate in cloud texture if we are on the sphere */ if( r2 < 1.0 ) { tx = dx / sqrt(1 - r2); ty = dy / sqrt(1 - r2); p = EnvCloud[ModulusRange(-1.0, 1.0, CLOUD_SIZE, tx)] [ModulusRange(-1.0, 1.0, CLOUD_SIZE, ty)]; if( p > 0.0 ) { for(i = 0; i < 3; i++) { m = (int)TextureImage[y][x][i] + (int)p; if( m > 255 ) TextureImage[y][x][i] = (unsigned char)255; else TextureImage[y][x][i] = (unsigned char)m; } } } } } } /* Write image to stdout */ static void WriteImage(void) { int x, y; printf("P3\n%d %d\n255\n", TEXTURE_SIZE, TEXTURE_SIZE); for(y = 0; y < TEXTURE_SIZE; y++) { for(x = 0; x < TEXTURE_SIZE; x++) { printf("%d %d %d\n", (int)TextureImage[y][x][0], (int)TextureImage[y][x][1], (int)TextureImage[y][x][2]); } } } int main(void) { srand((unsigned)time(NULL)); GenerateCloudImage(); GenerateTextureMap(); WriteImage(); return 0; }