0
\$\begingroup\$

I'm trying to create a spherical effect around my player that when they walk into range of hidden object, it gradually renders the object.

I found a shader that would work pretty well for this. When applied to an object, that object doesn't render, but when the object is brought into range of a cube shaped volume (by default it's located at the world origin) it renders.

Shader "Custom/ClipVolume" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0

        // Expose parameters for the minimum x, minimum z,
        // maximum x, and maximum z of the rendered volume.
        _Corners ("Min XZ / Max XZ", Vector) = (-1, -1, 1, 1)
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        // Allow back sides of the object to render.
        Cull Off

        CGPROGRAM

        #pragma surface surf Standard fullforwardshadows
        #pragma target 3.0

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
            float3 worldPos;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;

        // Read the min xz/ max xz material properties.
        float4 _Corners;

        void surf (Input IN, inout SurfaceOutputStandard o) {

            // Calculate a signed distance from the clipping volume.
            float2 offset;
            offset = IN.worldPos.xz - _Corners.zw;
            float outOfBounds = max(offset.x, offset.y);
            offset = _Corners.xy - IN.worldPos.xz;
            outOfBounds = max(outOfBounds, max(offset.x, offset.y));
            // Reject fragments that are outside the clipping volume.
            clip(-outOfBounds);

            // Default surface shading.
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    // Note that the non-clipped Diffuse material will be used for shadows.
    // If you need correct shadowing with clipped material, add a shadow pass
    // that includes the same clipping logic as above.
    FallBack "Diffuse"
}

Issue 1: I want this effect to be spherical so that it looks like it radiates from the player. I followed some signed distance and volumetric rendering tutorials to create the sphere, but I can't seem to hook that in with my existing shader (most likely because I'm very new to creating shaders).

Issue 2: I can't seem to figure out how to move the volume with my player. I can get the volume to shift in the inspector using the vector4 coordinates, but I can't get my player's position to translate in a way that uniformly moves the volume.

Can someone give me some tips or examples on how I can shape and move this volume?

Any help would be greatly appreciated!

\$\endgroup\$
0
\$\begingroup\$

First, spheres have a center and a radius, not a min/max corner. We can pack those into one 4-vector, with the center in xyz and the radius in w:

 Properties {
    _Color ("Color", Color) = (1,1,1,1)
    _MainTex ("Albedo (RGB)", 2D) = "white" {}
    _Glossiness ("Smoothness", Range(0,1)) = 0.5
    _Metallic ("Metallic", Range(0,1)) = 0.0

    // Change the exposed property for positioning / sizing the volume.
    _CenterRadius ("Center(xyz) + Radius(w)", Vector) = (0, 0, 0, 1)
}

And then use the variable like so:

float4 _CenterRadius;

void surf (Input IN, inout SurfaceOutputStandard o) {

    // Calculate offset from the center of the sphere.
    float3 offset = IN.worldPos.xyz - _CenterRadius.xyz;

    // Reject fragments that are outside the clipping volume.
    // (squared length of offset > squared radius)
    clip(_CenterRadius.w * _CenterRadius.w
        - dot(offset, offset));

    // Default surface shading.
    fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
    o.Albedo = c.rgb;
    o.Metallic = _Metallic;
    o.Smoothness = _Glossiness;
    o.Alpha = c.a;
}

You can update the position in your C# script like...

Vector4 centerRadius = player.transform.position;
centerRadius.w = radius;

clippingMaterial.SetVector("_CenterRadius", centerRadius);

(Assuming you've populated suitably named variables for your player avatar object, your radius parameter, and your clippingMaterial. You can of course replace the string "_CenterRadius" with a cached integer ID for efficiency)

\$\endgroup\$
1
  • \$\begingroup\$ Cool. Mark the answer Accepted using the checkmark button to signal to other users that it worked for you. :) \$\endgroup\$
    – DMGregory
    Sep 26 '18 at 15:54

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .