假阴影,低开销的阴影实现方式

news/2024/9/19 16:52:28/文章来源:https://www.cnblogs.com/jeason1997/p/18304894

参考:Unity无光照假阴影Shader实现及常见问题总结 - 简书 (jianshu.com)

 

游戏实现阴影的常见处理方式 (动态人或物,非烘焙)

1.实时光照
实时光照属于真阴影,一般来说效果是最好的,但是开销也是最大的。 Shadow Map(阴影贴图)跟Soft Shadows(软阴影) - JeasonBoy - 博客园 (cnblogs.com)

2.脚底放置阴影面片模拟阴影
一般是无光照小型游戏的常见解决方案,开销较小,表现形式较差,面片是死的,无法根据人物动作变化

3.通过顶点shader变换成面片模拟阴影
如上图所示
优点 : 表现形式上比方案2强,阴影可跟随顶点动画,开销比实时阴影要少
缺点 : 无法在 "非平面" 使用,比如在斜坡上,会穿帮

4.通过 Projector 或者 Decal 来模拟投射阴影
优点 : 表现效果更近一步,也可以在斜面上进行投影了
缺点 : 开销也更近一步

 

方式3实现思路
1.我们通过2个Pass来渲染,第二个Pass正常渲染角色,第一个Pass模拟渲染阴影
2.我们需要将模型的所有 Y 值压到地面高度,这样就形成了一个头顶俯视图的阴影效果
3.我们再对 XZ 方向进行偏移,偏移量根据模型原先 Y 值高度为参考做插值
4.阴影的方向我们规定在 XZ 平面上 (X=0,Z=1) 为初始默认方向,以这个向量为基准进行旋转
5.旋转我们可以通过 二维旋转矩阵 来计算
 
