在Unity3D中使用Projector实现动态阴影

  无意中看见一篇博客叙述使用Projector实现动态阴影可以在移动平台拥有非常好的性能,遂按照其想法实现了一遍,发现其中竟有许多细节,写下这篇博客记录以供将来参考。

Projector

  

  从上图中我们发现Projector中的参数参数Camera的参数非常的相似,那Projector是做什么的呢?

  官方解释:A Projector allows you to project a Material onto all objects that intersect its frstum.也就是Projector是把一个材质投影到与Projector视锥体相交的物体上,这个描述比较抽象,我们可以用以前的胶片电影来类比一下:Projector就是胶片放映机,被投影的材质就是胶片,Projector投影就像胶片放映机把胶片内容投影到电影幕布一样。

  按照这个理解,我们发现这个与平时在OpenGL中提到的摄像机投影有点不一样,OpenGL的投影矩阵干的事是把三维物体投影到摄像机的近平面,也就是三维到二维的一个改变,但Projector投影确是相反,把一个纹理投影到三维物体的表面。

原理

  按照上述Projector的理解,我们可以设想一个产生阴影的方法:先把要产生阴影的物体绘制到纹理中,然后把这个纹理投影到要接收阴影的物体表面上(注意与产生阴影的物体区分开),这样就有了阴影,而这就是Projector产生阴影的原理。

实现细节

  首先是要生成要被投影的阴影,因为这个阴影要与物体完美衔接,所以我们需要用Projector的参数来生成这个纹理,在Unity3D中我们的做法是:

  1.创建一个新Camera,Camera的参数与Projector的一致;

  2.设置Camera的Culling Mask为要产生阴影的物体所在的LayerMask,Projector的ignore Layers同样设置为这个LayerMask,同时把要产生阴影的物体的Layer设置为这个LayerMask;

  3.设置Camera渲染使用的shader,即camera.setReplaceShader;

  4.创建RenderTexture,使用的分辨率视自己需求而定,分辨率越高,阴影越精细;

  5.设置新建的Camera的TargetTexture为新建的RenderTexture;

  6.新建Projector所需材质,可以使用standard assets中的“ProjectorMultipy”shader创建,设置材质的_ShadowTex为新建的RenderTexture;

  7.运行即可看到效果。

  需要注意的是:

  1.传递给Projector材质的RenderTexture必须是clamp模式,但是如果阴影到了RenderTexture边缘的像素,因为是Clamp的原因,地板就会出现整个长条形的阴影,解决方案可以通过给projector材质添加mask图来处理边缘的像素;

  2.RenderTexture其实我们只需要表示产生阴影物体的位置,所以Camera使用的ReplaceShader可以使用最简单的shader,只写入一个通道值就可以了;

效果

ProjectorMultiply.Shader

   被投影的材质需要特殊的shader,其实主要是要计算阴影的uv坐标。因为我们使用的是新建的一个纹理,这个纹理如何应用到物体的表面,需要使用Projector定义的一个投影矩阵,也就是通过这个矩阵来计算投影后的uv坐标。比较人性化的是Unity3D已经帮我们计算好了,直接在shader中声明float4x4 unity_Projector就可以使用了。shader中还用到了falloff的一个texture,通常是一张左白右黑的贴图,用于控制阴影的强弱。Falloff左边为白色,alpha值为1,对应投影距离最近时最亮,右边接近全黑,alpha值为0,表示投影距离变远时投影会渐渐接近透明甚至看不见。具体代码如下:

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

// Upgrade NOTE: replaced '_Projector' with 'unity_Projector'
// Upgrade NOTE: replaced '_ProjectorClip' with 'unity_ProjectorClip' Shader "Projector/Multiply" {
Properties {
_ShadowTex ("Cookie", 2D) = "black"{}
_FalloffTex ("FallOff", 2D) = "white" {}
}
Subshader {
Tags {"Queue"="Transparent"}
Pass {
ZWrite Off
ColorMask RGB
Blend DstColor Zero
Offset -1, -1 CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#include "UnityCG.cginc" struct v2f {
float4 uvShadow : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 pos : SV_POSITION;
}; float4x4 unity_Projector; v2f vert (float4 vertex : POSITION)
{
v2f o;
o.pos = UnityObjectToClipPos (vertex);
o.uvShadow = mul (unity_Projector, vertex);
UNITY_TRANSFER_FOG(o,o.pos);
return o;
} sampler2D _ShadowTex;
sampler2D _FalloffTex; fixed4 frag (v2f i) : SV_Target
{
fixed4 texS = tex2Dproj (_ShadowTex, UNITY_PROJ_COORD(i.uvShadow));
fixed4 texF = tex2Dproj (_FalloffTex, UNITY_PROJ_COORD(i.uvShadow));
fixed ratio = texF.r * texS.a;
fixed4 res = fixed4(1,1,1,1) * (1 - ratio);
UNITY_APPLY_FOG_COLOR(i.fogCoord, res, fixed4(1,1,1,1));
return res;
}
ENDCG
}
}
}

  

优缺点

  优点:

    1.可控性强。可以看出我们可以控制在哪个区域、哪些物体、什么时间产生或更新阴影,也可以对阴影的质量进行控制(使用不同分辨率的RenderTexture);

    2.可以很方便的是实现模糊和软阴影(这个还没实践,不过因为我们可以取得阴影的rendertexture,所以完全可以实现);

  缺点:

    1.很明显,产生阴影的物体不能接收阴影;

    2.Unity3D 的Betch无法使用(因为要分层);

工程源代码:https://github.com/xin-lover/ProjectorShadow

Unity3D AssetStore中有一个使用这种方法生成阴影的插件,做的比较完善,可以参考使用:Dynamic Shadow Projector.

参考博客:

  1.https://yrsc.github.io/2018/03/Projector%E5%AE%9E%E7%8E%B0%E4%BC%AA%E5%8A%A8%E6%80%81%E9%98%B4%E5%BD%B1/

最新文章

  1. 检测cpu是否支持虚拟化和二级地址转换【转】
  2. Mysql binlog
  3. Building Apps for Windows Phone 8.1教程下载地址整理
  4. 详细了解HTML标签内容模型
  5. java新手笔记1 Hello World!
  6. Javascript Promise 学习 (中)
  7. WebApi2官网学习记录---Attribute Routing
  8. MFC 消息的分类
  9. SqlHelp
  10. 读书笔记--用Python写网络爬虫02--数据抓取
  11. [知了堂学习笔记]_eclipse引入svn插件,并将项目同步到svn
  12. 带着新人学springboot的应用08(springboot+jpa的整合)
  13. shell脚本中的逻辑判断 文件目录属性判断 if特殊用法 case判断
  14. Scrapy实战篇(八)之Scrapy对接selenium爬取京东商城商品数据
  15. tyvj 创世纪 - 基环树
  16. LaTeX字体设置
  17. 你对linux了解多少,Linux 系统结构详解!
  18. 001Java输入、eclipse快捷键
  19. POJ3272 Cow Traffic
  20. MySQL5.7新特性

热门文章

  1. js中push(),pop(),unshift(),shift()的用法
  2. matlab新手入门(一)(翻译)
  3. 2013年第四届蓝桥杯国赛试题(JavaA组)
  4. iOS证书和描述文件的配置
  5. 通过ssh X11转发使用远程gui程序
  6. koa-router 路由参数与前端路由的结合
  7. 51nod1412(dp)
  8. 洛谷P1337 [JSOI2004]平衡点 / 吊打XXX(模拟退火)
  9. 坑爹的 Java 可变参数,把我整得够惨。。
  10. 通过T4模板解决EF模型序列号的循环引用问题