【Unity Shader入门精要 第11章】让画面动起来(二)

news2024/11/18 1:47:46

1. 顶点动画的原理

顶点动画的原理是,在顶点着色器中按照一定的规则或函数计算得到一段偏移量对顶点进行移动,最后将改变位置后的顶点变换到裁剪空间进行后续的渲染工作。

可见,与纹理动画只是改变从纹理中哪一部分开始显示图案不同,顶点动画的原理是通过移动顶点改变物体的形状。

当我们在模型空间中操作顶点实现顶点动画时,如果使用顶点的绝对位置和方向,就需要将SubShader的 “DisableBatching” 标签设置为“True”,否可会因为合批丢失模型空间信息。更好的做法是避免使用绝对位置和方向,通过颜色通道存储顶点到模型原点的距离,这样就可以不必打断合批。

另外,由于顶点动画移动了顶点位置,因此在用到 ShadowCaster 相关的效果时(比如生成阴影),都会出现错误。如果由这种需求,就需要在 Shader 中手动实现对应的 ShadowCaster 的Pass。

2. 流动的河流

下面的例子通过顶点动画实现简单的河流的效果。原理为利用正弦函数的图形变换:f(X)= Asin(ωX+φ),于每一时刻计算正弦函数的值作为当前顶点的偏移量。

例子中使用了书里类似的模型网格,如下图所示:
在这里插入图片描述
代码中有两处不容易理解的地方,其实都跟使用的网格有关:

  1. 为什么偏移量是加到顶点的x分量上而不是y分量上?
    因为代码中移动顶点的操作是在模型空间里进行的,因此需要按照模型空间的坐标轴决定移动哪一个分量,由上图可以看到,例子中使用的网格,控制上下起伏的轴是模型空间的x轴而不是y轴。
  2. 为什么代码中将顶点的xyz分量都乘以了 _InvWaveLen 后加到一起来进行平移效果?
    流动效果需要在表示河流方向的轴上做正弦函数的平移,书中三个分量都处理是为了使代码在任意方向上流动都生效,其实在这个例子中没必要,观察上图可以看到,河流网格是在Z轴方向上延申的(依然要看模型空间的轴),因此实际只需要将 Z 分量乘以 _InvWaveLen 作为函数平移即可。

测试Shader如下:

Shader "MyShader/Chapter_11/Chapter_11_Flow_Shader"
{
    Properties
    {
        _MainTex("MainTex", 2D) = "white"{}
        _Color("Color",  Color) = (1, 1, 1, 1)
        _Magnitude("Magnituede", Float) = 1
        _Frequency("Frequency", Float) = 1
        _InvWaveLen("InvWaveLen", Float) = 1
        _UVSpeed("UVSpeed", Float) = 1
    }
    SubShader
    {
        Tags{"Queue" = "Transparent" "RenderType" = "Transparent" "IgnoreProjector" = "true" "DisableBatching" = "True"}
    
        Pass
        {
            Tags{"LightMode" = "ForwardBase"}
            Cull Off
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase
            #include "UnityCG.cginc"
            
            struct a2v
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            
            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
            };
            
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Color;
            half _Magnitude;
            half _Frequency;
            half _InvWaveLen;
            half _UVSpeed;
            
            v2f vert(a2v v)
            {
               v2f o;
               float4 _offset = float4(0, 0, 0, 0);
               //_offset.x = _Magnitude * sin(_Frequency * _Time.y + _InvWaveLen * v.vertex.x + _InvWaveLen * v.vertex.y + _InvWaveLen * v.vertex.z);
               _offset.x = _Magnitude * sin(_Frequency * _Time.y + _InvWaveLen * v.vertex.z);
               o.pos = UnityObjectToClipPos(v.vertex + _offset);
               o.uv = TRANSFORM_TEX(v.uv, _MainTex) + float2(0, frac(_UVSpeed * _Time.y));
               return o;
            }
            
            fixed4 frag(v2f i) : SV_Target
            {
                fixed4 _samplerColor = tex2D(_MainTex, i.uv);
                return fixed4(_samplerColor.rgb * _Color.rgb, _samplerColor.a);
            }
            
            ENDCG
        }
    }
}


效果如下:
在这里插入图片描述

3. 广告牌技术

广告牌(Billboarding)是在实际项目中经常被使用到的一种技术,原理是通过摄像机与当前物体的连线方向,实时修改顶点位置,使物体整体发生旋转,从而做到让物体的某一面始终朝向摄像机。

在实际游戏中,Billboarding 通常分为两种效果:

  • 一种是Y轴固定,即物体的Y轴不发生倾斜永远指向上方,只是在XZ平面跟随摄像机旋转,比如地上的草。
  • 另一种是朝向固定,即物体的Z轴永远指向摄像机,使物体永远都保持正面正对摄像机,此时Y轴不再固定指向上方,有可能发生倾斜,比如天上的云。

要计算顶点旋转后的位置,我们只需要得到旋转后的模型空间,然后按照新模型空间在XYZ分量上分别做平移再加上新模型空间的原点偏移即可,因此最重要的便是构建旋转后的模型空间。