Shader代码
Shader "loom/fake_shadow_test_pass_order"
{Properties{//材质属性面板_MainTex ("主贴图",2D) = "white"{}_GroundY ("地面Y高度 (外部传入)",float) = 0_Shadow_Color("影子颜色",Color) = (1,1,1,1)_Shadow_Length("影子长度",float) = 0_Shadow_Rotated("影子旋转角度",range(0,360)) = 0}SubShader{Tags{"Queue" = "Geometry+1"  //注意这里很重要,因为影子是要绘制在地面上,所以地面必须应该先绘制,否则blend混合的时候就是和背后的skybox进行混合了
        }pass{Stencil{Ref 1//Comp取值依次为  0:Disabled  1:Never  2:Less  3:Equal  4:LessEqual  5:Greater  6:NotEqual  7:GreaterEqual  8:AlwaysComp Greater //或者改成NotEqual//Pass取值依次为  0:Keep  1:Zero  2:Replace  3:IncrementSaturate  4:DecrementSaturate  5:Invert  6:IncrementWrap  7:DecrementWrap
                Pass Replace}Blend SrcAlpha oneMinusSrcAlpha   //因为和地面重叠所以做个偏移//也可以不做偏移,将传入的地面高度抬高一点即可Offset -2,-2CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;};struct v2f{float4 pos : SV_POSITION;//这里worldPos一定是float4,因为vert()中实际是手动两次空间变换如果是float3会导致w分量丢失,透视除法会出错//如果不参与变换,只是传到frag()中使用的话,比如进行Blinn-Phong光照计算V向量那么float3就够了
                float4 worldPos : TEXCOORD0;//做阴影插值和Clip地面以下阴影用float cacheWorldY : TEXCOORD1;};half _GroundY;half4 _Shadow_Color;   half _Shadow_Length;     half _Shadow_Rotated;v2f vert(appdata v){v2f o = (v2f)0;//获取世界空间的位置o.worldPos = mul(unity_ObjectToWorld,v.vertex);//缓存世界空间下的y分量,后续两点作用//第一点 : 做插值用做计算xz的偏移量的多少//第二点 : 防止在地面以下o.cacheWorldY = o.worldPos.y;//设置世界空间下y的值全部都设置为传入的地面高度值o.worldPos.y = _GroundY;//根据世界空间下模型y值减去传入的地面高度值_GroundY//以这个值为传入 lerp(0,_Shadow_Length) 进行线性插值//最后获取到模型y值由低到高的插值lerpVal//这个max()函数 假设腿部在地面以下则裁切掉腿部阴影,后续使用clip后无需Max//half lerpVal = lerp(0,_Shadow_Length,max(0,o.cacheWorldY-_GroundY));half lerpVal = lerp(0,_Shadow_Length,o.cacheWorldY-_GroundY);//常量PI//const float PI = 3.14159265;//角度转换成弧度half radian = _Shadow_Rotated / 180.0 * UNITY_PI;//旋转矩阵,对(0,1)向量进行旋转,计算旋转后的向量,该向量就是阴影方向//2D旋转矩阵如下// [x]        [ cosθ , -sinθ ]// [ ]  乘以  // [y]        [ sinθ , cosθ  ]// x' = xcosθ - ysinθ// y' = xsinθ + ycosθhalf2 ratatedAngle = half2((0*cos(radian)-1*sin(radian)),(0*sin(radian)+1*cos(radian)));//用以y轴高度为参考计算的插值 lerpVal 去 乘以一个旋转后的方向向量,作为阴影的方向//最终得到偏移后的阴影位置o.worldPos.xz += lerpVal * ratatedAngle;//变换到裁剪空间o.pos = mul(UNITY_MATRIX_VP,o.worldPos);return o;}fixed4 frag(v2f i) : SV_TARGET{//剔除低于地面部分的片段clip(i.cacheWorldY - _GroundY);//用作阴影的Pass直接输出颜色即可return _Shadow_Color;}ENDCG}pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"sampler2D _MainTex;half4 _MainTex_ST;struct appdata{float4 vertex : POSITION;float2 uv0 : TEXCOORD0;};struct v2f{float4 pos : SV_POSITION;float2 uv : TEXCOORD0;};v2f vert(appdata v){v2f o = (v2f)0;o.pos = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv0,_MainTex);return o;}fixed4 frag(v2f i) : SV_TARGET{return tex2D(_MainTex,i.uv);}ENDCG}}
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/744626.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

你要的高效方案!基于Apache SeaTunnel快速集成SAP进入Redshift

摘要 本文深入探讨了Apache SeaTunnel及其商业版可视化数据同步平台WhaleTunnel在数据整合领域的应用,特别是如何高效地将SAP系统中的数据同步到Amazon Redshift。通过技术介绍、操作流程、性能对比以及实际案例分析,本文为企业介绍了一种快速、可靠的数据整合解决方案。 Apa…

testfile

卷积神经网络CNN标题酷酷酷极度饥饿 你还记得呢哇 的稳定器 的味道气味的文字的修改: 现场尺寸,哈哈哈哈 尝试多次受到 分析,设计,改进 dwdwd dwdw dw dwd w dwd wd wd wd wd wd wd w dw wd d wd wd wd wdwdw dw d wd d wd w dwd w dw dw d行内公式\(\sqrt{3x-1}+(1+x)^2\)…

SSR和Nust介绍

SSR 什么是SSR(Server-Side Rendering服务端渲染)注意:服务端只是生成前期首屏页面所需的 html ,后期的交互和数据处理还是需要能支持浏览器脚本的 Client Bundle 来完成。 Nust 基础

Docker如何将本地镜像上传到服务器并解压使用该镜像?

第一步 本地导出镜像包.tar文件 docker save -o app.tar nuxt_app:1.0.1提示: app.tar这个文件名称任意取,.tar后缀不能随意修改。第二步 将导出的镜像包文件app.tar上传到服务器第三步 在服务器解压镜像 cd /opt/docker-compose/save_image/ sudo docker load < app.tar第…

[题解]POJ3304 Segment

POJ3304 Segment 题意简述 多测,每次给定\(n\)条线段,请问是否能找到\(1\)条直线,使得所有线段在该直线上的投影有公共部分。 注:两点距离\(<10^{-8}\)被认为是相等的。 思路分析 题意转化一下,就是要我们找一条直线\(l_1\),穿过所有线段。这样对于任意直线\(l_2\perp…

C#开发socket通信winfrom

UI界面: 实现: 服务端Server :开启服务端按钮点击事件Thread threadWatch = null; //负责监听客户端的线程Socket socketWatch = null; //负责监听客户端的套接字private void button3_Click(object sender, EventArgs e){try{//定义一个套接字用于监听客户端发来的信息 …

车载以太网交换机入门基本功(2)— 初识VLAN

这么方便的VLAN,究竟是用了什么“魔法”做到的呢? 在《交换机入门基本功 -上》提到,交换机在物理层面划分通信区域并产生局域网(Local Area Network, LAN)。局域网具有一个特点:连线拓扑一旦确定,一定时间内不会发生通信区域的变动。在实际通信过程中,广播报文和…

基础知识

主要是怕忘记,就当简单的记事本了,随时补充( 32位&64位 32(x86) 函数参数在函数返回地址上方 64(x64) 前六个参数依次保存在 RDI、RSI、RDX、RCX、R8、R9寄存器中 剩余的保存在栈上 各寄存器主要功能系统调用 步骤 1.加载系统调用号 将系统调用号加载到rax寄存器 2.准备…

NewSQL-TiDB 分布式数据库运维技术从0到1实战入门

一、TiDB简介 TiDB 是一种新型的分布式数据库,它结合了传统关系型数据库的易用性和 NoSQL 数据库的高性能、可扩展性。作为一款 NewSQL 数据库,TiDB 旨在解决大规模在线事务处理(OLTP)系统的需求,同时支持 JSON、图形等非关系型数据结构。 TiDB 的优势兼容性:TiDB 兼容 M…

W外链有哪些功能?短网址/活码/私信卡片

在当今互联网高速发展的时代,短网址生成平台已经成为网络营销、社交媒体推广以及个人用户分享链接时不可或缺的工具。在众多短网址生成平台中,W外链短网址生成平台凭借其独特的功能和优势,赢得了广大用户的青睐。本文将详细介绍W外链短网址生成平台的核心功能,帮助读者更好…

[FireshellCTF2020]Caas 1

c语言编译器 打开之后看见是个输入框,随便输入然后发现报错是c语言的,尝试输出helloword #include <stdio.h>int main() {printf("Hello, World! \n");return 0; }给我们了一个文件,运行一下编译器没跑了,直接 #include "/flag"报错中漏了flag

网页文件加载失败如何重试

本文由 ChatMoney团队出品 在我们开发网站应用时,我们可能会遇到脚本加载失败的情况,导致脚本加载失败的原因有很多,比如用户的网络问题、终端设备问题、用户浏览器版本等诸多因素。 解决方案 在 JavaScript 中,我们可以创建一个监听来监听脚本加载失败的情况,然后针对加载…