Search code examples
unity-game-enginematerial-designvirtual-reality

Unity - glossy blur material?


Ok Im trying to do a standard blur material like this (the darkened beat saber menu)

enter image description here

Or enter image description here

But on a 3D object, so not a camera effect or canvas material. I found some assets that provide a low quality blur but I need it glossy, and a nice Gaussian blur. The one I have has weird streaks:

enter image description here

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,)' with 'UnityObjectToClipPos()'

Shader "Custom/WaterBlur" {
    Properties {
    _blurSizeXY("BlurSizeXY", Range(0,10)) = 0
}
    SubShader {

        // Draw ourselves after all opaque geometry
        Tags { "Queue" = "Transparent" }

        // Grab the screen behind the object into _GrabTexture
        GrabPass { }

        // Render the object with the texture generated above
        Pass {


CGPROGRAM
#pragma debug
#pragma vertex vert
#pragma fragment frag 
#ifndef SHADER_API_D3D11

    #pragma target 3.0

#else

    #pragma target 4.0

#endif

            sampler2D _GrabTexture : register(s0);
            float _blurSizeXY;

struct data {

    float4 vertex : POSITION;

    float3 normal : NORMAL;

};



struct v2f {

    float4 position : POSITION;

    float4 screenPos : TEXCOORD0;

};



v2f vert(data i){

    v2f o;

    o.position = UnityObjectToClipPos(i.vertex);

    o.screenPos = o.position;

    return o;

}



half4 frag( v2f i ) : COLOR

{

    float2 screenPos = i.screenPos.xy / i.screenPos.w;
    float depth= _blurSizeXY*0.0005;

    screenPos.x = (screenPos.x + 1) * 0.5;

    screenPos.y = 1-(screenPos.y + 1) * 0.5;

    half4 sum = half4(0.0h,0.0h,0.0h,0.0h);   
    sum += tex2D( _GrabTexture, float2(screenPos.x-5.0 * depth, screenPos.y+5.0 * depth)) * 0.025;    
    sum += tex2D( _GrabTexture, float2(screenPos.x+5.0 * depth, screenPos.y-5.0 * depth)) * 0.025;

    sum += tex2D( _GrabTexture, float2(screenPos.x-4.0 * depth, screenPos.y+4.0 * depth)) * 0.05;
    sum += tex2D( _GrabTexture, float2(screenPos.x+4.0 * depth, screenPos.y-4.0 * depth)) * 0.05;


    sum += tex2D( _GrabTexture, float2(screenPos.x-3.0 * depth, screenPos.y+3.0 * depth)) * 0.09;
    sum += tex2D( _GrabTexture, float2(screenPos.x+3.0 * depth, screenPos.y-3.0 * depth)) * 0.09;

    sum += tex2D( _GrabTexture, float2(screenPos.x-2.0 * depth, screenPos.y+2.0 * depth)) * 0.12;
    sum += tex2D( _GrabTexture, float2(screenPos.x+2.0 * depth, screenPos.y-2.0 * depth)) * 0.12;

    sum += tex2D( _GrabTexture, float2(screenPos.x-1.0 * depth, screenPos.y+1.0 * depth)) *  0.15;
    sum += tex2D( _GrabTexture, float2(screenPos.x+1.0 * depth, screenPos.y-1.0 * depth)) *  0.15;



    sum += tex2D( _GrabTexture, screenPos-5.0 * depth) * 0.025;    
    sum += tex2D( _GrabTexture, screenPos-4.0 * depth) * 0.05;
    sum += tex2D( _GrabTexture, screenPos-3.0 * depth) * 0.09;
    sum += tex2D( _GrabTexture, screenPos-2.0 * depth) * 0.12;
    sum += tex2D( _GrabTexture, screenPos-1.0 * depth) * 0.15;    
    sum += tex2D( _GrabTexture, screenPos) * 0.16; 
    sum += tex2D( _GrabTexture, screenPos+5.0 * depth) * 0.15;
    sum += tex2D( _GrabTexture, screenPos+4.0 * depth) * 0.12;
    sum += tex2D( _GrabTexture, screenPos+3.0 * depth) * 0.09;
    sum += tex2D( _GrabTexture, screenPos+2.0 * depth) * 0.05;
    sum += tex2D( _GrabTexture, screenPos+1.0 * depth) * 0.025;

    return sum/2;

}
ENDCG
        }
    }

Fallback Off
} 

