UnityShader学习笔记——动态效果

——内容源自唐老狮的shader课程


目录

1.原理

2.Shader中内置的时间变量

3.Shader中经常会改变的数据

4.纹理动画

4.1.背景滚动

4.1.1.补充知识

4.1.2.基本原理

4.2.帧动画

4.2.1.基本原理

5.流动的2D河流

5.1.基本原理

5.2.关键步骤

5.3.补充知识

6.广告牌效果

6.1.概念

6.2.基本原理

7.顶点动画注意事项

7.1.批处理

7.1.1.为什么批处理会影响顶点动画

7.1.2.关闭批处理的问题

7.1.3.如何解决问题

7.2.阴影

7.2.1.问题的产生

7.2.2.解决

8.如有疏漏,还请指出


1.原理

        利用时间变化来改变数据,从而导致渲染结果改变,带来画面变化


2.Shader中内置的时间变量

        1.float4 _TIme:四个分量分别是(t/20,t,2t,3t),t代表游戏场景从加载开始所经过的时间

        2.float4 _SinTime:四个分量分别是(t/8,t/4,t/2,t),t代表游戏运行时间的正弦值

        3.float4 _CosTime:四个分量分别是(t/8,t/4,t/2,t),t代表游戏运行时间的余弦值

        4.float4 unity_DeltaTime:(dt,1/dt,smootDt,1/smootDt),dt代表帧间隔时间(上一帧到当前帧间隔时间),smootDt是平滑处理过的时间间隔,对帧间隔时间进行了某种平滑算法处理后得到的结果


3.Shader中经常会改变的数据

        1.颜色:通过时间控制颜色的变化,如 渐变,闪烁 等

        2.位置:利用时间使顶点在某个方向上移动,如 波动 等

        3.纹理坐标:利用时间变化来改变纹理坐标,如 水流,云彩,序列帧动画 等

        4.法线:利用时间动态修改法线方向,如 风吹草动 等

        5.缩放:利用时间改变物体缩放比例,如 脉动,跳动 等

        6.透明度:利用时间控制物体透明度,如 淡入淡出,闪烁 等


4.纹理动画

4.1.背景滚动

4.1.1.补充知识

        frac(x):内部计算规则为frac(x) = 1 - floor(x),它能保留一个数的小数部分,负数保留的是小数部分+1的结果。它能保证uv坐标在0-1之间。

4.1.2.基本原理

        不停地利用时间变量对uv坐标进行偏移运算,超过1的部分从0开始采样,小于1同理

