Convert object normal map

Yeah i see. Just for the sake of conpleteness,

So im saying theoretically the shader could get the normal back to object space with the the world_Inverse or model matrix inverse ..(or was it the inverse transpose?)

vec4 obj_normal = world_inverse * vec4(NORMAL, 1.0);

then do the same for the tangent, and the binormal, then form the matrix…

Godot reverses the direction of the B, so (T,N,B) becomes (T,-B,N)

mat3 tbn = (mat3(TANGENT , -BINORMAL , NORMAL));

mat3 ob_tbn = (mat3(obj_tangent , -obj_binormal , obj_normal));

and then for each pixel,

vec4 norm_sample = sampler2D(normal_map, UV);

norm_sample =unpack(norm_sample);

So we then could take the unpacked object space vector which is a vector on a unit sphere and change basis to tangent space …the object space tangent space …

vec3 norm_sample_tan_space=obj_tbn * norm_sample.xyz;

And

vec4 norm_sample_world_tan = mat_world * norm_sample_tan_space;

To get it to the world transformed tangent space.

and unfortunately, after all that, it might (edit: probably wont even) work.

Edit 2: im probably changing basis wrong here, rusty memory, position should not be a factor, The idea is that you could put the object space normal into the tangent basis.

So something like sample_normal.dot( vertex_normal) in the vertex normal direction, and the same for the -B and T directions.

You can totally do that. It’s even simpler though, you can do everything in view space. In the fragment shader simply construct the TBN basis in view space (as T, B and N vectors will be in that space), then get the object space normal map sample, transform it to view space and project it to the basis, use the result as NORMAL_MAP.

You’ll get the same end result as if overriding NORMAL directly.

Here’s an example (for some reason the x coordinate from that normal map needed inverting):

shader_type spatial;
uniform sampler2D object_space_normal_map;

uniform int normal_mode = 0;
// 0 - vertex normal
// 1 - override normal directly from object space normal map
// 2 - ad hoc create normal map by projecting object space normal map to TBN basis in view space 

void fragment() {
	vec3 object_space_normal = texture(object_space_normal_map, UV).rgb * 2.0 - 1.0;
	object_space_normal.x = -object_space_normal.x;
	
	if (normal_mode == 0){
		// do nothing, just use the default normal coming in from the vertex function 
	}
	else if (normal_mode == 1){ //override normal directly from object space normal map
		NORMAL = mat3(MODEL_MATRIX * VIEW_MATRIX) * object_space_normal;
	}
	else if (normal_mode == 2){ // create normal map by projecting object space normal map to TBN basis in view space
		vec3 view_space_normal = mat3(MODEL_MATRIX * VIEW_MATRIX) * object_space_normal;
		mat3 TBN_basis = mat3(TANGENT, BINORMAL, NORMAL);
		vec3 tangent_space_normal = normalize(inverse(TBN_basis) * view_space_normal);
		NORMAL_MAP = tangent_space_normal * 0.5 + 0.5;
	}
}

vertex normal:

direct override of NORMAL by object space normal map:

creating NORMAL_MAP by projecting object space normal map onto TBN basis:

However, this still won’t help you when bone deformations come into play. NORMAL and consequently TANGENT and BINORMAL you get in the shader will have already been deformed by the skeleton and thus you cannot make the referential non deformed TBN basis to correctly perturb the normal from.

But you can reproduce the shader path (2) in Blender and simply bake the resulting tangent space normal. Haven’t tried it but it should work. I guess this would then be the actual answer to the original question. Either that or deformation of the normal via skeleton matrices as described earlier. Those are the options.

Its funny how they unpack from RGBA8 format, this link

Gives the formula (without any typecasts)

b = 255( (a+1)/2)

then to decode you would use

a = 2*(b /255) - 1

But the vector returned by sampler2D

is already a float in the range (0.0, 1.0).

Line 433 shows the calculation to unpack the normals in #if NORMAL_MAP_USED and also does not do that for the #if NORMAL_USED.

The OP probably forgot they started this thread. By now they might have even changed their hobby from gamedev to knitting :smiley:

I didn’t remember to read that shader until yesterday, its not very lightweight. That was just in the back of my mind then I realized I’d done the calculation and assigned the result to NORMAL_MAP instead of NORMAL.

Don’t see what’s the actual relevance of this here. As already stated, the solution is to re-create the shader I posted earlier in Blender, bake it in a non-deformed pose and use the texture as a regular tangent space normal map in Godot. That’s all there is to it.

I forgot the forum rules about relevance. I always say its relevant if it occurs in a discussion.

Just a little update:

I used the shader @normalized made. Then I put the magnemite scene into my main XR scene and so far it looks fine during its idle animation. When I play its action animation it looks a little wonky but I’ll take what I can get

You should try baking the tangent space normal map in Blender.

Yeah I could, but then there would be several hundred other models I would potentially deal with.

Btw, this is what I ended up working on :smiley:

Once you have the pipeline, it doesn’t matter how many models there are. You can script Blender to batch process them all in one go.