I have a simple raymarch shader that has is applied as a material in a big cube. Everything works fine when i have direction light. However when i switch to point light or any light it looks like it doesnt work. I have tried like everything in terms of #pragma compile or LightMode = Always but still doesnt work.
SubShader
{
Tags{"Queue" = "Transparent" "RenderType"="Transparent" "LightMode"="ForwardBase"}
// No culling or depth
Cull Off ZWrite Off ZTest Always
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdadd
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
float4 screenPos : TEXCOORD3;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.screenPos = ComputeScreenPos(o.vertex);
return o;
}
sampler2D _MainTex;
sampler2D _CameraDepthTexture;
float sdSphere(float3 p, float s)
{
return length(p) - s;
}
float sdf(float3 p)
{
return sdSphere(p-float3(-60,20,-10), 5);
}
float raymarch(float3 rayDir, float3 rayPos,float depth)
{
float result = 0;
int iterations = 128;
float t = 0;
float3 p;
for (int i = 0; i < iterations; i++)
{
p = rayPos + t * rayDir;
if (t > depth || t >300)
return 0.0;
float d = sdf(p);
if (d < 0.01)
{
return 1.0;
}
t += d;
}
return result;
}
fixed4 frag(v2f i) : SV_Target
{
float3 rayDir = normalize(i.worldPos - _WorldSpaceCameraPos.xyz);
float3 rayPos = _WorldSpaceCameraPos;
float depth = LinearEyeDepth(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos)).r);
float result = raymarch(rayDir, rayPos,depth);
fixed4 col = fixed4(1,0,0,result);
return col;
}
ENDCG
}
Unity’s lighting system for the built in forward rendering path is a multi-pass renderer. Each light requires an additional render of the object, with there being separate base and add passes used for the first light and all additional lights.
The ”LightMode” tag is what controls how a pass is used for lighting, should be on the Pass, not the SubShader. “LightMode”=“ForwardBase” explicitly only renders the main directional light and ambient lighting. “LightMode”=“Always” explicitly renders with no lighting. You need a second pass with “LightMode”=“ForwardAdd” to support point and spot lights.
For a raymarching shader this is unfortunate, as it means you need to do the ray marching every pass. Alternatively you could look into supporting deferred rendering instead.
Yes i could do use point light at forwardbase using vertex lights. However i got a better insight on whats the issue. Of course i use the _CameraDepthTexture to stop at opaque geometry. However, what happens is that when i switch to point light i no longer have access to the depthTexture but to some default.
The shader part works fine. If i get too close i can actually see the circle. It seems i dont get depth values. So somehow and for some reason _CameraDepthTexture does not get the depth texture. Why and how can I obtain it, without using another pass.
It seems that i manage to make it work by having two lights one directional and one point using both base and add. However, in the base part i basically returned a white color with opacity zero so that it doesnt take time calculating and raymarching. Since i have to do raymarching to render volumetric rendering, will that be terrible for performance. Also with directional light only, i tried to sample the shadowtexture using UNITY_LIGHT_ATTENUATION but it didnt work either.
Because you don’t have the depth texture enabled on your camera. On desktop, if the main directional light cast shadows, Unity will automatically generate the camera depth texture. When you remove that light that automatic depth texture generation no longer happens. You need to set the depth texture mode on the camera with a c# script.
Alternatively you can have your shader output a depth value and have it compare against the scene depth rather than doing the compare in the shader from the depth texture. Then you don’t need a camera depth texture at all. You can read up on conversations on the topic in these two threads: