在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/

Unity3D中使用Projector生成阴影的更多相关文章

  1. 在Unity3D中实现安卓平台的本地通知推送

    [前言] 对于手游来说,什么时候需要推送呢?玩过一些带体力限制的游戏就会发现,我的体力在恢复满后,手机会收到一个通知告诉我体力已完全恢复了.这类通知通常是由本地的客户端发起的,没有经过服务端. 在安卓 ...

  2. Json数据解析在Unity3d中的应用

    最近做项目过程中因为Json文件名写错了一个字母Unity报错,找错误找到半夜,当时为了验错,写了一个小Demo,正好借此总结一下Json. 1.什么是Json JSON(JavaScript Obj ...

  3. UE4/Unity3D中同时捕获多高清摄像头的高效插件

    本文主要讲实现过程的一些坑. 先说下要实现的目标,主要功能在UE4/Unity中都要用,能同时捕获多个摄像头,并且捕获的图片要达到1080p25桢上,并且需要经过复杂的图片处理后丢给UE4/Unity ...

  4. Unity3D中的shader基础知识

    1.Unity中配备了强大的阴影和材料的语言工具称为ShaderLab,以程式语言来看,它类似于CgFX和Direct3D的效果框架语法,它描述了材质所必须要的一切咨询,而不仅仅局限于平面顶点/像素着 ...

  5. 【转】Unity3D中脚本的执行顺序和编译顺序

    支持原文,原文请戳: Unity3D中脚本的执行顺序和编译顺序 在Unity中可以同时创建很多脚本,并且可以分别绑定到不同的游戏对象上,它们各自都在自己的生命周期中运行.与脚本有关的也就是编译和执行啦 ...

  6. unity3d中的自定义模型的顶点法线和建模软件中的术语“软硬边”和立方体

    在unity3d中我是想用Mesh生成一个正方体,直到遇到了法线的问题. 我是想显示如下图所示的正方体,却发现法线设置上的问题. 这里我先使用了8个顶点 按照每个顶点一个法线的结果,只能是这样:(也就 ...

  7. 在Unity中实现屏幕空间阴影(2)

    参考文章: https://www.imgtec.com/blog/implementing-fast-ray-traced-soft-shadows-in-a-game-engine/ 完成的工程: ...

  8. Unity3D系列教程--使用免费工具在Unity3D中开发2D游戏 第一节

    声明:   本博客文章翻译类别的均为个人翻译,版权全部.出处: http://blog.csdn.net/ml3947,个人博客:http://www.wjfxgame.com. 译者说明:这是一个系 ...

  9. Unity3D游戏开发之游戏读/存档功能在Unity3D中的实现

    喜欢我的博客请记住我的名字:秦元培,我的博客地址是:http://qinyuanpei.com 转载请注明出处,本文作者:秦元培, 本文出处:http://blog.csdn.net/qinyuanp ...

随机推荐

  1. gridview把textbox的值修改还是旧值的解决方法

    解决方法很简单,加上if(!IsPostBack) 就OK了,因为之前加载之前都会调用InitData(). protected void Page_Load(object sender, Event ...

  2. 通过ssh X11转发使用远程gui程序

    ssh协议可以转发X11数据, 从而达到使用远程gui程序的功能, 假定现在有 客服端 C :192.168.0.13 服务器 S :192.168.0.200 首先确保在客服端C 上能够通过 ssh ...

  3. ue4 c++ anim notify

    http://blog.csdn.net/or_7r_ccl/article/details/54564962 直接在sequence or montage中new个Event 然后在graph中接收 ...

  4. 51nod1154(dp)

    题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1154 题意:中文题目诶- 思路:字符串长度不大于5e3,O(n ...

  5. [Xcode 实际操作]七、文件与数据-(7 )使用UserDefaults检测App是否首次运行

    目录:[Swift]Xcode实际操作 本文将演示UserDefaults的使用,它常被用于存储程序的配置数据. 当关闭程序之后,再次打开程序时,之前存储的数据依然可以从UserDefaults里读取 ...

  6. Centos7.x 安装 pptp

    VPN 1:检查是否支持PPTP #返回OK && echo ok ok 2:安装ppp yum install -y ppp 3:导入EPEL源 rpm -ivh http://dl ...

  7. 03.Jquery Mobile中的按钮

    一. 基础按钮 1.设置链接的data-role,使其变成按钮. <a href="index.html" data-role="button">L ...

  8. linux 01 基础命令

    linux 01 基础命令 对于Linux要记住一个概念,一切皆文件,哪怕是目录,也是一个文件 1.修改用户密码 sudo passwd pyvip@Vip:~$ #pyvip表示用户名, Vip表示 ...

  9. 解决git从remote clone后所有文件都改变的问题

    遇到2次这种情况了,git从remote clone项目代码后发现所有文件都要改变,因为权限改变了,可以通过git来设置忽略权限变化 git config --global core.fileMode ...

  10. oracle中CAST函数使用简介【转】

    CAST()函数可以进行数据类型的转换. CAST()函数的参数有两部分,源值和目标数据类型,中间用AS关键字分隔. 以下例子均通过本人测试. 一.转换列或值 语法:cast( 列名/值 as 数据类 ...