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.mzph.cn/diannao/69319.shtml

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

相关文章

【Redis keys命令有什么问题?】

Redis keys命令有什么问题? 性能问题实际使用中的限制替代方案示例讲解Redis keys命令的问题示例替代方案:使用SCAN命令Java代码示例性能问题 时间复杂度:keys命令的时间复杂度是O(n),其中n是Redis中键的总数。这意味着,当Redis中存储的键数量非常大时,执行keys命令会遍历…

Python用langchain、OpenAI大语言模型LLM情感分析苹果股票新闻数据及提示工程优化应用...

全文链接&#xff1a;https://tecdat.cn/?p39614 本文主要探讨了如何利用大语言模型&#xff08;LLMs&#xff09;进行股票分析。通过使用提供的股票市场和金融新闻获取数据&#xff0c;结合Python中的相关库&#xff0c;如Pandas、langchain等&#xff0c;实现对股票新闻的情…

第19章 Future设计模式(Java高并发编程详解:多线程与系统设计)

1.先给你一张凭据 假设有个任务需要执行比较长的的时间&#xff0c;通常需要等待任务执行结束或者出错才能返回结果&#xff0c; 在此期间调用者只能陷入阻塞苦苦等待&#xff0c; 对此&#xff0c; Future设计模式提供了一种凭据式的解决方案。在我们日常生活中&#xff0c;关…

[Android] 全球网测-版本号4.3.8

[Android] 全球网测 链接&#xff1a;https://pan.xunlei.com/s/VOIV5G3_UOFWnGuMQ_GlIW2OA1?pwdfrpe# 应用介绍 "全球网测"是由中国信通院产业与规划研究所自主研发的一款拥有宽带测速、上网体验和网络诊断等功能的综合测速软件。APP突出六大亮点优势&#xff1a…

判断您的Mac当前使用的是Zsh还是Bash:echo $SHELL、echo $0

要判断您的Mac当前使用的是Zsh还是Bash&#xff0c;可以使用以下方法&#xff1a; 查看默认Shell: 打开“终端”应用程序&#xff0c;然后输入以下命令&#xff1a; echo $SHELL这将显示当前默认使用的Shell。例如&#xff0c;如果输出是/bin/zsh&#xff0c;则说明您使用的是Z…

MYSQL第四次

目录 题目分析 代码实现 一、修改 Student 表中年龄&#xff08;sage&#xff09;字段属性&#xff0c;数据类型由 int 改变为 smallint 二、为 Course 表中 Cno 字段设置索引&#xff0c;并查看索引 三、为 SC 表建立按学号&#xff08;sno&#xff09;和课程号&#xff…

MATLAB | 基于Theil-Sen斜率和Mann-Kendall检验的栅格数据趋势分析

最近看到一些博主分享关于 SenMK 检验的代码&#xff0c;对于新手来说可能有点复杂。我们编写了一段 MATLAB 代码&#xff0c;能够一次性解决这些问题&#xff0c;简化操作流程。我们还准备了几个关于趋势检验的空间分布图&#xff0c;供大家参考。 一、Sens Slope和Mann-Kenda…

72.在 Vue3 中使用 OpenLayers 进行 Drag-and-Drop 拖拽文件解析并显示图形

在 WebGIS 相关的开发中&#xff0c;我们经常需要加载各种地理数据文件&#xff0c;如 GeoJSON、KML、GPX 等。而 OpenLayers 提供了 DragAndDrop 交互组件&#xff0c;使得我们可以通过拖拽方式加载这些文件&#xff0c;并将其中的地理要素渲染到地图上。 本文将详细介绍如何…

VM虚拟机安装群晖系统

下载群晖系统 https://download.csdn.net/download/hmxm6/90351935 安装群晖连接软件 synology-assistant-6.2-24922(在上面的压缩包里面) 准备好VM虚拟机 创建群晖虚拟机 打开下载下来的虚拟机 添加硬盘 选择类型 创建新的磁盘 指定容量 指定存储文件 完成硬盘添加…

瞬态分析中的时域分析与频域分析:原理、对比与应用指南

