[Unity]实时阴影技术方案总结

news2024/11/16 17:27:54

一,Planar Shadow

原理就是将模型压扁之后绘制在需要接受阴影的物体上,这种方式十分高效,消耗很低。具体实现过程参考Unity Shader - Planar Shadow - 平面阴影。具按照自己的理解,其实就是根据光照方向计算片元在接受阴影的平面上的投影位置,然后绘制即可,这种方式还是只适合在平面上绘制阴影。
PlanarShadow.shader

Shader "Custom/PlanarShadow"
{
    Properties
    {
        _Tint("_Tint", Color) = (1,1,1,1)
        _MainTex("_MainTex (albedo)", 2D) = "white" {}

        [Header(Alpha)]
	[Toggle(_CLIPPING)] _Clipping ("Alpha Clipping", Float) = 1
        _Cutoff("_Cutoff (Alpha Cutoff)", Range(0.0, 1.0)) = 0.5 // alpha clip threshold
        
        [Header(Shadow)]
        // _GroundHeight("_GroundHeight", Range(-100, 100)) = 0
        _GroundHeight("_GroundHeight", Float) = 0
        _ShadowColor("_ShadowColor", Color) = (0,0,0,1)
	_ShadowFalloff("_ShadowFalloff", Range(0,1)) = 0.05

        // Blending state
        [HideInInspector] _SrcBlend("__src", Float) = 1.0
        [HideInInspector] _DstBlend("__dst", Float) = 0.0
        [HideInInspector] _ZWrite("__zw", Float) = 1.0
        [HideInInspector] _Cull("__cull", Float) = 2.0
    }
    SubShader
    {
	Pass {
		// 其他Pass请自行实现
	}
        
        // Planar Shadows平面阴影
        Pass
        {
            Name "PlanarShadow"

            //用使用模板测试以保证alpha显示正确
            Stencil
            {
                Ref 0
                Comp equal
                Pass incrWrap
                Fail keep
                ZFail keep
            }

            Cull Off

            //透明混合模式
            Blend SrcAlpha OneMinusSrcAlpha

            //关闭深度写入
            ZWrite off

            //深度稍微偏移防止阴影与地面穿插
            Offset -1 , 0

            CGPROGRAM
            #pragma shader_feature _CLIPPING
            #pragma shader_feature _ALPHATEST_ON
            #pragma shader_feature _ALPHAPREMULTIPLY_ON

            #include "UnityCG.cginc"

            #pragma vertex vert
            #pragma fragment frag
            
            float _GroundHeight;
            float4 _ShadowColor;
            float _ShadowFalloff;
            half4 _Tint;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _Clipping;
            half _Cutoff;

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float4 color : COLOR;
                float2 uv : TEXCOORD0;
            };

            float3 ShadowProjectPos(float4 vertPos)
            {
                float3 shadowPos;

                //得到顶点的世界空间坐标
                float3 worldPos = mul(unity_ObjectToWorld , vertPos).xyz;

                //灯光方向
                // float3 lightDir = normalize(_LightDir.xyz);
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);

                //阴影的世界空间坐标(低于地面的部分不做改变)
                shadowPos.y = min(worldPos .y , _GroundHeight);
                shadowPos.xz = worldPos .xz - lightDir.xz * max(0 , worldPos .y - _GroundHeight) / lightDir.y; 

                return shadowPos;
            }

            float GetAlpha (v2f i) {
                float alpha = _Tint.a * tex2D(_MainTex, i.uv.xy).a;
                return alpha;
            }

            v2f vert (appdata v)
            {
                v2f o;

                //得到阴影的世界空间坐标
                float3 shadowPos = ShadowProjectPos(v.vertex);

                //转换到裁切空间
                o.vertex = UnityWorldToClipPos(shadowPos);

                //得到中心点世界坐标
                float3 center = float3(unity_ObjectToWorld[0].w , _GroundHeight , unity_ObjectToWorld[2].w);
                //计算阴影衰减
                float falloff = 1-saturate(distance(shadowPos , center) * _ShadowFalloff);

                //阴影颜色
                o.color = _ShadowColor;
                o.color.a *= falloff;
                
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);

                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                if (_Clipping)
                {
                    float alpha = GetAlpha(i);
                    i.color.a *= step(_Cutoff, alpha);
                }
                return i.color;
            }
            ENDCG
        }
    }
    // FallBack "Diffuse"
}

