Unity3D中使用Projector生成阴影
在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.
参考博客:
最新文章
- 检测cpu是否支持虚拟化和二级地址转换【转】
- Mysql binlog
- Building Apps for Windows Phone 8.1教程下载地址整理
- 详细了解HTML标签内容模型
- java新手笔记1 Hello World!
- Javascript Promise 学习 (中)
- WebApi2官网学习记录---Attribute Routing
- MFC 消息的分类
- SqlHelp
- 读书笔记--用Python写网络爬虫02--数据抓取
- [知了堂学习笔记]_eclipse引入svn插件,并将项目同步到svn
- 带着新人学springboot的应用08(springboot+jpa的整合)
- shell脚本中的逻辑判断 文件目录属性判断 if特殊用法 case判断
- Scrapy实战篇(八)之Scrapy对接selenium爬取京东商城商品数据
- tyvj 创世纪 - 基环树
- LaTeX字体设置
- 你对linux了解多少,Linux 系统结构详解!
- 001Java输入、eclipse快捷键
- POJ3272 Cow Traffic
- MySQL5.7新特性
热门文章
- js中push(),pop(),unshift(),shift()的用法
- matlab新手入门(一)(翻译)
- 2013年第四届蓝桥杯国赛试题(JavaA组)
- iOS证书和描述文件的配置
- 通过ssh X11转发使用远程gui程序
- koa-router 路由参数与前端路由的结合
- 51nod1412(dp)
- 洛谷P1337 [JSOI2004]平衡点 / 吊打XXX(模拟退火)
- 坑爹的 Java 可变参数,把我整得够惨。。
- 通过T4模板解决EF模型序列号的循环引用问题