Search Unity

  1. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Resolved Shader custom reflections

Discussion in 'Shaders' started by Perimetric, Jul 27, 2020.

  1. Perimetric

    Perimetric

    Joined:
    Jan 24, 2015
    Posts:
    15
    Hi,

    I'm generating custom reflections with Camera.RenderToCubemap() and setting it globally in shaders with Shader.SetGlobalTexture(). My question is can I use the samplerCUBE to make custom reflections behave like Standard Surface reflections in my shader or do I need to write a whole new shader model?

    Thanks
     
  2. Perimetric

    Perimetric

    Joined:
    Jan 24, 2015
    Posts:
    15
    Anyone?
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    9,261
  4. Perimetric

    Perimetric

    Joined:
    Jan 24, 2015
    Posts:
    15
    I'm not really familiar with shaders so,
    is this right ?
    Code (CSharp):
    1. Unity_GlossyEnvironmentData glossIn = UnityGlossyEnvironmentSetup(_Glossiness, IN.viewDir, o.Normal, half3(0, 0, 0));
    2. half3 env0 = Unity_GlossyEnvironment(UNITY_PASS_TEXCUBE(_RealtimeCubemap), _RealtimeCubemap_HDR, glossIn);
    I'm using env0 as emission color but it gives me no reflections, am I doing something wrong ?
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    9,261
    Presumably this is in a Surface Shader. If that's the case then no, this is not correct. The
    UnityGlossyEnvironmentSetup
    function is expecting the world space view direction and normal.
    IN.viewDir
    is in tangent space, if
    o.Normal
    is set in the
    surf
    function, and
    o.Normal
    is also in tangent space. You need to calculate the world space view direction using
    float3 worldPos;
    from the
    Input
    struct and
    _WorldSpaceCameraPos.xyz
    , and transform the
    o.Normal
    you've set to world space using
    WorldNormalVector
    . See the bottom of the Surface Shader documentation on how to use that function.
    https://docs.unity3d.com/Manual/SL-SurfaceShaders.html

    After that, it still might not work. You mentioned you're using
    SetGlobalTexture
    . But global shader properties only work if you don't define them as material properties. You didn't post the rest of your shader, so I don't know if you're doing that or not, but it's a common mistake (I do it frequently too).
     
  6. Perimetric

    Perimetric

    Joined:
    Jan 24, 2015
    Posts:
    15
    I just set _RealtimeCubemap_HDR as half instead of half4, sorry for that. Got this :

    customshader.png

    And with the standard shader + reflection probe :

    standardshader.png

    Looks like the reflections are just overlayed over the albedo.

    Here's the shader :

    Code (CSharp):
    1. Shader "Custom/CarPaint"
    2. {
    3.     Properties
    4.     {
    5.         _Color ("Color", Color) = (1,1,1,1)
    6.         _Glossiness("Smoothness", Range(0,1)) = 0.5
    7.         _Metallic("Metallic", Range(0,1)) = 0.0
    8.     }
    9.         SubShader
    10.         {
    11.             Tags { "RenderType" = "Opaque" }
    12.             LOD 200
    13.  
    14.             CGPROGRAM
    15.             #pragma surface surf Standard fullforwardshadows
    16.             #pragma target 3.0
    17.             UNITY_DECLARE_TEXCUBE(_RealtimeCubemap);
    18.  
    19.         sampler2D _MainTex;
    20.  
    21.         struct Input
    22.         {
    23.             float2 uv_MainTex;
    24.             float3 worldRefl;
    25.             float3 viewDir;
    26.         };
    27.  
    28.         half _Glossiness;
    29.         half _Metallic;
    30.         fixed4 _Color;
    31.         half4 _RealtimeCubemap_HDR;
    32.      
    33.         void surf (Input IN, inout SurfaceOutputStandard o)
    34.         {
    35.             fixed4 c = _Color;
    36.             o.Albedo = c.rgb;
    37.             o.Metallic = _Metallic;
    38.             o.Smoothness = _Glossiness;
    39.             o.Alpha = c.a;
    40.  
    41.             Unity_GlossyEnvironmentData glossIn = UnityGlossyEnvironmentSetup(_Glossiness, IN.viewDir, o.Normal, half3(0, 0, 0));
    42.             half3 env0 = Unity_GlossyEnvironment(UNITY_PASS_TEXCUBE(_RealtimeCubemap), _RealtimeCubemap_HDR, glossIn);
    43.  
    44.             o.Emission = env0;
    45.         }
    46.         ENDCG
    47.     }
    48.     FallBack "Diffuse"
    49. }
    50.  
     
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    9,261
  8. Perimetric

    Perimetric

    Joined:
    Jan 24, 2015
    Posts:
    15
    Ok looks really good except the standard shader is slightly brighter (left custom, right standard) :

    comparison.png
    Here's what I've done so far :

    Code (CSharp):
    1. void surf (Input IN, inout SurfaceOutputStandard o)
    2.         {
    3.             fixed4 c = _Color;
    4.             o.Albedo = c.rgb;
    5.             o.Metallic = _Metallic;
    6.             o.Smoothness = _Smoothness;
    7.             o.Alpha = c.a;
    8.  
    9.             half3 specColor;
    10.             half oneMinusReflectivity;
    11.             half3 diff = DiffuseAndSpecularFromMetallic(c.rgb, _Metallic, specColor, oneMinusReflectivity);
    12.            
    13.             Unity_GlossyEnvironmentData glossIn = UnityGlossyEnvironmentSetup(_Smoothness, IN.viewDir, o.Normal, half3(0, 0, 0));
    14.             half3 env0 = Unity_GlossyEnvironment(UNITY_PASS_TEXCUBE(_RealtimeCubemap), _RealtimeCubemap_HDR, glossIn);
    15.  
    16.             half surfaceReduction = 1.0 / (_Smoothness * _Smoothness + 1.0);
    17.             half grazingTerm = saturate(_Smoothness + (1 - oneMinusReflectivity));
    18.             half nv = abs(dot(o.Normal, IN.viewDir));
    19.  
    20.             o.Emission = env0 * surfaceReduction * FresnelLerp(specColor, grazingTerm, nv);
    21.         }
    Last problem, I use a 3d rendertexture to capture the scene but there not enough mips for smooth transition between different levels of smoothness and get this :

    gif_animation_001.gif
     
  9. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    9,261
    My guess is you're not rendering to a ARGBHalf cube map, and using the default (aka ARGB32). That's definitely going to end up a little darker. Unity supports HDR data encoded in an 8 bit per channel texture, but only for baked probes or imported HDR cube maps. Anything generated in real time has to either be a floating point format or just won't be as bright.

    Looks correct to me in terms of the number of mip maps. But you're not doing any convolution on the cube map you're generating. This is a whole "thing" that Unity does internally for reflection probes and imported cube maps (if you enable convolution). But there's no built in utilities for doing it to manually generated cube maps outside of a reflection probe. Plus Unity's real time convolution is no where near as nice as the baked / imported one. That's way too expensive to do in real time.

    The shader they use for real time convolution is this one:
    https://github.com/TwoTailsGames/Un...efaultResourcesExtra/Cubemaps/CubeBlur.shader

    It needs to be run on each face / mip level of the cubemap with different settings for each mip level. Specifically you need to set
    _Level
    for the current mip level you're rendering to, and
    _Texel
    which is 1 divided by that mip's resolution.
     
  10. Perimetric

    Perimetric

    Joined:
    Jan 24, 2015
    Posts:
    15
    Does the shader returns a 3d texture? How can different shaders have different convolutions over the same cubemap?
     
  11. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    9,261
    You should stop using the term “3D texture” in the context of a cube map. It is not a 3D texture, it is a collection of 6 2D textures. There is an actual 3D texture type that’s completely different.

    And GPUs don’t really ever render to anything but a 2D slice of any kind of texture. RenderToCubemap() renders 6 times to each face of a cubemap. Running any shader that also needs to update the faces of a cube map needs to run once per face too.

    So that shader is intended to run on and output a single blurred face of a cubemap, for a specific mip level. Though I’m not entirely sure the easiest way to do this since the built in Blit() functions don’t really work correctly with cube maps. It’s probably a custom setup that generates the appropriate UVW quad to render each face with, though it’s possible some secret combination of settings to a normal Blit() call will make it work, I don’t know if anyone has found it.

    And it’s not the same cubemap. The one you’re generating manually via RenderToCubemap() isn’t being blurred, and the one the ReflectionProbe component is rendering and then blurring behind the scenes before being passed to the shader on the scene. If it’s a real-time reflection probe, it uses the shader I linked to above to do that blur. If it’s a baked reflection probe it does a much more expensive and thorough blur that doesn’t appear to be exposed anywhere.
     
  12. Perimetric

    Perimetric

    Joined:
    Jan 24, 2015
    Posts:
    15
    Ok thanks for your help !
     
unityunity