Unity Shader - 绘制半透明效果 + 投影 + 遮挡绘制 (半透明物体接收阴影还不知道怎么弄)

141 篇文章 31 订阅

Shader

// jave.lin 2019.06.05
Shader "Custom/Player"
{
    Properties
    {
		_TintColor ("Tint Color", Color) = (1,1,1,1)
        _Tex ("Tex", 2D) = "white" {}
        _BackTex("BackTex", 2d) = "white" {}
        _MaskColor ("Mask Color", Color) = (1,1,1,0.5)
        _BackIntensity ("BackIntensity", Range(0.0, 1.0)) = 0.2
        _FrontIntensity ("FrontIntensity", Range(0.0, 1.0)) = 0.2
    }
    SubShader
    {
        // Project Shadow
        Pass
        {
			Name "ShadowCaster"
			Tags { "LightMode" = "ShadowCaster" }
 
			CGPROGRAM

            #pragma vertex vertShadow
            #pragma fragment fragShadow
            #pragma multi_compile_shadowcaster

            #include "UnityCG.cginc"
            
            struct v2f
            {
                V2F_SHADOW_CASTER;
            };

            v2f vertShadow(appdata_base v)
            {
                v2f o;
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
                return o;
            }

            fixed4 fragShadow(v2f i) : SV_Target
            {
                SHADOW_CASTER_FRAGMENT(i);
            }

			ENDCG
		}

        // Draw Back
        Pass
        {
            Name "DrawBack"
            Tags { "Queue" = "Transparent" "RenderType" = "Transparent" }
            Blend SrcAlpha OneMinusSrcAlpha
            ZWRITE Off
            Cull Front

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile SHADOWS_SCREEN

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            struct a2f
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                SHADOW_COORDS(2)
                float2 uv : TEXCOORD3;
                fixed3 diffuse : COLOR0;
                fixed3 ambient : COLOR1;
            };

            fixed4 _TintColor;
            sampler2D _Tex;
            float4 _Tex_ST;
            float _BackIntensity;

            v2f vert(a2f v)
            {
                v2f o = (v2f)0;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _Tex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                fixed3 normalDir = normalize(mul(v.normal,(float3x3)unity_WorldToObject));
                o.diffuse = _LightColor0.rgb * ((dot(lightDir,normalDir) + 1) * 0.5);
                o.ambient = UNITY_LIGHTMODEL_AMBIENT;
                TRANSFER_SHADOW(o);
                return o;
            }

            fixed4 frag(v2f i) : SV_TARGET
            {
                //fixed shadow = SHADOW_ATTENUATION(i);
                UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
                fixed3 albedo = tex2D(_Tex, i.uv) * _TintColor.rgb;
                fixed3 diffuse = i.diffuse * albedo * _BackIntensity;
                atten = max(0.5, atten);
                return fixed4(i.ambient + diffuse * atten, _TintColor.a);
            }

            ENDCG
        }
        // Draw Front
        Pass
        {
            Name "DrawFront"
            Tags { "Queue" = "Transparent" "RenderType" = "Transparent" }
            Blend SrcAlpha OneMinusSrcAlpha
            ZWRITE Off

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile SHADOWS_SCREEN

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            struct a2f
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float3 worldNormal : TEXCOORD0;
                float3 worldPos : TEXCOORD1;
                SHADOW_COORDS(2)
                float2 uv : TEXCOORD3;
                fixed3 diffuse : COLOR0;
                fixed3 ambient : COLOR1;
            };

            fixed4 _TintColor;
            sampler2D _Tex;
            float4 _Tex_ST;
            float _FrontIntensity;

            v2f vert(a2f v)
            {
                v2f o = (v2f)0;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _Tex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                fixed3 normalDir = normalize(mul(v.normal,(float3x3)unity_WorldToObject));
                o.diffuse = _LightColor0.rgb * ((dot(lightDir,normalDir) + 1) * 0.5);
                o.ambient = UNITY_LIGHTMODEL_AMBIENT;
                TRANSFER_SHADOW(o);
                return o;
            }

            fixed4 frag(v2f i) : SV_TARGET
            {
                //fixed shadow = SHADOW_ATTENUATION(i);
                UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
                fixed3 albedo = tex2D(_Tex, i.uv) * _TintColor.rgb;
                fixed3 diffuse = i.diffuse * albedo * _FrontIntensity;
                atten = max(0.5, atten);
                return fixed4(i.ambient + diffuse * atten, _TintColor.a);
            }

            ENDCG
        }
        // Draw Mask
        Pass
		{
            Name "DrawMask"
			ZTest Greater
			ZWrite Off
			Blend SrcAlpha OneMinusSrcAlpha

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
			};

			float4 _MaskColor;
			
			v2f vert (appdata v)
			{
				v2f o = (v2f)0;
				o.vertex = UnityObjectToClipPos(v.vertex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				return _MaskColor;
			}
			ENDCG
		}
    }
	FallBack "Diffuse"
}