根据上面提到的两种效果,构建模型空间也有两种情况:

  • Y轴固定
    在这里插入图片描述
    此时 Y 轴固定为(0,1, 0),我们可以用摄像机与物体的连线方向作为 Z’,通过 cross(Y, Z’) 得到 X 轴方向,然后用 cross(X, Y) 得到真正的 Z 轴方向。

  • 朝向固定
    在这里插入图片描述
    此时 摄像机与物体的连线方向即为 Z 轴,我们可以取(0, 1, 0)也就是图中的 Y‘ 作为参考轴,通过 cross(Y’, Z) 得到 X 轴方向,然后用 cross(Z, X) 得到真正的 Y 轴方向。

但其中会有一种特殊的情况,即 Z’ 于 Y 轴重合时,如下图:
在这里插入图片描述
这时候在计算时, Y(或Y‘)轴就不能直接用(0, 1, 0)而要改成用(0,0,1):
在这里插入图片描述

另外需要注意的一点是,在计算Z轴的时候,要以模型空间的原点与摄像机的连线作为方向,而不能直接取ObjSpaceViewDir,否则就变成每个顶点单独算出一个Z轴,导致各个顶点最后不在同一个坐标空间下,显示就会出错。

测试Shader如下:

Shader "MyShader/Chapter_11/Chapter_11_Billboarding_Shader"
{
    Properties
    {
        _MainTex("MainTex", 2D) = "white"{}
        _BillboardingFixZ("_BillboardingFixZ", Range(0, 1)) = 1
    }
    SubShader
    {
        Tags{"Queue" = "Transparent" "RenderType" = "Transparent" "IgnoreProjector" = "true" "DisableBatching" = "True"}
    
        Pass
        {
            Tags{"LightMode" = "ForwardBase"}
            Cull Off
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase
            #include "UnityCG.cginc"
            
            struct a2v
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            
            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
            };
            
            sampler2D _MainTex;
            float4 _MainTex_ST;
            //为 0 时相当于把Z轴放平了,因此后续不需要改变叉乘的顺序,依然保持Y轴向上,从而实现Y轴固定的效果
            //为 1 时就是正常的固定朝向
            fixed _BillboardingFixZ;
            
            v2f vert(a2v v)
            {
               v2f o;
               float3 _camDir = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1)).xyz;
               _camDir.y *= _BillboardingFixZ;
               float3 _z = normalize(_camDir);
               float3 _y = abs(_z.y) > 0.999 ? float3(0, 0, 1) : float3(0, 1, 0);
               float3 _x = normalize(cross(_y, _z));
               _y = normalize(cross(_z, _x));
               float3 _pos = v.vertex.x * _x + v.vertex.y * _y + v.vertex.z * _z;
               o.pos = UnityObjectToClipPos(float4(_pos, 1));
               o.uv = TRANSFORM_TEX(v.uv, _MainTex);
               return o;
            }
            
            fixed4 frag(v2f i) : SV_Target
            {
                return tex2D(_MainTex, i.uv);
            }
            
            ENDCG
        }
    }
}

效果如下:
在这里插入图片描述

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

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

相关文章

水质预测模型精度评估实例

研究背景 随着水资源管理需求的日益增长,水质预测模型的精准度成为了评估其有效性的关键因素。本文旨在通过实证研究,探讨自建水质预测模型的实际应用效能,通过与真实监测数据的比对,揭示模型预测精度的真实情况。 数据基础情况…

【任务调度】Apache DolphinScheduler中关于全局参数设置、自定义参数、补数的介绍

Apache DolphinScheduler是一个分布式和可扩展的开源工作流协调平台,具有强大的DAG可视化界面。 今天在海豚调度的一个接口中想入参一个当前时间(要求格式为yyyyMMddhhmmss),找了找发现如下几种方法,给记录一下: 1.全局参数设置 在设置DAG图名称这一位…

CTF实战分享 | RWZIP

前言 首先我们要了解,压缩包本身并不具备隐藏信息的功能,但由于在CTF竞赛中,经常出现压缩包与隐写术结合在一起的题目,所以我们需要掌握在CTF竞赛中有关 ZIP 压缩包题目的常见题型及分析手段。 读者福利 | CSDN大礼包&#xff1a…

css设置文字在固定宽度中等距分开(仅限于单行文本)

一、要实现的效果: 二、代码 要在CSS中设置文本在一个固定宽度的容器中等距分开, 可以使用text-align: justify;属性,它可以让文本两端对齐,看起来就像是等距分开的。 但是要注意,单独使用text-align:justify;只能对单…

Midjourney如何控制光照?提示词灵感来了!

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 Midjourney如何控制光照?提示词灵感来了!文章目录 前言总结 前言 Midjourney v6 已经更新好久了,你知道有哪些可以控制光照效果的关键词吗…

数据结构---单向链表

思路分析: 1. 设计 struct LinkNode 节点结构体 strut LList 链表结构体 typedef void *LinkList 给用户使用链表指针 2. 初始化链表 LinkList mylist init_LinkList(); 3. 插入链表 void inser…

