前言

在游戏开发中实时阴影是比较常见的需求,我们最常见的方法是实时光照,但是这个会带来性能的问题,如果场景中模型比较多,例如我最近在做的3D足球游戏,场景中22个球员,如果采用实时光照DC会增加好几百,会造成渲染的压力,就有必要采用关照贴图的方案,比关闭掉实时光,但这种方案就降低了DC,减轻了渲染压力,这就要求阴影必须采用其他方案,我这里介绍Shader来现在的方案,貌似是王者荣耀采用的一种方案,专门用一个pass来渲染球员的阴影。

效果图


会看到模型有实时阴影效果。

原理

下面用2D视角来分析,阴影产生正如下图:

由上图可知,阴影的a点与遮挡物的b点在光照方向的垂直面上其实映射的是同一个点d。这很容易让我们联想到MVP当中的投影变换。其实是一样的。我们将于光照方向当做照相机的视线,而与光照方向垂直的面,就是投影变换最后的盒子的正面。这意味着我们可以制作一个正交照相机,使得它与光照方向一致,再将地面模型的各个顶点投影到该照相机上,这时候照相机的纹理UV坐标范围是01,地面投影到了照相机,也将其坐标映射到01之间,由此与照相机的纹理一一对应,于是将照相机照出来的纹理叠加在地面上,就形成了阴影。

程序实现

shader

