Shader学习笔记-HLSL

前言 上一篇介绍了ShaderLab的结构,这一篇继续介绍Shader的主要逻辑部分。 Unity的旧版本用的是CG语言,现在逐步淘汰了,主要用微软的HLSL,并加了一些Unity自己的语法。 预处理 include include and include_with_pragmas directives in HLSL - Unity 手册 在 HLSL 中,#include 指令是一种预处理器指令。它们指示编译器将一个 HLSL 文件的内容包含在另一个 HLSL 文件中。它们包含的文件称为包含文件。 #include_with_pragmas 指令的工作方式与常规 #include 指令相同,但它也允许您在包含文件中使用 #pragma 指令。这意味着 #include_with_pragmas 指令允许您在多个文件之间共享 #pragma 指令。 在 HLSL 中向着色器编译器提供信息 Provide information to the shader compiler in HLSL - Unity 手册 #pragma target 3.0 #pragma exclude_renderers vulkan #pragma vertex vert #pragma fragment frag // The rest of your HLSL code goes here 指定着色器阶段 语句 功能 #pragma vertex <name> Compile the function with the given name as the vertex shader. Replace with the function name. This directive is required in regular graphics shaders.将具有给定名称的函数编译为顶点着色器。 替换为函数名称。此指令在常规图形着色器中是必需的。 #pragma fragment <name> Compile the function with the given name as the fragment shader. Replace with the function name. This directive is required in regular graphics shaders.将具有给定名称的函数编译为片段着色器。 替换为函数名称。此指令在常规图形着色器中是必需的。 #pragma geometry <name> Compile the function with the given name as the geometry shader. Replace with the function name. This option automatically turns on #pragma require geometry; for more information, see Targeting shader models and GPU features in HLSL.将具有给定名称的函数编译为几何着色器。 替换为函数名称。此选项会自动打开 #pragma 需要几何图形;有关详细信息,请参阅在 HLSL 中面向着色器模型和 GPU 功能。Note: Metal does not support geometry shaders.注意:Metal 不支持几何体着色器。 #pragma hull <name> Compile the function with the given name as the DirectX 11 hull shader. Replace with the function name. This automatically adds #pragma require tessellation; for more information, see Targeting shader models and GPU features in HLSL.将具有给定名称的函数编译为 DirectX 11 hull 着色器。 替换为函数名称。这会自动添加 #pragma 需要曲面细分;有关详细信息,请参阅在 HLSL 中面向着色器模型和 GPU 功能。 #pragma domain <name> Compile the function with the given name as the DirectX 11 domain shader. Replace with the function name. This option automatically turns on #pragma require tessellation; for more information, see Targeting shader models and GPU features in HLSL.将具有给定名称的函数编译为 DirectX 11 域着色器。 替换为函数名称。此选项会自动打开 #pragma 需要镶嵌;有关详细信息,请参阅在 HLSL 中面向着色器模型和 GPU 功能。 着色器变体和关键字 ...

2024-9-1 · Dand

Shader学习笔记-ShaderLab结构