二,Projector Shadow

Projector Shadow是常用的实时阴影实现方式,其基本原理是通过摄像机将需要显示阴影的物体,渲染到一张RenderTexture(RT)上,记录下物体的颜色值(可设置为自定义颜色),并将RT关联到Projector组件的材质上;然后通过Projector组件将需要接收阴影的物体以Projector组件的材质再渲染一遍来实现阴影的显示。
在这里插入图片描述
可下载这个插件DynamicShadowProjector

结构是这样的
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【备忘】今天写一下如何买免费证书

使用场景 使用微信支付宝支付转账时小游戏小程序接口开发时其它情况 开发中不可避免的会接触https,有的公司有运维去做这个事,有的是老板自己会搞https证书,咱多了解一项技术也是好事。 如何买证书 登录阿里云控制台,搜索ssl证…

Jenkins部署项目

一.安装jenkins 1.1进入jenkins官网下载jenkins war包,上传到服务器/usr/local目录。 1.2执行启动jenkins命令,(注意jenkins版本需要的jdk版本) /usr/local/java11/bin/java -Djava.awt.headlesstrue -jar /usr/local/jenkins.wa…

大文件快速传输解决办法汇总

在数据传输普及的当今时代,文件体量也在不断的突破它”大“的上线,很多企业也在面临着这类大文件快速传输的烦恼,而且这里面的“大”可不是一般意义的几M,几G的文件,它有可能上T级甚至是PB级别、TB级别的大文件,或者是…

使用 Docker 部署 SSCMS 内容管理系统

1)SSCMS 介绍 官网:https://sscms.com/ GitHub:https://github.com/siteserver/cms SSCMS 基于 .NET Core,能够以最低的成本、最少的人力投入在最短的时间内架设一个功能齐全、性能优异、规模庞大并易于维护的网站平台。 2&#…

​如何把图片里背景的路人P掉?教你四种方法消除路人

在日常生活中,我们经常会遇到需要将图片中背景的路人P掉的情况。有时候,这些路人会破坏图片的整体美感,或者我们只想要图片中的某些元素,而路人的出现会分散注意力。那么,如何才能有效地将图片中的背景路人P掉呢&#…

2023我的编程之旅-地质人的山和水

引言 大家好,我是搞地质的。外行人有的说我们游山玩水,有的说我们灰头土脸,也有的说我们不是科学。 而我说,这是一门穷极一生青春,值得奉献的行业。这是一门贴近民生,又拥抱自然的学科。他的真理性在于探…

记事本在手机桌面上怎么找?手机里的记事本怎么找?

在日常生活、工作和学习中,我们时常需要随手记录一些重要的事项、灵感闪现的瞬间或者是待办的任务。比如,在超市购物前,列出购物清单;在开会时,记下重要的讨论点;在学习时,捕捉那一刹那的灵感。…

蓝桥圣诞树(C++)

问题描述 输入样例: 1 3 101 1 2 2 3 输出样例: YES 思路: 这道题还是比较好想的,因为它构造的二叉树是用边连接起来的,不是像之前一样从上到下从左到右按编号构造的,所以可以用邻接表来存每个点还有边&am…

Docker实战02|Namespace

在上一文《Docker实战01|容器与开发语言》中主要介绍了Docker的基本概念与Docker安装、Go语言安装等实战技巧。 本文继续针对Namespace技术展开讲解并利用Go语言进行实践。 本系列所有代码均已经开源。关公众号回复「Go语言实现Docker」即可获得。 目录 2.1.2 U…