Shader "Custom/PlayerShadow"
{Properties{_MainTex ("Texture", 2D) = "white" {}_ShadowInvLen ("ShadowInvLen", float) = 1.0 //0.4449261_Power("Power", Range(0,2)) = 1.7}SubShader{Tags{ "RenderType" = "Opaque" "Queue" = "Geometry+10" }LOD 100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag// make fog work#pragma multi_compile_fog#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;UNITY_FOG_COORDS(1)float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;fixed _Power;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);UNITY_TRANSFER_FOG(o,o.vertex);return o;}fixed4 frag (v2f i) : SV_Target{// sample the texturefixed4 col = tex2D(_MainTex, i.uv) * _Power;// apply fogUNITY_APPLY_FOG(i.fogCoord, col);return col;}ENDCG}Pass{Blend SrcAlpha  OneMinusSrcAlphaZWrite OffCull BackColorMask RGBStencil{Ref 0Comp EqualWriteMask 255ReadMask 255//Pass IncrSatPass InvertFail KeepZFail Keep}CGPROGRAM#pragma vertex vert#pragma fragment fragfloat4 _ShadowPlane; //渲染的地面float4 _ShadowProjDir; //渲染的光源点,这里只是记录点的位置,用于计算阴影的投影float4 _WorldPos;float _ShadowInvLen; //阴影的长度float4 _ShadowFadeParams; //阴影渐变的参数struct appdata{float4 vertex : POSITION;};struct v2f{float4 vertex : SV_POSITION;float3 xlv_TEXCOORD0 : TEXCOORD0;float3 xlv_TEXCOORD1 : TEXCOORD1;};v2f vert(appdata v){v2f o;float3 lightdir = normalize(_ShadowProjDir); //单位化光照方向向量float3 worldpos = mul(unity_ObjectToWorld, v.vertex).xyz; //模型坐标转成世界坐标// _ShadowPlane.w = p0 * n  // 平面的w分量就是p0 * nfloat distance = (_ShadowPlane.w - dot(_ShadowPlane.xyz, worldpos)) / dot(_ShadowPlane.xyz, lightdir.xyz); //计算阴影的长度worldpos = worldpos + distance * lightdir.xyz; //影子的投影点o.vertex = mul(unity_MatrixVP, float4(worldpos, 1.0));o.xlv_TEXCOORD0 = _WorldPos.xyz;o.xlv_TEXCOORD1 = worldpos;  //影子投影的点组成的纹理return o;}float4 frag(v2f i) : SV_Target{float3 posToPlane_2 = (i.xlv_TEXCOORD0 - i.xlv_TEXCOORD1);float4 color;color.xyz = float3(0.0, 0.0, 0.0);color.w = (pow((1.0 - clamp(((sqrt(dot(posToPlane_2, posToPlane_2)) * _ShadowInvLen) - _ShadowFadeParams.x), 0.0, 1.0)), _ShadowFadeParams.y) * _ShadowFadeParams.z);  //透明度渐变计算return color;}ENDCG}}
}

第二个pass专门用来渲染阴影,power参数是颜色加强,因为关闭了实时灯光,模型会显得暗淡,这个参数跟阴影效果没太大关系。

C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlayerShadow : MonoBehaviour
{public Camera mCamera;public GameObject mLight;private List<Material> mMatList = new List<Material>();private void Awake(){SkinnedMeshRenderer[] renderlist = GetComponentsInChildren<SkinnedMeshRenderer>();foreach (var render in renderlist){if (render == null)continue;foreach (var mt in render.materials){if (mt.shader.name == "Custom/PlayerShadow")mMatList.Add(mt);}}}void Start(){mCamera = Camera.main;mLight = GameObject.Find("LightPosition").gameObject;}void Update(){UpdateShader();}private void UpdateShader(){if (mLight == null)return;foreach (var mat in mMatList){if (mat == null)continue;mat.SetVector("_WorldPos", transform.position);mat.SetVector("_ShadowProjDir", mLight.transform.forward);mat.SetVector("_ShadowPlane", new Vector4(0f, 0.4f, 0.0f, 0.0f));mat.SetVector("_ShadowFadeParams", new Vector4(1f, 0.9f, 0.8f, 0.7f));}}
}

主要是将光照向量和当前模型的坐标传递给Shader,以便Shader实时计算出当前模型的阴影。

更多精品文章

Aladdin的博客

【Aladdin-Unity3D-Shader编程】之六-模型实时阴影相关推荐

  1. 【Aladdin Unity3D Shader编程】之四 贴图纹理

    关于纹理贴图介绍 纹理坐标也叫UV坐标,UV坐标都是0~1,并不是我们所理解的像素坐标,相当于是一个百分比. 编写shader映射纹理 将纹理的颜色取代漫反射的颜色 Shader "Alad ...

  2. 【Aladdin Unity3D Shader编程】之三 光照模型(二)

    高光反射模型 Specular=直射光*pow(cosθ,高光的参数) θ:是反射光和视野方向的夹角 编写高光反射Shader Shader "AladdinShader/07 Specul ...

  3. 【Unity3D Shader编程】之六 暗黑城堡篇: 表面着色器(Surface Shader)的写法(一)

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接: http://blog.csdn.net/poem_qianmo/article/details/42215079 作者:毛星云(浅墨) ...

  4. Unity3D Shader编程】之六 暗黑城堡篇: 表面着色器(Surface Shader)的写法(一)

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接: http://blog.csdn.net/poem_qianmo/article/details/42215079 作者:毛星云(浅墨) ...

  5. 【浅墨Unity3D Shader编程】之六 暗黑城堡篇: 表面着色器(Surface Shader)的写法(一)

    本文主要讲解了Unity中SurfaceShader的具体写法,以及几个常用的CG函数的用法. 在这里先说明一下,表面着色器将分为两次讲解,本文介绍表面着色器的基本概念和一些写法,用内置的兰伯特光照模 ...

  6. 【Unity3D Shader编程】之十一 深入理解Unity5中的Standard Shader(三)屏幕像素化特效的实现

    本系列文章由@浅墨_毛星云 出品,转载请注明出处.    文章链接:  http://blog.csdn.net/poem_qianmo/article/details/50095705 作者:毛星云 ...

  7. 【浅墨Unity3D Shader编程】之三 光之城堡篇:子着色器、通道与标签的写法 amp; 纹理混合...

    本系列文章由@浅墨_毛星云 出品,转载请注明出处.   文章链接: http://hpw123.net/a/C__/kongzhitaichengxu/2014/1117/120.html 作者:毛星 ...

  8. 【浅墨Unity3D Shader编程】之一 夏威夷篇:游戏场景的创建 第一个Shader的书写

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接: http://blog.csdn.net/poem_qianmo/article/details/40723789 作者:毛星云(浅墨) ...

  9. 【Unity3D Shader编程】之一 夏威夷篇:游戏场景的创建 第一个Shader的书写

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接: http://blog.csdn.net/poem_qianmo/article/details/40723789 作者:毛星云(浅墨) ...

最新文章

  1. 怎样提高WebService的性能
  2. PHP 批量生成静态html
  3. java中c/s模式传送数据
  4. eclipse m2e配置_使用此首选项可加快Eclipse m2e配置
  5. 【转载】浅析输入法原理
  6. 卡尔曼滤波、扩展卡尔曼滤波、无迹卡尔曼滤波以及粒子滤波原理
  7. FOI冬令营 Day2
  8. 独立看门狗及其实现的热启动
  9. 19 个接私活平台汇总升级版,你有技术就有钱
  10. 魔兽世界插件开发-暴雪插件源代码
  11. BitTorrent
  12. MSP430F149让LED灯闪烁
  13. 松下MINAS-A6伺服电机调试记录
  14. 2020C证(安全员)考试题及C证(安全员)考试题库
  15. word表格复制到excel回车换行问题 2
  16. 2020 零基础 Vue综合应用 教开发音乐播放器—悦听(激发编程乐趣)【整理+源码】
  17. 关于VMWare中的vmem文件
  18. 这么多的内置函数能记住吗?对python的68个内置函数分类总结!
  19. 断言Assertion
  20. (软考-软件设计师.下午)动态规划算法、回溯算法、贪心算法、分治算法的应用

热门文章

  1. 更新品牌与Z世代交互方式|朋氪元宇宙即将内测
  2. python二维数组求和_Python如何对二维数组求和
  3. SPI MOSI和MISO 相位不同
  4. YOLOV3--训练数据+视频检测
  5. Java笔记——反射常用方法
  6. python配置opencv最简单_Python版本OpenCV安装配置及简单实例
  7. 太原理工大于丹计算机,太原理工大学硕士生将参加中国第30次南极考察
  8. 调度程序所用数据结构—Linux
  9. python文件和数据格式化思维导图,思维导图:Numpy+Pandas
  10. 有些jpg图在IE浏览器中打不开