【UnityShader】双面透光布料材质
来源:互联网 发布:厦门微思网络 编辑:程序博客网 时间:2024/03/28 22:23
最近准备尝试写一个可以双面接受阴影,并且有类似透光效果的布料shader,大致效果如下:
总的来说就是在布料逆光的时候可以看到背面物体的阴影
实现的原理其实非常简单,unity本身已经提供了一整套阴影渲染的方案,我们所需要做的只是根据法线判断正反面,并在反面正确的渲染出阴影和光照即可,注意还需要自己实现ShadowCaster,以保证光照在背面时也可以投射阴影。
接下来分解一下实现步骤:
一、双面渲染
这涉及到渲染管线中的背面剔除,在渲染封闭的几何体时,其背面通常都是不可见的,因此不渲染背面可以提高渲染性能,即开启背面剔除功能,默认情况下背面剔除是开启的,我们需要在shader中设置cull off来关闭背面剔除。
二、接受阴影:
unity提供了内置的shadowmap采样api:
1.首先需要在顶点输出结构中使用SHADOW_COORDS定义shadowmap uv
2.顶点函数的结尾调用TRANSFER_SHADOW计算灯光空间坐标等
3.片段函数中调用UNITY_LIGHT_ATTENUATION计算阴影
如下代码
SubShader{ Tags { "RenderType"="Opaque" } LOD 100 Pass { Tags { "LightMode"="ForwardBase" } cull off CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fog #pragma multi_compile_fwdbase #include "UnityCG.cginc" #include "Lighting.cginc" #include "AutoLight.cginc" struct v2f { float2 uv : TEXCOORD0; float3 worldNormal : TEXCOORD1; float4 worldPos : TEXCOORD2; UNITY_FOG_COORDS(3) SHADOW_COORDS(4)//定义一个字段"_ShadowCoord:TEXCOORD4" float4 pos : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; v2f vert (appdata_base v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); UNITY_TRANSFER_FOG(o,o.pos); TRANSFER_SHADOW(o);//将顶点转换到灯光空间下 return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos)//投射shadowmap,并比较深度,以此计算出atten值 float3 litDir = normalize(UnityWorldSpaceLightDir(i.worldPos.xyz)); float ndl = abs(dot(i.worldNormal, litDir))*0.5+0.5; col.rgb = col.rgb * (UNITY_LIGHTMODEL_AMBIENT.rgb + _LightColor0.rgb* ndl*atten); UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDCG }}fallback "Diffuse" //fallback为必须,可以不返回Diffuse,但至少要返回一个带有shadowcaster的shader以投射阴影
但一旦旋转灯光到背面,可以发现阴影接受出现异常,且无法投射阴影:
这是因为我们在shader最后使用了fallback “Diffuse”,即使用了Diffuse的默认ShadowCaster pass来投射阴影,而这个pass是不渲染背面的,即当灯光照射背面时,背面不会渲染到shadowmap,我们可以打开framedebugger来验证:
正面(帆和桅杆都渲染到shadowmap):
背面(只渲染了桅杆和地面,没渲染帆):
三、背面也正确的接受阴影
在了解了上述原因之后,接下来只需要去掉fallback,手动实现一个双面渲染的shadowcaster即可:
SubShader{ Tags { "RenderType"="Opaque" } LOD 100 Pass { Tags { "LightMode"="ForwardBase" } cull off CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fog #pragma multi_compile_fwdbase #include "UnityCG.cginc" #include "Lighting.cginc" #include "AutoLight.cginc" struct v2f { float2 uv : TEXCOORD0; float3 worldNormal : TEXCOORD1; float4 worldPos : TEXCOORD2; UNITY_FOG_COORDS(3) SHADOW_COORDS(4) float4 pos : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; v2f vert (appdata_base v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); UNITY_TRANSFER_FOG(o,o.pos); TRANSFER_SHADOW(o); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos) float3 litDir = normalize(UnityWorldSpaceLightDir(i.worldPos.xyz)); float ndl = abs(dot(i.worldNormal, litDir))*0.5+0.5; col.rgb = col.rgb * (UNITY_LIGHTMODEL_AMBIENT.rgb + _LightColor0.rgb* ndl*atten); UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDCG } Pass{ Name "ShadowCaster" Tags{ "LightMode" = "ShadowCaster" } cull off//确保正面和背面都正确的渲染到shadowmap CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_shadowcaster #include "UnityCG.cginc" struct v2f { V2F_SHADOW_CASTER; }; v2f vert(appdata_base v) { v2f o; TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) return o; } float4 frag(v2f i) : SV_Target { SHADOW_CASTER_FRAGMENT(i) } ENDCG }}
四、计算背面光照:
现在阴影的投射和接受都正常了,接下来需要计算光照,考虑到正面和背面的处理需要有所不同,且要保证正面和背面在面对灯光的不同角度时效果相同,即:无论正面还是背面(这里指三角面的正面背面)在背光还是顺光时效果应该一致。
三角面的朝向实际上容易通过法线和视线的夹角求得:
float ndvSign = sign(dot(i.worldNormal, viewDir));//通过法线和视线的点积判断当前的面的朝向
float ndl = abs(dot(i.worldNormal, litDir))*0.5+0.5;float ndl = abs(dot(worldNormal, litDir))*0.5 + 0.5;float ndh = abs(dot(worldNormal, halfVec));
这样即可保证两面的光照是一致的。
五、透光:
这一步我只是简单的考虑当灯光穿透布料时,穿透布料后的颜色可能是灯光本身的颜色和布料颜色的简单混合,并使用一个透光率来简单的模拟透光和的灯光颜色:
float ndlS = 1-saturate(max(0,dot(i.worldNormal, litDir)*ndvSign));//计算一个衰减值,其只影响背光面 float3 lightCol = lerp(_LightColor0.rgb, _LightColor0.rgb * _BodyColor.rgb * col.rgb * _Transmittance, ndlS);
六、细节:
主要是增加了Rim,法线贴图等,使布料的效果更好:
Demo下载地址:http://www.lsngo.net/2017/10/23/unityshader_clothshader/
更多内容:http://www.lsngo.net
阅读全文
0 0
- 【UnityShader】双面透光布料材质
- unity双面材质攻略
- Unity3d 双面材质
- UE4 双面材质(双面显示)
- 终极DIY双面材质攻略
- Unityshader实例01:冰块材质
- Unityshader实例02:Xray材质
- UnityShader实例11:积雪材质
- UnityShader实例18:火焰材质
- UnityShader实例:遮挡透明材质
- Shader学习基础之七双面材质
- UnityShader实例03:边缘光材质
- UnityShader实例04:遮挡透明材质
- UnityShader实例05:Toon(卡通)材质
- UnityShader实例10:广告牌(Billboard)材质
- UnityShader实例10:广告牌(Billboard)材质
- UnityShader实例04:遮挡透明材质
- 强大的APEX系统 游戏中质感的布料材质
- log4j2配置文件log4j2.xml
- Arm学习之文件目录
- MySql数据库不能远程连接的解决办法总结
- 二周四次课(10月26日)
- 使用HttpClient获得网页内容
- 【UnityShader】双面透光布料材质
- 从c到c++到qt怎么计算时间的!!!
- Dubbo总结
- iOS 开发 UIWindow 说明
- arduino与C++异同
- MavenProject导神经网络框架neuroph包——java
- C++的默认构造函数与构造函数
- 周中训练笔记14
- 设计模式六大原则