Soft shadows for point lights (my implementation)

I made the realization of soft shadows for point lights. In order to make it work , you need to find in folder “…\Unity5\Editor\Data\CGIncludes” file “UnityShadowLibrary.cginc” and edit it.

To see the result , you need to find a folder of your project . In it find the folder “Library”. Remove from this folder directory “ShaderCache” and restart Unity. Yes, I restarted Unity after each compilation.

See in “UnityShadowLibrary.cginc”. We need this cut:

    #if defined (SHADOWS_SOFT)

        float z = 1.0/128.0;
        float4 shadowVals;
        shadowVals.x = SampleCubeDistance (vec+float3( z, z, z));
        shadowVals.y = SampleCubeDistance (vec+float3(-z,-z, z));
        shadowVals.z = SampleCubeDistance (vec+float3(-z, z,-z));
        shadowVals.w = SampleCubeDistance (vec+float3( z,-z,-z));
        half4 shadows = (shadowVals < mydist.xxxx) ? _LightShadowData.rrrr : 1.0f;

        return dot(shadows,0.25);

    #else

What do we see ? Artfully created a small offset vector. Works quickly, looks terrible:

First of course you need to choose the offset is not in all three dimensions, but in a plane perpendicular to the vector from light. Thus, we will work with the cubemap as a flat texture. So you need to be less than the sample for best results.
To obtain the virtual vector we start axes X and Y, then store them in different sequences , thereby selecting a point on the same virtual plane. No matrix , everything fast and simple.
And second - we need some random. It was effect like Disk Bloor.

  #if defined (SHADOWS_SOFT)

        // Smaller number is more blur
        float downscale = 32.0f;

        // Random vector
        const float3 rndseed = float3(12.9898,78.233,45.5432);
        float3 randomvec = float3( dot(vec,rndseed) , dot(vec.yzx,rndseed) , dot(vec.zxy,rndseed) );
        randomvec = frac(sin(randomvec) * 43758.5453);

        // Vectors of X and Y axis for virtual planar
        float3 xvec = normalize(cross(vec,randomvec));
        float3 yvec = normalize(cross(vec,xvec));


        // Two offsets
        float3 vec1 = xvec / downscale;
        float3 vec2 = yvec / downscale;

        float4 shadowVals;

        // Four samples
        shadowVals.x = SampleCubeDistance (vec+vec1);
        shadowVals.y = SampleCubeDistance (vec+vec2);
        shadowVals.z = SampleCubeDistance (vec-vec1);
        shadowVals.w = SampleCubeDistance (vec-vec2);

        half4 shadows = (shadowVals < mydist.xxxx) ? _LightShadowData.rrrr : 1.0f;
        return dot(shadows,0.25);

    #else

And see:

Here there are the same four samples , but the difference is enormous. Pixels are no longer visible. With good textures dithering was invisible. You can see what now there’s not a bias problem.

And now - warning! We can make Area Light like effect:

   #if defined (SHADOWS_SOFT)

        #define SHADOWS_SOFT_AREALIGHT

        float downscale = 32.0f;
        float areascalefactor  = 10.0f;

        const float3 rndseed = float3(12.9898,78.233,45.5432);
        float3 randomvec = float3( dot(vec,rndseed) , dot(vec.yzx,rndseed) , dot(vec.zxy,rndseed) );
        randomvec = frac(sin(randomvec) * 43758.5453);

        float3 xvec = normalize(cross(vec,randomvec));
        float3 yvec = normalize(cross(vec,xvec));

        float3 vec1 = xvec / downscale;
        float3 vec2 = yvec / downscale;

        float4 shadowVals;

        #if defined (SHADOWS_SOFT_AREALIGHT)

            // Lookup
            shadowVals.x = SampleCubeDistance (vec+vec1);
            shadowVals.y = SampleCubeDistance (vec+vec2);
            shadowVals.z = SampleCubeDistance (vec-vec1);
            shadowVals.w = SampleCubeDistance (vec-vec2);

            // Choose distances sum
            float shadowdist = mydist - dot(shadowVals, 0.25f);

            // Change bloor factor
            downscale *= 2.0f / (1.0 + (shadowdist > 0.0f) ? (shadowdist * areascalefactor ) : 0.0f);
           
            vec1 = xvec / downscale;
            vec2 = yvec / downscale;

        #endif

        // Continue old operations
        shadowVals.x = SampleCubeDistance (vec+vec1);
        shadowVals.y = SampleCubeDistance (vec+vec2);
        shadowVals.z = SampleCubeDistance (vec-vec1);
        shadowVals.w = SampleCubeDistance (vec-vec2);

        half4 shadows = (shadowVals < mydist.xxxx) ? _LightShadowData.rrrr : 1.0f;
        return dot(shadows,0.25);

    #else

If you know russian language you can find more information in this thread:
GameDev.ru

4 Likes

Where is that value mydist came from?
Edit : My Bad, haven’t check the real code before. ignore my question