Discontinued reflections with Lit/Metallic

Hey all, any idea where these discontinued reflections on a frame border object may come from?

attached image and video and snapshot of material - LWRP-Lit Metallic.
https://drive.google.com/open?id=1cjoIPItMAt6P4OAe-3QEZY9XngcGuXKH

There are no UV seams where this discontinued reflection occurs. Only directional light on, realtime lighting. Played with all settings but there is no change.

On lwrp 6.9.1, Unity 2019.2.2

Thanks a bunch for your thoughts, Sergio




Looks like specular aliasing.

1 Like

Thanks a bunch @Shane_Michael , can’t use MSAA cos i’m using AR Foundation LWRP, I guess I’ll tone down the spec unless anyone may know of other settings I can use? Thank you!

MSAA only helps edge aliasing so it doesn’t really help with specular. The easiest solution is to round your edges a bit more, make surfaces a bit rougher, add a roughness texture to break up the uniformity of the pattern, or bump up your render scale if you can afford the performance hit.

People have developed a few ways to deal with it that aren’t too expensive–basically reduce the glossiness when the normal variance is high. That method is for normal maps, for specular aliasing from the shape of your geometry, you could use the same sort of idea with a curvature map to selectively reduce the glossiness around sharp corners where you are going to get bad aliasing. You could use a texture (or pack it into the alpha channel of your normal texture), but baking it into the vertex data may work too if you want to save texture memory. Lots of programs can bake out curvature maps, and that would be easy enough to set up in shader graph.

1 Like

That’s very interesting and helpful @Shane_Michael , thanks a bunch !

Truly rad reading, unfortunately I could not find a Toksgvig gaussian filtering tool to convert maps like in Photoshop except for Photoshop’s Gaussian Blur which didn’t quite do the trick, do you know of any that would gaussian filter a map? I know too little on shaders to take the unpacking-packing approach to vertex data.

I ended up using the curvature map to select the aliased areas and reduce smoothness in those areas (using alpha channel metallic map) and worked pretty well. I hope I interpreted the approach correctly :slight_smile:

If it looks better, you are probably doing it correctly. The whole technique is more of a hack that selectively adds wrongness so if it’s looks OK, then it’s working. It might be nice to clamp to a threshold based on screen texel size for a given distance to further limit the “wrongness” of the technique.

There probably isn’t an easy way to calculate it in Photoshop, but it’s probably better to apply it in Unity because you probably want to calculate it for each mip level individually rather than calculating them from one top level. You could make an asset post-processor that automatically calculates one for each normal map, or make a editor window to generate them manually. Any kind of averaging filter will probably work, and then just output the length of the result.

1 Like

thanks for sharing @Shane_Michael , this next bit flies over my head hehehehe, too complex for me but if you have a link you favour to read about how the basics to get into building such asset post-processor it’d be really helpful you sent it over please, sounds so rad ! Either way, thanks a bunch again for your help ! :slight_smile:

Unity makes it pretty easy to set up asset pre- and post-processors. It’s nice if you want to automate using a technique like this. I wanted to play around with the Toksvig AA a bit so I made this postprocessor:

using UnityEngine;
using UnityEditor;

// Converts a normal map to a derivative normal map in the RG channels and the toksvig factor in B channel

public class ToksvigAA : AssetPostprocessor
{
    private const float toksvigFactorStrength = 10.0f; // Contrast of toksvig factor
    private static float[] gaussians = new float[] { 0.27901f, 0.44198f, 0.27901f};

    void OnPostprocessTexture(Texture2D texture)
    {    

        TextureImporter importer = (TextureImporter)TextureImporter.GetAtPath(assetPath);

        if (assetPath.ToLower().Contains("_toksvig"))
        {        
            if (importer.isReadable)
            {            
                for (int m = 0; m < texture.mipmapCount; m++)
                {                
                    int width = Mathf.Max(texture.width / Mathf.ClosestPowerOfTwo((int)Mathf.Pow(2, m)), 1);
                    int height = Mathf.Max(texture.height / Mathf.ClosestPowerOfTwo((int)Mathf.Pow(2, m)), 1);

                    Color[] c = texture.GetPixels(m);
                    Vector3[] n = new Vector3[c.Length];
                    // Pre-scale, bias, and normalize all normals
                    for(int i = 0; i < c.Length; i++) { n[i] = new Vector3(c[i].r * 2.0f - 1.0f, c[i].g * 2.0f - 1.0f, c[i].b * 2.0f - 1.0f).normalized; }
                
                    for (int x = 0; x < width; x++)
                    {
                        for (int y = 0; y < height; y++)
                        {
                            int i = 0;
                            Vector3 averageNormal = Vector3.zero;                                                    
                            for (int a = 0; a < gaussians.Length; a++)
                            {
                                for (int b = 0; b < gaussians.Length; b++)
                                {
                                    float weight = gaussians[a] * gaussians[b];
                                    i = Index(x + (a - 1), y + (b - 1), width, height);
                                    averageNormal += n[i] * weight;
                                }
                            }
                            float magnitude = Vector3.Magnitude(averageNormal);
                            float toksvigFactor = magnitude / Mathf.Lerp(toksvigFactorStrength, 1f, magnitude);
                            i = Index(x, y, width, height);
                            c[i] = new Color((n[i].x / n[i].z) * 0.5f + 0.5f, (n[i].y / n[i].z) * 0.5f + 0.5f, toksvigFactor);
                        }
                    }                
                    texture.SetPixels(c, m);
                }
            }
            else
            {
                Debug.LogError("ToksvigAA import cannot read texture. Make texture readable: " + assetPath);
            }
        }
    }

    // Convenience function to calculate wrapping linear index from 2d texture coordinates
    private int Index(int x, int y, int width, int height)
    {            
        int a = (x + width) % width;
        int b = (y + height) % height;    
        return (a + b * width);
    }
}

It will run on any texture that contains the string “_toksvig” in the file name. Kept it relatively simple though if you were importing lots of assets with it, there are ways it could probably be faster. It does a simple 3x3 gaussian filter on each mip level so it scales with the texel size better than if you calculated the top level and generated the mips automatically.

It stores the Toksvig factor in the blue channel, and a derivative normal map in the red/green channels so it can still be used all by itself as a normal map.

Using it would look something like

float3 normal = normalize(toksvig.rg * 2 + 1, 1);
glossiness *= toksvig.b;

either in a shader or in a shader graph.

I just did that to keep it to 3 color channels, but you could just as easily store the toksvig facor in the alpha channel and use RGB for a regular normal map.

1 Like

How awesome, keen to try it this week !!! Thanks a bunch @Shane_Michael :slight_smile: