im trying to figure out how to add triplanar detail without messing up the main normal map
however with no luck
even the standard shader doesnt give the correct results

shader_type spatial;
render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx;

uniform sampler2D texture_albedo : source_color,filter_linear_mipmap,repeat_enable;
uniform sampler2D texture_roughness : hint_roughness_gray,filter_linear_mipmap,repeat_enable;
uniform sampler2D texture_normal : hint_roughness_normal,filter_linear_mipmap,repeat_enable;

uniform sampler2D detail_normal : hint_roughness_normal,filter_linear_mipmap,repeat_enable;
uniform sampler2D detail_overlay : source_color,filter_linear_mipmap,repeat_enable;
uniform vec3 detail_scale = vec3(0.05);

uniform vec3 uv1_scale = vec3(1.0);
uniform float normal_scale = 1.0;
varying vec3 triplanar_pos;






void vertex() {
	UV=UV*uv1_scale.xy;
	TANGENT = vec3(0.0,0.0,-1.0) * abs(NORMAL.x);
	TANGENT += vec3(1.0,0.0,0.0) * abs(NORMAL.y);
	TANGENT += vec3(1.0,0.0,0.0) * abs(NORMAL.z);
	TANGENT = normalize(TANGENT);
	BINORMAL = vec3(0.0,1.0,0.0) * abs(NORMAL.x);
	BINORMAL += vec3(0.0,0.0,-1.0) * abs(NORMAL.y);
	BINORMAL += vec3(0.0,1.0,0.0) * abs(NORMAL.z);
	BINORMAL = normalize(BINORMAL);
	
	triplanar_pos = VERTEX;
	
	triplanar_pos *= vec3(1.0,-1.0, 1.0);
}
vec3 triplanar_texture(sampler2D p_sampler, vec3 p_triplanar_pos) {
	vec3 samp = vec3(0.0);
	samp += texture(p_sampler,p_triplanar_pos.xy).xyz ;
	samp += texture(p_sampler,p_triplanar_pos.xz).xyz ;
	samp += texture(p_sampler,p_triplanar_pos.zy * vec2(-1.0,1.0)).xyz ;
	return samp;
}


// Linear Blending
vec3 NormalBlend_Linear(vec3 n1, vec3 n2)
{
	n1 = n1*2.0 - 1.0;
    n2 = n2*2.0 - 1.0;
    
   return normalize(n1 + n2);    
}
vec3 CombineNormal(vec3 n1, vec3 n2)
{
 	return NormalBlend_Linear(n1, n2);
       
}

vec3 overlay(vec3 n1, vec3 n2)
{  
    return (vec3(n1.xy * n2.z + n2.xy  * n1.z, n1.z * n2.z));
}


