Hello I'm having problem with accidental-noise-library

Started by
9 comments, last by Eternal 4 years, 10 months ago

Hello I am trying to create a procedurally generated world to my game.I have already figured out how to do it but the problem is that it's taking too much time. I am trying to generate a world size of 8400x2500 and It takes 70 minutes to finish the process. Any thoughts how to improve speed of generating this world? Here are some code that I wrote to generate my world and an image to show how does it look:

Map


#include <iostream>
#include "anl.h"

int main()
{
	anl::CMWC4096 rnd;
	rnd.setSeedTime();

	anl::CImplicitGradient ground_gradient;
	ground_gradient.setGradient(0, 0, -1, 1);

	std::cout << "Generating Lowlands..." << std::endl;

	//LOWLANDS
	anl::CImplicitFractal lowland_shape_fractal(anl::BILLOW, anl::GRADIENT, anl::QUINTIC);
	lowland_shape_fractal.setNumOctaves(2);
	lowland_shape_fractal.setFrequency(1);

	//lowland_shape_fractal.setSeed(rnd.get());

	anl::CImplicitAutoCorrect lowland_autocorrect(-1, 1);
	lowland_autocorrect.setSource(&lowland_shape_fractal);

	anl::CImplicitScaleOffset lowland_scale(0.125, -0.10);
	lowland_scale.setSource(&lowland_autocorrect);

	anl::CImplicitScaleDomain lowland_y_scale;
	lowland_y_scale.setSource(&lowland_scale);
	lowland_y_scale.setYScale(-1);

	anl::CImplicitTranslateDomain lowland_terrain;
	lowland_terrain.setSource(&ground_gradient);
	lowland_terrain.setYAxisSource(&lowland_y_scale);
	//LOWLANDS//

	std::cout << "Generating Highlands..." << std::endl;

	//HIGHLANDS
	anl::CImplicitFractal highland_shape_fractal(anl::FBM, anl::GRADIENT, anl::QUINTIC);
	highland_shape_fractal.setNumOctaves(4);
	highland_shape_fractal.setFrequency(2);

	//highland_shape_fractal.setSeed(rnd.get());

	anl::CImplicitAutoCorrect highland_autocorrect(-1, 1);
	highland_autocorrect.setSource(&highland_shape_fractal);

	anl::CImplicitScaleOffset highland_scale(0.25, 0.0);
	highland_scale.setSource(&highland_autocorrect);

	anl::CImplicitScaleDomain highland_y_scale;
	highland_y_scale.setSource(&highland_scale);
	highland_y_scale.setYScale(-1);

	anl::CImplicitTranslateDomain highland_terrain;
	highland_terrain.setSource(&ground_gradient);
	highland_terrain.setYAxisSource(&highland_y_scale);
	//HIGHLANDS//

	std::cout << "Generating Mountains..." << std::endl;

	//MOUNTAINS
	anl::CImplicitFractal mountain_shape_fractal(anl::RIDGEDMULTI, anl::GRADIENT, anl::QUINTIC);
	mountain_shape_fractal.setNumOctaves(8);
	mountain_shape_fractal.setFrequency(1);

	//mountain_shape_fractal.setSeed(rnd.get());

	anl::CImplicitAutoCorrect mountain_autocorrect(-1, 1);
	mountain_autocorrect.setSource(&mountain_shape_fractal);

	anl::CImplicitScaleOffset mountain_scale(0.53, 0.10);
	mountain_scale.setSource(&mountain_autocorrect);

	anl::CImplicitScaleDomain mountain_y_scale;
	mountain_y_scale.setSource(&mountain_scale);
	mountain_y_scale.setYScale(-0.5);

	anl::CImplicitTranslateDomain mountain_terrain;
	mountain_terrain.setSource(&ground_gradient);
	mountain_terrain.setYAxisSource(&mountain_y_scale);
	//MOUNTAINS//

	std::cout << "Mixing Terrains..." << std::endl;

	//TERRAIN
	anl::CImplicitFractal terrain_type_fractal(anl::FBM, anl::GRADIENT, anl::QUINTIC);
	terrain_type_fractal.setNumOctaves(3);
	terrain_type_fractal.setFrequency(0.2);

	//terrain_type_fractal.setSeed(rnd.get());

	anl::CImplicitAutoCorrect terrain_autocorrect(0, 1);
	terrain_autocorrect.setSource(&terrain_type_fractal);

	anl::CImplicitScaleDomain terrain_type_y_scale;
	terrain_type_y_scale.setSource(&terrain_autocorrect);
	terrain_type_y_scale.setYScale(-1);

	anl::CImplicitCache terrain_type_cache;
	terrain_type_cache.setSource(&terrain_type_y_scale);
	//TERRAIN//

	anl::CImplicitSelect highland_mountain_select;
	highland_mountain_select.setLowSource(&highland_terrain);
	highland_mountain_select.setHighSource(&mountain_terrain);
	highland_mountain_select.setControlSource(&terrain_type_cache);
	highland_mountain_select.setThreshold(0.55);
	highland_mountain_select.setFalloff(0.2);

	anl::CImplicitSelect highland_lowland_select;
	highland_lowland_select.setLowSource(&lowland_terrain);
	highland_lowland_select.setHighSource(&highland_mountain_select);
	highland_lowland_select.setControlSource(&terrain_type_cache);
	highland_lowland_select.setThreshold(0.25);
	highland_lowland_select.setFalloff(0.15);

	anl::CImplicitCache highland_lowland_select_cache;
	highland_lowland_select_cache.setSource(&highland_lowland_select);

	anl::CImplicitSelect ground_select;
	ground_select.setLowSource(0.0);
	ground_select.setHighSource(1);
	ground_select.setThreshold(0.5);
	ground_select.setFalloff(0.0);
	ground_select.setControlSource(&highland_lowland_select_cache);

	std::cout << "Generating Caves..." << std::endl;

	//CAVES
	//CAVES ORYGINAL
	anl::CImplicitFractal cave_shape(anl::RIDGEDMULTI, anl::GRADIENT, anl::QUINTIC);
	cave_shape.setNumOctaves(1);
	cave_shape.setFrequency(5);

	//cave_shape.setSeed(rnd.get());

	anl::CImplicitBias cave_attenuate_bias(0.91);
	cave_attenuate_bias.setSource(&highland_lowland_select_cache);

	anl::CImplicitCombiner cave_shape_attenuate(anl::MULT);
	cave_shape_attenuate.setSource(0, &cave_shape);
	cave_shape_attenuate.setSource(1, &cave_attenuate_bias);

	anl::CImplicitFractal cave_perturb_fractal(anl::FBM, anl::GRADIENT, anl::QUINTIC);
	cave_perturb_fractal.setNumOctaves(6);
	cave_perturb_fractal.setFrequency(5);

	//cave_perturb_fractal.setSeed(rnd.get());

	anl::CImplicitScaleOffset cave_perturb_scale(1.0, 0.0);
	cave_perturb_scale.setSource(&cave_perturb_fractal);

	anl::CImplicitTranslateDomain cave_perturb;
	cave_perturb.setSource(&cave_shape_attenuate);
	cave_perturb.setXAxisSource(&cave_perturb_scale);

	anl::CImplicitSelect cave_select;
	cave_select.setLowSource(1.0);
	cave_select.setHighSource(0.0);
	cave_select.setControlSource(&cave_perturb);
	cave_select.setThreshold(0.9);
	cave_select.setFalloff(0.0);
	
	//CAVES ORYGINAL//
	//CAVES MINE

	anl::CImplicitFractal cave_perturb_fractal_deep(anl::RIDGEDMULTI, anl::GRADIENT, anl::QUINTIC);
	cave_perturb_fractal_deep.setNumOctaves(1);
	cave_perturb_fractal_deep.setFrequency(100);

	//cave_perturb_fractal_deep.setSeed(rnd.get());

	anl::CImplicitSelect cave_select_deep;
	cave_select_deep.setLowSource(1.0);
	cave_select_deep.setHighSource(0.0);
	cave_select_deep.setControlSource(&cave_perturb_fractal_deep);
	cave_select_deep.setThreshold(0.98);
	cave_select_deep.setFalloff(0.0);

	anl::CImplicitScaleDomain scaledomain;
	scaledomain.setSource(&cave_select_deep);
	scaledomain.setYScale(0.0);

	/////->>>>>>>>>>>>>>>>>>>>

	anl::CImplicitFractal cave_perturb_fractal_deep1(anl::RIDGEDMULTI, anl::GRADIENT, anl::QUINTIC);
	cave_perturb_fractal_deep1.setNumOctaves(1);
	cave_perturb_fractal_deep1.setFrequency(50);

	//cave_perturb_fractal_deep1.setSeed(rnd.get());

	anl::CImplicitSelect cave_select_deep1;
	cave_select_deep1.setLowSource(0.0);
	cave_select_deep1.setHighSource(1.0);
	cave_select_deep1.setControlSource(&cave_perturb_fractal_deep1);
	cave_select_deep1.setThreshold(0.8);
	cave_select_deep1.setFalloff(0.0);

	anl::CImplicitScaleDomain scaledomain1;
	scaledomain1.setSource(&cave_select_deep1);
	scaledomain1.setYScale(0.0);

	/////<<<<<<<<<<<<<<<<<<<<<<<<<-
	////->>>>>>>>>>>>>>>>>>>>>>>>>>

	anl::CImplicitFractal cave_perturb_fractal_deep2(anl::RIDGEDMULTI, anl::GRADIENT, anl::QUINTIC);
	cave_perturb_fractal_deep2.setNumOctaves(1);
	cave_perturb_fractal_deep2.setFrequency(25);

	//cave_perturb_fractal_deep2.setSeed(rnd.get());

	anl::CImplicitSelect cave_select_deep2;
	cave_select_deep2.setLowSource(0.0);
	cave_select_deep2.setHighSource(1.0);
	cave_select_deep2.setControlSource(&cave_perturb_fractal_deep2);
	cave_select_deep2.setThreshold(0.85);
	cave_select_deep2.setFalloff(0.0);

	anl::CImplicitScaleDomain scaledomain2;
	scaledomain2.setSource(&cave_select_deep2);
	scaledomain2.setYScale(0.0);

	////<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<-
	////->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

	anl::CImplicitFractal cave_perturb_fractal_deep3(anl::RIDGEDMULTI, anl::GRADIENT, anl::QUINTIC);
	cave_perturb_fractal_deep3.setNumOctaves(1);
	cave_perturb_fractal_deep3.setFrequency(7);

	//cave_perturb_fractal_deep3.setSeed(rnd.get());

	anl::CImplicitSelect cave_select_deep3;
	cave_select_deep3.setLowSource(0.0);
	cave_select_deep3.setHighSource(1.0);
	cave_select_deep3.setControlSource(&cave_perturb_fractal_deep3);
	cave_select_deep3.setThreshold(0.85);
	cave_select_deep3.setFalloff(0.0);

	anl::CImplicitScaleDomain scaledomain3;
	scaledomain3.setSource(&cave_select_deep3);
	scaledomain3.setYScale(0.0);

	/////<<<<<<<<<<<<<<<<<<<<<<<<<<<-

	anl::CImplicitCombiner combiner0(anl::MAX);
	combiner0.setSource(0, &scaledomain1);
	combiner0.setSource(1, &scaledomain2);

	anl::CImplicitCombiner combiner1(anl::MAX);
	combiner1.setSource(0, &combiner0);
	combiner1.setSource(1, &scaledomain3);

	anl::CImplicitCombiner combiner2(anl::MAX);
	combiner2.setSource(0, &scaledomain);
	combiner2.setSource(1, &combiner1);


	anl::CImplicitFractal fractal(anl::FBM, anl::GRADIENT, anl::QUINTIC);
	fractal.setNumOctaves(6);
	fractal.setFrequency(0.25);

	//fractal.setSeed(rnd.get());

	anl::CImplicitScaleOffset scaleoff(0.5, 0.0);
	scaleoff.setSource(&fractal);


	anl::CImplicitTranslateDomain translate;
	translate.setSource(&combiner2);
	translate.setXAxisSource(&scaleoff);

	anl::CImplicitFractal fractal1(anl::FBM, anl::GRADIENT, anl::QUINTIC);
	fractal1.setNumOctaves(4);
	fractal1.setFrequency(1);

	//fractal1.setSeed(rnd.get());

	anl::CImplicitScaleOffset scaleoff1(0.5, 0.0);
	scaleoff1.setSource(&fractal1);

	anl::CImplicitTranslateDomain end;
	end.setSource(&translate);
	end.setXAxisSource(&scaleoff1);

	//CAVES MINE//

	anl::CImplicitCombiner all_caves_combine(anl::MIN);
	all_caves_combine.setSource(0, &cave_select);
	all_caves_combine.setSource(1, &end);

	anl::CImplicitCombiner ground_cave_multiply(anl::MULT);
	ground_cave_multiply.setSource(0, &all_caves_combine);
	ground_cave_multiply.setSource(1, &ground_select);
	//CAVES

	std::cout << "World Generated..." << std::endl;

	std::cout << "Adding to compose..." << std::endl;

	anl::CRGBACompositeChannels compose1(anl::RGB);
	compose1.setBlueSource(&ground_cave_multiply);
	compose1.setGreenSource(&ground_cave_multiply);
	compose1.setRedSource(&ground_cave_multiply);
	compose1.setAlphaSource(1.0);

	std::cout << "End compose-ing" << std::endl;
	
	anl::TArray2D<anl::SRGBA> img(8400  /1, 2400  /1);
	anl::SMappingRanges ranges;

	anl::mapRGBA2D(anl::SEAMLESS_NONE, img, compose1, ranges, 0);
	anl::saveRGBAArray((char*)"img/a.tga", &img);

 

Advertisement

Use an OpenCL or CUDA Kernel for stuff like this, you will be able to generate noise data waaay faster that way. 

Your code indicates you are using a very old version. The most recent version from github allows the option of multiple threads for faster generation. Still, @mattbick2003 isnt wrong. In this day and age, you probably want to use a library that generates on the GPU for far better parallelism. That's the main reason I dont really work on accidental anymore.

I have no idea how this particular library works, however doing noise generation on even the CPU should not be this slow. Personally I think the CPU gets a bad rap. I can do progressive world generation at game run-time with no problem and my computer is at least 7 or 8 years old.

However if you are running through your entire data set several times, that's going to be a cache disaster. You should try to do all your calculation in one area at one time before moving on.

1 hour ago, Gnollrunner said:

I have no idea how this particular library works, however doing noise generation on even the CPU should not be this slow. Personally I think the CPU gets a bad rap. I can do progressive world generation at game run-time with no problem and my computer is at least 7 or 8 years old.

However if you are running through your entire data set several times, that's going to be a cache disaster. You should try to do all your calculation in one area at one time before moving on.

There are quite a few problems with the posted code. For one, it uses an ancient version of the library that was a pointer indirection hellscape modeled after libnoise. That old version didn't do any caching or memoization, so calling a particular function multiple times recalculated the full thing every time. In the posted code, the entire thing is executed 3 times in order to populate the fields of a color output, so right there is a huge source of pain that could be eliminated. And that's without addressing any of the internal structure.

The most important thing is to use the latest. Seriously, if I can remember my sourceforge password or the password to the email account I used way back then, I will make that ancient version go away for good. Its terrible. A similar graph based on the latest version can populate a large image like that in seconds.

Ok thanks guys for your help, I'll try your suggestions and see if it works.

7 hours ago, QwePek said:

Ok thanks guys for your help, I'll try your suggestions and see if it works.

Here is a very quick example of that effect using the latest version:


#include <iostream>
#define ANL_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#define ANL_LONG_PERIOD_HASHING

#include "accidental-noise-library/anl.h"

using namespace std;
using namespace anl;

int main()
{
    CKernel k;

    auto ground_gradient=k.y();

    auto lowland_shape_fractal=k.simpleBillow(BASIS_GRADIENT, INTERP_QUINTIC, 8, 1.0, 12345);
    auto lowland_power=k.constant(0.005);
    auto lowlands_shear=k.scaleY(lowland_shape_fractal, k.zero());
    auto lowlands=k.multiply(lowlands_shear, lowland_power);

    auto highlands_power=k.constant(0.03);
    auto highlands_shear=k.scaleY(lowland_shape_fractal, k.constant(0.01));
    auto highlands_shape=k.multiply(highlands_shear, highlands_power);
    auto highlands_raise=k.constant(0.1);
    auto highlands=k.add(highlands_shape, highlands_raise);

    auto land_select_fractal=k.simplefBm(BASIS_GRADIENT, INTERP_QUINTIC, 4, 0.125, 456);
    auto land=k.select(lowlands, highlands, land_select_fractal, k.constant(0.1), k.constant(0.1));


    auto terrain_shape=k.translateY(ground_gradient, land);
    auto terrain_clip=k.select(k.zero(), k.one(), terrain_shape, k.point5(), k.zero());

    auto cave_shape=k.simpleRidgedMultifractal(BASIS_GRADIENT, INTERP_QUINTIC, 1, 5, 234);
    auto terrain_shape_clamp=k.clamp(terrain_shape, k.zero(), k.one());
    auto cave_attenuate_bias = k.bias(k.constant(0.81), terrain_shape_clamp);

    auto cave_perturb_fractal=k.simplefBm(BASIS_GRADIENT, INTERP_QUINTIC, 6.0, 5.0, 9234);
    auto cave_perturb_power=k.multiply(cave_perturb_fractal, k.constant(0.03));
    auto cave_perturb_x=k.translateX(cave_shape, cave_perturb_power);

    auto cave_shape_attenuate=k.multiply(cave_perturb_x, cave_attenuate_bias);

    auto cave_select=k.select(k.one(), k.zero(), cave_shape_attenuate, k.constant(0.5), k.zero());

    auto cave_cutout=k.multiply(cave_select, terrain_clip);


    CArray2Dd img(1048,256);
    map2D(SEAMLESS_NONE, img, k, SMappingRanges(0,8,0,1), 0, k.lastIndex());
    img.scaleToRange(0,1);
    saveDoubleArray("output.png", &img);
    return 0;
}

 

It's very fast (I made it while trying to pack for vacationi), and I think I messed up the cave attenuation as a result, but you can see that the latest version is also quite a bit more concise than the old one.It uses a flat array execution kernel, rather than a web of pointer indirection, and caching is performed automatically. It can also be multithreaded, and includes very long-period hashing in the noise generation for extreme noise domain size.

On an image of your size, it took 1..5 minutes to generate, which still isn't great. However, you can split up your world generation into regions if necessary.

On ‎5‎/‎31‎/‎2019 at 12:36 AM, JTippetts said:

Here is a very quick example of that effect using the latest version:



#include <iostream>
#define ANL_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#define ANL_LONG_PERIOD_HASHING

#include "accidental-noise-library/anl.h"

using namespace std;
using namespace anl;

int main()
{
    CKernel k;

    auto ground_gradient=k.y();

    auto lowland_shape_fractal=k.simpleBillow(BASIS_GRADIENT, INTERP_QUINTIC, 8, 1.0, 12345);
    auto lowland_power=k.constant(0.005);
    auto lowlands_shear=k.scaleY(lowland_shape_fractal, k.zero());
    auto lowlands=k.multiply(lowlands_shear, lowland_power);

    auto highlands_power=k.constant(0.03);
    auto highlands_shear=k.scaleY(lowland_shape_fractal, k.constant(0.01));
    auto highlands_shape=k.multiply(highlands_shear, highlands_power);
    auto highlands_raise=k.constant(0.1);
    auto highlands=k.add(highlands_shape, highlands_raise);

    auto land_select_fractal=k.simplefBm(BASIS_GRADIENT, INTERP_QUINTIC, 4, 0.125, 456);
    auto land=k.select(lowlands, highlands, land_select_fractal, k.constant(0.1), k.constant(0.1));


    auto terrain_shape=k.translateY(ground_gradient, land);
    auto terrain_clip=k.select(k.zero(), k.one(), terrain_shape, k.point5(), k.zero());

    auto cave_shape=k.simpleRidgedMultifractal(BASIS_GRADIENT, INTERP_QUINTIC, 1, 5, 234);
    auto terrain_shape_clamp=k.clamp(terrain_shape, k.zero(), k.one());
    auto cave_attenuate_bias = k.bias(k.constant(0.81), terrain_shape_clamp);

    auto cave_perturb_fractal=k.simplefBm(BASIS_GRADIENT, INTERP_QUINTIC, 6.0, 5.0, 9234);
    auto cave_perturb_power=k.multiply(cave_perturb_fractal, k.constant(0.03));
    auto cave_perturb_x=k.translateX(cave_shape, cave_perturb_power);

    auto cave_shape_attenuate=k.multiply(cave_perturb_x, cave_attenuate_bias);

    auto cave_select=k.select(k.one(), k.zero(), cave_shape_attenuate, k.constant(0.5), k.zero());

    auto cave_cutout=k.multiply(cave_select, terrain_clip);


    CArray2Dd img(1048,256);
    map2D(SEAMLESS_NONE, img, k, SMappingRanges(0,8,0,1), 0, k.lastIndex());
    img.scaleToRange(0,1);
    saveDoubleArray("output.png", &img);
    return 0;
}

 

It's very fast (I made it while trying to pack for vacationi), and I think I messed up the cave attenuation as a result, but you can see that the latest version is also quite a bit more concise than the old one.It uses a flat array execution kernel, rather than a web of pointer indirection, and caching is performed automatically. It can also be multithreaded, and includes very long-period hashing in the noise generation for extreme noise domain size.

On an image of your size, it took 1..5 minutes to generate, which still isn't great. However, you can split up your world generation into regions if necessary.

Thanks for your code! But do you have any idea why on my computer it takes me 5 minutes to generate an image size 1024x256 and image with my size takes >15 minutes to generate? I copied your entire code to my program and I didn't change anything.

Processor and number of cores, maybe. My result of 1.5 minutes was with 4 threads.

This kind of thing is quite calculation heavy, so the more cores you can throw at it the faster it will be, which is why GPU computation really shines.

Also make sure you're compiling in Release mode, with optimizations enabled.

This topic is closed to new replies.

Advertisement