- Home /
Custom shadow mapping not working properly.
Hello,
I am trying to create a shader that used shadow mapping to render shadows. I am using a Camera that renders the depth to a texture from the light's point of view and then I am passing that to a shader that uses tex2Dproj to calculate the shadowed fragments.
The first problem I am having is that a shadow is cast on the floor from no obvious light occluder as shown here:
The red cube is indicating the light's position (and of course it's invisible so it's not casting any shadows)
The second issue I am having is that the small white cube on the right hand side is casting a shadow on the wall but this is drawn on the back side of that wall.
This is my shader code
Shader "Custom/Custom Shadows"
{
Properties
{
_Color("Main Color", Color) = (1,1,1,1)
_MainTex("Base (RGB) Trans (A)", 2D) = "white" {}
}
SubShader
{
ZWrite On
ZTest LEqual
Cull Back
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f
{
float4 vertex : SV_POSITION;
float4 world : TEXCOORD1;
float2 texcoord : TEXCOORD0;
float4 shadowProj : TEXCOORD2;
};
sampler2D _MainTex;
fixed4 _Color;
sampler2D _shadowMap;
float4x4 _textureMatrix;
v2f vert (appdata_base v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.world = mul(UNITY_MATRIX_M, v.vertex);
o.texcoord = v.texcoord;
o.shadowProj = mul(_textureMatrix, mul(UNITY_MATRIX_M, v.vertex));
return o;
}
bool in_frustum(float4 shadowProj)
{
return shadowProj.w > 0.0 &&
shadowProj.x >= 0.0 &&
shadowProj.x <= shadowProj.w &&
shadowProj.y >= 0.0 &&
shadowProj.y <= shadowProj.w &&
shadowProj.z > 0.0 &&
shadowProj.z < shadowProj.w;
}
fixed4 frag (v2f i) : SV_Target
{
float shadow = 1.0;
if (in_frustum(i.shadowProj))
{
shadow = tex2Dproj(_shadowMap, i.shadowProj).r;
/*if (shadow < 1.0)
shadow = 0.0;*/
}
return shadow * _Color * tex2D(_MainTex, i.texcoord);
}
ENDCG
}
}
}
This is the script I am using to pass the shadow map in the shader
using UnityEngine;
namespace Assets.MyTest
{
/// <summary>
/// Calculates the texture matrix and assigns it along
/// with the depth texture to the replaced shader
/// </summary>
[RequireComponent(typeof(Camera))]
class Occlussion : MonoBehaviour
{
/// <summary>
/// The camera to use as our light source
/// </summary>
public Camera source = null;
/// <summary>
/// The shader to use for the main camera
/// </summary>
public Shader replacementShader = null;
/// <summary>
/// The camera attached on this GameObject
/// </summary>
[SerializeField, HideInInspector]
private Camera m_camera;
/// <summary>
/// Bias matrix to convert a light's NDC-space X,Y coordinate (-1..1, -1..1) into (0..1, 0..1)
/// for use as a texture coordinate for shadow map texture lookup
/// </summary>
[SerializeField]
Matrix4x4 m_biasMatrix = new Matrix4x4()
{
m00 = 0.5f, m01 = 0.0f, m02 = 0.0f, m03 = 0.5f,
m10 = 0.0f, m11 = 0.5f, m12 = 0.0f, m13 = 0.5f,
m20 = 0.0f, m21 = 0.0f, m22 = 0.5f, m23 = 0.5f,
m30 = 0.0f, m31 = 0.0f, m32 = 0.0f, m33 = 1.0f,
};
void Reset()
{
//get the camera attached on this gameobject
m_camera = GetComponent<Camera>();
//reset the source and shader values to null
source = null;
replacementShader = null;
}
void Awake()
{
//if the script was not attached from the editor this will be null
if (m_camera == null)
m_camera = GetComponent<Camera>();
}
void OnEnable()
{
//replace the default shader with the custom
m_camera.SetReplacementShader(replacementShader, string.Empty);
//make the camera to calculate only depth values
source.depthTextureMode = DepthTextureMode.Depth;
//create a new render texture
source.targetTexture = new RenderTexture(Screen.width, Screen.height, 16, RenderTextureFormat.Depth);
}
void OnDisable()
{
//check if the camera has been destroyed and reset the shader
if (m_camera != null)
m_camera.ResetReplacementShader();
//check if the camera used as our light source has been destroyed and reset it
if (source != null)
{
source.depthTextureMode = DepthTextureMode.None;
source.targetTexture = null;
}
}
void Update()
{
//set the depth texture on the shader
Shader.SetGlobalTexture("_shadowMap", source.targetTexture);
//calculate the texture matrix -> bias_Mat * (projection_Mat * view_Mat)
Matrix4x4 textureMatrix = m_biasMatrix * (source.projectionMatrix * source.worldToCameraMatrix);
//and assign it on the shader as well
Shader.SetGlobalMatrix("_textureMatrix", textureMatrix);
}
void OnDrawGizmos()
{
if (source != null)
{
Gizmos.color = source.enabled ? Color.green : Color.red;
Gizmos.matrix = Matrix4x4.TRS(source.transform.position, source.transform.rotation, source.transform.lossyScale);
Gizmos.DrawFrustum(source.transform.position, source.fieldOfView, source.farClipPlane, source.nearClipPlane, source.aspect);
}
}
}
}
The depth values from the light's POV that are written in the texture seem to be correct.
Here is a link to the project for further details https://dl.dropboxusercontent.com/u/101866346/Custom%20Shadow%20Mapping.zip
Notifying user @KATARAMENOS1988 who has an interest to the subject
Answer by Curious-George · May 19, 2017 at 01:51 PM
One thing you seem to be doing wrong is using the value from the shadow map directly. You should be comparing it to i.shadowProj.z
float depth = tex2Dproj(_shadowMap, i.shadowProj).r;
float shadow = depth > i.shadowProj.z ? 1.0 : 0.5;
return shadow * _Color * tex2D(_MainTex, i.texcoord);
Also, I'm not sure how you're rendering your depth texture, but mine was being rendered as inverted, forcing me to use (1.0 - depth) in my shader.
Your answer
Follow this Question
Related Questions
Weird graphics issue on iPad 0 Answers
Shadow Black Lines Glitches 0 Answers
Invisible Emissive GameObject? 0 Answers
Good Soft Shadows with HDRP 2 Answers