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!

| improve this question | | | | |
\$\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)

| improve this answer | | | | |
\$\endgroup\$
  • \$\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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.