code: package org.sunflow.core.modifiers;
  
  import org.sunflow.SunflowAPI;
  import org.sunflow.core.Modifier;
  import org.sunflow.core.ParameterList;
  import org.sunflow.core.ShadingState;
  import org.sunflow.math.OrthoNormalBasis;
  import org.sunflow.math.PerlinScalar;
  import org.sunflow.math.Point3;
  import org.sunflow.math.Vector3;
  
  public class PerlinModifier implements Modifier {
      private int function = 0;
      private float scale = 50;
      private float size = 1;
  
      public boolean update(ParameterList pl, SunflowAPI api) {
          function = pl.getInt("function", function);
          size = pl.getFloat("size", size);
          scale = pl.getFloat("scale", scale);
          return true;
      }
  
      public void modify(ShadingState state) {
          Point3 p = state.transformWorldToObject(state.getPoint());
          p.x *= size;
          p.y *= size;
          p.z *= size;
          Vector3 normal = state.transformNormalWorldToObject(state.getNormal());
          double f0 = f(p.x, p.y, p.z);
          double fx = f(p.x + .0001, p.y, p.z);
          double fy = f(p.x, p.y + .0001, p.z);
          double fz = f(p.x, p.y, p.z + .0001);
  
          normal.x -= scale * (fx - f0) / .0001;
          normal.y -= scale * (fy - f0) / .0001;
          normal.z -= scale * (fz - f0) / .0001;
          normal.normalize();
  
          state.getNormal().set(state.transformNormalObjectToWorld(normal));
          state.getNormal().normalize();
          state.setBasis(OrthoNormalBasis.makeFromW(state.getNormal()));
      }
  
      double f(double x, double y, double z) {
          switch (function) {
              case 0:
                  return .03 * noise(x, y, z, 8);
              case 1:
                  return .01 * stripes(x + 2 * turbulence(x, y, z, 1), 1.6);
              default:
                  return -.10 * turbulence(x, y, z, 1);
          }
      }
  
      private static final double stripes(double x, double f) {
          double t = .5 + .5 * Math.sin(f * 2 * Math.PI * x);
          return t * t - .5;
      }
  
      private static final double turbulence(double x, double y, double z, double freq) {
          double t = -.5;
          for (; freq <= 300 / 12; freq *= 2)
              t += Math.abs(noise(x, y, z, freq) / freq);
          return t;
      }
  
      private static final double noise(double x, double y, double z, double freq) {
          double x1, y1, z1;
          x1 = .707 * x - .707 * z;
          z1 = .707 * x + .707 * z;
          y1 = .707 * x1 + .707 * y;
          x1 = .707 * x1 - .707 * y;
          return PerlinScalar.snoise((float) (freq * x1 + 100), (float) (freq * y1), (float) (freq * z1));
      }
  }