- Home /
Best way to paint a Camera's Line of sight
Hello everyone.
In my unity scene I have a Camera and I would like to paint everything to green dynamically which is seen by that camera and red which is not seen by the camera. I have near plane's and far plane's corner points. And for now I'm casting rays from near plane to far plane one by one and it takes too much performance to cast rays and I'm putting decals at hit positions of rays so it lowers the FPS dramatically.
So is there any performance-friendly method to create such effect inside Unity ?
What I'd like to achieve is shown perfectly in this picture:
Anyone got any ideas to how to create such imagery in Unity ?
Answer by Eno-Khaon · Dec 19, 2020 at 09:20 PM
I'm afraid I don't have a solution prepared for this myself, but I stumbled across a pretty reasonable-looking example of a Volumetric Occlusion Mask here. Combine this with an impromptu mesh made based on the intended camera and you'll have your "occlusion" field to recolor within a post-processing shader.
Vector3 fbl; // front-bottom-left
Vector3 fbr; // front-bottom-right
Vector3 ftl; // front-top-left
Vector3 ftr; // front-top-right
Vector3 bbl; // back-bottom-left
Vector3 bbr; // back-bottom-right
Vector3 btl; // back-top-left
Vector3 btr; // back-top-right
void Start()
{
// A few examples, based on camera at (0, 0, 0), no rotation
// (use the camera's Transform.InverseTransformPoint()
// if it has any applied transformation)
fbl = targetCam.ViewportToWorldPoint(new Vector3(0f, 0f, targetCam.nearClipPlane));
btr = targetCam.ViewportToWorldPoint(new Vector3(1f, 1f, targetCam.farClipPlane));
// ...
// Once you have all 8 vertices, create a mesh from them, attach a MeshFilter and MeshRenderer to the GameObject as a child of the camera so it moves with the camera
}
Hey again !,
The idea looks really close to what I'm trying to achieve but the github repo that you've referenced seems broken even with it's required packages and unity version installed. So I would need further ideas and references if you have any ?
Any help would be appreciated :)
Okay, I threw together a few scripts as a rudimentary self-teaching process for using Stencil buffer for this effect (For one, this means that it's probably a more expensive approach than my original link could/would have been, but my actively testing it right now says it's working). It's definitely not in a finalized state, but should serve as a potential starting point. I'll explain what I can as best as I can, but I wasn't kidding when I said I didn't have a solution prepared for this previously. The suggestions are primarily sourced from this answer by @Bunny83 on the subject of drawing Stencil Buffer shadows, since this will use exactly the same principle.
I also referenced https://docs.unity3d.com/$$anonymous$$anual/SL-Stencil.html constantly, for obvious-enough reasons.
The first script would be attached to the camera, and is based on https://docs.unity3d.com/ScriptReference/$$anonymous$$onoBehaviour.OnPostRender.html. In fact, the only real change I made was using a custom shader rather than "Hidden/Internal-Colored", but I'll get to that soon.
-- Writing to the Stencil Buffer --
First, we need a simple Stencil Buffer write that doesn't need to (visibly) draw anything itself. To do this, we'll need two passes.
The first draws the back faces into the stencil buffer.
The second removes ineligible regions of the current buffer by factoring in the front faces.
// This shader attaches to a material on the camera projection-based box
Shader "StencilWrite"
{
CGINCLUDE
#include "UnityCG.cginc"
struct appdata_t
{
float4 vertex : POSITION;
};
struct v2f
{
float4 vertex : SV_POSITION;
};
v2f vert(appdata_t v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
return 0;
}
ENDCG
SubShader
{
Tags { "RenderType"="Opaque" "Queue"="Geometry+1"}
Pass
{
Blend SrcColor Zero
ZWrite Off
Cull front
Color$$anonymous$$ask 0
stencil
{
Ref 2
Comp Always
Pass Keep
ZFail incrWrap
}
CGPROGRA$$anonymous$$
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
ENDCG
}
Pass
{
Blend SrcColor Zero
ZWrite Off
Cull back
Color$$anonymous$$ask 0
stencil
{
Ref 2
Comp Always
Pass Keep
ZFail decrWrap
}
CGPROGRA$$anonymous$$
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
ENDCG
}
}
}
(continued in next reply)
Edit: Fixed an edge case related to concave meshes
Next, we need a shader that will read back from the Stencil Buffer, which will be called on from the script (based on the OnPostRender() page). This simple example will just apply a color tint (_Color) with no fancy effects, but again, this is simply meant to be a potential starting point.
// This shader is used in place of the Hidden Internal shader referenced in the OnPostRender() documentation
Shader "StencilRead"
{
Properties
{
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
Pass
{
Blend DstColor Zero
ZWrite Off
ZTest Always
Cull Back
stencil
{
Ref 1
Comp Equal
Fail Replace
}
CGPROGRA$$anonymous$$
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#include "UnityCG.cginc"
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
};
struct v2f
{
fixed4 color : COLOR;
float4 vertex : SV_POSITION;
};
float4 _Color;
v2f vert(appdata_t v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.color = v.color * _Color;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
return i.color;
}
ENDCG
}
}
}
To be thorough, this is the script to go on the camera, condensed down due to using a less verbose shader:
using UnityEngine;
public class SimpleEffect : $$anonymous$$onoBehaviour
{
private $$anonymous$$aterial mat;
private void OnPostRender()
{
if(!mat)
{
Shader s = Shader.Find("StencilRead");
mat = new $$anonymous$$aterial(s);
}
GL.Push$$anonymous$$atrix();
GL.LoadOrtho();
mat.SetPass(0);
GL.Begin(GL.QUADS);
GL.Vertex3(0.0f, 0.0f, 0.0f);
GL.Vertex3(1.0f, 0.0f, 0.0f);
GL.Vertex3(1.0f, 1.0f, 0.0f);
GL.Vertex3(0.0f, 1.0f, 0.0f);
GL.End();
GL.Pop$$anonymous$$atrix();
}
}
Hey there, I've followed your idea step by step and the result seems to be same as decal. What I'm exactly trying to reach is shown with this video. And this is the link for the github repo demonstrated in the video.
But the problem here is : It's cone not a camera (trapezoid) but uses camera's depth render guess it's allright and not sure if that's a real problem.