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 Off
        Blend 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 animation
	Tags {"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 animation
	Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}
  • 在广告牌技术中,我们需要使用物体模型空间下的位置来作为模电进行计算,所以要取消批处理
v2f vert (a2v v) {
	v2f o;
	
	// Suppose the center in object space is fixed
	float3 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 fixed
	normalDir.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 front
	float3 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 quad
	float3 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 fixed
	normalDir.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 quad
	float3 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 (见书)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Chiasmodon:一款针对域名安全的公开资源情报OSINT工具

关于Chiasmodon Chiasmodon是一款针对域名安全的公开资源情报OSINT工具,该工具可以帮助广大研究人员从各种来源收集目标域名的相关信息,并根据域名、Google Play应用程序、电子邮件地址、IP地址、组织和URL等信息进行有针对性的数据收集。 该工具可以提…

window系统openssl开发环境搭建(VS2017)

window系统openssl开发环境搭建 VS2017 一、下载openssl二、安装openssl三、openssl项目配置3.1 配置include文件3.2 配置openssl动态库四、编写openssl测试代码五、问题总结5.1 问题 一5.2 问题二一、下载openssl https://slproweb.com/products/Win32OpenSSL.html 根据自己…

如何查看MCU编译生成的elf(out)文件内容

一般地,我们想要知道单片机程序编译完后的结构我们可以查看map文件或者是elf/out文件,map文件不能看函数的汇编格式,只能查看编译完成后变量、代码的地址和占用空间大小,而elf文件里面更加详细,还包含了函数的汇编&…

CobaltStrike的内网安全

1.上线机器的Beacon的常用命令 2.信息收集和网站克隆 3.钓鱼邮件 4.CS传递会话到MSF 5.MSF会话传递到CS 1上线机器的Beacon的常用命令 介绍:CobaltStrike分为服务端和客户端,一般我们将服务端放在kali,客户端可以在物理机上面&#xff0…

跨境人最怕的封店要怎么规避?

跨境人最怕的是什么?——封店 造成封店的原因很多,IP关联、无版权售卖、虚假发货等等,其中IP关联这个问题导致店铺被封在跨境商家中简直是屡见不鲜 IP关联,是指被海外平台检测到多家店铺开设在同一个站点上的情况。我们知道有些…

您的私人办公室!-----ONLYOFFICE8.1版本的桌面编辑器测评

随时随地创建并编辑文档,还可就其进行协作 ONLYOFFICE 文档是一款强大的在线编辑器,为您使用的平台提供文本文档、电子表格、演示文稿、表单和 PDF 编辑工具。 网页地址链接: https://www.onlyoffice.com/zh/office-suite.aspxhttps://www…

“拆分盘投资:机遇与风险并存

一、引言 随着互联网技术的日新月异,金融投资领域迎来了前所未有的变革,其中拆分盘作为一种新兴的投资模式,正逐渐进入公众的视野。其独特的价值增长逻辑和创新的投资机制,为投资者开辟了新的财富增值渠道。本文旨在深入探讨拆分…

tinyshop商城学习

1、使用badboy屏幕录制工具,获得服装购物业务的结果,生成.jmx文件 2、在JMeter中新建线程组,导入.jmx文件 3、完成进入商城,登录,服装页面进入,随机选择服装,添加购物车,开始结算&…

WAIC上官宣!大模型语料提取工具MinerU正式发布,开源免费“敲”好用

7月4日,2024 WAIC科学前沿全体会议在上海世博中心红厅隆重举行。上海人工智能实验室与商汤科技联合香港中文大学和复旦大学正式发布新一代大语言模型书⽣浦语2.5(InternLM2.5),同时全链条工具体系迎来重磅升级,对于大模…

第六篇——谋攻篇:上兵伐谋,不是说打仗要用计谋

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么? 四、总结五、升华 一、背景介绍 战术层面的东西,即便战略对了,战术不对&#xff0…

公司管理系统

准备工作 上图mapper类型错了,不是class,是interface,修正过后的图片,如下所示 修正如下 spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver spring.datasource.urljdbc:mysql://localhost:3306/webm spring.datasour…

CAN总线(上)

CAN总线(Controller Area Network Bus)控制器局域网总线 CAN总线是由BOSCH公司开发的一种简洁易用、传输速度快、易扩展、可靠性高的串行通信总线,广泛应用于汽车、嵌入式、工业控制等领域 CAN总线特征: 两根通信线(…

关于ORACLE单例数据库中的logfile的切换、删除以及添加

一、有关logfile的状态解释 UNUSED: 尚未记录change的空白group(一般会出现在loggroup刚刚被添加,或者刚刚使用了reset logs打开数据库,或者使用clear logfile后) CURRENT: 当前正在被LGWR使用的gro…

产科管理系统 专科电子病历系统源码,前后端分离架构,多家医院产科广泛运用,系统稳定,功能齐全

产科管理系统 专科电子病历系统源码,前后端分离架构,多家医院产科广泛运用,系统稳定,功能齐全 产科管理系统,特别是产科信息管理系统(Obstetrical Information Management System,简称OIMS&…

建智慧医院核心:智能导航系统的功能全析与实现效益

在数字化转型的浪潮中,智慧医院的建设是医疗行业数字化转型的关键步骤。随着医院规模的不断扩大和医疗设施的日益复杂,传统的静态不连续的导航方式已无法满足患者的需求。院内智能导航系统,作为医疗数字化转型的关键组成部分,正逐…

2024骨传导耳机品牌排行榜!盘点10款优质热门机型推荐!

骨传导耳机逐渐成为当下最受欢迎的热门机型,但随着耳机热度的增高,市面上一些不法商家仿佛看到了商机,纷纷投入骨传导耳机市场,这也导致骨传导耳机市场出现鱼龙混杂,劣质品牌横行的局面,纷纷有消费者反馈说…

android2024 gradle8 Processor和ksp两种编译时注解实现

android的编译时注解,老生常谈,外面的例子都是bindView,脑壳看疼了,自己学习和编写下。 而且现在已经进化到kotlin2.0了,google也逐渐放弃kapt,进入维护状态。所以要好好看看本贴。 参考我的工程&#xff1…

djangoGD高校信管专业就业信息管理系统-计算机毕业设计源码59343

djangoGD高校信管专业就业信息管理系统 摘 要 随着高校信管专业的快速发展,学生就业问题日益受到广泛关注。为了更好地服务学生,提高就业率,许多高校开始引入信息化手段来管理学生就业信息。然而,传统的就业信息管理方式存在很多问…

Linux关于文件的高级命令

tree命令 tree命令用于以树状图的形式显示目录结构。它可以帮助用户快速了解目录和文件的层次关系,非常适合用于浏览和理解大型文件系统的结构。 基础用法 显示当前目录的树状结构:tree 显示指定目录的树状结构:tree 指定目录路径 tree命…

【C++】 解决 C++ 语言报错:Segmentation Fault

文章目录 引言 段错误(Segmentation Fault)是 C 编程中常见且令人头疼的错误之一。段错误通常发生在程序试图访问未被允许的内存区域时,导致程序崩溃。本文将深入探讨段错误的产生原因、检测方法及其预防和解决方案,帮助开发者在…