zoukankan      html  css  js  c++  java
  • 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.

    参考

      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/

  • 相关阅读:
    基础总结深入:数据类型的分类和判断(数据、内存、变量) 对象 函数 回调函数 IIFE 函数中的this 分号
    BOM 定时器 通过修改元素的类来改变css JSON
    事件 事件的冒泡 事件的委派 事件的绑定 事件的传播
    DOM修改 使用DOM操作CSS
    包装类 Date Math 字符串的相关的方法 正则表达式 DOM DOM查询
    数组 call()、apply()、bind()的使用 this arguments
    autocad 二次开发 最小包围圆算法
    win10 objectarx向导在 vs2015中不起作用的解决办法
    AutoCad 二次开发 jig操作之标注跟随线移动
    AutoCad 二次开发 文字镜像
  • 原文地址:https://www.cnblogs.com/xin-lover/p/10245057.html
Copyright © 2011-2022 走看看