Shader "Models_4/RollingBackground"
{Properties{_MainTex("Texture", 2D) = ""{}//控制流速_RollingSpeedU("RollingSpeedU", Float) = 1_RollingSpeedV("RollingSpeedV", Float) = 1}SubShader{Tags { "RenderType"="Transparent" "Queue" = "Transparent" "IgnoreProjector" = "True"}ZWrite OffBlend SrcAlpha OneMinusSrcAlphaPass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"sampler2D _MainTex;float _RollingSpeedU;float _RollingSpeedV;struct v2f{float2 uv : TEXCOORD0;float4 pos : SV_POSITION;};v2f vert (appdata_full v){v2f data;data.pos = UnityObjectToClipPos(v.vertex);data.uv = v.texcoord.xy;return data;}fixed4 frag (v2f f) : SV_Target{float2 uv = frac(float2(_Time.y * _RollingSpeedU, _Time.y * _RollingSpeedV) + f.uv);fixed4 backTex = tex2D(_MainTex, uv);return backTex;}ENDCG}}
}
两个背景以不同速度滚动

4.2.帧动画

4.2.1.基本原理

        通过_Time.y确认当前具体应该是哪一帧,然后算出在图中的几行几列,即确认采样范围,然后将采样范围缩放到 0-1 之间,但由于uv采样是从左下角开始,故采样范围要经过一点变化。

Shader "Models_4/SequenceFrame"
{Properties{_MainTex("MainTex", 2D) = ""{}//图集行列_Rows("Rows", Int) = 8_Columns("Columns", Int) = 8_SequenceFrameSpeed("SequenceFrameSpeed", Float) = 1}SubShader{Tags { "RenderType" = "Transparent" "IgnoreProjector" = "True" "Queue" = "Transparent" }ZWrite OffBlend SrcAlpha OneMinusSrcAlphaPass{Tags {}CGPROGRAM#include "UnityCG.cginc"#include "Lighting.cginc"#pragma vertex vert#pragma fragment fragsampler2D _MainTex;float _Rows;float _Columns;float _SequenceFrameSpeed;struct v2f{float4 pos : SV_POSITION;float2 uv : TEXCOORD0;};v2f vert(appdata_full v){v2f data;data.pos = UnityObjectToClipPos(v.vertex);data.uv = v.texcoord.xy;return data;}fixed4 frag(v2f f) : SV_TARGET{//当前帧的索引 float frameIndex = floor(_Time.y * _SequenceFrameSpeed) % (_Rows * _Columns);//图片采样起始位置的运算,除以对应的行和列的目的是 将其转换到0-1的坐标范围内//这里1 - ,是因为要把图片坐标系(左上为原点)转换为 uv坐标系(左下为原点)//+ 1 也差不多这个原因float2 frameUV = float2((frameIndex % _Columns) / _Columns, 1 - ((floor(frameIndex / _Columns) + 1) / _Rows));float2 size = float2(1 / _Columns, 1 / _Rows);//* size 相当于把0 - 1范围 缩放到了0 - 1/8的范围内//+ frameUV 是把起始的采样位置 移到了 对应帧(小格子)的起始采样位置float2 uv = f.uv * size + frameUV;fixed4 frameColor = tex2D(_MainTex, uv);return frameColor;}ENDCG}}
}
帧动画


5.流动的2D河流

5.1.基本原理

        让我们的顶点在对应的轴上产生偏移,主要运用的是Shader中的内置函数sin以及内置时间变量_Time.y

        波浪感的关键因素:

        1.波长:                  其越大,波动越缓慢,周期越长
        2.波长的倒数:       其越大,波动越频繁,周期越短
        3.频率:                  单位时间内波动发生的次数
        4.幅度:                  波峰或波谷相对于中线的最大偏移位置

5.2.关键步骤

        1.让顶点上下动起来:让sin参与计算,如sin(_Time.y),可使其不断返回-1~1之间的值,而为了控制波动频率,可以用sin(_Time.y * 波动频率)

        问题是所有顶点的偏移都一样,会出现整体移动的效果

        2.让顶点有差异地动起来:以不同地坐标制造差异性,可以使用sin(_Time.y * 波动频率 + 顶点某轴的坐标),然后用得到的返回值作为顶点在某一轴向的偏移值,便可以让顶点有差异性的动起来

        问题是无法体现波长和波动幅度(振幅,或者说幅度)

        3.体现波长和幅度:使用

波动幅度 * sin(_Time.y * 波动频率 + 顶点某轴坐标 * 波长的倒数)

        倒数越大,波形周期越短

        具体轴向根据模型空间决定

5.3.补充知识

        渲染标签DisableBatching:其作用是是否对SubShader关闭批处理,原因是我们在制作顶点动画的时候,有时需要使用模型空间下的数据,而批处理会合并所有相关的模型,这些模型各自的模型空间会丢失,导致我们无法正确使用模型空间下相关数据。在实现2d河流效果时,我们就需要让顶点在模型空间下进行偏移,因此需要使用该标签,为Shader关闭批处理。

        同时,对于导入的模型资源,要观察其符不符合unity轴向标准(左右x,上下y,前后z)

Shader "Models_4/Water_2D"
{Properties{_MainTex("MainTex", 2D) = ""{}//类似漫反射颜色(大概)_Color("Color", Color) = (1, 1, 1, 1)//振幅_WaveAmplitude("WaveAmplitude", Float) = 1//波动频率_WaveFrequency("WaveFrequency", Float) = 1//波长的倒数_InvWaveLength("InvWaveLength", Float) = 1//纹理变化速度_Speed("Speed", Float) = 1}SubShader{Tags { "Queue" = "Transparent" "IgnorProjector" = "True" "RenderType" = "Transparent" "DisableBatching" = "True" }Pass{Tags { "LightMode" = "ForwardBase" }ZWrite OffBlend SrcAlpha OneMinusSrcAlphaCGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_fwdbase#include "UnityCG.cginc"#include "Lighting.cginc"#include "AutoLight.cginc"sampler2D _MainTex;float4 _MainTex_ST;float4 _Color;float _WaveAmplitude;float _WaveFrequency;float _InvWaveLength;float _Speed;struct v2f{float4 pos : SV_POSITION;float2 uv : TEXCOORD0;};v2f vert(appdata_full v){v2f data;float4 offset = float4(0, 0, 0, 0);//在世界空间下看,模型的x是y轴,我们要对模型的x轴进行改变,但不能直接改变世界空间下的y轴,因为世界空间下改变不会作用到模型空间下//直接改模型空间的点,并且对其原始状态上的轴做判断offset.x = _WaveAmplitude * sin(_WaveFrequency * _Time.y + v.vertex.z * _InvWaveLength);float4 vertex = v.vertex + offset;data.pos = UnityObjectToClipPos(vertex);data.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;//让主纹理也动data.uv += float2(0, _Time.y * _Speed);return data;}fixed4 frag(v2f f) : SV_TARGET{//float2 uv = frac(f.uv + float2(0, _Time.y * _Speed));fixed4 mainColor = tex2D(_MainTex, f.uv) * _Color;return mainColor;}ENDCG}}
}
2D河流效果

6.广告牌效果

6.1.概念

        是一种图形技术,用于确保对象始终面对摄像机,同时在某些轴上保持固定的方向(一般分为全向广告牌和轴对齐广告牌)

        全向广告牌:任何视角下,对象在所有轴上始终面向 摄像机

        轴对齐广告牌:对象在一个轴上保持固定方向,而在其他轴上面向摄像机。其中垂直广告牌尤为特殊,他在水平面(XZ)平面上旋转,但在垂直方向上始终保持不变

6.2.基本原理

        核心是旋转模型空间坐标系让其始终面向摄像机,故而需要构建一个基于模型空间的新坐标系。改坐标系有两个关键因素构成:

        1.原点:基于模型空间的,可以自定义,但一般还是用000

        2.三个轴向(x轴,y轴,z轴):通常情况下这仨由 右方向,垂直向上方向,视角方向 构成。

        轴的计算:获得视角向量(新z轴)后,将其与 旧y轴(垂直向上的轴,(0, 1, 0))叉乘得到 右方向 轴(即 新x轴),然后将 视角方向 与 右方向 叉乘得到 新y轴。

        最后,新顶点的位置如下:

偏移位置 = 顶点坐标 - Center

新顶点位置 = Center + X轴 * 偏移位置.x + Y轴 * 偏移位置.y + Z轴 * 偏移位置.z

        垂直广告牌只需要在计算视角方向(新x轴)的时候,让该轴的y分量为0即可

Shader "Models_4/BillboardEffect"
{Properties{_MainTex("MainTex", 2D) = ""{}_Color("Color", Color) = (1, 1, 1, 1)_VerticalAmount("VerticalAmount", Range(0, 1)) = 0}SubShader{Tags { "Queue" = "Transparent" "RenderType" = "Transparent" "IgnoreProjector" = "True" "DisableBatching" = "True" }Pass{Tags { "LightMode" = "ForwardBase" }ZWrite OffBlend SrcAlpha OneMinusSrcAlphaCull OffCGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_fwdbase#include "UnityCG.cginc"sampler2D _MainTex;float4 _MainTex_ST;float4 _Color;float _VerticalAmount;struct v2f{float4 pos : SV_POSITION;float2 uv : TEXCOORD0;};v2f vert(appdata_full v){v2f data;float3 center = float3(0, 0, 0);float3 cameraInObjectPos = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1)); //zfloat3 normalDir = cameraInObjectPos - center;//全向或是垂直normalDir.y *= _VerticalAmount;normalDir = normalize(normalDir);float3 oldUpDir = normalDir.y > 0.999 ? float3(0, 0, 1) : float3(0, 1, 0);//float3 oldUpDir = float3(0, 1, 0);//x//xfloat3 rightDir = normalize(cross(oldUpDir, normalDir));//yfloat3 newUpDir = normalize(cross(normalDir, rightDir));float3 centerOffset = v.vertex.xyz - center;float3 newVertex = center + rightDir * centerOffset.x + newUpDir * centerOffset.y + normalDir * centerOffset.z;data.pos = UnityObjectToClipPos(newVertex);data.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;return data;}fixed4 frag(v2f f) : SV_TARGET{fixed4 mainColor = tex2D(_MainTex, f.uv) * _Color;return mainColor;}ENDCG}}
}
全向广告牌效果


7.顶点动画注意事项

7.1.批处理

7.1.1.为什么批处理会影响顶点动画

        unity中默认有静态批处理和动态批处理,而批处理的主要作用是合并多个对象,将它们作为一个DrawCall来处理。之所以批处理会对顶点动画带来影响,是因为 不同的对象拥有不同的变换矩阵(平移,旋转,缩放)。而进行批处理之后,它们的变换矩阵将会进行统一处理,进而令其失去独立性。

        举例子就是两个魔尺(颜色一样),分开各拼各的时候,你能分辨出它们各自的顶点(就当是每个三角衔接处),而如果把这俩魔尺合一块拼起来,那么某一个点究竟是哪把魔尺的就无法辨别了。

7.1.2.关闭批处理的问题

        DrawCall的提升,进而导致性能的问题,而如果因为关闭批处理带来了性能问题,并且必须优化带有定点动画的Shader,该如何解决呢

7.1.3.如何解决问题

        提前将独立的模型顶点存储起来:

        1.通过c#代码存储到网格的颜色属性中:在Shader中通过颜色属性获取顶点信息。我们可以在appdata_full中点出color成员来使用这些顶点

    private void SaveToMeshColor(){MeshFilter meshFilter = GetComponent<MeshFilter>();if (meshFilter != null){Mesh mesh = meshFilter.mesh;Vector3[] vertices = mesh.vertices;Color[] colors = new Color[vertices.Length];for (int i = 0; i < vertices.Length; i++){colors[i] = new Color(vertices[i].x, vertices[i].y, vertices[i].z, 1);}mesh.colors = colors;}}

        2.通过c#代码存到uv中:与存储到color类似,但是一般只在存储两个值的时候使用。

7.2.阴影

7.2.1.问题的产生

        顶点动画通过Fallback所产生的阴影是根据 没有变形过的顶点 来的,所以对于2d河流这种,直接使用Fallback产生的阴影会跟实际不符

7.2.2.解决

        自己实现阴影,并在该Pass中及逆行顶点偏移的计算即可。无需进行裁剪空间坐标变换以及uv相关计算

        Pass{Tags { "LightMode" = "ShadowCaster" }CGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_shadowcaster#include "UnityCG.cginc"float _WaveAmplitude;float _WaveFrequency;float _InvWaveLength;struct v2f{V2F_SHADOW_CASTER;};v2f vert(appdata_full v){v2f data;float4 offset = float4(0, 0, 0, 0);offset.x = _WaveAmplitude * sin(_WaveFrequency * _Time.y + v.vertex.z * _InvWaveLength);v.vertex += offset;//这个会自动调用v的数据TRANSFER_SHADOW_CASTER_NORMALOFFSET(data);return data;}fixed4 frag(v2f f) : SV_TARGET{SHADOW_CASTER_FRAGMENT(f);return fixed4(1, 1, 1, 1);}ENDCG}

 


8.如有疏漏,还请指出

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

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

相关文章

Node.js 实现简单爬虫

介绍 爬虫是一种按照一定的规则&#xff0c;自动地抓取万维网信息的程序或者脚本。 本文将使用 Nodejs 编写一个简单的爬虫脚本&#xff0c;爬取一个美食网站&#xff0c;获取菜品的标题和图片链接&#xff0c;并以表格的形式输出。 准备工作 1、初始化项目 首先&#xff0…

JVM执行流程与架构(对应不同版本JDK)

直接上图&#xff08;对应JDK8以及以后的HotSpot&#xff09; 这里主要区分说明一下 方法区于 字符串常量池 的位置更迭&#xff1a; 方法区 JDK7 以及之前的版本将方法区存放在堆区域中的 永久代空间&#xff0c;堆的大小由虚拟机参数来控制。 JDK8 以及之后的版本将方法…

2025蓝桥杯JAVA编程题练习Day3

1.黛玉泡茶【算法赛】 问题描述 话说林黛玉闲来无事&#xff0c;打算在潇湘馆摆个茶局&#xff0c;邀上宝钗、探春她们一起品茗赏花。黛玉素来讲究&#xff0c;用的茶杯也各有不同&#xff0c;大的小的&#xff0c;高的矮的&#xff0c;煞是好看。这不&#xff0c;她从柜子里…

p5r预告信生成器API

p5r预告信生成器API 本人将js生成的p5r预告信使用go语言进行了重写和部署&#xff0c;并开放了其api&#xff0c;可以直接通过get方法获取预告信的png。 快速开始 http://api.viogami.tech/p5cc/:text eg: http://api.viogami.tech/p5cc/persona5 感谢p5r风格字体的制作者和…

VsCode创建VUE项目

1. 首先安装Node.js和npm 通过网盘分享的文件&#xff1a;vsCode和Node&#xff08;本人电脑Win11安装&#xff09; 链接: https://pan.baidu.com/s/151gBWTFZh9qIDS9XWMJVUA 提取码: 1234 它们是运行和构建Vue.js应用程序所必需的。 1.1 Node安装&#xff0c;点击下一步即可 …

软件设计模式

目录 一.创建型模式 抽象工厂 Abstract Factory 构建器 Builder 工厂方法 Factory Method 原型 Prototype 单例模式 Singleton 二.结构型模式 适配器模式 Adapter 桥接模式 Bridge 组合模式 Composite 装饰者模式 Decorator 外观模式 Facade 享元模式 Flyw…

Maven架构项目管理工具

1.1什么是Maven 翻译为“专家”&#xff0c;“内行”Maven是跨平台的项目管理工具。主要服务于基于Java平台的项目构建&#xff0c;依赖管理和项目信息管理。什么是理想的项目构建&#xff1f; 高度自动化&#xff0c;跨平台&#xff0c;可重用的组件&#xff0c;标准化的 什么…

【Linux】25.进程信号(1)

文章目录 1. 信号入门1.1 进程与信号的相关知识1.2 技术应用角度的信号1.3 注意1.4 信号概念1.5 信号处理常见方式概览 2. 产生信号2.1 通过终端按键产生信号2.2 调用系统函数向进程发信号2.3 由软件条件产生信号2.4 硬件异常产生信号2.5 信号保存 3. 阻塞信号3.1 信号其他相关…

第二个Qt开发实例:在Qt中利用GPIO子系统和sysfs伪文件系统实现按钮(Push Button)点击控制GPIO口(效果为LED2灯的灭和亮)

引言 本文承接博文 https://blog.csdn.net/wenhao_ir/article/details/145420998 里的代码&#xff0c;在那里面代码的基础上添加上利用sysfs伪文件系统实现按钮(Push Button)点击控制GPIO口的代码&#xff0c;进而实现LED2灯的灭和亮。 最终的效果是点击下面的LED按钮实现LED…

Unity 2D实战小游戏开发跳跳鸟 - 记录显示最高分

上一篇文章中我们实现了游戏的开始界面,在开始界面中有一个最高分数的UI,本文将接着实现记录最高分数以及在开始界面中显示最高分数的功能。 添加跳跳鸟死亡事件 要记录最高分,则需要在跳跳鸟死亡时去进行判断当前的分数是否是最高分,如果是最高分则进行记录,如果低于之前…

2025.2.5——五、[网鼎杯 2020 青龙组]AreUSerialz 代码审计|反序列化

题目来源&#xff1a;BUUCTF [网鼎杯 2020 青龙组]AreUSerialz 目录 一、打开靶机&#xff0c;整理信息 二、解题思路 step 1&#xff1a;代码审计 step 2&#xff1a;开始解题 突破protected访问修饰符限制 三、小结 一、打开靶机&#xff0c;整理信息 直接得到一串ph…

芯科科技的BG22L和BG24L带来应用优化的超低功耗蓝牙®连接

全新的BG22L为常见蓝牙设备提供强大的安全性和处理能力&#xff0c;而BG24L支持先进的AI/ML加速和信道探测功能 2025年2月6日 – 致力于以安全、智能无线连接技术&#xff0c;建立更互联世界的全球领导厂商Silicon Labs&#xff08;亦称“芯科科技”&#xff0c;NASDAQ&#x…

iOS 音频录制、播放与格式转换

iOS 音频录制、播放与格式转换:基于 AVFoundation 和 FFmpegKit 的实现 在 iOS 开发中,音频处理是一个非常常见的需求,比如录音、播放音频、音频格式转换等。本文将详细解读一段基于 AVFoundation 和 FFmpegKit 的代码,展示如何实现音频录制、播放以及 PCM 和 AAC 格式之间…

区块链技术:Facebook 重塑社交媒体信任的新篇章

在这个信息爆炸的时代&#xff0c;社交媒体已经成为我们生活中不可或缺的一部分。然而&#xff0c;随着社交平台的快速发展&#xff0c;隐私泄露、数据滥用和虚假信息等问题也日益凸显。这些问题的核心在于传统社交媒体依赖于中心化服务器存储和管理用户数据&#xff0c;这种模…

STM32的HAL库开发---高级定时器---输出比较模式实验

一、高级定时器输出比较模式实验原理 定时器的输出比较模式总共有8种&#xff0c;本文使用其中的翻转模式&#xff0c;当TIMXCCR1TIMXCNT时&#xff0c;翻转OC1REF的电平&#xff0c;OC1REF为输出参考信号&#xff0c;高电平有效&#xff0c;OC1REF信号连接到0C1上面&#xff…

Games104——游戏引擎Gameplay玩法系统:基础AI

这里写目录标题 寻路/导航系统NavigationWalkable AreaWaypoint NetworkGridNavigation Mesh&#xff08;寻路网格&#xff09;Sparse Voxel Octree Path FindingDijkstra Algorithm迪杰斯特拉算法A Star&#xff08;A*算法&#xff09; Path Smoothing Steering系统Crowd Simu…

Nginx进阶篇 - nginx多进程架构详解

文章目录 1. nginx的应用特点2. nginx多进程架构2.1 nginx多进程模型2.2 master进程的作用2.3 进程控制2.4 worker进程的作用2.5 worker进程处理请求的过程2.6 nginx处理网络事件 1. nginx的应用特点 Nginx是互联网企业使用最为广泛的轻量级高性能Web服务器&#xff0c;其特点是…

grafana面板配置opentsdb

新增面板&#xff1a; 这里add-panel: 如果不是想新增面板而是想新增一行条目&#xff0c;则点击convert to row: 在新增的面板这里可以看到选择数据源 Aggregator&#xff1a;聚合条件&#xff0c;区分下第一行和第二行的aggregator&#xff0c;第一个是对指标值的聚合&…

记一次golang环境的变化

前两天编译打包了了个文件&#xff0c;把env的 goos 搞坏了 导致运行项目一直报错 先是这样 go: unsupported GOOS/GOARCH pair windows/amd64再是这样 /amd64supported GOOS/GOARCH pair linux咱就说&#xff0c;咱也是知道环境配置的有问题 &#xff08; go env GOOS &…

ARM嵌入式学习--第十三天(I2C)

I2C --介绍 I2C&#xff08;Inter-intergrated Circuit 集成电路&#xff09;总线是Philips公司在八十年代初推出的一种串行、半双工的总线&#xff0c;主要用于近距离、低速的芯片之间的通信&#xff1b;I2C总线有俩根双向的信号线&#xff0c;一根数据线SDA用于收发数据&…