使用python实现炫酷的渐变色

使用python实现炫酷的渐变色 1、前言2、所需条件3、实现步骤步骤1:定义渐变函数步骤2:将渐变应用于目标颜色步骤3:定义参数并执行 4、完整代码5、总结 1、前言 通过应用颜色渐变,可以大大提升图像的视觉效果。在这篇博客中&#…

2025第十届美陈展

展位又遭疯抢!2025第十届美陈展释放“无界之美” 美是全球通用的语言,人类对美的追求始终如一,大众审美在经历了时代的变迁后开始趋同,东方文明深处的美学经济开始崛起。 在如今商业迈入存量阶段,以品牌为突破口打造…

基于VMware安装Linux虚拟机

1.准备Linux环境 首先,我们要准备一个Linux的系统,成本最低的方式就是在本地安装一台虚拟机。为了统一学习环境,不管是使用MacOS还是Windows系统的同学,都建议安装一台虚拟机。 windows采用VMware,Mac则采用Fusion …

有免费通配符证书吗?哪里可以申请?

市面上的免费SSL证书大多数为单域名证书,如果您的主域名拥有众多子域名,逐一申请单域名SSL证书不太现实,下面为介绍一款永久免费使用的通配符SSL证书申请流程 1 选择免费通配符证书提供商 免费通配符证书申请点击这里直接获取https://www.…

【DZ模板】价值288克米设计APP手机版DZ模板 数据本地化+完美使用

模版介绍 【DZ模板】价值288克米设计APP手机版DZ模板 数据本地化完美使用 腾讯官方出品discuz论坛DIY的后台设置,功能齐全,论坛功能不亚于葫芦侠,自定义马甲,自定义认证,自定义广告,完全可以打造出自己想…

据阿谱尔APO Research调研显示,2023年全球模塑纤维包装市场销售额约为60.4亿美元

根据阿谱尔 (APO Research)的统计及预测,2023年全球模塑纤维包装市场销售额约为60.4亿美元,预计在2024-2030年预测期内将以超过2.7%的CAGR(年复合增长率)增长。 对永续和环保包装解决方案的需求不断增长,以…

Python | Leetcode Python题解之第103题二叉树的锯齿形层序遍历

题目: 题解: class Solution:def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:if not root: return []res, deque [], collections.deque()deque.append(root)while deque:tmp []# 打印奇数层for _ in range(len(deque)…

java8以上版本

java9及其以上版本 一、JDK17 LTS 常用新特性1、switch语句的增强2、字符串拼接3、判断类型instanceof自动类型转换4、密封类 关键字 sealed permits5、record类6、优化空指针异常7、ZGC垃圾收集器 一、JDK17 LTS 常用新特性 1、switch语句的增强 在 Java 17中,sw…

全面指南:IP SSL证书的申请与部署步骤

不同于常见的域名型SSL证书,IP SSL证书是专门用于为IP地址提供安全保护的SSL证书类型,适用于那些直接通过IP地址访问的网站或服务。本文将详细介绍IP SSL证书的申请步骤及其部署过程,帮助您轻松实现IP地址的安全加密。 一、了解IP SSL证书 …

CSS Canvas鼠标点击特效之天女散花(文本粒子动画)

1.效果 2.代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><style>body,html {margin: 0;padding: 0;w…

Docker HTTPS api V2 Manifest V 2, Schema 2 下的免装docker下载镜像的方法

目录 前言 下载镜像代码 使用方法 原代码中无法适配 Schema 2 的原因浅析 如何解决 相对原代码改动的东西 前言 本文提供代码主要是基于 https://github.com/NotGlop/docker-drag 提供的代码修改的。链接中提供的代码应该是是基于HTTPS api V2 Manifest V 2, Schema 1实…

【算法实战】每日一题:统计一个序列向某个方向的比他小的数的个数(非暴力)

题目 统计一个序列向某个方向的比他小的数的个数 思路 用单调栈&#xff0c;虽然这里说的是统计比他小的&#xff0c;但是是求和&#xff0c;所以我们可以用在用单调栈的时候统计里面所有比他大的元素 这两个级别上是一样的 伪代码 声明变量 n、num 和 sum 为整数。 声明…

实操专区-第15周-课堂练习专区-漏斗图与金字塔图

实操专区-第15周-课堂练习专区-漏斗图 下载安装ECharts&#xff0c;完成如下样式图形。 代码和截图上传 基本要求&#xff1a;下图3选1&#xff0c;完成代码和截图 完成 3.1.3.16 漏斗图中的任务点 基本要求&#xff1a;2个选一个完成&#xff0c;多做1个加2分。 请用班级学号姓…

vue组件的基本使用方法

组件 【1】组件是什么&#xff1f; 组件就是&#xff1a;扩展 HTML 元素&#xff0c;封装可重用的代码&#xff0c;目的是复用例如&#xff1a;有一个轮播图&#xff0c;可以在很多页面中使用&#xff0c;一个轮播有js&#xff0c;css&#xff0c;html组件把js&#xff0c;cs…