以下摘录自《Unity 3D ShaderLab开发实战详解》,第10章。
转注:这是一个非常简单且廉价的实时假阴影,比较常用到,据我所知,王者荣耀中就使用了类似的手法。缺点就是比较假,只能投射到平面上,不能应对地面的凹凸,最明显的问题就是会插到墙里去。

转注:需要传给shader接受阴影的地面(平面)的worldToLocalMatrix和localToWorldMatrix。对应的变量分别为_World2Ground和_Ground2World。

Shader "Tut/Shadow/PlanarShadow_3" {
//转注:这里对原作里的平行光计算部分和阴影淡出部分做了整合,略去了点光源计算部分。Properties{_Intensity("atten",range(1,16))=1}SubShader {Pass {Tags {"LightMode" = "ForwardBase"}Material {Diffuce(1,1,1,1)}Lighting On}Pass {Tags {"LightMode" = "ForwardBase"}Cull FrontBlend DstColor SrcColorOffset -1,-1 //使阴影在平面之上CGPROGRAM#pragma vertex vert#pragma fragment frag#inlcude "UnityCG.cginc"float4x4 _World2Ground;float4x4 _Ground2World;float _Intensity;struct v2f{float4 pos:SV_POSITION;float atten:TEXCOORD0;};v2f vert(float4 vertex:POSITION){v2f o;float3 litDir;litDir = WorldSpaceLightDir(vertex);litDir = mul(float3x3(_World2Ground), litDir);//把光源方向转换到接受平面空间litDir = normalize(litDir);float4 vt;vt = mul(_Object2World, vertex);vt = mul(_World2Ground, vt);//将物体顶点转换到接受平面空间vt.xz=vt.xz-(vt.y/litDir.y)*litDir.xz;//用三角形相似计算沿光源方法投射后的xz//上面这行代码可拆解为如下的两行代码,这样可能在进行三角形相似计算时更好理解//vt.x=vt.x-(vt.y/litDir.y)*litDir.x;//vt.z=vt.z-(vt.y/litDir.y)*litDir.z;vt.y=0;//使阴影保持在接受平面上vt=mul(_Ground2World,vt);//返回到世界坐标空间vt=mul(_World2Object,vt);//将结果重新表达为Object Space坐标o.pos = mul(UNITY_MATRIX_MVP, vt);//经典的MVP变换,输出到Clip Spaceo.atten=distance(vertex,vt)/_Intensity;//根据前后定点的距离计算衰减return o;}float4 frag(void) : COLOR{return smoothstep(0,1,i.atten/2);}ENDCG}}
}

在此Shader中,我们首先使用固定管线对物体做了一个简单的照明。在计算阴影的ForwardBase中,首先使用一个可以叠加阴影的混合模式,然后使用z偏移保证出来的阴影在接受平面之上。_World2Ground和_Ground2World分别是我们自定义的两个进出阴影接受平面矩阵(转注:这里原作解释的不好,按字面意思理解即可)。在具体计算中,首先将光源方向和投影物体的顶点都转换到接受平面的空间(接受平面的物体空间),在它们都处于同一空间后,通过简单的三角形近似算法,来计算投影物体顶点沿光线投影后在接受平面上的新位置。因为这是一个Build In的(Built-in)Unity Plane,所以只计算其xz分量即可,并将y分量设为0,这样投影出来的阴影就出现在接受平面上了。投影计算完成后,返回世界空间,然后到投影物体本身的Object Space,之后的事情就如通常那样,一个经典的MVP变换即可。
这个方法还有一个显而易见的问题,那就是物体本身是立体的,不在一个平面,因此,这个计算前后的点的距离是包括物体本身厚度的,这个厚度就会表现在阴影上。
转注:使用Stencil可以解决这个问题,但是原作中并没有涉及,所以暂时略过
算了,以下为原创:

