001/* 002Copyright 2006 Jerry Huxtable 003 004Licensed under the Apache License, Version 2.0 (the "License"); 005you may not use this file except in compliance with the License. 006You may obtain a copy of the License at 007 008 http://www.apache.org/licenses/LICENSE-2.0 009 010Unless required by applicable law or agreed to in writing, software 011distributed under the License is distributed on an "AS IS" BASIS, 012WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013See the License for the specific language governing permissions and 014limitations under the License. 015*/ 016 017package com.jhlabs.image; 018 019import java.awt.*; 020import java.awt.image.*; 021import java.util.*; 022import com.jhlabs.math.*; 023 024public class SkyFilter extends PointFilter { 025 026 private float scale = 0.1f; 027 private float stretch = 1.0f; 028 private float angle = 0.0f; 029 private float amount = 1.0f; 030 private float H = 1.0f; 031 private float octaves = 8.0f; 032 private float lacunarity = 2.0f; 033 private float gain = 1.0f; 034 private float bias = 0.6f; 035 private int operation; 036 private float min; 037 private float max; 038 private boolean ridged; 039 private FBM fBm; 040 protected Random random = new Random(); 041 private Function2D basis; 042 043 private float cloudCover = 0.5f; 044 private float cloudSharpness = 0.5f; 045 private float time = 0.3f; 046 private float glow = 0.5f; 047 private float glowFalloff = 0.5f; 048 private float haziness = 0.96f; 049 private float t = 0.0f; 050 private float sunRadius = 10f; 051 private int sunColor = 0xffffffff; 052 private float sunR, sunG, sunB; 053 private float sunAzimuth = 0.5f; 054 private float sunElevation = 0.5f; 055 private float windSpeed = 0.0f; 056 057 private float cameraAzimuth = 0.0f; 058 private float cameraElevation = 0.0f; 059 private float fov = 1.0f; 060 061 private float[] exponents; 062 private float[] tan; 063 private BufferedImage skyColors; 064 private int[] skyPixels; 065 066 private final static float r255 = 1.0f/255.0f; 067 068 private float width, height; 069 070 public SkyFilter() { 071 if ( skyColors == null ) { 072 skyColors = ImageUtils.createImage( Toolkit.getDefaultToolkit().getImage( getClass().getResource("SkyColors.png") ).getSource() ); 073 } 074 } 075 076 public void setAmount(float amount) { 077 this.amount = amount; 078 } 079 080 public float getAmount() { 081 return amount; 082 } 083 084 public void setOperation(int operation) { 085 this.operation = operation; 086 } 087 088 public int getOperation() { 089 return operation; 090 } 091 092 public void setScale(float scale) { 093 this.scale = scale; 094 } 095 096 public float getScale() { 097 return scale; 098 } 099 100 public void setStretch(float stretch) { 101 this.stretch = stretch; 102 } 103 104 public float getStretch() { 105 return stretch; 106 } 107 108 public void setT(float t) { 109 this.t = t; 110 } 111 112 public float getT() { 113 return t; 114 } 115 116 public void setFOV(float fov) { 117 this.fov = fov; 118 } 119 120 public float getFOV() { 121 return fov; 122 } 123 124 public void setCloudCover(float cloudCover) { 125 this.cloudCover = cloudCover; 126 } 127 128 public float getCloudCover() { 129 return cloudCover; 130 } 131 132 public void setCloudSharpness(float cloudSharpness) { 133 this.cloudSharpness = cloudSharpness; 134 } 135 136 public float getCloudSharpness() { 137 return cloudSharpness; 138 } 139 140 public void setTime(float time) { 141 this.time = time; 142 } 143 144 public float getTime() { 145 return time; 146 } 147 148 public void setGlow(float glow) { 149 this.glow = glow; 150 } 151 152 public float getGlow() { 153 return glow; 154 } 155 156 public void setGlowFalloff(float glowFalloff) { 157 this.glowFalloff = glowFalloff; 158 } 159 160 public float getGlowFalloff() { 161 return glowFalloff; 162 } 163 164 public void setAngle(float angle) { 165 this.angle = angle; 166 } 167 168 public float getAngle() { 169 return angle; 170 } 171 172 public void setOctaves(float octaves) { 173 this.octaves = octaves; 174 } 175 176 public float getOctaves() { 177 return octaves; 178 } 179 180 public void setH(float H) { 181 this.H = H; 182 } 183 184 public float getH() { 185 return H; 186 } 187 188 public void setLacunarity(float lacunarity) { 189 this.lacunarity = lacunarity; 190 } 191 192 public float getLacunarity() { 193 return lacunarity; 194 } 195 196 public void setGain(float gain) { 197 this.gain = gain; 198 } 199 200 public float getGain() { 201 return gain; 202 } 203 204 public void setBias(float bias) { 205 this.bias = bias; 206 } 207 208 public float getBias() { 209 return bias; 210 } 211 212 public void setHaziness(float haziness) { 213 this.haziness = haziness; 214 } 215 216 public float getHaziness() { 217 return haziness; 218 } 219 220 public void setSunElevation(float sunElevation) { 221 this.sunElevation = sunElevation; 222 } 223 224 public float getSunElevation() { 225 return sunElevation; 226 } 227 228 public void setSunAzimuth(float sunAzimuth) { 229 this.sunAzimuth = sunAzimuth; 230 } 231 232 public float getSunAzimuth() { 233 return sunAzimuth; 234 } 235 236 public void setSunColor(int sunColor) { 237 this.sunColor = sunColor; 238 } 239 240 public int getSunColor() { 241 return sunColor; 242 } 243 244 public void setCameraElevation(float cameraElevation) { 245 this.cameraElevation = cameraElevation; 246 } 247 248 public float getCameraElevation() { 249 return cameraElevation; 250 } 251 252 public void setCameraAzimuth(float cameraAzimuth) { 253 this.cameraAzimuth = cameraAzimuth; 254 } 255 256 public float getCameraAzimuth() { 257 return cameraAzimuth; 258 } 259 260 public void setWindSpeed(float windSpeed) { 261 this.windSpeed = windSpeed; 262 } 263 264 public float getWindSpeed() { 265 return windSpeed; 266 } 267 268float mn, mx; 269 public BufferedImage filter( BufferedImage src, BufferedImage dst ) { 270long start = System.currentTimeMillis(); 271 sunR = (float)((sunColor >> 16) & 0xff) * r255; 272 sunG = (float)((sunColor >> 8) & 0xff) * r255; 273 sunB = (float)(sunColor & 0xff) * r255; 274 275mn = 10000; 276mx = -10000; 277 exponents = new float[(int)octaves+1]; 278 float frequency = 1.0f; 279 for (int i = 0; i <= (int)octaves; i++) { 280 exponents[i] = (float)Math.pow(2, -i); 281 frequency *= lacunarity; 282 } 283 284 min = -1; 285 max = 1; 286 287//min = -1.2f; 288//max = 1.2f; 289 290 width = src.getWidth(); 291 height = src.getHeight(); 292 293 int h = src.getHeight(); 294 tan = new float[h]; 295 for (int i = 0; i < h; i++) 296 tan[i] = (float)Math.tan( fov * (float)i/h * Math.PI * 0.5 ); 297 298 if ( dst == null ) 299 dst = createCompatibleDestImage( src, null ); 300 int t = (int)(63*time); 301// skyPixels = getRGB( skyColors, t, 0, 1, 64, skyPixels ); 302 Graphics2D g = dst.createGraphics(); 303 g.drawImage( skyColors, 0, 0, dst.getWidth(), dst.getHeight(), t, 0, t+1, 64, null ); 304 g.dispose(); 305 BufferedImage clouds = super.filter( dst, dst ); 306// g.drawRenderedImage( clouds, null ); 307// g.dispose(); 308long finish = System.currentTimeMillis(); 309System.out.println(mn+" "+mx+" "+(finish-start)*0.001f); 310 exponents = null; 311 tan = null; 312 return dst; 313 } 314 315 public float evaluate(float x, float y) { 316 float value = 0.0f; 317 float remainder; 318 int i; 319 320 // to prevent "cascading" effects 321 x += 371; 322 y += 529; 323 324 for (i = 0; i < (int)octaves; i++) { 325 value += Noise.noise3(x, y, t) * exponents[i]; 326 x *= lacunarity; 327 y *= lacunarity; 328 } 329 330 remainder = octaves - (int)octaves; 331 if (remainder != 0) 332 value += remainder * Noise.noise3(x, y, t) * exponents[i]; 333 334 return value; 335 } 336 337 public int filterRGB(int x, int y, int rgb) { 338 339// Curvature 340float fx = (float)x / width; 341//y += 20*Math.sin( fx*Math.PI*0.5 ); 342 float fy = y / height; 343 float haze = (float)Math.pow( haziness, 100*fy*fy ); 344// int argb = skyPixels[(int)fy]; 345 float r = (float)((rgb >> 16) & 0xff) * r255; 346 float g = (float)((rgb >> 8) & 0xff) * r255; 347 float b = (float)(rgb & 0xff) * r255; 348 349 float cx = width*0.5f; 350 float nx = x-cx; 351 float ny = y; 352// FOV 353//ny = (float)Math.tan( fov * fy * Math.PI * 0.5 ); 354ny = tan[y]; 355nx = (fx-0.5f) * (1+ny); 356ny += t*windSpeed;// Wind towards the camera 357 358// float xscale = scale/(1+y*bias*0.1f); 359 nx /= scale; 360 ny /= scale * stretch; 361 float f = evaluate(nx, ny); 362float fg = f;//FIXME-bump map 363 // Normalize to 0..1 364// f = (f-min)/(max-min); 365 366 f = (f+1.23f)/2.46f; 367 368// f *= amount; 369 int a = rgb & 0xff000000; 370 int v; 371 372 // Work out cloud cover 373 float c = f - cloudCover; 374 if (c < 0) 375 c = 0; 376 377 float cloudAlpha = 1 - (float)Math.pow(cloudSharpness, c); 378//cloudAlpha *= amount; 379//if ( cloudAlpha > 1 ) 380// cloudAlpha = 1; 381mn = Math.min(mn, cloudAlpha); 382mx = Math.max(mx, cloudAlpha); 383 384 // Sun glow 385 float centreX = width*sunAzimuth; 386 float centreY = height*sunElevation; 387 float dx = x-centreX; 388 float dy = y-centreY; 389 float distance2 = dx*dx+dy*dy; 390// float sun = 0; 391 //distance2 = (float)Math.sqrt(distance2); 392distance2 = (float)Math.pow(distance2, glowFalloff); 393 float sun = /*amount**/10*(float)Math.exp(-distance2*glow*0.1f); 394// sun = glow*10*(float)Math.exp(-distance2); 395 396 // Sun glow onto sky 397 r += sun * sunR; 398 g += sun * sunG; 399 b += sun * sunB; 400 401 402// float cloudColor = cloudAlpha *sun; 403// Bump map 404/* 405 float nnx = x-cx; 406 float nny = y-1; 407 nnx /= xscale; 408 nny /= xscale * stretch; 409 float gf = evaluate(nnx, nny); 410 float gradient = fg-gf; 411if (y == 100)System.out.println(fg+" "+gf+gradient); 412 cloudColor += amount * gradient; 413*/ 414// ... 415/* 416 r += (cloudColor-r) * cloudAlpha; 417 g += (cloudColor-g) * cloudAlpha; 418 b += (cloudColor-b) * cloudAlpha; 419*/ 420 // Clouds get darker as they get thicker 421 float ca = (1-cloudAlpha*cloudAlpha*cloudAlpha*cloudAlpha) /** (1 + sun)*/ * amount; 422 float cloudR = sunR * ca; 423 float cloudG = sunG * ca; 424 float cloudB = sunB * ca; 425 426 // Apply the haziness as we move further away 427 cloudAlpha *= haze; 428 429 // Composite the clouds on the sky 430 float iCloudAlpha = (1-cloudAlpha); 431 r = iCloudAlpha*r + cloudAlpha*cloudR; 432 g = iCloudAlpha*g + cloudAlpha*cloudG; 433 b = iCloudAlpha*b + cloudAlpha*cloudB; 434 435 // Exposure 436 float exposure = gain; 437 r = 1 - (float)Math.exp(-r * exposure); 438 g = 1 - (float)Math.exp(-g * exposure); 439 b = 1 - (float)Math.exp(-b * exposure); 440 441 int ir = (int)(255*r) << 16; 442 int ig = (int)(255*g) << 8; 443 int ib = (int)(255*b); 444 v = 0xff000000|ir|ig|ib; 445 return v; 446 } 447 448 public String toString() { 449 return "Texture/Sky..."; 450 } 451 452}