So you want to make a VR shooter and you realized that generic gun sights don’t work that well in VR. The red dot sight is much better for VR since it is a lot easier to use especially if you are like me an have a Windows MR headset where you sometimes have to deal with twitchy tracking. The one above works by using the stencil buffer to make a red sphere visible only when viewed through the sights.

The Stencil Buffer

put simply the stencil buffer stores a number for each pixel on the screen with the default being zero. Using custom shaders we can check and set these numbers. The sights work in two steps: First, the mesh representing the glass in the sights is set to render before anything else, and when it does it sets the numbers in the buffer representing the pixels it is covering equal to one. Second, when the ball renders it checks every pixel it will render two and if they are not set to one It doesn’t render, making it invisible, but if they are equal to one it does render and it thus becomes visible.

The Shaders

I make this work by using two custom shaders, the first sets the stencil buffer to 1, and the second reads it and decides whether or not to render.

Here Is the First:

Shader "Custom/MakeItOne" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Glossiness ("Smoothness", Range(0,1)) = 0.5 _Metallicout("Metallic", Range(0,1)) = 0.0 } SubShader { //START OF CUSTOM CODE // make sure "Queue" = "Geometry-1" "ForceNoShadowCasting" = "True" are set, the first makes it so that it is renderd first, the scond makes it so it doesn't cast shadows Tags { "RenderType"="Opaque" "Queue" = "Geometry-1" "ForceNoShadowCasting" = "True" } ColorMask 0//makes this object invisable ZWrite off Stencil{ Ref 1//says we are using 1 you can use any number Comp always//always compleats action regardless of Stencil value Pass replace//sets stencil buffer two the Ref number } //END OF CUSTOM CODE CGPROGRAM // Physically based Standard lighting model, and enable shadows on all light types #pragma surface surf Standard fullforwardshadows // Use shader model 3.0 target, to get nicer looking lighting #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; }; half _Glossiness; half _Metallic; fixed4 _Color; // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader. // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing. // #pragma instancing_options assumeuniformscaling UNITY_INSTANCING_BUFFER_START(Props) // put more per-instance properties here UNITY_INSTANCING_BUFFER_END(Props) void surf (Input IN, in // Albedo comes from a texture tinted by color fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; // Metallic and smoothness come from slider variables o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }

And Here is the second:

Shader "Custom/ShowIfOne" { 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 } SubShader { //START OF CUSTOM CODE Tags { "RenderType"="Opaque" "ForceNoShadowCasting" = "True" } LOD 200 Stencil{ Ref 1//sets refrence to one Comp Equal//checks if the Ref and Stencil buffer are the same if set to Notequal the shader will do the oppisite function Pass Keep//Keeps the render for the pixel but only if the Stencil and Ref are the same } //END OF CUSTOM CODE CGPROGRAM // Physically based Standard lighting model, and enable shadows on all light types #pragma surface surf Standard fullforwardshadows // Use shader model 3.0 target, to get nicer looking lighting #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; }; half _Glossiness; half _Metallic; fixed4 _Color; // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader. // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing. // #pragma instancing_options assumeuniformscaling UNITY_INSTANCING_BUFFER_START(Props) // put more per-instance properties here UNITY_INSTANCING_BUFFER_END(Props) void surf (Input IN, inout SurfaceOutputStandard o) { // Albedo comes from a texture tinted by color fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; // Metallic and smoothness come from slider variables o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }

To use the Shaders, create a new material and drag the shaders onto the material, then use the material on any object you want.

To make the Scope above, just make a sight out of a cylinder assign the first material to it, then make a sphere and place it about 6 meters from the front of the sight and assign the second material and there you go!