当前位置: 首页 > news >正文

Chapter11让画面动起来——Shader入门精要学习笔记

Chapter11让画面动起来

  • 一、Unity Shader中的内置变量(时间篇)
  • 二、纹理动画
    • 1.序列帧动画
    • 2.滚动背景
  • 三、顶点动画
    • 1.流动的河流
    • 2.广告牌
    • 3.注意事项
      • ①批处理问题
      • ②阴影投射问题

一、Unity Shader中的内置变量(时间篇)

Unity Shader 提供了一系列时间变量来允许我们方便地在Shader中访问运行时间
在这里插入图片描述

二、纹理动画

1.序列帧动画

  • 原理:依次播放关键帧图像,形成连续动画
 Properties{_Color ("Color Tint", Color) = (1,1,1,1)_MainTex ("Image Sequence", 2D) = "white"{}_HorizontalAmount ("Horizontal Amount", Float) = 4_VerticalAmount ("Vertical Amount", Float) = 4_Speed ("Speed", Range(1,100)) = 3}
  • _MainTex 就是包含了所有关键帧图像的纹理
  • _HorizontalAmount 和 _VerticalAmount 分别代表了该图像在水平方向和竖直方向包含的关键帧图像的个数
  • _Speed 用于控制序列帧动画的播放速度
