Hi everyone. I am making a kind of toon shading style combining textures and normals (thanks to the Noob To Pro Writing Shaders from CGCOOKIE). I implemented the multiple light options by a second pass render, but my question is: How can I detect that there is an obstacle between the light source and my analized model? Here is the image that explain that: Between the lamp and the object there is a "wall", so in real life there will be parts that the light will not reach the object, but on shaders I don't know how to detect the "wall" that is inside the red box. If there is a defined variable that unity has for shaders that can detect other objects, is what I want to know. Thanks for your answers
You're taking about shadows. If you're using surface shaders you just need to add fullforwardshadows to the #pragma surface line. https://docs.unity3d.com/Manual/SL-SurfaceShaders.html If you're using vertex fragment shaders it's a bit more work, but not too bad. There's an example for the base pass in the official documentation. https://docs.unity3d.com/Manual/SL-VertexFragmentShaderExamples.html Adding shadows for the forward add pass is almost identical. You just need to include the same SHADOW lines and use multi_compile_fwdadd_fullshadows instead of multi_compile_fwdbase https://docs.unity3d.com/Manual/SL-MultipleProgramVariants.html
Hi, thanks, the forwardbase shadows Works great but I am having problems with the forwardAdd shadows. I put in the second pass the multi_compile_fwdadd_fullshadows but I have an error message that says: "undeclared identifier UnityDecodeCobeShadowDepth at UnityShadowLibrary", I look for that variable and it says that is related to cubemaps o_o, How can it be configure this kind of shadows?
Shouldn't be anything you need to do particularly special in the shader code. Cube map shadows are used by point lights, and all the special handling should be done by those shadow macros (the lines all in caps). Best guess is this is problem caused by having missing #include lines.
No, you can use pointlights ... once you fix the shader so it compiles properly. Post the whole shader here (inline in a code block, not as an attachment) and we can look at it, otherwise I can't really say what's wrong for sure.
Ok, it is too long but I am still learning how to optimize this, so here it is : Code (CSharp): Shader "UnityCookie/NoobToProTut/Intermediate/10 - Toon Map" { Properties { _Color ("Lit Color", Color) = (1.0,1.0,1.0,1.0) _UnlitColor ("Unlit Color", Color) = (0.5,0.5,0.5,1.0) _MainTex ("Diffuse Texture", 2D) = "white" {} _BumpMap ("Normal Texture", 2D) = "bump" {} _BumpDepth ("Bump Depth", Range(0.0, 1.0)) = 1.0 _DiffuseThreshold ("Lighting Threshold", Range(0.0, 1.0)) = 0.1 // Control the line transition between both colors _Diffusion ("Diffusion", Range(0.0, 1.0)) = 0.0 // Control the blurry the line transition it is _SpecColor ("Specular Color", Color) = (1.0,1.0,1.0,1.0) _Shininess ("Shininess", Range(0.5, 1)) = 1 _SpecDiffusion ("Specular Diffusion", Range(0, 0.99)) = 0.0 // Controls the blurry border line of the specular light _OutlineColor ("Outline Color", Color) = (0.0,0.0,0.0,1.0) _OutlineThickness ("Outline Thickness", Range(0.0, 1.0)) = 0.1 _OutlineDiffusion ("Outline Diffusion", Range(0.0, 1.0)) = 0 } SubShader { // FIRST RENDERER PASS Pass { Tags {"LightMode" = "ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag // compile shader into multiple variants, with and without shadows // (we don't care about any lightmaps yet, so skip these variants) #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight // shadow helper functions and macros #include "AutoLight.cginc" #include "UnityCG.cginc" //user defined variables uniform fixed4 _Color; uniform fixed4 _UnlitColor; uniform sampler2D _MainTex; uniform half4 _MainTex_ST; uniform sampler2D _BumpMap; uniform half4 _BumpMap_ST; uniform fixed _BumpDepth; uniform fixed _DiffuseThreshold; uniform fixed _Diffusion; uniform fixed4 _SpecColor; uniform fixed _Shininess; uniform fixed _SpecDiffusion; uniform fixed4 _OutlineColor; uniform fixed _OutlineThickness; uniform fixed _OutlineDiffusion; //unity defined variables uniform half4 _LightColor0; //base input structs struct vertexInput { half4 vertex : POSITION; half3 normal : NORMAL; half4 texcoord : TEXCOORD0; // Gets the UV map from the texture on the object half4 tangent : TANGENT; // Gets the tangent direction of the normals }; struct vertexOutput { half4 pos : SV_POSITION; half4 tex : TEXCOORD0; SHADOW_COORDS(1) // put shadows data into TEXCOORD1 fixed3 normalDir : TEXCOORD2; fixed4 lightDir : TEXCOORD3; fixed3 viewDir : TEXCOORD4; fixed3 tangentDir : TEXCOORD5; // Store the tangent direction of the normals fixed3 binomialDir : TEXCOORD6; // Store the perpendicular direction between the normal and tangent }; //vertex Function vertexOutput vert(vertexInput v) { vertexOutput o; //normalDirection o.normalDir = normalize( mul( half4( v.normal, 0.0 ), unity_WorldToObject ).xyz ); // calculate directions for bump map o.tangentDir = normalize( mul( unity_ObjectToWorld, v.tangent).xyz); o.binomialDir = normalize( cross( o.normalDir, o.tangentDir) * v.tangent.w); // Store texture coordinates o.tex = v.texcoord; //unity transform position o.pos = mul(UNITY_MATRIX_MVP, v.vertex); //world position half4 posWorld = mul(unity_ObjectToWorld, v.vertex); //view direction o.viewDir = normalize( _WorldSpaceCameraPos.xyz - posWorld.xyz ); //light direction half3 vertexToLightSource = _WorldSpaceLightPos0.xyz - posWorld.xyz; o.lightDir = fixed4( normalize( lerp(_WorldSpaceLightPos0.xyz , vertexToLightSource, _WorldSpaceLightPos0.w) ), lerp(1.0 , 1.0/length(vertexToLightSource), _WorldSpaceLightPos0.w) ); // compute shadows data TRANSFER_SHADOW(o) return o; } //fragment function fixed4 frag(vertexOutput i) : SV_TARGET { // Texture and Bump Maps fixed4 tex = tex2D(_MainTex, i.tex.xy * _MainTex_ST.xy + _MainTex_ST.zw); fixed4 texN = tex2D(_BumpMap, i.tex.xy * _BumpMap_ST.xy + _BumpMap_ST.zw); // Unpack Normal function fixed3 localCoords = fixed3( 2.0 * texN.ag - float2( 1.0, 1.0), _BumpDepth); // Normal transpose matrix fixed3x3 local2WorldTranspose = fixed3x3( i.tangentDir, i.binomialDir, i.normalDir ); // Calculate Bump Direction fixed3 bumpDirection = normalize( mul( localCoords, local2WorldTranspose)); // compute shadow attenuation (1.0 = fully lit, 0.0 = fully shadowed) fixed shadow = smoothstep(0.0, 1.0, SHADOW_ATTENUATION(i)); // Transition Masks fixed diffuseCutoff = saturate( smoothstep( _DiffuseThreshold, 1.0, i.lightDir.w * dot(bumpDirection, i.lightDir.xyz) * shadow) * pow( (2 - _Diffusion), 10)); fixed specularCutoff = saturate( smoothstep( _Shininess, 1.0, i.lightDir.w * dot( reflect( -i.lightDir.xyz, bumpDirection), i.viewDir)) * shadow * pow( ( 2 - _SpecDiffusion), 10)); // Calculate outlines fixed outlineStrenth = saturate( ( dot( i.normalDir, i.viewDir) - _OutlineThickness) * pow( ( 2 - _OutlineDiffusion), 10) + _OutlineThickness); fixed3 outlineOverlay = (_OutlineColor.xyz * _LightColor0.xyz * ( 1 - outlineStrenth)) + outlineStrenth; // Lighting fixed3 ambientLight = tex.xyz * ( 1 - diffuseCutoff) * _UnlitColor.xyz * (_LightColor0.xyz + UNITY_LIGHTMODEL_AMBIENT.xyz); fixed3 diffuseReflection = tex.xyz * (1 - specularCutoff) * _Color.xyz * _LightColor0.xyz * diffuseCutoff; fixed3 specularReflection = specularCutoff * _SpecColor.xyz * tex.a; fixed3 lightFinal = (ambientLight + diffuseReflection) * outlineOverlay + specularReflection; return fixed4(lightFinal, 1.0); } ENDCG } // SECOND RENDERER PASS Pass { Tags {"LightMode" = "ForwardAdd"} Blend SrcAlpha OneMinusSrcColor CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdadd_fullshadows #include "AutoLight.cginc" #include "UnityCG.cginc" //user defined variables uniform fixed4 _Color; uniform fixed4 _UnlitColor; uniform sampler2D _MainTex; uniform half4 _MainTex_ST; uniform sampler2D _BumpMap; uniform half4 _BumpMap_ST; uniform fixed _BumpDepth; uniform fixed _DiffuseThreshold; uniform fixed _Diffusion; uniform fixed4 _SpecColor; uniform fixed _Shininess; uniform fixed _SpecDiffusion; uniform fixed4 _OutlineColor; uniform fixed _OutlineThickness; uniform fixed _OutlineDiffusion; //unity defined variables uniform half4 _LightColor0; //base input structs struct vertexInput { half4 vertex : POSITION; half3 normal : NORMAL; half4 texcoord : TEXCOORD0; half4 tangent : TANGENT; }; struct vertexOutput { half4 pos : SV_POSITION; half4 tex : TEXCOORD0; SHADOW_COORDS(1) fixed3 normalDir : TEXCOORD2; fixed4 lightDir : TEXCOORD3; fixed3 viewDir : TEXCOORD4; fixed3 tangentDir : TEXCOORD5; fixed3 binomialDir : TEXCOORD6; }; //vertex Function vertexOutput vert(vertexInput v) { vertexOutput o; //normalDirection o.normalDir = normalize( mul( half4( v.normal, 0.0 ), unity_WorldToObject ).xyz ); // calculate directions for bump map o.tangentDir = normalize( mul( unity_ObjectToWorld, v.tangent).xyz); o.binomialDir = normalize( cross( o.normalDir, o.tangentDir) * v.tangent.w); // Store texture coordinates o.tex = v.texcoord; //unity transform position o.pos = mul(UNITY_MATRIX_MVP, v.vertex); //world position half4 posWorld = mul(unity_ObjectToWorld, v.vertex); //view direction o.viewDir = normalize( _WorldSpaceCameraPos.xyz - posWorld.xyz ); //light direction half3 vertexToLightSource = _WorldSpaceLightPos0.xyz - posWorld.xyz; o.lightDir = fixed4( normalize( lerp(_WorldSpaceLightPos0.xyz , vertexToLightSource, _WorldSpaceLightPos0.w) ), lerp(1.0 , 1.0/length(vertexToLightSource), _WorldSpaceLightPos0.w) ); // compute shadows data TRANSFER_SHADOW(o) return o; } //fragment function fixed4 frag(vertexOutput i) : SV_TARGET { // Texture and Bump Maps fixed4 tex = tex2D(_MainTex, i.tex.xy * _MainTex_ST.xy + _MainTex_ST.zw); fixed4 texN = tex2D(_BumpMap, i.tex.xy * _BumpMap_ST.xy + _BumpMap_ST.zw); // Unpack Normal function fixed3 localCoords = fixed3( 2.0 * texN.ag - float2( 1.0, 1.0), _BumpDepth); // Normal transpose matrix fixed3x3 local2WorldTranspose = fixed3x3( i.tangentDir, i.binomialDir, i.normalDir ); // Calculate Bump Direction fixed3 bumpDirection = normalize( mul( localCoords, local2WorldTranspose)); // compute shadow attenuation (1.0 = fully lit, 0.0 = fully shadowed) fixed shadow = smoothstep(0.0, 1.0, SHADOW_ATTENUATION(i)); // Transition Masks fixed diffuseCutoff = saturate( smoothstep( _DiffuseThreshold, 1.0, i.lightDir.w * shadow * dot(bumpDirection, i.lightDir.xyz)) * pow( (2 - _Diffusion), 10)); fixed specularCutoff = saturate( smoothstep( _Shininess, 1.0, i.lightDir.w * dot( reflect( -i.lightDir.xyz, bumpDirection), i.viewDir)) * shadow * pow( ( 2 - _SpecDiffusion), 10)); // Calculate outlines fixed outlineStrenth = saturate( ( dot( i.normalDir, i.viewDir) - _OutlineThickness) * pow( ( 2 - _OutlineDiffusion), 10) + _OutlineThickness); fixed3 outlineOverlay = (_OutlineColor.xyz * ( 1 - outlineStrenth)) + outlineStrenth; // Lighting fixed3 ambientLight = tex.xyz * ( 1 - diffuseCutoff) * _UnlitColor.xyz; fixed3 diffuseReflection = tex.xyz * (1 - specularCutoff) * _Color.xyz * _LightColor0.xyz * diffuseCutoff; fixed3 specularReflection = specularCutoff * _SpecColor.xyz * _LightColor0.xyz * tex.a; fixed3 lightFinal = (ambientLight + diffuseReflection) * outlineOverlay + specularReflection; return fixed4(lightFinal, 1.0); } ENDCG } // SHADOW CASTER RENDERING PASS Pass { Tags {"LightMode" = "ShadowCaster"} CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_shadowcaster #include "UnityCG.cginc" //base input structs struct v2f { V2F_SHADOW_CASTER; }; v2f vert (appdata_base v) { v2f o; TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) return o; } half4 frag(v2f i) : SV_Target { SHADOW_CASTER_FRAGMENT(i) } ENDCG } } //Fallback "Specular" }
The order of #include lines can matter! Try swapping them so the #include "UnityCG.cginc" is before the #include "AutoLight.cginc" line in the add pass specifically, though it wouldn't hurt to do it for both.
Oh yeah, it Works, Thanks!!!. Besides it is a little complicate for me to understand this kind of programming. I noticed some other details, when the object recieves a shadow I notice that this shadow is a little sharpy, different what I have on the lighting threshold transition. As you can see on the code, I try to put a smoothstep function on the "shadow" variable, but the recieve shadow still seems sharpy, and the bump map doesn't follow the shadow at all. How can I change the shadow variable to work as the "diffuseCutoff" that has that smooth chagne between the shadow and the light?
Not really, no. You should read up on shadow maps. Cat like coding has a decent tutorial delving deep into shadow maps in Unity. You can try turning on soft shadows on your light to help some, however Unity's point light shadows in particular are kind of terrible and always a little harsh. There are some assets out there that soften the shadows at the cost of performance and / or noise.