0
\$\begingroup\$

I need a way to censore part of characters' bodies like Cyberpunk 2077.this is mean I want to apply Image effect only to specific parts of bodies that you can see in the below Image:

enter image description here

\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

Solution 1

Using GrabPass

GrabPass is a special pass type - it grabs the contents of the screen where the object is about to be drawn into a texture. This texture can be used in subsequent passes to do advanced image based effects.

Just GrabPass { } grabs the current screen contents into a texture. The texture can be accessed in further passes by _GrabTexture name. Note: this form of grab pass will do the time-consuming screen grabbing operation for each object that uses it.

GrabPass { "TextureName" } grabs the current screen contents into a texture, but will only do that once per frame for the first object that uses the given texture name. The texture can be accessed in further passes by the given texture name. This is a more performant method when you have multiple objects using GrabPass in the scene

so first you should grab behind of object:

GrabPass{ "_GrabTexture" }

Initializing position and grab position in shader:

    v2f vert(appdata_base v) {
        v2f o;
        // use UnityObjectToClipPos from UnityCG.cginc to calculate 
        // the clip-space of the vertex
        o.pos = UnityObjectToClipPos(v.vertex);
        // use ComputeGrabScreenPos function from UnityCG.cginc
        // to get the correct texture coordinate
        o.grabPos = ComputeGrabScreenPos(o.pos);
        return o;
    }

pixelating results:

    float _PixelSize;
    sampler2D _GrabTexture;

    float4 frag(v2f IN) : COLOR
    {
        float2 steppedUV = IN.grabPos.xy / IN.grabPos.w;
        steppedUV /= _PixelSize / _ScreenParams.xy;
        steppedUV = round(steppedUV);
        steppedUV *= _PixelSize / _ScreenParams.xy;
        //Sampling GrabTexture according to screen capture positon, tex2DProj is equivalent to tex2D (grabPos.xy / grabpos.w)
        return tex2D(_GrabTexture, steppedUV);
    }

so this is our pixelate shader:

Shader "Custom/Pixelate"
{
    Properties
    {
        _PixelSize("Pixel Size", Float) = 10
    }
 
    SubShader
    {
        Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" }
        Blend Off
        Lighting Off
        Fog{ Mode Off }
        ZWrite Off
        LOD 200
        Cull Off
 
        GrabPass{ "_GrabTexture" }
 
        Pass
        {
            CGPROGRAM
 
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
 
            struct v2f
            {
                float4 pos : SV_POSITION;
                float4 grabPos  : TEXCOORD0;
            };
 
 
            v2f vert(appdata_base v) {
                v2f o;
                // use UnityObjectToClipPos from UnityCG.cginc to calculate 
                // the clip-space of the vertex
                o.pos = UnityObjectToClipPos(v.vertex);
                // use ComputeGrabScreenPos function from UnityCG.cginc
                // to get the correct texture coordinate
                o.grabPos = ComputeGrabScreenPos(o.pos);
                return o;
            }
            
            float _PixelSize;
            sampler2D _GrabTexture;
 
            float4 frag(v2f IN) : COLOR
            {
                float2 steppedUV = IN.grabPos.xy / IN.grabPos.w;
                steppedUV /= _PixelSize / _ScreenParams.xy;
                steppedUV = round(steppedUV);
                steppedUV *= _PixelSize / _ScreenParams.xy;
                //Sampling GrabTexture according to screen capture positon, tex2DProj is equivalent to tex2D (grabPos.xy / grabpos.w)
                return tex2D(_GrabTexture, steppedUV);
            }
         
            ENDCG
        }
    }
}

enter image description here

Solution 2

Using two cameras that I explained before

1.Make secondary camera

2.On second camera, in Clear Flags set it to Don't clear

enter image description here enter image description here

Solution 3

Using Command Buffer

we know, that the GPU can work on stuff in parallel. But what’s about the communication between CPU and GPU? Does the CPU has to wait until the GPU finished the job before it can receive new commands?

enter image description here

No! because this communication would create bottlenecks (e.g. when the CPU can’t deliver commands fast enough) and would make parallel working impossible.bottleneck, as for how it is named, occurs when there is a limit on how much data is being sent for processing or how much data can be processed at the same time.

The solution is a list where commands can be added by the CPU and read by the GPU – independent from each other! This list is called: Command Buffer.

What is Command Buffer?

Command buffers hold list of rendering commands ("set render target, draw mesh, ..."). They can be set to execute at various points during camera rendering (see Camera.AddCommandBuffer), light rendering (see Light.AddCommandBuffer) or be executed immediately (see Graphics.ExecuteCommandBuffer).

How it work?

The command buffer makes it possible that CPU and GPU can work independent from each other. When the CPU wants something to be rendered, it can push that command into the queue and when the GPU has free resources, it can take the command out of the list and execute it (but the list works as a FIFO – so the GPU can only take the oldest item in the list (which was first/earlier added than all others) and work on that).

By the way: there are different commands possible. One example is a draw call, another would be to change the render state.

enter image description here

https://vulkan-tutorial.com/Drawing_a_triangle/Drawing/Command_buffers

https://developer.nvidia.com/engaging-voyage-vulkan

enter image description here

Let's Start to use command buffer enter image description here Masking part of character with sphere:

 Stencil
     {
         Ref 1
         Comp Always
         Pass Replace
     }

enter image description here

create a command:

var commandBuffer = new CommandBuffer();
commandBuffer.name = "pixelate";

enter image description here

Temporarily copy the rendering result to the Render Texture:

  int tempTextureIdentifier = Shader.PropertyToID("_PostEffect");
        commandBuffer.GetTemporaryRT(tempTextureIdentifier, -1, -1);
        commandBuffer.Blit(BuiltinRenderTextureType.CameraTarget, tempTextureIdentifier);

enter image description here

Apply the rendering result to the material that you want to apply image effects:

commandBuffer.Blit(tempTextureIdentifier, BuiltinRenderTextureType.CameraTarget, material);

enter image description here

Release temporary render texture:

commandBuffer.ReleaseTemporaryRT(tempTextureIdentifier);

enter image description here

Register where you want to add the command buffer Below is a high-level overview of how Cameras use the Forward or Deferred pipeline to render a Scene in Unity.

  • Black boxes represent an internal Unity process. Blue boxes represent
  • a CameraEvent where you can add Command Buffers.

enter image description here

camera.AddCommandBuffer(CameraEvent.BeforeImageEffects, commandBuffer);

enter image description here

add Image effects to where Ref is equal to 1

   Stencil
    {
        Ref 1
        Comp Equal
    }

enter image description here

adding color to pixelated area

enter image description here

More Examples!

pixelating specific objects

enter image description here

grayscale where that is not equal to Ref 1

Stencil
{
    Ref 1
    Comp NotEqual
}

enter image description here

References:

https://simonschreibt.de/gat/renderhell-book1/

https://gurutaka-log.com/commandbuffer-stencilbuffer

\$\endgroup\$

You must log in to answer this question.

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