如上图所示,Cube和Cylinder貌似没有什么问题,但是右边那个绳结形状的物体的阴影就出现了问题。解决方法,在第二个pass里CGPROGRAM之前,加入以下代码:

        Stencil{Ref 1           //参考值为1,stencilBuffer值默认为0  Comp Greater    //stencil比较方式是大于Pass replace    //通过的处理是替换,就是拿1替换buffer 的值  Fail Keep       //深度检测和模板检测双失败的处理是保持ZFail keep      //深度检测失败的处理是保持}

结果:

效果略好,但是也并不太理想,那是因为原作并不是把绳结物体当成一整个物体对待,而是当做一个一个的顶点处理,用原顶点和对应的影子顶点计算距离明显有问题。另外有一种做法是传入对象的世界坐标,然后跟世界空间的影子顶点计算距离,但其实也略复杂了,最简单的就是把o.atten=distance(vertex,vt)/_Intensity;这一段替换成:

o.atten=length(vt)/_Intensity;

其实就是计算影子顶点和物体的原点的距离。
效果如图:

这就需要在制作模型的时候把物体的原点设在y=0的平面上,这样这种影子才能达到较好的效果。
最后……需要提一句,原作中将物体和影子在一个shader里渲染,我认为这是有问题的,因为没法分别设置Queue,物体的Queue应该是Geometry,而影子的Queue应该是Transparent。所以正确的做法应该是,物体该怎么渲染就怎么渲染,影子做一个GameObject挂在物体上,添加MeshFilter和MeshRenderer使用相同的Mesh,然后MeshRenderer里使用阴影的材质。即上文中的Shader去掉第一个Pass,并修改Tags。Blend方法建议修改为Blend SrcAlpha OneMinusSrcAlpha,最后frag方法建议修改为:

        float4 frag(v2f i) : COLOR {return float4(0,0,0, smoothstep(1, 0, i.atten / 2));}

综上所述,整合一下代码:

Shader "Tut/Shadow/PlanarShadow_Chaos" {Properties{_Intensity("atten",range(1,16))=1}SubShader {Tags{ "LightMode" = "ForwardBase""Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }pass {Cull FrontBlend SrcAlpha OneMinusSrcAlphaOffset -1,-1Stencil{Ref 1           //参考值为1,stencilBuffer值默认为0  Comp Greater    //stencil比较方式是大于Pass replace    //通过的处理是替换,就是拿1替换buffer 的值  Fail Keep       //深度检测和模板检测双失败的处理是保持ZFail keep      //深度检测失败的处理是保持}CGPROGRAM#pragma vertex vert #pragma fragment frag#include "UnityCG.cginc"float4x4 _World2Ground;float4x4 _Ground2World;float _Intensity;struct v2f{float4 pos:SV_POSITION;float atten:TEXCOORD0;};v2f vert(float4 vertex: POSITION){v2f o;float3 litDir;litDir=normalize(WorldSpaceLightDir(vertex));  litDir=mul(_World2Ground,float4(litDir,0)).xyz;float4 vt;vt= mul(unity_ObjectToWorld, vertex);vt=mul(_World2Ground,vt);vt.xz=vt.xz-(vt.y/litDir.y)*litDir.xz;vt.y=0;vt=mul(_Ground2World,vt);//back to worldvt=mul(unity_WorldToObject,vt);o.pos=UnityObjectToClipPos(vt);o.atten=length(vt)/_Intensity;return o;}float4 frag(v2f i) : COLOR {return float4(0,0,0, smoothstep(1, 0, i.atten / 2));}ENDCG }//}
}

Shader山下(二十七)平面阴影相关推荐

  1. Shader山下(十七)语义Semantic

    当在编写Cg或者HLSL着色器程序的时候,我们需要使用语义(Semantic)来指明输入输出变量的"意图". 例如下面的这段Shader代码: Shader "Custo ...

  2. Shader山下(二十一)多重变体(Multiple Variants)

    2019独角兽企业重金招聘Python工程师标准>>> Shader山下(二十)编译指令(Compilation Directives)介绍了如何使用编译指令,本文就专文介绍一下多重 ...

  3. 2d shader unity 阴影_【Unity Shader】平面阴影(Planar Shadow)

    来介绍一种适用于移动平台的高性能实时阴影解决方案--平面阴影(Planar Shadow). 由于Unity内置的实时阴影实现方式是屏幕空间阴影贴图(Screen Space Shadow Map)非 ...

  4. Unity Shader - Planar Shadow - 平面阴影

    文章目录 整体运行效果 思路 Shader 问题 Z-Fighting,解决:按法线方向偏移一丢丢 绘制 Alpha 混合重叠,解决:使用 stencil buffer 来规避 为何出现这个问题 解决 ...

  5. Shader山下(十二)材质属性绘制器

    Unity5增加了一个叫MaterialPropertyDrawer的东西,可以使用它们标记在shader的Properties里面,扩充shader的一些参数在编辑器上的样式. 本文以Shader山 ...

  6. Shader山下(一)第一个Shader

    学习Unity3D就得学Shader,然而新手学习Shader往往不知道从何处下手,没有找对门,谈何入门. 本系列主要介绍Shader的一些基础知识,希望可以带你找对门,然后入得了门. (本文作者也尚 ...

  7. 【Visual C++】游戏开发笔记二十七 Direct3D 11入门级知识介绍

    游戏开发笔记二十七 Direct3D 11入门级知识介绍 作者:毛星云    邮箱: happylifemxy@163.com    期待着与志同道合的朋友们相互交流 上一节里我们介绍了在迈入Dire ...

  8. 【Unity】Planar Shadows平面阴影的实现

    Plannar Shadows,即平面阴影,是一个适用于平坦地形的假阴影技术.要求阴影的Receiver为平面,Occluder不与其他物体穿插. 实现效果 1.定向光源Planar Shadows ...

  9. 8.3实例程序:平面阴影

    在场景中被灯光照射的地方会产生阴影,这将使场景变的更真实.在这一部分我们将演示怎样实现平面阴影,即在平面上的阴影(如图8.5). 使用这种阴影只是一种权宜之计,虽然它增强了场景的真实效果,但是这并不是 ...

  10. OpenCV C++案例实战二十七《角度测量》

    OpenCV C++案例实战二十七<角度测量> 前言 一.鼠标响应事件 1.1功能源码 1.2功能效果 二.计算直线角度 2.1 计算直线斜率 2.2计算直线角度 2.3功能源码 三.绘制 ...

最新文章

  1. 【Java】 链表的回文结构
  2. 部署Symantec Antivirus 10.0网络防毒服务器之六
  3. Linux文件系统上的特殊权限      SUID, SGID, Sticky(粘之位)
  4. android输入法中的imeoption
  5. oracle让查出来的数据排序,Oracle数据库的查询排序
  6. 【计算机基础】存储器层次 Memory hierarchy
  7. linux 扩充db2表空间,如何扩充db2的表空间、加容器等表空间维护操作
  8. Python正则表达式如何进行字符串替换
  9. oracle监听服务无法启动不了,关于ORACLE监听服务无法启动的问题
  10. 由通项为In(1+1\n)的级数引申...
  11. tar命令压缩和解压缩
  12. matlab db5是什么小波,3、代码 - matlab小波分析步骤是什么
  13. php 绕不过的laravel,php – 为什么在Laravel中不能轻松下载大文件?
  14. 系统中 用户操作日志管理
  15. web项目连接阿里云云数据库RDS-MySQL8.0
  16. Smart-Link
  17. 虚拟机(VMware Workstation Pro15)及系统(Windows10)安装
  18. RecyclerView调用notifyDataSetChanged()不起作用
  19. LabWindows界面的程序控制
  20. 螺旋矩阵java_Java实现螺旋矩阵

热门文章

  1. python参数种类_python的参数类型
  2. mysql 中英按字母排序_利用MySQL数据库来处理中英文取首字母排序
  3. 数据结构实验一,第1题:基于顺序存储结构的图书信息表的创建和输出
  4. php 无级分类源代码,php+mysql无限级分类程序代码
  5. python为什么流行头上长草_头上“长草”真的可以实现吗?
  6. 我的.emacs(Ubuntu版)(二十二)
  7. android播放器如何获取音乐文件信息
  8. 创业团队最好的时代 - 上篇
  9. Fresco图片加载框架使用方法完全指南
  10. ppt播放影片时出现提示某些文件可能携带病毒,损害您的计算机,我的PPT没病毒—禁用PowerPoint的病毒提示...