Replicating shader behavior

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Thummper

Ok so I have the following shader code:

shader_type spatial;

uniform sampler2D noise;
uniform float noiseCoordsScale;
uniform sampler2D subnoise;
uniform sampler2D grass;
uniform sampler2D grassAlt;
uniform sampler2D dirt;
uniform float maxHeight;



const float epsilon   = 1e-4;
const float minHeight = 0.0;
varying vec3  world_pos;
varying vec3  normal;



void vertex(){
	// In vertex this position is local i think (not affected by camera)
	world_pos = VERTEX;
	normal = abs(NORMAL);
	normal /= normal.x + normal.y + normal.z;
}

float inverseLerp(float a, float b, float value){
	float val = (value - a) / (b - a);
	return clamp(val, 0, 1);
}

vec3 triplanar(vec3 position, float scale, vec3 blendAxes, sampler2D text ){
	vec3 scaledPos = position / scale;
	vec3 xProj = texture(text, vec2(scaledPos.y, scaledPos.z)).rgb * blendAxes.x;
	vec3 yProj = texture(text, vec2(scaledPos.x, scaledPos.z)).rgb * blendAxes.y;
	vec3 zProj = texture(text, vec2(scaledPos.x, scaledPos.y)).rgb * blendAxes.z;
	return xProj + yProj + zProj;
}



void fragment() {
	vec3 scaledPos = world_pos / noiseCoordsScale;
	
	float noiseValue = texture(noise, vec2(scaledPos.x, scaledPos.z)).x;
	float noisePercent  = inverseLerp(0, 1, noiseValue); 
	//float noiseValue = triplanar(world_pos * noiseCoordsScale, 1800f, normal, noise).x;
	
	float grassNoiseValue = triplanar(world_pos * .6f, 500f, normal, noise).x;
	
	
	
	float blend = 0.05;
	float grassBlend = 0.65;
	

	float grassStrength = inverseLerp(-blend/2.0 - epsilon, blend/2.0, noisePercent - 0.0);
	float grassPercent  = inverseLerp(-grassBlend/2.0 - epsilon, grassBlend/2.0, grassNoiseValue - 0.5);
	
	
	vec3 grassTex =  (grassPercent * triplanar(world_pos, 30f, normal, grass)) + ((1.0 - grassPercent) * triplanar(world_pos, 50f, normal, grassAlt));
	float dirtStrength  = inverseLerp(-blend/2.0 - epsilon, blend/2.0,  noisePercent - 0.60);
	
	
	//ALBEDO = ALBEDO * (1.0 - grassAStrength - grassBStrength) + (triplanar(world_pos, 25f, normal, grass)  * grassAStrength) + (triplanar(world_pos, 25f, normal, grassAlt)  * grassBStrength);
	ALBEDO = ALBEDO * (1.0 - grassStrength) + (grassTex * grassStrength);
	ALBEDO = ALBEDO * (1.0 - dirtStrength)  +  (triplanar(world_pos, 75f, normal, dirt)  * dirtStrength);
	
	
	
	
	
	}

This shader is used to texture some procedural terrain. Right now, there are 2 possible biomes decided by noiseValue in the fragment section.

I’m now working on adding trees to my landscape - I only want my tress to spawn in one of the possible biomes.

This is my tree location picker:

func generateTrees(gridPoints, biomeNoise, coordScale):
	var biomeImage = biomeNoise.get_data()
	var fails = 0

	for i in numberTrees:
		var model = pickTree()
		var positionGood = false
		var position
		while positionGood == false:
			# Pick a point from grid, if not used it is ok
			position = gridPoints[randi() % gridPoints.size()]
			var scaledPos = position / coordScale
			# Check the biome of this position
			# Get pixel takes ints, with the coord scale our values are very low 
			biomeImage.lock()
			print(scaledPos)
			var imageValue = invLerp(0, 1, biomeImage.get_pixel(scaledPos.x, scaledPos.z).r)
			biomeImage.unlock()
			
			var noiseValue = biomeNoise.noise.get_noise_2d(scaledPos.x, scaledPos.z) 
			
			print("VAL: ", noiseValue)
			if !treePositions.has(position):
				treePositions.push_back(position)
				positionGood = true
			else:
				fails += 1
			if fails == 500: 
				print("Tree gen failed")
				return

		# We have a position
		position.y -= 5
		model.translation = position
		
		var rotation = rand_range(0, 2 * PI)
		model.rotate_y(rotation)
		treeInstances.push_back(model)
		add_child(model)

In this function I need to get the same noise value that the shader gets in order to set the correct biome - but both imageValue and noiseValue are incorrect.

Both the shader and this function use the same NoiseTexture resource, the same scalar is also applied to both sets of coordinates. The scalar is usually a large number (900+) as this seems to get me better biome distribution.

The get_pixels function only works on ints, so when the scalar is large it always returns the same value as all coords are less than 1, while the texture function the the shader seems to work on the same small numbers but gives different results.

Any ideas how to get the same noise value inside and outside of my shader?

:bust_in_silhouette: Reply From: Thummper

Seems like I need to interpolate the scaled values and make sure the coordinates start from 0,0 as I am sampling from an image.

This code seems to give me the desired results:

func interpolate(minimum, maximum, value):
	return minimum + (maximum - minimum) * value

func generateTrees(gridPoints, biomeNoise, noiseZoom, minx, minz):

	var biomeImage = biomeNoise.get_data()

	var fails = 0

	for i in numberTrees:
		var model = pickTree()
		var positionGood = false
		var position
		while positionGood == false:
			# Pick a point from grid, if not used it is ok
			position = gridPoints[randi() % gridPoints.size()]
			
			var interX = int(interpolate(0, biomeImage.get_width(), (position.x + (- minx)) / noiseZoom ))
			var interZ = int(interpolate(0, biomeImage.get_height(), (position.z + (- minz)) / noiseZoom))
			
			# Check the biome of this position
			# Get pixel takes ints, with the coord scale our values are very low 
			biomeImage.lock()
			var imageValue = invLerp(0, 1, biomeImage.get_pixel(interX, interZ).r)
			biomeImage.unlock()
			
			print("VAL: ", imageValue)
			
			
			
			
			if !treePositions.has(position) && imageValue > 0.5:
				treePositions.push_back(position)
				positionGood = true
			else:
				fails += 1
			if fails == 500: 
				print("Tree gen failed")
				return

		# We have a position
		position.y -= 5
		model.translation = position
		
		var rotation = rand_range(0, 2 * PI)
		model.rotate_y(rotation)
		treeInstances.push_back(model)
		add_child(model)