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.
参考博客:
Unity3D中使用Projector生成阴影的更多相关文章
- 在Unity3D中实现安卓平台的本地通知推送
[前言] 对于手游来说,什么时候需要推送呢?玩过一些带体力限制的游戏就会发现,我的体力在恢复满后,手机会收到一个通知告诉我体力已完全恢复了.这类通知通常是由本地的客户端发起的,没有经过服务端. 在安卓 ...
- Json数据解析在Unity3d中的应用
最近做项目过程中因为Json文件名写错了一个字母Unity报错,找错误找到半夜,当时为了验错,写了一个小Demo,正好借此总结一下Json. 1.什么是Json JSON(JavaScript Obj ...
- UE4/Unity3D中同时捕获多高清摄像头的高效插件
本文主要讲实现过程的一些坑. 先说下要实现的目标,主要功能在UE4/Unity中都要用,能同时捕获多个摄像头,并且捕获的图片要达到1080p25桢上,并且需要经过复杂的图片处理后丢给UE4/Unity ...
- Unity3D中的shader基础知识
1.Unity中配备了强大的阴影和材料的语言工具称为ShaderLab,以程式语言来看,它类似于CgFX和Direct3D的效果框架语法,它描述了材质所必须要的一切咨询,而不仅仅局限于平面顶点/像素着 ...
- 【转】Unity3D中脚本的执行顺序和编译顺序
支持原文,原文请戳: Unity3D中脚本的执行顺序和编译顺序 在Unity中可以同时创建很多脚本,并且可以分别绑定到不同的游戏对象上,它们各自都在自己的生命周期中运行.与脚本有关的也就是编译和执行啦 ...
- unity3d中的自定义模型的顶点法线和建模软件中的术语“软硬边”和立方体
在unity3d中我是想用Mesh生成一个正方体,直到遇到了法线的问题. 我是想显示如下图所示的正方体,却发现法线设置上的问题. 这里我先使用了8个顶点 按照每个顶点一个法线的结果,只能是这样:(也就 ...
- 在Unity中实现屏幕空间阴影(2)
参考文章: https://www.imgtec.com/blog/implementing-fast-ray-traced-soft-shadows-in-a-game-engine/ 完成的工程: ...
- Unity3D系列教程--使用免费工具在Unity3D中开发2D游戏 第一节
声明: 本博客文章翻译类别的均为个人翻译,版权全部.出处: http://blog.csdn.net/ml3947,个人博客:http://www.wjfxgame.com. 译者说明:这是一个系 ...
- Unity3D游戏开发之游戏读/存档功能在Unity3D中的实现
喜欢我的博客请记住我的名字:秦元培,我的博客地址是:http://qinyuanpei.com 转载请注明出处,本文作者:秦元培, 本文出处:http://blog.csdn.net/qinyuanp ...
随机推荐
- POJ 2387 Til the Cows Come Home Dijkstra求最短路径
Til the Cows Come Home Bessie is out in the field and wants to get back to the barn to get as much s ...
- JavaScript中两个数组的拼接
方法一:使用for循环 var arr = ['tom', 'jerry']; var arr2 = [1, 2]; for(var i=0; i<arr2.length; i++){ arr. ...
- python列表逆序三种方法
栗子: # 题目:将一个数组逆序输出. # # 程序分析:用第一个与最后一个交换. import random list =[random.randint(0,100) for _ in range( ...
- c语言的小问题
在c语言编程中要注意一个小问题,如果你编写scanf("%d",&n);printf("%d",n)这个你输入几就输出几,毫无疑问.但是现在问题来了?如 ...
- [Xcode 实际操作]七、文件与数据-(7 )使用UserDefaults检测App是否首次运行
目录:[Swift]Xcode实际操作 本文将演示UserDefaults的使用,它常被用于存储程序的配置数据. 当关闭程序之后,再次打开程序时,之前存储的数据依然可以从UserDefaults里读取 ...
- js中的原型以及原型链
在js中原型是每个构造函数的属性: 这个算 js 核心概念的一部分 var f1 = new Foo(); 对象 f1 的构造函数就是 Foo , f1的原型 __proto__ 就指向构造函数 Fo ...
- 在VMware上克隆Linux虚拟机及其网卡配置方法
最近在搭建Hadoop集群,1个Master,3个Workers.使用VMware workstations创建Linux虚拟机,版本是CentOS7.安装完成并做了相应的网络配置后,使用VMware ...
- node学习(2)-路由读取图片
通常我们开发项目的时候都要用到图片,那么nodejs是如何读取图片的呢? 我们来看一下,因为一个网页中图片的数量是不确定的,而我们不能每一张图片都去写它的src路径,这时候最好的方法就是写一个专门读取 ...
- 1366 - Incorrect string value:'\xE5\xBC\xA0\xE4\xB8\x89' for column 'name' a 错误修改
把name的字符集修改成 utf8 ,然后把表关了从新打开,就可以了 如果还不行,就从新创表,在创表的时候修改name的字符集 如果还不行,就修改my.ini 它在你的mysql安装路径里 [mysq ...
- bzoj1095: [ZJOI2007]Hide 捉迷藏 动态点分治学习
好迷啊...感觉动态点分治就是个玄学,蜜汁把树的深度缩到logn (静态)点分治大概是递归的时候分类讨论: 1.答案经过当前点,暴力(雾)算 2.答案不经过当前点,继续递归 由于原树可以长的奇形怪状( ...