You might have to actually jump into the code and Frankenstein it - I donât even have a clue though, how that would look or work. I tried doing this a little and realized it wasnât worth the time for my game. I spent a day or so tinkering around trying to add specular lighting to the 2d rendererâŚ
For me, I would simply put simple-lit material on your sprite and use 3d lights. I would probably use a normal map with the sprites though - even if it is a simple up pointing normal so they are lit better by lights from above. You would have to play with it. I wanted something similar, but decided I wanted 2d lights and their âfogâ more than 3d. Iâm actually still using 3d objects, but Iâm using the 2d render asset. Its fine so long as you donât rotate the camera.
Hi @VEWO , did you find a solution about this?
I have the same question.
convert sprite into mesh and then use material
So I was able to find a hack to get this kind of thing to work using Stencil on copies of some built-in shaders.
The idea is to tackle this in 2 passes:
- Use the Sprites-Default shader to draw the sprite in the world, while adding a stencil to it to mark each pixel shaded by it.
- Use the Lit shader from URP, but only filter it to operate on pixels that were drawn into by the sprite shader in the previous pass.
The sprite is drawn normally, unshadowed. The magic happens in the 2nd step.
What this does is basically put up a plane on top of your sprite, which receives shadows via the lit shader, but only draws the pixels that the sprite occupies.
The full details for achieving this:
Create a Shadowed Sprite shader:
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
Shader "Sprites/Shadowed"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
[HideInInspector] _RendererColor ("RendererColor", Color) = (1,1,1,1)
[HideInInspector] _Flip ("Flip", Vector) = (1,1,1,1)
[PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {}
[PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Cull Off
Lighting Off
ZWrite Off
Blend One OneMinusSrcAlpha
Stencil
{
Ref 584
Comp Always
Pass Replace
Fail Keep
ZFail Keep
}
Pass
{
CGPROGRAM
#pragma vertex SpriteVert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile_instancing
#pragma multi_compile_local _ PIXELSNAP_ON
#pragma multi_compile _ ETC1_EXTERNAL_ALPHA
#include "UnitySprites.cginc"
fixed4 frag(v2f IN) : SV_Target
{
fixed4 c = SpriteFrag(IN);
if (c.a == 0)
{
discard;
}
return c;
}
ENDCG
}
}
}
This is just the Sprite-Default shader with the following 2 changes:
- Added the stencil which will let other shaders know which pixels on the screen were drawn into by this shader.
- Added an intermediate step to the fragment shader which will discard any pixels that draw transparent pixels (The shader runs on every pixel of the sprite, including transparent ones, so unless this is called, the shadow will be applied to the full rect of the sprite instead of just the visible parts of it) (Note: This works because the âdiscardâ keyword also discards Stencil information of the pixel and not just the color. This was an annoyance to me many a-time in the past, but this time it actually works in our favor).
Next, we need to create a ShadowOnly variant of the Lit shader from the URP package:
Shader "Universal Render Pipeline/Lit - Shadows Only"
{
Properties
{
// Specular vs Metallic workflow
[HideInInspector] _WorkflowMode("WorkflowMode", Float) = 1.0
[MainColor] _BaseColor("Color", Color) = (1,1,1,1)
[MainTexture] _BaseMap("Albedo", 2D) = "white" {}
_Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
_Smoothness("Smoothness", Range(0.0, 1.0)) = 0.5
_GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0
_SmoothnessTextureChannel("Smoothness texture channel", Float) = 0
[Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
_MetallicGlossMap("Metallic", 2D) = "white" {}
_SpecColor("Specular", Color) = (0.2, 0.2, 0.2)
_SpecGlossMap("Specular", 2D) = "white" {}
[ToggleOff] _SpecularHighlights("Specular Highlights", Float) = 1.0
[ToggleOff] _EnvironmentReflections("Environment Reflections", Float) = 1.0
_BumpScale("Scale", Float) = 1.0
_BumpMap("Normal Map", 2D) = "bump" {}
_OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
_OcclusionMap("Occlusion", 2D) = "white" {}
_EmissionColor("Color", Color) = (0,0,0)
_EmissionMap("Emission", 2D) = "white" {}
// Blending state
[HideInInspector] _Surface("__surface", Float) = 0.0
[HideInInspector] _Blend("__blend", Float) = 0.0
[HideInInspector] _AlphaClip("__clip", Float) = 0.0
[HideInInspector] _SrcBlend("__src", Float) = 1.0
[HideInInspector] _DstBlend("__dst", Float) = 0.0
[HideInInspector] _ZWrite("__zw", Float) = 1.0
[HideInInspector] _Cull("__cull", Float) = 2.0
_ReceiveShadows("Receive Shadows", Float) = 1.0
// Editmode props
[HideInInspector] _QueueOffset("Queue offset", Float) = 0.0
// ObsoleteProperties
[HideInInspector] _MainTex("BaseMap", 2D) = "white" {}
[HideInInspector] _Color("Base Color", Color) = (1, 1, 1, 1)
[HideInInspector] _GlossMapScale("Smoothness", Float) = 0.0
[HideInInspector] _Glossiness("Smoothness", Float) = 0.0
[HideInInspector] _GlossyReflections("EnvironmentReflections", Float) = 0.0
}
SubShader
{
// Universal Pipeline tag is required. If Universal render pipeline is not set in the graphics settings
// this Subshader will fail. One can add a subshader below or fallback to Standard built-in to make this
// material work with both Universal Render Pipeline and Builtin Unity Pipeline
Tags{"RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" "IgnoreProjector" = "True" "Queue"="Transparent"}
LOD 300
Stencil
{
Ref 584
Comp Equal
Pass Keep
Fail Zero
}
// ------------------------------------------------------------------
// Forward pass. Shades all light in a single pass. GI + emission + Fog
Pass
{
// Lightmode matches the ShaderPassName set in UniversalRenderPipeline.cs. SRPDefaultUnlit and passes with
// no LightMode tag are also rendered by Universal Render Pipeline
Name "ForwardLit"
Tags{"LightMode" = "UniversalForward"}
Blend[_SrcBlend][_DstBlend]
ZWrite[_ZWrite]
Cull[_Cull]
HLSLPROGRAM
// Required to compile gles 2.0 with standard SRP library
// All shaders must be compiled with HLSLcc and currently only gles is not using HLSLcc by default
#pragma prefer_hlslcc gles
#pragma exclude_renderers d3d11_9x
#pragma target 2.0
// -------------------------------------
// Material Keywords
#pragma shader_feature _NORMALMAP
#pragma shader_feature _ALPHATEST_ON
#pragma shader_feature _ALPHAPREMULTIPLY_ON
#pragma shader_feature _EMISSION
#pragma shader_feature _METALLICSPECGLOSSMAP
#pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma shader_feature _OCCLUSIONMAP
#pragma shader_feature _SPECULARHIGHLIGHTS_OFF
#pragma shader_feature _ENVIRONMENTREFLECTIONS_OFF
#pragma shader_feature _SPECULAR_SETUP
#pragma shader_feature _RECEIVE_SHADOWS_OFF
// -------------------------------------
// Universal Pipeline keywords
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
#pragma multi_compile _ _SHADOWS_SOFT
#pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE
// -------------------------------------
// Unity defined keywords
#pragma multi_compile _ DIRLIGHTMAP_COMBINED
#pragma multi_compile _ LIGHTMAP_ON
#pragma multi_compile_fog
//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
#pragma vertex LitPassVertex
#pragma fragment LitPassFragment
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitForwardPass.hlsl"
ENDHLSL
}
Pass
{
Name "ShadowCaster"
Tags{"LightMode" = "ShadowCaster"}
ZWrite On
ZTest LEqual
Cull[_Cull]
HLSLPROGRAM
// Required to compile gles 2.0 with standard srp library
#pragma prefer_hlslcc gles
#pragma exclude_renderers d3d11_9x
#pragma target 2.0
// -------------------------------------
// Material Keywords
#pragma shader_feature _ALPHATEST_ON
//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
#pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma vertex ShadowPassVertex
#pragma fragment ShadowPassFragment
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl"
ENDHLSL
}
Pass
{
Name "DepthOnly"
Tags{"LightMode" = "DepthOnly"}
ZWrite On
ColorMask 0
Cull[_Cull]
HLSLPROGRAM
// Required to compile gles 2.0 with standard srp library
#pragma prefer_hlslcc gles
#pragma exclude_renderers d3d11_9x
#pragma target 2.0
#pragma vertex DepthOnlyVertex
#pragma fragment DepthOnlyFragment
// -------------------------------------
// Material Keywords
#pragma shader_feature _ALPHATEST_ON
#pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl"
ENDHLSL
}
// This pass it not used during regular rendering, only for lightmap baking.
Pass
{
Name "Meta"
Tags{"LightMode" = "Meta"}
Cull Off
HLSLPROGRAM
// Required to compile gles 2.0 with standard srp library
#pragma prefer_hlslcc gles
#pragma exclude_renderers d3d11_9x
#pragma vertex UniversalVertexMeta
#pragma fragment UniversalFragmentMeta
#pragma shader_feature _SPECULAR_SETUP
#pragma shader_feature _EMISSION
#pragma shader_feature _METALLICSPECGLOSSMAP
#pragma shader_feature _ALPHATEST_ON
#pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma shader_feature _SPECGLOSSMAP
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitMetaPass.hlsl"
ENDHLSL
}
Pass
{
Name "Universal2D"
Tags{ "LightMode" = "Universal2D" }
Blend[_SrcBlend][_DstBlend]
ZWrite[_ZWrite]
Cull[_Cull]
HLSLPROGRAM
// Required to compile gles 2.0 with standard srp library
#pragma prefer_hlslcc gles
#pragma exclude_renderers d3d11_9x
#pragma vertex vert
#pragma fragment frag
#pragma shader_feature _ALPHATEST_ON
#pragma shader_feature _ALPHAPREMULTIPLY_ON
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/Utils/Universal2D.hlsl"
ENDHLSL
}
}
FallBack "Hidden/Universal Render Pipeline/FallbackError"
CustomEditor "UnityEditor.Rendering.Universal.ShaderGUI.LitShader"
}
The only change here is the addition of the stencil that will filter out any pixels that werenât rendered by the sprite.
Then, all we need is to create materials for the 2 shaders, making sure that the Lit-ShadowsOnly material uses the Transparent surface type, Multiply Blending mode and a priority that has it take effect AFTER the sprite shader.
(Important note: Up to URP version 7.4.0 there was a bug where the Priority wouldnât be added correctly to offset the rendering queue of the shader, and would have the (-50, 50) priority range in the inspector translated to the (3000, 3100) rendering queue instead of (2950, 3050). The tooltip for priority still seems incorrect as it claims that higher priority values get rendered first, which is the opposite of what happens when increasing the queue. So just a heads up that this screenshot is from a project using 7.4.0 and this might be different for you if youâre on a lower version)
To apply the shadows, you need to use the shader on a quad that will cover the sprite you want shadowed:
Note: You can also place this across the entire screen and itâll process all sprites on the screen that use the Sprite-Shadowed shader above. It doesnât necessarily need to be placed on top of a specific sprite.
This approach isnât ideal in terms of performance, as it requires either a screen-wide shader, or an extra 5-pass shader for every sprite in the scene.
But I guess itâs at least a way to achieve the effect for now, until more proper support is provided officially.
This is how I did it, without using scripts⌠Youâll need 2 cameras for this and youâll need to get the Light Weight Render Pipeline. Once youâve setup your Render pipeline, you can add both renders to the pipeline youâve created. Next, youâll need two cameras. Camera âAâ will render all of your sprites and Camera âBâ will render your 3D assets. Camera âAâ will need to be an overlay camera and Camera âBâ will need to be your base camera. Once youâve setup everything up, simply hit the plus button on the camera in the section for âStackâ, then add your overlay camera to it. You should now have both 3D and 2D lights interacting with 3D objects and Sprites.
I found a solution that works perfectly.
I canât get this solutiuon to work with a sprite renderer and the standard 2d animation implimentation using sprite sheets and updating the sprite in the sprite renderer in a unity animation.
Is this how the shader is intended to work or am I missing something?
I realize this is quite old. But it is exactly what I am looking for! Can you elaborate on how to âsetup your render pipelineâ?
if that is your question, you need to start by reading unityâs docs related to rendering, and maybe watch some tutorials on using the URP, too. in general, when programming, always scour the docs thoroughly before asking your question. they are your first and most important resource. they will answer all of your basic questions.
in the age of google this is extremely easy. just look up âunity [whatever thing you are confused about]â and find a page whose url starts with âdocs.unity3d.comâ.
if youâre confused about anything you read, open unity and fuck around with the mechanic until you understand how it works. use the docs as your roadmap. then come to the forums when youâre ready for more. most programming advice in general, including game dev advice, will not be useful until you have that foundational knowledge.
Compared to every reply, this is the most useless.