如何评估 RAG 应用的质量?最典型的方法论和评估工具都在这里了

随着 LLM(Large Language Model)的应用逐渐普及,人们对 RAG(Retrieval Augmented Generation)场景的关注也越来越多。然而,如何定量评估 RAG 应用的质量一直以来都是一个前沿课题。 很显然,简单的几个例子的对比,并不能准确地衡量…

关于对物料计量单位的维护

1、业务背景 一般情况下,在设计产品时,明确了物料的计量单位,并在维护物料主数据时,维护完整单位数据。 但也有例外情况,例如当设计产品时,不明确未来的打包方式,不明确要维护哪些种计量单位&…

JVM虚拟机:各种JVM报错总结

错误 java.lang.StackOverflowError java.lang.OutOfMemoryError:java heap space java.lang.OutOfMemoryError:GC overhead limit exceeded java.lang.OutOfMemoryError:Direct buffer memory java.lang.OutOfMemoryError:unable to create new native thread java.lang.OutOf…

霹雳吧啦Wz《pytorch图像分类》-p2AlexNet网络

《pytorch图像分类》p2AlexNet网络基础及代码 一、零碎知识点1.过拟合2.使用dropout后的正向传播3.正则化regularization4.代码中所用的知识点 二、总体架构分析1.ReLU激活函数2.手算3.模型代码 三、训练花分类课程代码1.model.py2.train.py3.predict.py 一、零碎知识点 1.过拟…

LeetCode刷题---旋转图像

解题思路: 首先对主对角线两边的元素进行交换 接着走一轮遍历,将第1列和第n列进行交换,第2列和第n-1列进行交换,直至得到最终的矩阵。 代码实现: public void rotate(int[][] matrix) {//首先对主对角线的元素进行交换…

企业如何做好客户管理?有哪些关键因素?

客户管理是建立和维护客户关系的重要组成部分,对于企业的发展至关重要。下面就让我们来看看在做好客户管理时有哪些关键因素吧。 第一个关键因素是提供优质的客户服务。无论是线上还是线下,当客户需要帮助时,他们希望能够得到有效且及时的支持…

Spring ApplicationEvent事件处理

Spring的事件 ApplicationEvent以及Listener是Spring为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式,设计初衷也是为了系统业务逻辑之间的解耦,提高可扩展性以及可维护性。 ApplicationEvent就是Spring的事件接口Applic…

南昌找工作用什么APP或者招聘网站

南昌找工作用吉鹿力招聘网 通过吉鹿力招聘网,可以随时查看最新职位,跟踪简历投递动态,与正在进行招聘的CEO、部门负责人、HR在线沟通,查看其他候选人面试该职位后对面试官、公司环境的面试评价等,为求职者提供参考。 …

阿里云2核2G3M服务器能放几个网站?有限制吗?

阿里云2核2g3m服务器可以放几个网站?12个网站,阿里云服务器网的2核2G服务器上安装了12个网站,甚至还可以更多,具体放几个网站取决于网站的访客数量,像阿里云服务器网aliyunfuwuqi.com小编的网站日访问量都很少&#xf…

2023年度最热 AI 应用 TOP 50,除了 ChatGPT 还有这么多宝藏

原文章链接:年度最热 AI 应用 TOP 50,除了 ChatGPT 还有这么多宝藏 - IT之家 更多消息:AI人工智能行业动态,aigc应用领域资讯 在 AI 工具激烈竞争的一年中,尽管ChatGPT在访问量上遥遥领先,但单次使用时长未…

扫码出入库让仓库管理更加智能化

一、扫码出入库管理的概念 扫码出入库管理系统是一种基于条码技术的仓库管理系统。它通过扫描条码的方式,实现货物的自动化入库、存储、打包和出库。条码技术是一种利用光学识别原理对字符进行识别的技术,具有高效、准确、便捷的特点。 二、扫码出入库能…