Runtime

  • 下面的GIF中墙不是透明的
  • 只有那个长方体是半透明的
  • 图中可以看到有投影阴影
  • 有遮挡绘制(一般很多3D游戏主角被挡住都会绘制一些半透明的轮廓)
    在这里插入图片描述

查看内部的背面绘制
在这里插入图片描述

总结

  • Unity 自带的Standard 材质的RenderMode:Fade/Transparent也是接收不了阴影的
  • 而且Fade/Transparent中,没有绘制背面,对半透明物体来,效果不是很好,除非说半透明物体薄的像张纸,那还好一些
  • 理论上是可以半透明 + 接收阴影的
  • 甚至还可以实现半透明阴影的强弱,和投影的颜色
  • 不过目前我对块底层不了解

因为目前我们是通过Unity 自带的 ShadowMap实现方式

ShadowMap大致原理:

  • 所有的需要投影的对象通过从灯光的角度来拍射出深度图
  • 然后再开始渲染非透明对象,在片段着色阶段,得出像素的坐标,再转换到灯光坐标系对比顶点坐标Z与灯光角度投影出来的深度图的值,如果深度图的值比顶点Z值小,说明此片段像素在阴影中,确认中阴影中后,在绘制时用一些数值来影响最终的RGB值(亮度)即可,阴影亮度会第一下所以讲RGB同时降低,这个降低系数值一般配置中定,如unity Light中的Shadow的Intensity决定,0.0~1.0,次值越小说明阴影越淡,越大则越暗的阴影,所以可以推断出,unity最终使用时:
    // fragment phase
    float _ShadowIntensity; // 类似上面的Intensity
    fixed4 frag(...)
    {
    	fixed4 color = ...; // may be : ambient + tex2D * diffuse * tintColor;
    	color.rgb = color.rgb * (1 - _ShadowIntensity);
    	return color;
    }

unity中第一点是从shader中找第一个Pass的Tags { “LightMode” = “ShadowCaster” }的作为投影出深度图的处理。(如:我们上面提供的第一个Pass),如果用户自定义的Pass没找到,unity将会到Fallback中指定的内置Shader去找,如果没有指定Fallback shader或是再Fallback shader也没有找到,那么将取消此对象投影处理

再上面我们自定义的ShadowCaster 的Pass中,可以看到此Pass看不到太多的实现细节,基本都再#include对应的宏处理了(我尝试过看Unity的GCInclude下的shader,看得我云里雾里的,太多,如果要看某个宏,可以使用Sublime、Notepad++使用字符方式来搜索,即可马上看到对应代码)

如果说,可以控制处理ShadowCaster的Pass中,除了写入深度图缓存外,再将颜色值写入类似帧缓存的地方,在渲染对象是,读取深度图确定在阴影后,在读取帧缓存取颜色,那就可以实现带颜色的阴影叠加了(虽然这个透射光的功能可以用光线追踪来实现,但光线追踪性能消耗太大,一般只能用于离线渲染的非实时渲染),此外阴影强弱还可以通过帧缓存中的alpha来处理了

Shadow Map实现

我之后实现了ShadowMap:Unity Shader - Custom DirectionalLight ShadowMap 自定义方向光的ShadowMap

现在终于知道为何Unity内置的半透明物体为何接收不了阴影了,因为Unity的阴影是在:SSS(Screen Space Shadow,屏幕空间的阴影)中收集的。

而SSS需要先取屏幕不透明物体的深度纹理,然后对每个像素还原到WorldSpace,再转换到LightSpace处计算是否阴影中。

而我们半透明物体是不写深度的,所以屏幕深度纹理就没有半透明的信息。

这就是原因了。

我在那篇文章中有实现:
在这里插入图片描述
虽然可以接收阴影了。
但是半透明的物体,没有投射阴影是很奇怪的。

Project

链接: https://pan.baidu.com/s/1YNB1YykA0cXsKby3VWM4Qg 提取码: awdg 复制这段内容后打开百度网盘手机App,操作更方便哦

测试时的Unity 版本为2018.3.0f2
打开TriggerTestScene场景
选中Hierarchy中的"Player",就是我们GIF中的长方体

References

  • 8
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值