alelievr / HDRP-Custom-Passes

A bunch of custom passes made for HDRP
MIT License
1.25k stars 136 forks source link

Filter See Through #17

Closed Mushe94 closed 3 years ago

Mushe94 commented 3 years ago

Hi there, quick question, is there a way to make the See Through shader to only get activated when certain objects are in front? At the moment everything is getting detected which is sometimes not desired.

Thanks!

dkyeck commented 3 years ago

no easy hookfor this, sorry.

Hi there, quick question, is there a way to make the See Through shader to only get activated when certain objects are in front? At the moment everything is getting detected which is sometimes not desired.

Thanks!

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/alelievr/HDRP-Custom-Passes/issues/17, or unsubscribe https://github.com/notifications/unsubscribe-auth/AADYEPYCP75SGC4NAKYHL5DR7QK43ANCNFSM4PXXGBWQ.

Mushe94 commented 3 years ago

Mm, I see. And the hard way? Or the most hacky way possible?

We are basically trying to make the Player to get renderer when a wall is on top of him (isometric perspective), but the shader is also getting applied whenever any other thing gets in the way, including the Player itself (due to it having clothing on top, helmet, etc.), including the weapons that he carries on its back/leg are getting triggered by the shader.

alelievr commented 3 years ago

but the shader is also getting applied whenever any other thing gets in the way, including the Player itself (due to it having clothing on top, helmet, etc.),

Hum I see, I guess it's because these objects are not in the same layer than the player?

There might be an easy solution here, if you look at line 41 of SeeThrough.cs there is the rendering of all the blockers into the stencil buffer.
Then at line 49 we use this stencil to only render the meshes with the ShaderGraph material that have the stencil bit enabled (so that we know they are behind something).
If instead of having one layer mask for the two draw renderers you had two (one for each call), it would allow you to only put your walls into the blocker stencil while still having the other see through objects.

Mushe94 commented 3 years ago

Wow that worked perfectly! Thank you very much!

If anyone finds this and is looking for the same solution, here is the entire SeeThrough.cs:


using UnityEngine;
using UnityEngine.Rendering.HighDefinition;
using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering;
using System.Collections.Generic;

class SeeThrough : CustomPass
{
    public LayerMask seeThroughBlocker = 1;
    public LayerMask seeThroughTarget = 1;
    public Material seeThroughMaterial = null;

    [SerializeField, HideInInspector]
    Shader stencilShader;

    Material stencilMaterial;

    ShaderTagId[] shaderTags;

    protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd)
    {
        if (stencilShader == null)
            stencilShader = Shader.Find("Hidden/Renderers/SeeThroughStencil");

        stencilMaterial = CoreUtils.CreateEngineMaterial(stencilShader);

        shaderTags = new ShaderTagId[4]
        {
            new ShaderTagId("Forward"),
            new ShaderTagId("ForwardOnly"),
            new ShaderTagId("SRPDefaultUnlit"),
            new ShaderTagId("FirstPass"),
        };
    }

    protected override void Execute(ScriptableRenderContext renderContext, CommandBuffer cmd, HDCamera hdCamera, CullingResults cullingResult)
    {
        // We first render objects into the user stencil bit 0, this will allow us to detect
        // if the object is behind another object.
        stencilMaterial.SetInt("_StencilWriteMask", (int)UserStencilUsage.UserBit0);

        RenderObjects(renderContext, cmd, stencilMaterial, 0, CompareFunction.LessEqual, cullingResult, hdCamera, seeThroughBlocker);

        // Then we render the objects that are behind walls using the stencil buffer with Greater Equal ZTest:
        StencilState seeThroughStencil = new StencilState(
            enabled: true,
            readMask: (byte)UserStencilUsage.UserBit0,
            compareFunction: CompareFunction.Equal
        );
        RenderObjects(renderContext, cmd, seeThroughMaterial, seeThroughMaterial.FindPass("ForwardOnly"), CompareFunction.GreaterEqual, cullingResult, hdCamera, seeThroughTarget, seeThroughStencil);
    }

    public override IEnumerable<Material> RegisterMaterialForInspector() { yield return seeThroughMaterial; }

    void RenderObjects(ScriptableRenderContext renderContext, CommandBuffer cmd, Material overrideMaterial, int passIndex, CompareFunction depthCompare, CullingResults cullingResult, HDCamera hdCamera, LayerMask layer, StencilState? overrideStencil = null)
    {
        // Render the objects in the layer blur mask into a mask buffer with their materials so we keep the alpha-clip and transparency if there is any.
        RendererListDesc result = new RendererListDesc(shaderTags, cullingResult, hdCamera.camera)
        {
            rendererConfiguration = PerObjectData.None,
            renderQueueRange = RenderQueueRange.all,
            sortingCriteria = SortingCriteria.BackToFront,
            excludeObjectMotionVectors = false,
            overrideMaterial = overrideMaterial,
            overrideMaterialPassIndex = passIndex,
            layerMask = layer,
            stateBlock = new RenderStateBlock(RenderStateMask.Depth){ depthState = new DepthState(true, depthCompare)},
        };

        if (overrideStencil != null)
        {
            RenderStateBlock block = result.stateBlock.Value;
            block.mask |= RenderStateMask.Stencil;
            block.stencilState = overrideStencil.Value;
            result.stateBlock = block;
        }

        HDUtils.DrawRendererList(renderContext, cmd, RendererList.Create(result));
    }

    protected override void Cleanup()
    {
        // Cleanup code
    }
}`
Mushe94 commented 3 years ago

Hello, I'm back!

Is there a way to use different materials based on which layer the object is being rendered into? (Basically Player = Blue, Enemy = Red)

We tried adding a second Custom Pass Game Object but it works some of the times (sometimes both and others just one of the two). Any ideas how to do this?

mariandev commented 3 years ago

You could use MaterialPropertyBlock to change some shader properties. I use it to change the color of the outline on my character (made using a custom pass) and it works perfectly.

Mushe94 commented 3 years ago

That's a very nice idea. Thanks. I would need some pointers about how to integrate that with the current script setup because I don't really know were (and perhaps how) to integrate it.

Mushe94 commented 3 years ago

Well I managed to fix it having 2 Custom Pass Game Objects and deleting the second camera we had for the GUI (which also gave us like 30 FPS). Now we need a way of rendering TMPro World Space on top of everything but that's out of the scope of this shader. Thanks anyways!