目录 一、核心概念区分 二、时域分析&#xff1a;时间维度直接求解 1. 基本原理 2. 关键特点 3. 典型算法 4. 应用案例 三、频域分析&#xff1a;频率维度的等效映射 1. 基本原理 2. 关键特点 3. 典型方法 4. 应用案例 四、对比与选择依据 1. 方法论对比 2. 工程…

基于LMStudio本地部署DeepSeek R1

DeepSeek R1 DeepSeek R1是由DeepSeek团队开发的一款高性能AI推理模型&#xff0c;其开源版本包括完整的DeepSeek R1 671B权重&#xff0c;以及基于其蒸馏出的多个小型模型。 DeepSeek R1通过蒸馏技术将推理模式迁移到更小的模型中&#xff0c;显著提升了这些模型的推理能力。…

2.攻防世界 ics-06

题目描述处给出提示 进入题目页面如下 发现只有报表中心能进入下一个页面 页面内容&#xff1a; 发现有传参 改变日期也没有变化 更改id数值页面也没有回显 猜测应该有一个特定id对应的页面即为那一处入侵者留下的数据 下面使用burp suite爆破id值 先用burp suite抓包 右键…

Linux 的使用

补充内容&#xff1a;EasyHPC - Linux基础入门【笔记】 文章目录 文档与教程终端命令 文档与教程 Linux 操作系统目录结构解释 - Linux迷 (linuxmi.com) 一个专注于Linux和开源技术的在线平台&#xff1a;It’s FOSS (itsfoss.com)理解各种命令&#xff1a;explainshell.com -…

机器学习-线性回归(最大似然估计)

机器学习任务可以分为两类: 一类是样本的特征向量 &#x1d499; 和标签 &#x1d466; 之间存在未知的函数关系&#x1d466; h(&#x1d499;)&#xff0c;另一类是条件概率&#x1d45d;(&#x1d466;|&#x1d499;)服从某个未知分布。最小二乘法是属于第一类&#xff0c…

数据完整性与约束的分类

一、引言 为什么需要约束&#xff1f;为了保证数据的完整性。 &#xff08;1&#xff09;数据完整性 数据完整性指的是数据的精确性和可靠性。 为了保证数据的完整性&#xff0c;SQL对表数据进行额外的条件限制&#xff0c;从以下四方面考虑&#xff1a; ①实体完整性&…

autMan奥特曼机器人-对接deepseek教程

一、安装插件ChatGPT 符合openai api协议的大模型均可使用此插件&#xff0c;包括chatgpt-4/chatgpt-3.5-turbo&#xff0c;可自定义服务地址和模型&#xff0c;指令&#xff1a;gpt&#xff0c;要求Python3.7以上&#xff0c;使用官方库https://github.com/openai/openai-pyt…

@[TOC](优先级队列(堆)) 【本节目标】 1. 掌握堆的概念及实现 2. 掌握 PriorityQueue 的使用

优先级队列&#xff08;堆&#xff09; 1. 优先级队列1.1 概念 2. 优先级队列的模拟实现2.1 堆的概念2.2 堆的存储方式2.3 堆的创建2.3.1 堆向下调整2.3.2 堆的创建2.3.3 建堆的时间复杂度 2.4 堆的插入与删除2.4.1 堆的插入2.4.2 堆的删除 2.5 用堆模拟实现优先级队列 【本节目…

【Linux网络编程】之守护进程

【Linux网络编程】之守护进程 进程组进程组的概念组长进程 会话会话的概念会话ID 控制终端控制终端的概念控制终端的作用会话、终端、bash三者的关系 前台进程与后台进程概念特点查看当前终端的后台进程前台进程与后台进程的切换 进程组 进程组的概念 当我们使用以下命令查与…

11.PPT:世界动物日【25】

目录 NO12​ NO34 NO56​ NO789视频音频​ NO10/11/12​ NO12 设计→幻灯片大小→ →全屏显示&#xff08;16&#xff1a;9&#xff09;确定调整标题占位符置于图片右侧&#xff1a;内容占位符与标题占位符左对齐单击右键“世界动物日1”→复制版式→大小→对齐 幻灯片大小…

Java项目: 基于SpringBoot+mybatis+maven+mysql实现的智能学习平台管理系(含源码+数据库+毕业论文)

一、项目简介 本项目是一套基于SpringBootmybatismavenmysql实现的智能学习平台管理系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、…