前言 之前的业务一直对图形学涉及不多,现在打算系统学习一下。 Shader代码见得多,但也仅限于对着代码猜其功能,具体的结构和写法不求甚解,现在总结下。 属性 定义可变属性 Color Vector4 Texture2D 等等 Properties { // 在此处定义您的属性,例如颜色、纹理等 _Color ("Color", Color) = (1, 1, 1, 1) _MainTex ("Texture", 2D) = "white" {} } SubShader ShaderLab:定义 Shader 对象 - Unity 手册 ShaderLab:定义子着色器 - Unity 手册 子Shader,渲染时会逐个使用,找到第一个生效的,如果都不生效,执行FallBack的Shader。 Tags(子着色器) ShaderLab:向子着色器分配标签 - Unity 手册 子着色器标签定义了子着色器块或一个通道在何时以及在何种条件下被执行。 RenderPipeline RenderPipeline 标签向 Unity 告知子着色器是否与通用渲染管线 (URP) 或高清渲染管线 (HDRP) 兼容。 Queue Queue 标签向 Unity 告知要用于它渲染的几何体的渲染队列。 “Queue” = “[queue name] + [offset]” [queue name] Background 指定背景渲染队列。 Geometry 指定几何体渲染队列。 AlphaTest 指定 AlphaTest 渲染队列。 Transparent 指定透明渲染队列。 Overlay 指定覆盖渲染队列。 [offset] 整数 指定 Unity 渲染未命名队列处的索引(相对于命名队列)。 RenderType 在运行时替换着色器 - Unity 手册 ...

2024-8-7 · Dand

游戏框架-Tween

前言 Unity 中的补间动画(Tween Animation)是一种在两个关键帧之间自动生成平滑过渡的动画技术。一般在程序动画中使用,如位置、旋转、缩放、颜色等的变化。 这里介绍的框架在这基础上,加入了时间节点(延时执行、反复执行)和行为树的概念,把动画抽象在“节点”概念之下。 节点类型 链式节点 链式节点相当于容器,本身没有具体的执行逻辑,管理了节点列表,在合适的时机标记链式节点为完成状态,根据完成的条件分为以下节点,类似行为树。 顺序节点:按顺序执行节点,上一个节点执行完才执行下一个,全部完成以后标记完成 并行节点:并行执行节点,全部完成以后标记完成 执行节点 执行完回调以后就标记完成 条件节点 Update中判断条件,返回True以后标记完成 时间节点 延后当Timer完成后标记完成,Timer可以简单延迟,也可以反复执行一定次数以后标记完成。 值节点 值节点才是传统意义上的补间动画,就是通过输入时间,计算出值的结果,每帧执行,下面具体介绍。 值节点 值节点主要由两个部分组成,一个是缓动函数,一个是插值函数。 在Update先通过缓动函数,默认为线性函数,根据流逝的时间计算出当前的进度。 再调用插值函数,根据当前进度算出结果值,默认为线性函数。 根据值节点派生出的Color、Vector、Float节点,其实就是插值函数不一样。 缓动函数(Easing Function) 缓动函数是一种用于控制动画或过渡效果速度变化的数学函数。 它的主要作用是使动画或过渡效果看起来更加自然和真实。在没有缓动函数的情况下,动画可能会以恒定的速度进行,显得生硬和不流畅。 缓动函数可以改变动画在不同阶段的速度,常见的效果包括: 加速:动画开始时速度较慢,然后逐渐加快。 减速:动画开始时速度较快,然后逐渐减慢。 先加速后减速:动画开始时加速,然后在接近结束时减速。 先减速后加速:动画开始时减速,然后在中间或接近结束时加速。 //-------------------------------------------------- // Author : David Ochmann // Website:https://easings.net/ //-------------------------------------------------- using System; namespace UniFramework.Tween { /// <summary> /// 公共补间方法 /// </summary> public static class TweenEase { public static class Linear { public static float Default(float t, float b, float c, float d) { return c * t / d + b; } public static float EaseIn(float t, float b, float c, float d) { return c * t / d + b; } public static float EaseOut(float t, float b, float c, float d) { return c * t / d + b; } public static float EaseInOut(float t, float b, float c, float d) { return c * t / d + b; } } public static class Sine { public static float EaseIn(float t, float b, float c, float d) { return -c * (float)Math.Cos(t / d * ((float)Math.PI / 2)) + c + b; } public static float EaseOut(float t, float b, float c, float d) { return c * (float)Math.Sin(t / d * ((float)Math.PI / 2)) + b; } public static float EaseInOut(float t, float b, float c, float d) { return -c / 2f * ((float)Math.Cos((float)Math.PI * t / d) - 1) + b; } } public static class Quad { public static float EaseIn(float t, float b, float c, float d) { return c * (t /= d) * t + b; } public static float EaseOut(float t, float b, float c, float d) { return -c * (t /= d) * (t - 2) + b; } public static float EaseInOut(float t, float b, float c, float d) { if ((t /= d / 2) < 1) return c / 2 * t * t + b; return -c / 2 * ((--t) * (t - 2) - 1) + b; } } public static class Cubic { public static float EaseIn(float t, float b, float c, float d) { return c * (t /= d) * t * t + b; } public static float EaseOut(float t, float b, float c, float d) { return c * ((t = t / d - 1) * t * t + 1) + b; } public static float EaseInOut(float t, float b, float c, float d) { if ((t /= d / 2) < 1) return c / 2 * t * t * t + b; return c / 2 * ((t -= 2) * t * t + 2) + b; } } public static class Quart { public static float EaseIn(float t, float b, float c, float d) { return c * (t /= d) * t * t * t + b; } public static float EaseOut(float t, float b, float c, float d) { return -c * ((t = t / d - 1) * t * t * t - 1) + b; } public static float EaseInOut(float t, float b, float c, float d) { if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b; return -c / 2 * ((t -= 2) * t * t * t - 2) + b; } } public static class Quint { public static float EaseIn(float t, float b, float c, float d) { return c * (t /= d) * t * t * t * t + b; } public static float EaseOut(float t, float b, float c, float d) { return c * ((t = t / d - 1) * t * t * t * t + 1) + b; } public static float EaseInOut(float t, float b, float c, float d) { if ((t /= d / 2) < 1) return c / 2 * t * t * t * t * t + b; return c / 2 * ((t -= 2) * t * t * t * t + 2) + b; } } public static class Expo { public static float EaseIn(float t, float b, float c, float d) { return (t == 0) ? b : c * (float)Math.Pow(2, 10 * (t / d - 1)) + b; } public static float EaseOut(float t, float b, float c, float d) { return (t == d) ? b + c : c * (-(float)Math.Pow(2, -10 * t / d) + 1) + b; } public static float EaseInOut(float t, float b, float c, float d) { if (t == 0) return b; if (t == d) return b + c; if ((t /= d / 2) < 1) return c / 2 * (float)Math.Pow(2, 10 * (t - 1)) + b; return c / 2 * (-(float)Math.Pow(2, -10 * --t) + 2) + b; } } public static class Circ { public static float EaseIn(float t, float b, float c, float d) { return -c * ((float)Math.Sqrt(1 - (t /= d) * t) - 1) + b; } public static float EaseOut(float t, float b, float c, float d) { return c * (float)Math.Sqrt(1 - (t = t / d - 1) * t) + b; } public static float EaseInOut(float t, float b, float c, float d) { if ((t /= d / 2) < 1) return -c / 2 * ((float)Math.Sqrt(1 - t * t) - 1) + b; return c / 2 * ((float)Math.Sqrt(1 - (t -= 2) * t) + 1) + b; } } public static class Back { public static float EaseIn(float t, float b, float c, float d) { float s = 1.70158f; return c * (t /= d) * t * ((s + 1) * t - s) + b; } public static float EaseOut(float t, float b, float c, float d) { float s = 1.70158f; return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b; } public static float EaseInOut(float t, float b, float c, float d) { float s = 1.70158f; if ((t /= d / 2) < 1) return c / 2 * (t * t * (((s *= (1.525f)) + 1) * t - s)) + b; return c / 2 * ((t -= 2) * t * (((s *= (1.525f)) + 1) * t + s) + 2) + b; } } public static class Elastic { public static float EaseIn(float t, float b, float c, float d) { if (t == 0) return b; if ((t /= d) == 1) return b + c; float p = d * .3f; float a = c; float s = p / 4; return -(a * (float)Math.Pow(2, 10 * (t -= 1)) * (float)Math.Sin((t * d - s) * (2 * (float)Math.PI) / p)) + b; } public static float EaseOut(float t, float b, float c, float d) { if (t == 0) return b; if ((t /= d) == 1) return b + c; float p = d * .3f; float a = c; float s = p / 4; return (a * (float)Math.Pow(2, -10 * t) * (float)Math.Sin((t * d - s) * (2 * (float)Math.PI) / p) + c + b); } public static float EaseInOut(float t, float b, float c, float d) { if (t == 0) return b; if ((t /= d / 2) == 2) return b + c; float p = d * (.3f * 1.5f); float a = c; float s = p / 4; if (t < 1) return -.5f * (a * (float)Math.Pow(2, 10 * (t -= 1)) * (float)Math.Sin((t * d - s) * (2 * (float)Math.PI) / p)) + b; return a * (float)Math.Pow(2, -10 * (t -= 1)) * (float)Math.Sin((t * d - s) * (2 * (float)Math.PI) / p) * .5f + c + b; } } public static class Bounce { public static float EaseIn(float t, float b, float c, float d) { return c - Bounce.EaseOut(d - t, 0, c, d) + b; } public static float EaseOut(float t, float b, float c, float d) { if ((t /= d) < (1 / 2.75f)) { return c * (7.5625f * t * t) + b; } else if (t < (2 / 2.75f)) { return c * (7.5625f * (t -= (1.5f / 2.75f)) * t + .75f) + b; } else if (t < (2.5f / 2.75f)) { return c * (7.5625f * (t -= (2.25f / 2.75f)) * t + .9375f) + b; } else { return c * (7.5625f * (t -= (2.625f / 2.75f)) * t + .984375f) + b; } } public static float EaseInOut(float t, float b, float c, float d) { if (t < d / 2) return Bounce.EaseIn(t * 2, 0, c, d) * .5f + b; else return Bounce.EaseOut(t * 2 - d, 0, c, d) * .5f + c * .5f + b; } } } } 插值函数(Lerp Function) 值函数根据当前进度算出结果值,默认为线性函数,一般情况下不需要改动,只要更换缓动函数就可以实现不同的动态效果。 ...

2024-7-27 · Dand

游戏框架-UI管理器

前言 UI一般是入门程序员首先接触到的内容,除了业务的开发,还有很多值得研究的地方。 例如共用的组件设计,渲染的效果(如各种文字效果,在UI上显示模型和特效),性能上的优化(如虚拟的滚动列表,UI的动静分离,图集等),还有UI的管理。 这篇文章就来实现一个简单的UI管理器,主要的类就是管理类和窗口类。 框架功能 打开单例UI 加载 层级管理 显隐管理 UI生命周期管理(初始化,打开后,轮询更新,关闭后,销毁) 类图 管理类 管理类是外部调用的入口,打开和关闭窗口,管理UI的层级以及显示隐藏状态。 打开窗口以后,实例化一个对应的类,调用其加载方法 窗口加载完成或者窗口关闭以后回调到管理类中,遍历窗口列表重新设置其深度值,根据是否有全屏窗口,把比全屏窗口以下的窗口都设置为隐藏。 窗口类 窗口类定义了窗口的特性,如是否全屏,所在层级等,按项目需求添加特性。 窗口类中需要复写其生命周期函数,在合适的时机做合适的事情。 一般在初始化时,初始化组件,打开后,显示内容,关闭或者销毁时,做清理。 另外一个值得注意的时,当UI被隐藏的时候,不是使用SetActive(false),因为这样会引起UI的网格重构,可以把UI移动到一个渲染不到的layer下,或者直接移出相机以外。 打开UI 打开UI时的流程如下 功能拓展 这里只是实现了基础的功能,实际项目中还有许多功能可能用得上,但只需在这基础上拓展即可。 支持面板中的嵌套Prefab:可以在UIWindow给再写一个父类。 打开关闭动画支持:涉及到生命周期的调用实际,需要等待动画完成以后再调用打开关闭方法。 增加缓存:关闭UI时不立即销毁,而且先缓存起来,真正销毁的时机,可以是达到一定的缓存值,或者是存活时机,按实际需要写。 增加通用的遮罩管理。 增加多实例的窗口打开,一个窗口可以多开。 总结 UI管理器基本的功能,以及拓展的功能介绍完了。 实现这些功能不难,但是想要优雅把这些功能都组织起来,让调用简单,代码阅读清晰,也是一门艺术。 参考 UniFramework/UniFramework/UniWindow at main · gmhevinci/UniFramework

2024-7-23 · Dand

Unity使用Socket

前言 之前开发一直都在关注客户端的逻辑,没有涉及到网络相关的知识,这几天参考网上的资料学习了一下做个简易的网络聊天室,记录一下。 简单的网络通信于 Unity 客户端借助 Socket 与服务端达成连接,并依据事先约定的协议(诸如 json、protobuf 等)展开通信。 客户端 客户端的主要功能:连接,发送数据,接收数据。 这里参考开源的简易网络框架,可以自定义其包体,编码器和解码器。 using System.Net; using System.Net.Sockets; using System.Text; using UnityEngine; using UniFramework.Network; // 登录请求消息 class LoginRequestMessage { public string Name; public string Password; } // 登录反馈消息 class LoginResponseMessage { public string Result; } // TCP客户端 UniFramework.Network.TcpClient _client = null; // 创建TCP客户端 void CreateClient() { // 初始化网络系统 UniNetwork.Initalize(); // 创建TCP客户端 int packageMaxSize = short.MaxValue; var encoder = new DefaultNetPackageEncoder(); var decoder = new DefaultNetPackageDecoder(); _client = UniNetwork.CreateTcpClient(packageMaxSize, encoder, decoder); // 连接服务器 var remote = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8000); _client.ConnectAsync(remote, OnConnectServer); } // 关闭TCP客户端 void CloseClient() { if(_client != null) { _client.Dispose(); _client = null; } } void OnConnectServer(SocketError error) { Debug.Log($"Server connect result : {error}"); if (error == SocketError.Success) Debug.Log("服务器连接成功!"); else Debug.Log("服务器连接失败!"); } void Update() { // 每帧去获取解析的网络包 DefaultNetPackage networkPackage = client.PickPackage() as DefaultNetPackage; if(networkPackage != null) { string json = Encoding.UTF8.GetString(networkPackage.BodyBytes); LoginResponseMessage message = JsonUtility.FromJson<LoginResponseMessage>(json); Debug.Log(message.Result); } } // 发送登录请求消息 void SendLoginMessage() { LoginRequestMessage message = new LoginRequestMessage(); message.Name = "hevinci"; message.Password = "1234567"; DefaultNetPackage networkPackage = new DefaultNetPackage(); networkPackage.MsgID = 10001; networkPackage.BodyBytes = Encoding.UTF8.GetBytes(JsonUtility.ToJson(message)); _client.SendPackage(networkPackage); } Buffer格式 // 写入包头 { // 写入消息ID ringBuffer.WriteInt(package.MsgID); // 写入包体长度 ringBuffer.WriteInt(bodyData.Length); } // 写入包体 ringBuffer.WriteBytes(bodyData, 0, bodyData.Length); 协议号:4位 包体长度:4位 内容:其他位,这里可以使用JSON或者Protobuf序列化 服务端 服务端主要功能:管理连接的客户端,接收数据,处理数据,发送数据。 ...

2024-7-17 · Dand