首页 > 其他分享 >Unity Shader学习随笔

Unity Shader学习随笔

时间:2023-12-26 11:12:01浏览次数:37  
标签:MainTex uv Shader pos Unity xy v2f screenUV 随笔

阴影:

光源看不到,但相机看得到的地方,就是阴影

变体:

一个普通的Shader可能会有很多种效果

例如一个火焰溶解效果,写在Shader里,但其实在未触发之前我们不需要去计算该效果

因此需要在未触发前,将火焰溶解的效果计算关闭

这就用到了变体,把火焰溶解的效果计算变成变体

无论如何都会被编译的变体multi_compile

#pragma multi_compile _ _NAME

变体名必须要全大写

Properties {  //在属性面部设置一个开关,来表示是否要启用变体(需求全大小)   [Toggle]_DISSLOVEEABLED("Disslove Eabled", int) = 0 } SubShader {   pass   {     ...     //在pass中的命名,前面的‘_’表示空变体,之所以有这个是为了不默认开启 _DISSLOVEEABLED 变体     #pragma multi_compile _ _DISSLOVEEABLED_ON     ...     fixed4 func()     {       ...       #if _DISSLOVEEABLED_ON                        fixed4 dis = tex2D(_Dissolve, i.uv.zw);                        fixed weight = dis.r - _Clip;                        clip(weight);                        if(weight < _Clip) {                          tex = tex * lerp(_Color0, _Color1, weight/_Scope);                       }                   #endif       ...     }   }   }

通过材质使用情况来决定是否进行编译的变体shader_feature

一般用于特效,即运行过程中不会去开启该变体

属性中就是要暴露一个开关:

[Toggle]_MaskEnabled("启用遮罩", int) = 0

pass中要启用变体:

#pragma shader_feature _MASKENABLED_ON

然后就是和上面的判断一样了

#if

  xxx;

#else

  xxx;

#endif

这就可以做到:

同一个Shader可以给不同的材质球使用;

是否开启某个变体由材质球决定,并且在打包时不会将该材质球未开启的变体打进包里。

然后信息存在于这里

常用函数:

abs(x) 绝对值 frac(x) 取小数 floor(x) 向下取整 ceil(x) 向上取整 max(x, y) 取大的 min(x, y) 取小的 pow(x, y) x的y次方 rcp(x) x的倒数 exp(x) e的x次方 exp2(x) 2的x次方 fmod(x, y) x%y saturate(x) 将x限制在0~1 clamp(x, a, b) 将x限制在a~b sqrt(x) 对x开方 rsqrt(x) 对x开方后取倒数 lerp(x, y, a) x+(y-x)*a sin(x) cos(x) distance(x, y) 返回xy之间的距离(几维都可以) length(x) 返回模长(必须是二维及以上) step(x, y) 返回 x >=y ?1 : 0 smoothstep(a, b, x) 返回介于0和1之间的平滑 Hermite 内插

 

渲染队列Queue

2500以下会被认为是不透明物体,从前往后渲染,效率更高

2500以上就是透明物体,从后往前渲染,效率低,但这是为了保证显示效果正确

Queue=Background,1000,最先渲染

Queue=Geometry,2000,默认场景中的渲染对象

Queue=AlphaTest,2450,要么不透要么全透,常常用来实现美术部分的透贴

Queue=Transparent,3000,场景中的半透明对象

Queue=Overlay,4000,叠加效果,最后渲染的东西放这,如镜头光晕等

混合Blend

Blend后面一般跟随两至三个参数

Blend One Zero

Blend One One

第一个参数就是针对片段着色器pass计算出来的SrcFactor源颜色,要对该参数干什么

第二个参数就是针对存在于帧缓冲区的DstFactor目标颜色,要对该参数干什么

其实中间还有一个,不填就是默认加法

Blend SrcAlpha OneMinusSrcAlpha - 传统透明度

Blend One OneMinusSrcAlpha - 自左乘透明度

Blend One One

Blend OneMinusDstColor One - 软合并

Blend DstColor Zero - 乘法

Blend DstColor SrcColor - 双倍乘法

面剔除Cull

剔除不需要的面,优化渲染速度

 

Cull Front - 剔除正面

Cull Back - 剔除背面

 

正面背面是指模型的正面背面(顶点环绕方向,及法线),不是玩家看到的是正面

Cull Off - 关闭剔除

屏幕后处理

实现热扭曲的效果

1.做好场景

2.由代码抓取当前一帧的内容

3.获取受扭曲影响的部分屏幕坐标

4.利用屏幕坐标对抓取的图片采样

5.再采样扰动贴图做扭曲

使用unity参数组成屏幕坐标

查看代码
 Pass
{
    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag

    #include "UnityCG.cginc"

    struct v2f
    {
        float2 uv : TEXCOORD0;
    };

    sampler2D _GrabTex;
    sampler2D _MainTex;float4 _MainTex_ST;
    fixed _SpeedX, _SpeedY, _Distort;

    v2f vert (float4 vertex : POSITION, float2 uv : TEXCOORD0, out float4 pos : SV_POSITION)
    {
        pos = UnityObjectToClipPos(vertex);
        v2f o;
        o.uv = TRANSFORM_TEX(uv, _MainTex) + float2(_SpeedX, _SpeedY) * _Time.x;
        return o;
    }
    //由于VPOS和SV_POSITION冲突,因此需要在vert里out
    fixed4 frag (v2f i, UNITY_VPOS_TYPE screenPos:VPOS) : SV_Target
    {
        //最初的版本,通过unity参数去计算,_ScreenParams.xy是屏幕的宽高
        fixed2 uv = lerp(screenPos.xy / _ScreenParams.xy, tex2D(_MainTex, i.uv).xy, _Distort);
        fixed4 grabTex = tex2D(_GrabTex, uv);
        return grabTex;
    }
    ENDCG
}

使用正交空间下的pos计算屏幕坐标

查看代码
 Pass
{
    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag

    #include "UnityCG.cginc"

    struct a2f
    {
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
    };

    struct v2f
    {
        float2 uv : TEXCOORD0;
        float4 pos : SV_POSITION;
        float2 screenUV : TEXCOORD1;
    };

    sampler2D _GrabTex;
    sampler2D _MainTex;float4 _MainTex_ST;
    fixed _SpeedX, _SpeedY, _Distort;

    v2f vert (a2f v)
    {
        v2f o;
        o.uv = TRANSFORM_TEX(v.uv, _MainTex) + float2(_SpeedX, _SpeedY) * _Time.x;
        o.pos = UnityObjectToClipPos(v.vertex);
        //1.此时pos是放在正交矩形中的顶点
        //2.因此可以直接拿该值当做屏幕坐标
        //3.但要注意pos的范围是-1到1
        //4.因此需要把它规范到0到1,但OpenGL和DX11的屏幕坐标原点不同
        //5.OpenGL的在左下角,仅需将xy * 0.5 + 0.5即可
        //6.但DX11的在左上角,需要将y轴反转,即y = 1 - y
        // o.screenUV = o.pos.xy / o.pos.w * 0.5 + 0.5;
        // o.screenUV.y = 1 - o.screenUV.y;
        //7.上面就是根据正交中的顶点坐标转换成屏幕坐标的操作
        //8.可以注意到稍微有点扭曲,原因是顶点不是片元,顶点和顶点之间的参数都是靠插值算出来的
        // o.screenUV = ComputeScreenPos(o.pos) / o.pos.w;
        //9.当然,unitycg提供了方法,上面
        //10.这里除以一个w是因为:正交矩形中是正交的,而相机是透视的,要将其还原
        return o;
    }

    fixed4 frag (v2f i) : SV_Target
    {
        fixed2 uv = lerp(i.screenUV, tex2D(_MainTex, i.uv).xy, _Distort);
        fixed4 grabTex = tex2D(_GrabTex, uv);
        return grabTex;
    }
    ENDCG
}

使用片元计算后的pos计算屏幕坐标

查看代码
 Pass
{
    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag

    #include "UnityCG.cginc"

    struct a2f
    {
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
    };

    struct v2f
    {
        float2 uv : TEXCOORD0;
        float4 pos : SV_POSITION;
        float2 screenUV : TEXCOORD1;
    };

    sampler2D _GrabTex;
    sampler2D _MainTex;float4 _MainTex_ST;
    fixed _SpeedX, _SpeedY, _Distort;

    v2f vert (a2f v)
    {
        v2f o;
        o.uv = TRANSFORM_TEX(v.uv, _MainTex) + float2(_SpeedX, _SpeedY) * _Time.x;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.screenUV = o.pos;
        return o;
    }

    fixed4 frag (v2f i) : SV_Target
    {
        // return i.screenUV.x;
        // return i.pos.x;
        //可以看到,我明明对screenUV赋了pos的值,但上面两个居然不一样
        //那是因为SV_POSITION在中间经过了改变
        //在经过计算后,pos就是屏幕像素大小了,不再是-1到1了,那这样就更方便了;
        i.screenUV = i.pos.xy / _ScreenParams.xy;
        //OK,就上面这一行就解决战斗了
        fixed2 uv = lerp(i.screenUV, tex2D(_MainTex, i.uv).xy, _Distort);
        fixed4 grabTex = tex2D(_GrabTex, uv);
        return grabTex;
    }
    ENDCG
}

[PerRendererData]

属性的特性

Properties
{
    _MainTex ("Texture", 2D) = "white" {}
    [PerRendererData]_Color ("Color", Color) = (0,0,0,0)
}

该材质cs被调用时,会被打包进cs 的MaterialPropertyBlock

每帧修改一万个对象的颜色,

方法1,使用材质的方法去修改颜色,开销是16ms每帧

方法2,使用GetPropertyBlock(MaterialPropertyBlock prop)的方法修改颜色,开销是7ms每帧

Color

模板测试

模板缓冲区

自己的话

是屏幕大小的区域,例如1920*1080的大小

每个像素是8位

当某一个物体被渲染到屏幕上时,它看到带有材质,也就是肯定带有shader

而shader中可以定义,当前我被渲染出来的区域的模板值

因此,当另一个物体被渲染时,另一个shader上面的也会有模板值

两个模板值比较,新的模板值大会怎么样,小会怎么样,可以自己定义

因此就可以做出类似PS遮罩的效果

官方的话

模板缓冲区可以为屏幕上的每个像素点保存一个无符号的整数值

这个值的意义视程序的具体操作而定

在渲染过程中

可以用这个值与一个预先设定的参考值相比较

根据比较的结果来决定是否更新相应的像素点的颜色值,这个比较的过程被称为模板测

代码部分

公式

将StencilBuffer的值与ReadMask进行与运算,然后与Ref值进行Comp比较,结果为True时进行Pass操作,否则进行Fail操作

操作值写入StencilBuffer前先与WriteMask进行与运算

(Ref & ReadMask)Comp(StencilBuffer & ReadMask)? Pass : Fail

Comp

Less    <

Greater  >

Lequal  <=

Gequal  >=

Equal   =

NotEqual  !=

Always  总为true

Never  总为false

实践

UI组件加了Mask之后

会有这个,这是由UGUI自己去写的

Id就是公式中的StencilBuffer ,已经被写到深度缓冲区中了

我们写的新shader就是要和这个Id:1去比

Properties
{
    [PerRendererData]_MainTex ("Texture", 2D) = "white" {}
    _Color ("Color", Color) = (0,0,0,0)
    _Ref("Stencil Ref", int) = 0
    [Enum(UnityEngine.Rendering.CompareFunction)]_Comp("Stencil Comp", int) = 0
    [Enum(UnityEngine.Rendering.StencilOp)]_OP("Stencil OP", int) = 0
}
Stencil//深度测试
{
    Ref [_Ref]
    //ReadMask [0~255]
    //WriteMask [0~255]
    Comp [_Comp]
    Pass [_OP]
}

这就是Enum

此时_Ref = 1

这是Mask

这是效果

 

 

 

 

 

 

标签:MainTex,uv,Shader,pos,Unity,xy,v2f,screenUV,随笔
From: https://www.cnblogs.com/LateUpdate/p/17702778.html

相关文章

  • Unity3D 锁帧与垂直同步的不同处详解
    Unity3D是一款强大的游戏开发引擎,而锁帧与垂直同步是其中两个重要的概念。本文将详细解释锁帧与垂直同步的不同之处,并给出相关的技术详解和代码实现。对啦!这里有个游戏开发交流小组里面聚集了一帮热爱学习游戏的零基础小白,也有一些正在从事游戏开发的技术大佬,欢迎你来交流学习。......
  • Unity3D MVC框架和MVVM框架优缺点详解
    Unity3D是一款非常流行的游戏开发引擎,它为开发者提供了强大的工具和功能,使得开发者能够轻松地创建各种类型的游戏。在Unity3D中,使用模型-视图-控制器(MVC)框架和模型-视图-视图模型(MVVM)框架可以更好地组织和管理游戏的逻辑和界面。对啦!这里有个游戏开发交流小组里面聚集了一帮热爱......
  • Unity引擎2D游戏开发,撞墙判定和等候计时
    撞墙判定将野猪绑定PhysicsCheck脚本,并将GroundLayer选中Ground,调整ButtonOffset到脚边,CheckRadius为0.1,即可判定悬崖physicsCheck脚本中添加一些变量,用于墙壁判定逻辑publicbooltouchLeftWall;publicbooltouchRightWall;publicVector2leftOffset;publicVecto......
  • Unity引擎2D游戏开发,野猪基本的移动逻辑和动画
    一、类的继承在Scripts下创建Enemy文件夹,里面再创建两个C#文件将Boar文件内的代码修改为以下代码,:后的是Enemy,即继承了Enemy类publicclassBoar:Enemy{}在Enemy内,编写基本属性publicclassEnemy:MonoBehaviour{[Header("基本参数")]//基本移动速度......
  • Unity3D UI帧动画详解
    nity3D是一款非常强大的游戏开发引擎,它提供了丰富的功能和工具,使开发者能够轻松创建各种类型的游戏。其中,UI(UserInterface)是游戏开发中非常重要的一部分,它用于展示游戏中的各种信息和交互元素。在Unity3D中,我们可以使用UI帧动画来创建各种炫酷的UI效果。本文将详细介绍Unity3D中U......
  • Unity3D 中正确调用CUDA程序详解
    Unity3D是一款强大的游戏开发引擎,可以实现各种各样的游戏效果。然而,在某些情况下,使用CPU来处理游戏中的复杂计算任务可能会导致性能瓶颈。为了解决这个问题,我们可以利用CUDA来使用GPU进行并行计算,从而提高游戏的性能。对啦!这里有个游戏开发交流小组里面聚集了一帮热爱学习游戏的......
  • Unity3D UDP传输大文件怎么提高速度详解
    Unity3D是一款强大的游戏开发引擎,但是在处理大文件传输时,往往会遇到速度较慢的问题。本文将详细介绍如何通过使用UDP协议来提高大文件传输的速度,并给出相应的技术详解和代码实现。对啦!这里有个游戏开发交流小组里面聚集了一帮热爱学习游戏的零基础小白,也有一些正在从事游戏开发的......
  • Spring Boot学习随笔- 第一个Thymeleaf应用(基础语法th:,request、session作用域取值)
    学习视频:【编程不良人】2021年SpringBoot最新最全教程第十五章、ThymeleafThymeleaf是一种现代化的服务器端Java模板引擎,专门用于Web和独立环境。Thymeleaf在有网络和无网络的环境下皆可运行,即可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页......
  • Unity获取用户输入
    Unity获取用户输入基本概念​unity​中使用Input.GetAxis()​或Input.GetAxisRaw()​接口来获得用户输入,这两个接口的返回值取值范围为[-1,1],具有以下特性:该值的含义取决于输入控制的类型,例如,对于游戏杆的水平轴,值为1表示游戏杆向右推到底,值为-1表示游戏杆向左推到底;值为......
  • Unity 刷新文本
    privatevoidrefreshContentSizeFitters(){//获取contentParent下的所有ContentSizeFitter组件ContentSizeFitter[]contentSizeFitters=gameObject.GetComponentsInChildren<ContentSizeFitter>();for(inti=0;i<contentSize......