I am currently taking a seamless noise texture and doing a set pixel with a color on an image texture based on the noise value to produce an image of a tilemap essentially and applying it to a MeshInstance3D Sphere. The whole process takes around 2.5 seconds to generate the texture and apply it. In order to save time i thought i could just set the pixels on the active instance of the material but all my attempts to get the image only return a copy of the material and not the actual material instance. What would be the best way to accomplish this?
Forgive me if I’m incorrect but isn’t that just updating the image itself and not the version that’s on the mesh. I can set it after updating but i cant find a way to use SetPixel on the image the mesh is currently using vs a copy of that image acquired via AlbedoTexture.GetImage()…
I only have a working implementation using the create then set method.
This is the Generator
using Godot;
public partial class WorldGenerator : TileMapLayer
{
[Export] public FastNoiseLite testNoise;
[Export] public CsgMesh3D globe;
public const int WORLD_HEIGHT = 2000;
public const int WORLD_WIDTH = 4000;
private Color colorDeepWater = new(Color.Color8(26,35,126,255));
private Color colorShallowWater = new(Color.Color8(41,182,246,255));
private Color colorSand = new(Color.Color8(220,231,117,255));
private Color colorDirt = new(Color.Color8(121,85,72,255));
private Color colorGrass = new(Color.Color8(66,189,65,255));
private Color colorMountain = new(Color.Color8(117,117,117,255));
[Export] public ImageTexture output;
[Export] public Image texture;
private Image tilemapTexture;
private Image tilemapKey;
public Texture2D GenerateWorld()
{
texture.Resize(4000,2000);
var RNG = new RandomNumberGenerator();
testNoise.Seed = RNG.RandiRange(0,10000000);
tilemapTexture = testNoise.GetSeamlessImage(4000,2000);
output.SetImage(texture);
tilemapKey = output.GetImage();
for (int x = 0; x < WORLD_WIDTH; x++)
{
for (int y = 0; y < WORLD_HEIGHT; y++)
{
var noiseValue = tilemapTexture.GetPixel(x,y).Luminance;
if (noiseValue < .5)
{
tilemapKey.SetPixel(x,y,colorDeepWater);
}
else if (noiseValue < .6)
{
tilemapKey.SetPixel(x,y,colorShallowWater);
}
else if (noiseValue < .62)
{
tilemapKey.SetPixel(x,y,colorSand);
}
else if (noiseValue < .68)
{
tilemapKey.SetPixel(x,y,colorDirt);
}
else if (noiseValue < .8)
{
tilemapKey.SetPixel(x,y,colorGrass);
}
else
{
tilemapKey.SetPixel(x,y,colorMountain);
}
}
}
output.SetImage(tilemapKey);
return output;
}
}
And this is the Mesh
using Godot;
public partial class Planet : CsgMesh3D
{
[Export] public WorldGenerator generator;
private StandardMaterial3D material = new();
public override void _Ready()
{
material.AlbedoTexture = generator.GenerateWorld();
Material = material;
}
Sorry, I’m not sure if I follow what you want to do. You will need to generate the Image first anyway so you’ll need to pay those 2.5 seconds to generate it anyway.
You’ll need to use a shader if you need to do it faster.
Ok that’s what I was looking for. Thats what I had arrived on as well. I lack the knowledge however to figure out how to implement that and I hadn’t found any shaders online that did that probably because its trivial, just not something I’m familiar with in the least.
I took to using textures as noise/gradient masks as noise was just too slow, I found I could take in one or two RGB packed noise textures, and use them with some combination math to create some nice looks.
I did them in a way that I had a macro noise, mid noise and detail noise, and used them in different scales and combination ways that I was able to get some good large scale variation.
Then depending on the world I am making, I can have the different ones load and sample different noise masks to get different looks.