-
Bug
-
Resolution: Fixed
-
21w10a
-
None
-
Plausible
-
Performance
-
Normal
Currently, the noise blending algorithm in the overworld samples the main noise to linearly interpolate the min and max limit noises to create the terrain for the overworld. However, due to the number of octaves used in the main noise, the noise is regularly below 0 or above 1. due to this, the clamped lerp renders either the min or max limit noise completely unused. By only calculating the limit noises when they can affect the output (i.e., when the main noise is between 0 and 1), we can eliminate 16 octaves of calculation in the majority of instances. When the main noise is below 0, only the min limit noise is calculated, and when it's above 1, just the max limit noise is calculated. Here's the code that fixes it and makes it more performant:
public double sampleAndClampNoise(int x, int y, int z, double horizontalScale, double verticalScale, double horizontalStretch, double verticalStretch) { double mainNoiseSum = 0.0; double amplitude = 1.0; // First calculate the main noise to select which limit noise to use for (int i = 0; i < 8; i++) { ImprovedNoise mainNoise = this.mainNoise.getOctaveNoise(i); if (mainNoise != null) { mainNoiseSum += mainNoise.noise(PerlinNoise.wrap(x * horizontalStretch * amplitude), PerlinNoise.wrap(y * verticalStretch * amplitude), PerlinNoise.wrap(z * horizontalStretch * amplitude), verticalStretch * amplitude, y * verticalStretch * amplitude) / amplitude; } amplitude /= 2.0; } // Scale the main noise for use in interpolation double noiseLerp = (mainNoiseSum / 10.0 + 1.0) / 2.0; // Reset amplitude for calculation amplitude = 1.0; // If the interpolation is below 0, calculate and use the min limit noise if (noiseLerp <= 0) { double minNoiseSum = 0.0; for (int i = 0; i < 16; i++) { double scaledX = PerlinNoise.wrap(x * horizontalScale * amplitude); double scaledY = PerlinNoise.wrap(y * verticalScale * amplitude); double scaledZ = PerlinNoise.wrap(z * horizontalScale * amplitude); double scaledAmplitude = verticalScale * amplitude; ImprovedNoise minNoise = this.minLimitNoise.getOctaveNoise(i); if (minNoise != null) { minNoiseSum += minNoise.noise(scaledX, scaledY, scaledZ, scaledAmplitude, y * scaledAmplitude) / amplitude; } amplitude /= 2.0; } // Return the scaled min noise return minNoiseSum / 512.0; } else if (noiseLerp >= 1) { // If the interpolation is above 1, calculate and use the max limit noise double maxNoiseSum = 0.0; for (int i = 0; i < 16; i++) { double scaledX = PerlinNoise.wrap(x * horizontalScale * amplitude); double scaledY = PerlinNoise.wrap(y * verticalScale * amplitude); double scaledZ = PerlinNoise.wrap(z * horizontalScale * amplitude); double scaledAmplitude = verticalScale * amplitude; ImprovedNoise maxNoise = this.maxLimitNoise.getOctaveNoise(i); if (maxNoise != null) { maxNoiseSum += maxNoise.noise(scaledX, scaledY, scaledZ, scaledAmplitude, y * scaledAmplitude) / amplitude; } amplitude /= 2.0; } // Return the scaled max noise return maxNoiseSum / 512.0; } else { // If the interpolation is between 0 and 1, calculate both noises and blend it double minNoiseSum = 0.0; double maxNoiseSum = 0.0; for (int i = 0; i < 16; i++) { double scaledX = PerlinNoise.wrap(x * horizontalScale * amplitude); double scaledY = PerlinNoise.wrap(y * verticalScale * amplitude); double scaledZ = PerlinNoise.wrap(z * horizontalScale * amplitude); double scaledAmplitude = verticalScale * amplitude; ImprovedNoise minNoise = this.minLimitNoise.getOctaveNoise(i); if (minNoise != null) { minNoiseSum += minNoise.noise(scaledX, scaledY, scaledZ, scaledAmplitude, y * scaledAmplitude) / amplitude; } ImprovedNoise maxNoise = this.maxLimitNoise.getOctaveNoise(i); if (maxNoise != null) { maxNoiseSum += maxNoise.noise(scaledX, scaledY, scaledZ, scaledAmplitude, y * scaledAmplitude) / amplitude; } amplitude /= 2.0; } // Return the blended noise return Mth.lerp(noiseLerp, minNoiseSum / 512.0, maxNoiseSum / 512.0); } }
This change will retain the overworld's exact shape as it only changes how the interpolation works internally. This code is licensed under CC0. On a test where I generated chunks and measured with a profiler, these changes cut the amount of time this method took by half, a significant improvement as noise calculation is one of the most intensive tasks while generating chunks.