SubShader
{Tags {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}Pass {Tags {"LightMode" = "ForwardBase"}Zwrite OffBlend SrcAlpha OneMinusSrcAlpha
  • 序列帧通常是透明纹理,可以被当成一个半透明对象
v2f vert(a2v v)
{v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);return o;
}
  • 把顶点纹理坐标存储到了v2f中
            fixed4 frag(v2f i):SV_Target {float time = floor(_Time.y * _Speed);float row = floor(time / _HorizontalAmount);float column = time - row * _HorizontalAmount;//				half2 uv = float2(i.uv.x /_HorizontalAmount, i.uv.y / _VerticalAmount);
//				uv.x += column / _HorizontalAmount;
//				uv.y -= row / _VerticalAmount;half2 uv = i.uv + half2(column, -row);uv.x /= _HorizontalAmount;uv.y /= _VerticalAmount;fixed4 c = tex2D(_MainTex, uv);c.rgb *= _Color;return c;}
  • 前三行计算了行列数
    • _Time.y * _Speed 相乘得到模拟的时间,floor函数用来对结果取整
    • time / _HorizontalAmount 商作为当前对应的行索引,余数是列索引
  • 使用行列索引值来构建真正的采样坐标 (注释的)
    • 我们可以首先把原纹理坐标 i.uv 按行数和列数进行等分, 得到每个子图像的纹理坐标范围
    • 使用当前的行列数对上面的结果进行偏移,得到当前子图像的纹理坐标范围
    • 竖直方向坐标偏移需要使用减法,因为Unity纹理坐标竖直方向顺序是从上到下,序列帧的图像是从下到上的,相反的
  • 将原纹理坐标 i.uv 加上一个向量 (column, -row),然后分别除以水平方向和竖直方向关键帧数量,得到当前子图像的纹理坐标范围。这种方法更加通用,即使关键帧图像大小不同也可以使用。

2.滚动背景

Properties
{_MainTex ("Base Layer (RGB)", 2D) = "white" {}_DetailTex ("2nd Layer (RGB)", 2D) = "white" {}_ScrollX ("Base layer Scroll Speed", Float) = 1.0_Scroll2X ("2nd layer Scroll Speed", Float) = 1.0_Multiplier ("Layer Multiplier", Float) = 1
}
  • _MainTex 表示第一层(较远),_DetailTex 表示第二层(较近)
  • _ScrollX 和 _Scroll2X 对应了各自的水平滚动速度
  • _Multiplier 控制整体亮度
v2f vert (a2v v) {v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex) + frac(float2(_ScrollX, 0.0) * _Time.y);o.uv.zw = TRANSFORM_TEX(v.texcoord, _DetailTex) + frac(float2(_Scroll2X, 0.0) * _Time.y);return o;
}
  • 最基本的顶点变换,把顶点变换到裁剪空间
  • 首先利用 TRANSFORM_TEX() 得到初始纹理坐标,再使用内置的_Time.y 变量在水平方向上对纹理坐标进行偏移
  • 把两张纹理存在一个uv中
fixed4 frag (v2f i) : SV_Target {fixed4 firstLayer = tex2D(_MainTex, i.uv.xy);fixed4 secondLayer = tex2D(_DetailTex, i.uv.zw);fixed4 c = lerp(firstLayer, secondLayer, secondLayer.a);c.rgb *= _Multiplier;return c;
}
  • 使用 i.uv.xy 和 i.uv.zw 对两张背景纹理进行采样

  • 使用第二层纹理的透明通道来混合两张纹理

  • 远处的背景应该移动的比近处的慢

三、顶点动画

1.流动的河流

  • 原理就是使用正弦函数来模拟水流波动效果
Properties {_MainTex ("Main Tex", 2D) = "white" {}_Color ("Color Tint", Color) = (1, 1, 1, 1)_Magnitude ("Distortion Magnitude", Float) = 1_Frequency ("Distortion Frequency", Float) = 1_InvWaveLength ("Distortion Inverse Wave Length", Float) = 10_Speed ("Speed", Float) = 0.5
}
  • _MainTex 是河流纹理, _Color 控制整体颜色,_Magnitude 控制水波流动的幅度,_Frequency 控制波动频率,_InvWaveLength 控制波长的倒数,_Speed 控制速度
SubShader {// Need to disable batching because of the vertex animationTags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}
  • “DisableBatching”=“True” 通过该标签来直接指明是否对该SubShader使用批处理(需要特殊处理的Shader就包含了模型空间的顶点动画),批处理会合并所有相关模型,而这些模型各自的模型空间就会丢失,在本例中,需要在物体模型空间下对顶点进行位置偏移,因此需要取消批处理操作
v2f vert(a2v v) {v2f o;float4 offset;offset.yzw = float3(0.0, 0.0, 0.0);offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude;o.pos = UnityObjectToClipPos(v.vertex + offset);o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);o.uv +=  float2(0.0, _Time.y * _Speed);return o;
}
  • 先计算顶点位移量,我们只希望顶点x方向进行位移,所以yzw都被设置为0
  • 利用_Frequency 属性和内置的 _Time.y 来控制正弦函数的频率
  • 为了让不同位置有不同的位移,我们对上述结果加上了模型空间下的位置分量,并×_InvWaveLength 来控制波长
  • 最后×_Magnitude 控制波动幅度,得到最终位移
  • 然后把位移量添加到顶点上,再进行顶点变换即可
fixed4 frag(v2f i) : SV_Target {fixed4 c = tex2D(_MainTex, i.uv);c.rgb *= _Color.rgb;return c;
} 
  • 直接对纹理进行采样再添加颜色即可

2.广告牌

  • 广告牌技术:会根据视角方向来旋转一个被纹理着色的多边形,使得好像总是面对着相机
  • 本质就是构建旋转矩阵
  • 一个变换矩阵需要3个基向量,广告牌技术使用的通常是 表面法线、指向上的方向、指向右的方向,除此之外,还需要指定一个 锚点(在旋转过程中固定不变的,以此来确认多边形在空间中的位置)
  • 计算基向量(相互正交的):
    • 法线方向: 通常为视角方向或固定方向
    • 指向上的方向: 通常为 (0, 1, 0) 或固定方向
    • 指向右的方向: 通过法线方向和指向上的方向计算得到 r i g h t = u p × n o r m a l right = up\times normal right=up×normal 叉积
    • r i g h t right right 归一化后,再由法线方向和指向右的方向计算出正交的指向上的方向 u p ′ = n o r a m l × r i g h t up' = noraml \times right up=noraml×right
      在这里插入图片描述
	Properties {_MainTex ("Main Tex", 2D) = "white" {}_Color ("Color Tint", Color) = (1, 1, 1, 1)_VerticalBillboarding ("Vertical Restraints", Range(0, 1)) = 1 }
  • _MainTex 是广告牌的透明纹理,_Color 控制整体颜色,_VerticalBillboarding 调整是固定法线还是固定指向上的方向(0是向上,1是法线)
SubShader {// Need to disable batching because of the vertex animationTags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}
  • 在广告牌技术中,我们需要使用物体模型空间下的位置来作为模电进行计算,所以要取消批处理
v2f vert (a2v v) {v2f o;// Suppose the center in object space is fixedfloat3 center = float3(0, 0, 0);float3 viewer = mul(unity_WorldToObject,float4(_WorldSpaceCameraPos, 1));float3 normalDir = viewer - center;// If _VerticalBillboarding equals 1, we use the desired view dir as the normal dir// Which means the normal dir is fixed// Or if _VerticalBillboarding equals 0, the y of normal is 0// Which means the up dir is fixednormalDir.y =normalDir.y * _VerticalBillboarding;normalDir = normalize(normalDir);// Get the approximate up dir// If normal dir is already towards up, then the up dir is towards frontfloat3 upDir = abs(normalDir.y) > 0.999 ? float3(0, 0, 1) : float3(0, 1, 0);float3 rightDir = normalize(cross(upDir, normalDir));upDir = normalize(cross(normalDir, rightDir));// Use the three vectors to rotate the quadfloat3 centerOffs = v.vertex.xyz - center;float3 localPos = center + rightDir * centerOffs.x + upDir * centerOffs.y + normalDir * centerOffs.z;o.pos = UnityObjectToClipPos(float4(localPos, 1));o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);return o;
}
  • 所有的计算都是在模型空间下计算的 (下面解释顶点着色器内容)
  • 首先选择模型空间的原点作为广告牌的锚点,利用内置变量 mul(unity_WorldToObject,float4(_WorldSpaceCameraPos, 1)) 获取模型空间下的视角位置
  • 然后开始计算三个正交基
float3 normalDir = viewer - center;// If _VerticalBillboarding equals 1, we use the desired view dir as the normal dir// Which means the normal dir is fixed// Or if _VerticalBillboarding equals 0, the y of normal is 0// Which means the up dir is fixednormalDir.y =normalDir.y * _VerticalBillboarding;normalDir = normalize(normalDir);
  • 根据观察位置和锚点计算目标法线方向,并使用_VerticalBillboarding 来控制垂直方向上的约束度
  • 1时法线方向固定为视角方向,0时向上方向固定为(0,1,0)
float3 upDir = abs(normalDir.y) > 0.999 ? float3(0, 0, 1) : float3(0, 1, 0);
  • 接着粗略地得到向上方向,为了放置法线方向和向上方向平行,对法线方向的y分量进行判断
float3 rightDir = normalize(cross(upDir, normalDir));
  • 然后得到了向右方向,并进行归一化
upDir = normalize(cross(normalDir, rightDir));
  • 在根据准确的法线方向和向右方向得到最后向上方向
// Use the three vectors to rotate the quadfloat3 centerOffs = v.vertex.xyz - center;float3 localPos = center + rightDir * centerOffs.x + upDir * centerOffs.y + normalDir * centerOffs.z;
  • 根据三个正交基矢量和原始位置相对于锚点的偏移量,计算得到新顶点的位置
o.pos = UnityObjectToClipPos(float4(localPos, 1));o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
  • 最后把模型空间的顶点位置变换到裁剪空间
  • 片元着色器只需要对纹理进行采样,再与颜色相乘即可
fixed4 frag (v2f i) : SV_Target {fixed4 c = tex2D (_MainTex, i.uv);c.rgb *= _Color.rgb;return c;
}

3.注意事项

①批处理问题

  • 因为批处理会 合并模型,导致模型空间丢失,而顶点动画又需要依赖模型空间的位置进行计算
    • 合并模型:将所有使用同一 Shader 的材质的模型合并成一个批次进行渲染,每个模型都被转换到了一个统一的坐标系中
  • 解决方法
    • 禁用批处理:通过在 SubShader 标签中 使用 “DisableBatching” 标签,可以强制禁用对该 Shader 的批处理,从而避免动画效果被破坏,但这样做会降低性能,因为会增加 Draw Call
    • 使用相对位置和方向:尽量避免使用模型空间下的绝对位置和方向进行计算,例如,使用顶点颜色存储每个顶点到锚点的距离,而不是直接使用模型空间的中心作为锚点
      • 为模型的每个顶点设置一个顶点颜色值,该颜色值表示该顶点到模型中心点的距离,以将颜色值的 R 分量设置为距离值,G、B 分量设置为 0
      • 读取每个顶点的顶点颜色值,并从中提取出距离值

②阴影投射问题

  • 内置的 ShadowCaster Pass 无法处理顶点动画: Unity 的内置阴影投射 Pass 没有进行顶点动画的处理,因此会导致阴影与物体的实际位置不匹配
  • 解决方法:自定义ShadowCaster Pass (见书)

相关文章:

Chapter11让画面动起来——Shader入门精要学习笔记

Chapter11让画面动起来 一、Unity Shader中的内置变量(时间篇)二、纹理动画1.序列帧动画2.滚动背景 三、顶点动画1.流动的河流2.广告牌3.注意事项①批处理问题②阴影投射问题 一、Unity Shader中的内置变量(时间篇) Unity Shader…...

c++之命名空间详解(namespace)

引例 在学习之前我们首先了来看这样一个情形: 在c语言下,我们写了两个头文件:链表和顺序表的。我们会定义一个type(typedef int type)方便改变数据类型(比如将int改成char),来做到整体代换。 但是我们两个头文件里面…...

【大模型】在大语言模型的璀璨星河中寻找道德的北极星

在大语言模型的璀璨星河中寻找道德的北极星 引言一、概念界定二、隐私保护的挑战2.1 数据来源的道德考量2.2 敏感信息的泄露风险 三、偏见与歧视的隐忧3.1 训练数据的偏见传递3.2 内容生成的不公倾向 四、责任归属的模糊地带4.1 生成内容的责任界定4.2 自动化决策的伦理考量 五…...

嵌入式Linux之Uboot简介和移植

uboot简介 uboot 的全称是 Universal Boot Loader,uboot 是一个遵循 GPL 协议的开源软件,uboot是一个裸机代码,可以看作是一个裸机综合例程。现在的 uboot 已经支持液晶屏、网络、USB 等高级功能。 也就是说,可以在没有系统的情况…...

算法整理——【贪心算法练习(1)】

上一篇博客算法整理——【贪心算法简述】-CSDN博客,我们介绍了贪心算法的基础知识,现在我们要对此进行进一步练习。 一、跳跃游戏II 例题为45. 跳跃游戏 II - 力扣(LeetCode),给定一个长度为 n 的 0 索引整数数组 nu…...

人脸识别课堂签到系统【PyQt5实现】

人脸识别签到系统 1、运用场景 课堂签到,上班打卡,进出门身份验证。 2、功能类别 人脸录入,打卡签到,声音提醒,打卡信息导出,打包成exe可执行文件 3、技术栈 python3.8,sqlite3,opencv,face_recognition,PyQt5,csv 4、流程图 1、导入库 2、编写UI界面 3、打…...

Linux dig命令常见用法

Linux dig命令常见用法 一、dig安装二、dig用法 DIG命令(Domain Information Groper命令)是常用的域名查询工具,通过此命令,你可以实现域名查询和域名问题的定位,对于网络管理员和在域名系统(DNS)领域工作的小伙伴来说,它是一个非…...

数学建模论文写作文档word

目录 1. 摘要写法1.1 确定题目与方法1.2 编写开头段落1.3 填写问题一1.4 重复步骤3填写其他问题1.5 编写结尾段落1.6 编写关键词 2. 问题重述2.1 问题背景2.2 问题提出 3. 问题分析4. 问题X模型的建立与求解5. 模型的分析5.1 灵敏度分析5.2 误差分析(主要用于预测类…...

嵌入式C语言面试相关知识——CPU、进程和线程相关(相关问题很多,会经常过来更新)

嵌入式C语言面试相关知识——CPU、进程和线程相关 一、博客声明二、自问题目——CPU相关1、什么是中断?如何处理中断?2、解释上下文切换(Context Switch)?3、在嵌入式中如何优化CPU使用? 三、自问题目——进程相关1、什么是进程&a…...

Linux学习看这一篇就够了,超超超牛的Linux基础入门

引言 小伙伴们,不管是学习c还是学习其他语言在我们学的路上都绕不过操作系统,而且,老生常谈的Linux更是每个计算机人的必修,那么我们对Linux的了解可能只是从别人那听到的简单的这个系统很牛,巴拉巴拉的,但…...

el-scrollbar组件使用踩坑记录

一、el-scrollbar和浏览器原生滚动条一起出现 问题描述 el-scrollbar组件主要用于替换浏览器原生导航条。如下图所示,使用el-scrollbar组件后,发现未能成功替换掉浏览器原生导航条,二者同时出现。 引发原因 el-scrollbar的height属性如果…...

Linux计算机结构

1.计算机设计原理 冯诺依曼体系结构 通过该结构得出:中央处理器 2.操作系统整体框架 操作系统是不会让你直接乱使用底层的各种硬件,但为了依旧能够让你使用到该资源则会给你预留一些窗口去让你与其交互(类比银行,直接小窗口交互,…...

应用进程、SurfaceFlinger进程、HWC进程 之间的关系

应用进程、SurfaceFlinger进程、HWC(Hardware Composer)进程在Android系统中扮演着重要的角色,它们之间的关系和通信流程是Android图形显示系统的核心部分。以下是这三者之间关系和通信流程的详细分析: 一、三者之间的关系 应用进…...

66.Python-web框架-Django-免费模板django-datta-able的分页的一种方式

目录 1.方案介绍 1.1实现效果 1.2django.core.paginator Paginator 类: Page 类: EmptyPage 和 PageNotAnInteger 异常: 1.3 templatetags 2.方案步骤 2.1创建一个common app 2.2创建plugins/_pagination.html 2.3 其他app的views.py查询方法 2.4在AIRecords.html里…...

Python编程学习笔记(1)--- 变量和简单数据类型

1、变量 在学习编程语言之前,所接触的第一个程序,绝大多数都是: print("Hello world!") 接下来尝试使用一个变量。在代码中的开头添加一行代码,并对第二行代码进行修改,如下: message "…...

第二证券:资金抱团“高股息”,超三成A股年内创历史新低!

A股商场行情冰火两重天。 “预制菜榜首股”跌破发行价 7月8日,味知香盘中最低跌至19.26元/股,股价跌破发行价,并创前史新低。揭露资料显现,公司是集研发、生产、销售为一体的半成品菜企业,现在具有8大产品系列&#…...

ASAN排查程序中内存问题使用总结

简介 谷歌有一系列Sanitizer工具,可用于排查程序中内存相关的问题。常用的Sanitizer工具包括: Address Sanitizer(ASan):用于检测内存使用错误。Leak Sanitizer(LSan):用于检测内存…...

day01:项目概述,环境搭建

文章目录 软件开发整体介绍软件开发流程角色分工软件环境 外卖平台项目介绍项目介绍定位功能架构 产品原型技术选型 开发环境搭建整体结构:前后端分离开发前后端混合开发缺点前后端分离开发 前端环境搭建Nginx 后端环境搭建熟悉项目结构使用Git进行版本控制数据库环…...

Python爬虫与数据可视化:构建完整的数据采集与分析流程

Python爬虫技术概述 Python爬虫是一种自动化的数据采集工具,它可以模拟浏览器行为,访问网页并提取所需信息。Python爬虫的实现通常涉及以下几个步骤: 发送网页请求:使用requests库向目标网站发送HTTP请求。获取网页内容&#xf…...

Java---包装类与泛型

1.包装类 1.1 包装类 在Java中,由于基本数据类型不是继承Object类,为了在泛型代码中可以支持基本数据类型,Java给每个基本数据类型各自提供了一个包装类。 如下图 除了char和int基本数据类型的包装类型有点特别,其他的都是首字…...

如何优化 PostgreSQL 中对于复杂数学计算的查询?

文章目录 一、理解复杂数学计算的特点二、优化原则(一)索引优化(二)查询重写(三)数据库配置调整(四)使用数据库内置函数的优势 三、具体的优化方案和示例(一)…...

前端面试题27(在实际项目中,如何有效地利用Vue3的响应式系统提高性能?)

在实际项目中,有效利用Vue3的响应式系统提高性能主要涉及以下几个关键点: 1. 合理使用reactive和ref reactive:用于将复杂的数据结构(如对象或数组)转换成响应式版本。确保只将需要实时更新的数据结构声明为响应式&am…...

掌握Vue 3生命周期:从组合式API到高效代码实践

引言 在 Vue 3 中,生命周期的概念得到了进一步的优化和简化。Vue 3 引入了组合式 API(Composition API),这为开发者提供了更灵活的方式来组织和重用代码逻辑。与传统的选项式 API(Options API)相比&#x…...

使用cgroup对pgsql进行分库资源限制

系统:Centos7 pg版本:14.11 自建pgsql14中有很多个库,一个库对应一个租户,偶尔会出现单个租户执行慢sql影响全局的问题,目前官方也没有比较合适的处理方案或者插件 解决方案: 因为pgsql是多进程应用,所以正好可以使用linux自带的cgroup功能进行资源限制。定时将进程中…...

【网络安全】一文带你了解什么是【网络劫持】

网络劫持(Network Hijacking)是一种网络攻击,攻击者通过非法手段劫持网络通信,导致合法用户的数据流被拦截、篡改或重定向到攻击者控制的系统。这种攻击可以在各种网络层面上进行,包括域名系统(DNS&#xf…...

springcloud分布式架构网上商城 LW +PPT+源码+讲解

3系统分析 3.1可行性分析 在开发系统之前要进行系统可行性分析,目的是在用最简单的方法去解决最大的问题,程序一旦开发出来满足了用户的需要,所带来的利益也很多。下面我们将从技术、操作、经济等方面来选择这个系统最终是否开发。 3.1.1技术…...

【Linux】动态库的制作与使用

💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤 📃个人主页 :阿然成长日记 …...

Vue框架引入

vue简介 1.1.vue是什么?Vue官网 英文官网: https://vuejs.org/中文官网: https://cn.vuejs.org/ vue是一套构建用户界面的渐进式javascript框架 构建用户界面:将我们手里拿到的数据通过某种办法变成用户可以看见的界面前端工程师的职责:就是在合适的时候发出合适的请求,然后…...

贝叶斯估计(1):期末大乱炖

写在前面! 1 先验分布和后验分布 三种信息:总体信息、样本信息、先验信息 总体信息:“总体是正态分布”;样本信息:总体抽取的样本提供的信息,是最新鲜的信息;先验信息:在抽样之前就…...

电脑找回彻底删除文件?四个实测效果的方法【一键找回】

电脑数据删除了还能恢复吗?可以的,只要我们及时撤销上一步删除操作,还是有几率找回彻底删除文件。 当我们的电脑文件被彻底删除后,尽管恢复的成功率可能受到多种因素的影响,但仍有几种方法可以尝试找回这些文件。本文整…...