How can I accomplish a glossy, or even just a highqaulity gaussian blur material for a mesh?


Solution

  • If you want to include a gloss onto your surface, here is one way to do it, based on the shader in the Glossy Textures article in the Unity Community wiki copied below:

    Shader "Cg per-pixel lighting with texture" {
       Properties {
          _MainTex ("RGBA Texture For Material Color", 2D) = "white" {} 
          _Color ("Diffuse Material Color", Color) = (1,1,1,1) 
          _SpecColor ("Specular Material Color", Color) = (1,1,1,1) 
          _Shininess ("Shininess", Float) = 10
       }
       SubShader {
          Pass {    
             Tags { "LightMode" = "ForwardBase" } 
                // pass for ambient light and first light source
    
             CGPROGRAM
    
             #pragma vertex vert  
             #pragma fragment frag 
    
             #include "UnityCG.cginc"
             uniform float4 _LightColor0; 
                // color of light source (from "Lighting.cginc")
    
             // User-specified properties
             uniform sampler2D _MainTex;    
             uniform float4 _Color; 
             uniform float4 _SpecColor; 
             uniform float _Shininess;
    
             struct vertexInput {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 texcoord : TEXCOORD0;
            };
             struct vertexOutput {
                float4 pos : SV_POSITION;
                float4 posWorld : TEXCOORD0;
                float3 normalDir : TEXCOORD1;
                float4 tex : TEXCOORD2;
            };
    
             vertexOutput vert(vertexInput input) 
             {
                vertexOutput output;
    
                float4x4 modelMatrix = unity_ObjectToWorld;
                float4x4 modelMatrixInverse = unity_WorldToObject;
    
                output.posWorld = mul(modelMatrix, input.vertex);
                output.normalDir = normalize(
                   mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);
                output.tex = input.texcoord;
                output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
                return output;
             }
    
             float4 frag(vertexOutput input) : COLOR
             {
                float3 normalDirection = normalize(input.normalDir);
    
                float3 viewDirection = normalize(
                   _WorldSpaceCameraPos - input.posWorld.xyz);
                float3 lightDirection;
                float attenuation;
    
                float4 textureColor = tex2D(_MainTex, input.tex.xy);
    
                if (0.0 == _WorldSpaceLightPos0.w) // directional light?
                {
                   attenuation = 1.0; // no attenuation
                   lightDirection = 
                      normalize(_WorldSpaceLightPos0.xyz);
                } 
                else // point or spot light
                {
                   float3 vertexToLightSource = 
                      _WorldSpaceLightPos0.xyz - input.posWorld.xyz;
                   float distance = length(vertexToLightSource);
                   attenuation = 1.0 / distance; // linear attenuation 
                   lightDirection = normalize(vertexToLightSource);
                }
    
                float3 ambientLighting = textureColor.rgb  
                   * UNITY_LIGHTMODEL_AMBIENT.rgb * _Color.rgb;
    
                float3 diffuseReflection = textureColor.rgb  
                   * attenuation * _LightColor0.rgb * _Color.rgb
                   * max(0.0, dot(normalDirection, lightDirection));
    
                float3 specularReflection;
                if (dot(normalDirection, lightDirection) < 0.0) 
                   // light source on the wrong side?
                {
                   specularReflection = float3(0.0, 0.0, 0.0); 
                      // no specular reflection
                }
                else // light source on the right side
                {
                   specularReflection = attenuation * _LightColor0.rgb 
                      * _SpecColor.rgb * (1.0 - textureColor.a) 
                         // for usual gloss maps: "... * textureColor.a" 
                      * pow(max(0.0, dot(
                      reflect(-lightDirection, normalDirection), 
                      viewDirection)), _Shininess);
                }
    
                return float4(ambientLighting + diffuseReflection 
                   + specularReflection, 1.0);
             }
    
             ENDCG
          }
    
          Pass {    
             Tags { "LightMode" = "ForwardAdd" } 
                // pass for additional light sources
             Blend One One // additive blending 
    
              CGPROGRAM
    
             #pragma vertex vert  
             #pragma fragment frag 
    
             #include "UnityCG.cginc"
             uniform float4 _LightColor0; 
                // color of light source (from "Lighting.cginc")
    
             // User-specified properties
             uniform sampler2D _MainTex;    
             uniform float4 _Color; 
             uniform float4 _SpecColor; 
             uniform float _Shininess;
    
            struct vertexInput {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 texcoord : TEXCOORD0;
            };
             struct vertexOutput {
                float4 pos : SV_POSITION;
                float4 posWorld : TEXCOORD0;
                float3 normalDir : TEXCOORD1;
                float4 tex : TEXCOORD2;
            };
    
             vertexOutput vert(vertexInput input) 
             {
                vertexOutput output;
    
                float4x4 modelMatrix = unity_ObjectToWorld;
                float4x4 modelMatrixInverse = unity_WorldToObject;
    
                output.posWorld = mul(modelMatrix, input.vertex);
                output.normalDir = normalize(
                   mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);
                output.tex = input.texcoord;
                output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
                return output;
             }
    
             float4 frag(vertexOutput input) : COLOR
             {
                float3 normalDirection = normalize(input.normalDir);
    
                float3 viewDirection = normalize(
                   _WorldSpaceCameraPos - input.posWorld.xyz);
                float3 lightDirection;
                float attenuation;
    
                float4 textureColor = tex2D(_MainTex, input.tex.xy);
    
                if (0.0 == _WorldSpaceLightPos0.w) // directional light?
                {
                   attenuation = 1.0; // no attenuation
                   lightDirection = 
                      normalize(_WorldSpaceLightPos0.xyz);
                } 
                else // point or spot light
                {
                   float3 vertexToLightSource = 
                      _WorldSpaceLightPos0.xyz - input.posWorld.xyz;
                   float distance = length(vertexToLightSource);
                   attenuation = 1.0 / distance; // linear attenuation 
                   lightDirection = normalize(vertexToLightSource);
                }
    
                float3 diffuseReflection = textureColor.rgb  
                   * attenuation * _LightColor0.rgb * _Color.rgb
                   * max(0.0, dot(normalDirection, lightDirection));
    
                float3 specularReflection;
                if (dot(normalDirection, lightDirection) < 0.0) 
                   // light source on the wrong side?
                {
                   specularReflection = float3(0.0, 0.0, 0.0); 
                      // no specular reflection
                }
                else // light source on the right side
                {
                   specularReflection = attenuation * _LightColor0.rgb 
                      * _SpecColor.rgb * (1.0 - textureColor.a) 
                         // for usual gloss maps: "... * textureColor.a" 
                      * pow(max(0.0, dot(
                      reflect(-lightDirection, normalDirection), 
                      viewDirection)), _Shininess);
                }
    
                return float4(diffuseReflection 
                   + specularReflection, 1.0);
                   // no ambient lighting in this pass
             }
    
             ENDCG
          }
       }
       Fallback "Specular"
    }
    

    The final Pass of your blur shader will need to be merged into the first and second Passes in the above shader.

    Shader Properties

    Add these properties in the blur shader if they don't already exist (you may need to rename things if they do):

      _MainTex ("RGBA Texture For Material Color", 2D) = "white" {} 
      _Color ("Diffuse Material Color", Color) = (1,1,1,1) 
      _SpecColor ("Specular Material Color", Color) = (1,1,1,1) 
      _Shininess ("Shininess", Float) = 10
    

    The first gloss pass

    Set the tag to match the glossy shader: "LightMode" = "ForwardBase"

    Add these variables to the pass if they don't already exist (you may need to rename things if they do):

     uniform sampler2D _MainTex; 
     uniform float4 _Color; 
     uniform float4 _SpecColor; 
     uniform float _Shininess;
    

    Include float3 normal : NORMAL; in your vertex input structure.

    Include float4 posWorld : TEXCOORD0; and float3 normalDir : TEXCOORD1; in your vertex output structure.

    In the vert function, set output.posWorld and output.normalDir the same way the glossy shader vert does.

    Then, in the frag function, take what your blur shader is already returning, and instead of returning it, use it as the textureColor variable in the 1st frag function in the glossy shader, and then do the rest of the 1st glossy frag.

    You will probably have to rename other things in the code to make it work with the pass you already have. I don't know what your blur shader looks like, so it's impossible for me to list every step in merging the two passes together.

    The second gloss pass

    Repeat the same process as the first gloss Pass, but using the code from the 2nd gloss Pass (especially important is the different tag "LightMode" = "ForwardAdd")