在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. centos6.5安装FTP服务器

    1.检测是否安装了FTP rpm -q vsftpd 2.安装ftp yum install vsftpd 3.完成ftp安装后,将 /etc/vsftpd/user_list 和 /etc/vsft ...

  2. centos6.5编译安装php7,及配置与nginx通信。

    一.配置编译环境 yum update && yum upgrade yum groupinstall "Development Tools" yum instal ...

  3. Ubuntu如何锁定分辨率

    终于把Ubuntu的虚拟机装好了,但是分辨率没有1920*1080是什么鬼啊? 下面详细讲一下如何设置1920*1080的分辨率并设置,主要都是照着前辈的博客自己在操作一遍熟悉一下,嘿嘿. 1.安装v ...

  4. win10彻底关闭自动更新

    第1步 https://jingyan.baidu.com/article/9faa7231e7b78b473c28cbb6.html 第2步 http://www.360doc.com/conten ...

  5. chrome调式工具

    1.Elementshttps://segmentfault.com/a/1190000008316690 2. Consolehttps://segmentfault.com/a/119000000 ...

  6. 2015 Noip提高组 Day1

    P2615 神奇的幻方 [题目描述] 幻方是一种很神奇的N*N矩阵:它由数字1,2,3,……,N*N构成,且每行.每列及两条对角线上的数字之和都相同. 当N为奇数时,我们可以通过以下方法构建一个幻方: ...

  7. loadrunner教程系列

    loadrunner教程系列,包括windows 环境和linux 环境. 第一讲:环境准备 链接:https://pan.baidu.com/s/1EnLP3ijZ1j1I_ysE1z4CJg  密 ...

  8. gui - tkinter 开发

    GUI 用户交互界面 tkinter 介绍 tkinter python自带的gui库,对图形图像处理库tk的封装 其他gui:pythonwin,wxpython,pyQT.. 概念介绍 组件:组成 ...

  9. 洛谷P2194 HXY烧情侣

    题目描述 众所周知,\(HXY\)已经加入了\(FFF\)团.现在她要开始喜\((sang)\)闻\((xin)\)乐\((bing)\)见\((kuang)\)地烧情侣了.这里有\(n\)座电影院, ...

  10. STP-7-RSTP的BPDU格式和处理方式的改变

    RSTP只使用一种BPDU,协议版本字段为2(STP为0). STP标志字段8位只使用了两位:TC(拓扑变化)和TCA(拓扑变化确认). RSTP也使用了其余6位:提议位,端口角色位,学习位,转发位, ...