Closed Nordskog closed 2 years ago
Great work! Didn't you say you didn't have much exposure to these things before?
A constant color opengl shader I made about 10 years back was about the extent of my existing shader knoweldge; there was a bit of a learning curve. Getting NSight working was a gamechanger; your previous conversations in the GTFO modding server proved very helpful.
What?
The new
thermal sights
in R7 use ashader
that, while having multiple reticles ( turns out that is the correct spelling, not "reticule" ) defined with most of the properties from the standard3-layer holographic sight shader
, does not actually support anything but plastering them all at the same depth, making it unusable in VR. To make matters worse, the VR aiming laser is not rendered at all through the thermal sight, making it impossible to aim it accurately.This PR makes the aiming laser visible as a bright-white object through the thermal sight, and also adds a 3-layer holographic sight to it, resulting in this: https://youtu.be/JnATkNCAO8Y?t=33
Is the laser actually needed? ... a strong maybe.
How?
Holographic Sight It turns out both of the
thermal sights
have a default3-layer sight
object named e.g.Sight_10_Glass
buried behind thethermal sight
, deactivated. Activate, shift backwards by a bit so it's visible, and you're good. Thethermal shader
doesn't use the same set of values to determine the layout of its reticles as the3-layer holographic shader
, so rather than transplant anything the reticles that come with the default material have been left alone.Had I know it would be so simple I would probably not have bothered with the laser.
Laser
GTFO uses deferred rendering, with the deferred pass spitting out
all in one pass. Encoded in the alpha channel of the
emission buffer
you will find the_ShadingType
property present on many of GTFO's materials. This value is extracted further down the pipe, and along with thenormal
andspec/SSS
values determine how brightly things will glow in thethermal sight
.If you just use any one of the GTFO materials that include this property, you will encounter 2 problems:
The
existing laser mesh
is not affected by any of this because it is rendered after thethermal shader
. It is also not adeferred shader
, so it renders separately after the deferred pass, skipping some of its logic. If you do lower itsrenderqueue
to render before the thermal sight, it will render as a blurry blob, as it is assigned the default_ShadingType
, which blurs surfaces to give the impression of a gradual heat gradient.The solution to this is to write our own deferred shader that:
_ShadingType
value so the surface will glow.specular/SSS
to glow morenormals
always facing the camera to glow yet moreZWrite Off
and does not write to the depth buffer. This fixes the outline problem. Ultimately the normal laser will overwrite these pixels anyway.Ramping the emission way up gives a cool looking glow in the thermal sight, but unfortunately reacts poorly with glass surfaces, and had to be toned down significantly.
This shader is assigned to children of the existing pointer/dot and just scale with them, so no extra logic beyond parenting them.
The Shader asset
The shader is added as its own asset in
\StreamingAssets
:vrthermal
along with its manifestvrthermal.manifest
. Like the existingAssetBundles
, I have included itsSecondaryAsssetBundles
, but I guess ultimately neither of these are actually used and not actually required. But they're there.Presumably they can be merged somehow, if that matters.
The code for any of the custom VR shaders is actually committed to the repo at the moment. Maybe do that, but for now I'll just attach it here.
GTFOVR/Thermal_Glow
```glsl Shader "GTFOVR/Thermal_Glow" { Properties { _MainTex("Texture", 2D) = "white" {} _Bump("Bump", Color) = (1,1,1,0.750) _Color("Color", Color) = (1,1,1,1) _EmissionColor("Emission", Color) = (1,1,1,1) [Enum(Normal,0,Translucent,1,Skin,2,Cloth,3,NormalWarm,4,SkinCold,5,SkinWarm,6)] _ShadingType("Shading Type", Float) = 2 } SubShader { Tags { "LightMode" = "Deferred" "RenderType" = "Opaque" "Queue" = "Geometry+10"} // Queue ignored? LOD 100 Pass { Cull Off ZWrite Off // Off to avoid outline CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct Attributes { float4 position : POSITION; float3 normal : NORMAL; float4 tangent : TANGENT; float2 uv : TEXCOORD; uint instance : SV_InstanceID; }; struct Varyings { float4 position : SV_POSITION; float2 uv : TEXCOORD0; float3 normal : TEXCOORD1; float3 uv1 : TEXCOORD2; float3 uv3 : TEXCOORD4; float4 uv4 : TEXCOORD5; float4 uv5 : TEXCOORD6; uint instance : SV_InstanceID; }; float4 _Color; float4 _Bump; float4 _EmissionColor; float _ShadingType; sampler2D _MainTex; float4 _MainTex_ST; struct FragmentOutput { float4 diffuse : SV_Target; float4 bump : SV_Target1; float4 normal : SV_Target2; float4 emission : SV_Target3; }; Varyings vert(Attributes v) { Varyings o; o.position = UnityObjectToClipPos(v.position); o.uv = TRANSFORM_TEX(v.uv, _MainTex); //Normal facing camera o.normal = (unity_CameraToWorld._m02_m12_m22) * -0.5 + 0.5; return o; } FragmentOutput frag(Varyings i) { FragmentOutput o; float shadingTypeMagicNumber = 0.003921569; o.diffuse = _Color; o.bump = _Bump; o.normal = float4(i.normal.x, i.normal.y, i.normal.z, 1); o.emission = float4( _EmissionColor.x, _EmissionColor.y, _EmissionColor.z, _ShadingType * shadingTypeMagicNumber + shadingTypeMagicNumber); // Converted to a very low alpha value, e.g. 2 maps to 0.012 return o; } ENDCG } } } ```Minor stuff
The dot would be deactivated when the laser hit is more than 100 units away, but it is never reactivated and will not reappearing until you switch to a different weapon. This has been fixed. Many colliders are not very accurate and the pointer dot will often get buried in geometry. It has been shifted by 0.1 units to help with this a bit.