xyz
as u can see
all non standard normal blending messes up the base normal map

  • xyz replied to this.

    DJMaesen For start, try disabling RGTC compression in import options for all normal maps. It's the Normal Map dropdown in Compress section. Set it to Disabled. Or disable the VRAM compression entirely. How does the blending look in that case?

      DJMaesen Here's the mix when compression is disabled. Looks fine to me.

      xyz cant find RGTC compression in import settings

      • xyz replied to this.

        DJMaesen cant find RGTC compression in import settings

        It doesn't say RGTC. It's the dropdown named "Normal Map". Select "Disabled" there.

          DJMaesen why would u check normal map disabled? i dont understand

          Just do it 😃. I'll explain it later if you get blending to work in satisfactory way.

          i got it working yay! there was a problem with my triplanar calculation in the shader.
          works fine now, i didnt had to set normal to disabled

          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 float roughness = 1.0;
          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(1.0);
          
          
          uniform float normal_scale = 1.0;
          varying vec3 triplanar_pos;
          varying vec3 power_normal;
          
          
          
          
          
          void vertex() {
          	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);
          	power_normal = pow(abs(NORMAL),vec3(1.0));
          	triplanar_pos = VERTEX;
          	power_normal /= dot(power_normal,vec3(1.0));
          	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 * power_normal.z;
          	samp += texture(p_sampler,p_triplanar_pos.xz).xyz * power_normal.y;
          	samp += texture(p_sampler,p_triplanar_pos.zy * vec2(-1.0,1.0)).xyz * power_normal.x;
          	return samp;
          }
          //Overlay
          vec3 overlay (vec3 target, vec3 blend){
              vec3 temp;
              temp.x = (target.x > 0.5) ? (1.0-(1.0-2.0*(target.x-0.5))*(1.0-blend.x)) : (2.0*target.x)*blend.x;
              temp.y = (target.y > 0.5) ? (1.0-(1.0-2.0*(target.y-0.5))*(1.0-blend.y)) : (2.0*target.y)*blend.y;
              temp.z = (target.z > 0.5) ? (1.0-(1.0-2.0*(target.z-0.5))*(1.0-blend.z)) : (2.0*target.z)*blend.z;
              return temp;
          }
          
          // Linear Blending
          vec3 NormalBlend_Linear(vec3 n1, vec3 n2)
          {
              // Unpack
          	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);
                 
          }
          
          
          
          
          void fragment() {
          	vec2 base_uv = UV;
          	vec4 albedo_tex = texture(texture_albedo,base_uv);
          	vec3 overlayertex = triplanar_texture(detail_overlay,triplanar_pos * detail_scale/100.0);
          	ALBEDO = overlay(albedo_tex.rgb,overlayertex.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 * roughness;
          	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 i didnt had to set normal to disabled

            You still want to do it though to make blending work properly and final result appear more crisp, especially if you use a better blending function than simple linear blend you initially had (the one I posted last). You also want to use a better blending function to blend triplanar normals. Using plain linear blend will again result in weakened texture appearance in areas where projections overlap. Do some test on gray spheres with only normal textures so you can better see what's happening.

            Note that "Normal map" option is in "Compression" section. So you're not disabling normal maps, you're disabling their RG compression. If compression is enabled then Godot will eliminate blue channel from the map immediately on import. When a shader reads/decodes such normal maps for blending, the z component will likely be the same for all normals, making the vectors mangled, specifically the normals will not have unit length and will point off the intended direction. It may look passable for some maps but you'll not be working with correct normals.

            The whole thing can still be made to work correctly even with RG compressed normals but in that case you'll need to calculate normal's Z component when decoding it from a texture, prior to blending.

            Bottom line, do some tests with gray spheres, compare compressed vs. uncompressed textures and try several blending functions and detail maps.

            You'll likely reuse this code in the future, possibly on less organic textures that are not as forgiving towards errors as rock textures, so do it properly to avoid future shader woes.

              xyz "The whole thing can still be made to work correctly even with RG compressed normals but in that case you'll need to calculate normal's Z component when decoding it from a texture, prior to blending."
              how?
              i get best results with linear normal blending.

              • xyz replied to this.

                DJMaesen Blending method and the z component are two separate problems. So if you haven't tried it with uncompressed normal maps you have no actual quality reference as all your blending function tests were done with a missing z component. You want to provide the proper normal vector for the shader, the one that's actually encoded in the original normal map, only then can you test and compare the nuances of various blending methods.

                Do as you wish. If it's good enough - fine. just have in mind that your current shader coupled with compressed normal maps is not performing correct calculations.

                  xyz yes they are not corect calculations. i have tried with uncompressed normals but no good results.
                  ive tried your blending technique and normals are worse than the simple linear blending.
                  i appreciate your help
                  but im a noob at shader coding u see.

                  all i want is a shader that can add detail color and detail normal maps in a triplanar way, so big objects dont look so pixelated when upclose.
                  thats what im trying to achieve

                  • xyz replied to this.

                    DJMaesen i have tried with uncompressed normals but no good results

                    Not sure what you mean by "no good results". The results should be the same at worst. Your triplanar blending code may be messing things up. I haven't checked it. That's why I suggested to first try detail blending without triplanar.

                    RG compressed normal maps, linear blend:

                    Without compression, UDN blend:

                      xyz definitly triplanar code messing up the normals,
                      i get the same results as your screenshot

                      also why would an engine discard the blue channel on import?

                        DJMaesen also why would an engine discard the blue channel on import?

                        From the docs:

                        When using a texture as normal map, only the red and green channels are required. Given regular texture compression algorithms produce artifacts that don't look that nice in normal maps, the RGTC compression format is the best fit for this data. Forcing this option to Enable will make Godot import the image as RGTC compressed. By default, it's set to Detect. This means that if the texture is ever detected to be used as a normal map, it will be changed to Enable and reimported automatically.

                        And especially relevant for the discussion here:

                        Note that RGTC compression affects the resulting normal map image. You will have to adjust custom shaders that use the normal map's blue channel to take this into account. Built-in material shaders already ignore the blue channel in a normal map (regardless of the actual normal map's contents).

                        https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_images.html#doc-importing-images-compress-mode

                        Why the blue channel can be discarded from normal maps? Because we operate under constraint that normals are always normalized, i.e. the normal vectors have length of exactly 1.0. This means that for each normal vector the following is true:
                        x*x + y*y + z*z = 1.0
                        From this follows that if we know x and y components of a normal vector, we can easily calculate the z component:
                        z = 1.0 - sqrt(x*x + y*y)
                        The whole thing is done to save texture storage at the cost of some additional computation.
                        In addition to dropping a channel, RG textures are additionally compressed using an algorithm that doesn't compromise the pixel data in the way the regular vram compression does.

                        for the love of god can someone post a working shader?
                        this is taking too much time allready

                        • xyz replied to this.

                          DJMaesen for the love of god can someone post a working shader?

                          Maybe ChatGPT can 🙂

                            DJMaesen the code comments says something along the lines of "normal B channel can not be trusted".
                            Normal maps generated from textures have the blue channel as pure white, while only baked normals have correct vectors.