Hi,
I’m quite new to shaders. I try to replicate the Draw Mode in the scene view where you see the scenes normals (image 1).
For now I managed to show the normals with with the following script (image 2):
My question is: how can I get the same results as the scene view?
It would be nice if i will be able to see the normals behind a transparent surface and the details of for example the pillow and the lamp. To be honest: I have no idea how
Any help?
Shader "Custom/VisualWorldSpaceNormals"
{
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f {
half3 worldNormal : TEXCOORD0;
float4 pos : SV_POSITION;
};
v2f vert (float4 vertex : POSITION, float3 normal : NORMAL)
{
v2f o;
o.pos = UnityObjectToClipPos(vertex);
o.worldNormal = UnityObjectToWorldNormal(normal);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 c = 0;
c.rgb = i.worldNormal*0.5+0.5;
return c;
}
ENDCG
}
}
}
Right now your shader is only sampling the vertex normals of the mesh… The details you see in the scene-view option are the normals output from all the shaders on those objects, which most have normal maps that result in more finer normal details being written to the normal buffer.
What you could do instead if you don’t want to have to modify all the different shaders those objects might use… Is simply make a shader that samples the normal buffer at the pixel position, like so:
Shader "Invertex/Utility/ScreenNormals"
{
SubShader
{
Tags { "Queue"="Transparent-50" }
Pass
{
ZTest Off
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _CameraDepthNormalsTexture;
struct v2f {
float4 pos : SV_POSITION;
float4 screenPos : TEXCOORD1;
};
v2f vert(float4 vertex : POSITION)
{
v2f o;
o.pos = UnityObjectToClipPos(vertex);
o.screenPos = ComputeScreenPos(o.pos);
return o;
}
float4 frag(v2f i) : SV_Target
{
float4 depthNorms = tex2D(_CameraDepthNormalsTexture, i.screenPos.xy / i.screenPos.w);
float3 norms = DecodeViewNormalStereo(depthNorms);
norms = mul((float3x3)UNITY_MATRIX_V, norms) * 0.5 + 0.5;
return float4(norms, 1);
}
ENDCG
}
}
}
But to sample the normal buffer, you’ll have to enable the option in your camera to show it, put this script on the camera to have an option to enable/disable the depth normals:
using UnityEngine;
namespace Invertex.Unity.Graphics
{
[RequireComponent(typeof(Camera))]
public class CamDepthNormals : MonoBehaviour
{
[SerializeField] private bool enableDepthNormals = true;
[SerializeField, HideInInspector] private Camera cam;
private void Reset()
{
cam = GetComponent<Camera>();
SetDepthNormalsActive(true);
}
private void OnValidate()
{
SetDepthNormalsActive(enableDepthNormals);
}
public void SetDepthNormalsActive(bool active)
{
if (active) { cam.depthTextureMode |= DepthTextureMode.DepthNormals; }
else { cam.depthTextureMode &= ~DepthTextureMode.DepthNormals; }
enableDepthNormals = active;
}
}
}
1 Like
Invertex:
Right now your shader is only sampling the vertex normals of the mesh… The details you see in the scene-view option are the normals output from all the shaders on those objects, which most have normal maps that result in more finer normal details being written to the normal buffer.
What you could do instead if you don’t want to have to modify all the different shaders those objects might use… Is simply make a shader that samples the normal buffer at the pixel position, like so:
Shader "Invertex/Utility/ScreenNormals"
{
SubShader
{
Tags { "Queue"="Transparent-50" }
Pass
{
ZTest Off
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _CameraDepthNormalsTexture;
struct v2f {
float4 pos : SV_POSITION;
float4 screenPos : TEXCOORD1;
};
v2f vert(float4 vertex : POSITION)
{
v2f o;
o.pos = UnityObjectToClipPos(vertex);
o.screenPos = ComputeScreenPos(o.pos);
return o;
}
float4 frag(v2f i) : SV_Target
{
float4 depthNorms = tex2D(_CameraDepthNormalsTexture, i.screenPos.xy / i.screenPos.w);
float3 norms = DecodeViewNormalStereo(depthNorms);
norms = mul((float3x3)UNITY_MATRIX_V, norms) * 0.5 + 0.5;
return float4(norms, 1);
}
ENDCG
}
}
}
But to sample the normal buffer, you’ll have to enable the option in your camera to show it, put this script on the camera to have an option to enable/disable the depth normals:
using UnityEngine;
namespace Invertex.Unity.Graphics
{
[RequireComponent(typeof(Camera))]
public class CamDepthNormals : MonoBehaviour
{
[SerializeField] private bool enableDepthNormals = true;
[SerializeField, HideInInspector] private Camera cam;
private void Reset()
{
cam = GetComponent<Camera>();
SetDepthNormalsActive(true);
}
private void OnValidate()
{
SetDepthNormalsActive(enableDepthNormals);
}
public void SetDepthNormalsActive(bool active)
{
if (active) { cam.depthTextureMode |= DepthTextureMode.DepthNormals; }
else { cam.depthTextureMode &= ~DepthTextureMode.DepthNormals; }
enableDepthNormals = active;
}
}
}
It does the job! Thank a lot
1 Like