void fragment() {
	vec2 base_uv = UV;
	vec4 albedo_tex = texture(texture_albedo,base_uv);
	vec3 overlayertex = triplanar_texture(detail_overlay,triplanar_pos * detail_scale);
	ALBEDO = overlay(albedo_tex.rgb,overlayertex.rgb);
	ALBEDO = albedo_tex.rgb;
	
	METALLIC = 0.0;
	vec4 roughness_texture_channel = vec4(0.333333,0.333333,0.333333,0.0);
	float roughness_tex = dot(texture(texture_roughness,base_uv),roughness_texture_channel);
	
	ROUGHNESS = roughness_tex;
	SPECULAR = 0.5;
	
	vec3 detail_norm_tex = triplanar_texture(detail_normal,triplanar_pos * detail_scale/100.0);
	detail_norm_tex *= 2.0;
	NORMAL_MAP = CombineNormal(texture(texture_normal,base_uv).rgb,detail_norm_tex.rgb );
	
	NORMAL_MAP_DEPTH = normal_scale;
	
}
  • xyz replied to this.

    DJMaesen Encode the normal components back to 0-1 range after blending because that's what NORMAL_MAP is expecting to get. Currently the NormalBlend_Linear() function decodes them before blending, but I don't see the result being encoded back.

      DJMaesen NormalBlend_Linear() decodes its arguments by multiplying by 2.0 and subtracting 1.0. Do the inverse of that to the return value: add 1.0 and divide by 2.0:

      return .5*(normalize(n1+n2) + 1.0);

      Or as cool kids do it:

      return fma(normalize(n1+n2), .5, .5);

      the base normal gets discarded that way somehow

      • xyz replied to this.

        xyz weird thing is when i use my old code on a vertex color terrainshader
        the blending is just fine. so i dont think the detail normal is too strong
        however trying to do the same thing on a uv mapped mesh doesnt seem to work

        • xyz replied to this.

          DJMaesen The problem is that you're mixing two normal maps in 50-50 ratio. The detail map is much smoother so it will scale down the other one by 50%.

          Best to use weighted blend, give smaller weight to the detail map and increase NORMAL_MAP_DEPTH until it all looks as desired. Here's weighted blending function:

          vec3 NormalBlend_Linear(vec3 n1, vec3 n2, float bias){
          	n1 = n1*2.0 - 1.0;
          	n2 = n2*2.0 - 1.0;
          	return fma(normalize(mix(n1, n2, bias)), vec3(.5), vec3(.5));    
          }

          With bias .5 it should behave like your old function, mixing two normals in a half-half ratio. Smaller bias values will put greater weight on n1, while larger values (up to 1.0) will weight the n2 more.

            xyz is there a way to combine 2 normal maps without the mix 50/50? so both normals get applied like u would apply 1?

            • xyz replied to this.

              DJMaesen There are various ways you can blend normals. None of them is perfect. The one you used is the simplest. A good one but still simple is:

              vec3 n = normalize(vec3(n1.xy + n2.xy, n1.z));

              See how that works. Don't forget to decode/encode before/after doing this as this works with proper normals, not with values read from normal maps. So:

              n1 = fma(n1, vec3(2.0), vec3(-1.0));
              n2 = fma(n2, vec3(2.0), vec3(-1.0));
              vec3 n = normalize(vec3(n1.xy + n2.xy, n1.z));
              n = fma(n, vec3(.5), vec3(.5));

                xyz
                i tried it
                sadly ....
                no good results, i give up for now, im waisting time

                • xyz replied to this.

                  DJMaesen Maybe your normal maps should be adjusted for better results. Can you replicate the look you want in Blender?
                  Here is a breakdown of various methods with some example shader code. Maybe you can find something adequate. At least try Reorient Blending, the last one presented. It's the most complex one and probably most performance heavy (not by much though) but it's supposed to give best results for detail maps.

                    xyz there was a discussion in github about this topic. Godot only uses the R and G channels of normal map and calculates the B channel. so maybe that's why it doesn't work?
                    currently godot uses a simple lerp to combine normal maps values. It gives good enough results for me.

                    • xyz replied to this.

                      Jesusemora there was a discussion in github about this topic. Godot only uses the R and G channels of normal map and calculates the B channel. so maybe that's why it doesn't work?

                      This shouldn't matter if maps are properly encoded (either DX or OGL style). However if maps are made specifically for Godot and are lacking the blue channel then this may indeed be the cause of poor results. It's easily fixed though by adding a line of code that calculates the third component when decoding.

                      Jesusemora currently godot uses a simple lerp to combine normal maps values. It gives good enough results for me.

                      It kinda depends on the detail frequency in the maps. Simple linear mixing may work well for some combinations. The problem is more pronounced when combining a low frequency base map with a high frequency detail map.

                      xyz i tried every technique allready from this example>
                      https://www.shadertoy.com/view/4t2SzR
                      strange thing is that when u use a standard material shader and ad a normal map to a model(UV1) and then a detail normal (UV2 with triplanar mapping on) in the shader,it also looks bad.
                      so i think @Jesusemora might be right

                      • xyz replied to this.

                        i would really like to get this to work to texture large objects in the 3d world

                        DJMaesen it also looks bad

                        What are you comparing it against?

                        It should be doable in general, regardless of how Godot interprets normal maps.

                          xyz wait a minute ill take some screenshots with the 3 options available:
                          1 no detail normal on triplanar(standard), 2 the shader im trying to get to work(customshader) , 3 standard shader with detail normal on triplanar(standardwithdetail)