001/******************************************************************************* 002 * This software is provided as a supplement to the authors' textbooks on digital 003 * image processing published by Springer-Verlag in various languages and editions. 004 * Permission to use and distribute this software is granted under the BSD 2-Clause 005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause). 006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved. 007 * Visit https://imagingbook.com for additional details. 008 ******************************************************************************/ 009 010package imagingbook.common.noise.perlin; 011 012import imagingbook.common.noise.hashing.HashFunction; 013 014/** 015 * <p> 016 * This class implements a 2D Perlin noise [1] generator. See Ch. 8 of [2] for details. 017 * </p> 018 * <p> 019 * [1] K. Perlin. Improving noise. In "SIGGRAPH’02: Proceedings of the 29th Annual Conference on Computer Graphics and 020 * Interactive Techniques", pp. 681–682, San Antonio, Texas (2002).<br> [2] W. Burger and M.J. Burge. "Principles of 021 * Digital Image Processing - Advanced Methods" (Vol. 3). Undergraduate Topics in Computer Science. Springer-Verlag, 022 * London (2013). 023 * </p> 024 * 025 * @author WB 026 * @version 2022/11/24 027 */ 028public class PerlinNoiseGenerator2d extends PerlinNoiseGenerator { 029 030 /** 031 * Constructor. 032 * @param f_min minimum frequency 033 * @param f_max maximum frequency 034 * @param persistence persistence 035 * @param hf hash function 036 */ 037 public PerlinNoiseGenerator2d(double f_min, double f_max, double persistence, HashFunction hf) { 038 super(f_min, f_max, persistence, hf); 039 } 040 041 /** 042 * 2D combined (multi-frequency) Perlin noise function. Returns the value of the combined Perlin noise function for 043 * the two-dimensional position (x,y). 044 * 045 * @param x interpolation position x 046 * @param y interpolation position y 047 * @return the noise value for position (x,y) 048 */ 049 public double getNoiseValue(double x, double y) { 050 double sum = 0; 051 for (int i = 0; i < F.length; i++) { 052 sum = sum + A[i] * noise(F[i] * x, F[i] * y); 053 } 054 return sum; 055 } 056 057 /** 058 * 2D elementary (single-frequency) Perlin noise function. 059 * @param x Interpolation position x. 060 * @param y Interpolation position y. 061 * @return The value of the elementary Perlin 062 * noise function for the two-dimensional position (x,y). 063 */ 064 private double noise(double x, double y) { 065 int px = ffloor(x); 066 int py = ffloor(y); 067 double[] g00 = gradient(px, py); 068 double[] g10 = gradient(px + 1, py); 069 double[] g01 = gradient(px, py + 1); 070 double[] g11 = gradient(px + 1, py + 1); 071 double x01 = x - px; // x01 is in [0,1] 072 double y01 = y - py; // y01 is in [0,1] 073 double w00 = g00[0] * (x01) + g00[1] * (y01); 074 double w10 = g10[0] * (x01 - 1) + g10[1] * (y01); 075 double w01 = g01[0] * (x01) + g01[1] * (y01 - 1); 076 double w11 = g11[0] * (x01 - 1) + g11[1] * (y01 - 1); 077 return interpolate(x01, y01, w00, w10, w01, w11); 078 } 079 080 /** 081 * @param px discrete horiz. position 082 * @param py discrete vert. position 083 * @return A pseudo-random gradient vector for the discrete position (px,py). 084 */ 085 private double[] gradient(int px, int py) { 086 double[] g = hashFun.hash(px, py); // hash() always returns a new double[] in [0,1] 087 g[0] = 2.0 * g[0] - 1; 088 g[1] = 2.0 * g[1] - 1; 089 return g; 090 } 091 092 /** 093 * Local interpolation function. 094 * @param x01 Horizontal interpolation position in [0,1] 095 * @param y01 Vertical interpolation position in [0,1] 096 * @param w00 Tangent value for position (0,0). 097 * @param w01 Tangent value for position (1,0). 098 * @param w10 Tangent value for position (0,1). 099 * @param w11 Tangent value for position (1,1). 100 * @return The interpolated noise value at position (x01,y01). 101 */ 102 private double interpolate(double x01, double y01, double w00, double w10, double w01, double w11) { 103 double sx = this.s(x01); 104 double w0 = (1 - sx) * w00 + sx * w10; 105 double w1 = (1 - sx) * w01 + sx * w11; 106 double sy = this.s(y01); 107 double w = (1 - sy) * w0 + sy * w1; 108 return w; 109 } 110 111}