菜鸡shader:L8 UV扰动动画——火焰和简单水面

news2024/11/22 20:31:29

文章目录

  • 卡通火焰
    • 代码
    • 最后效果
  • 水面
    • 代码
    • 最后效果

这此做笔记的两个shader其实是课程的作业,课程主要也是讲UV扰动的概念,因为课程的shader在另一台电脑上,做笔记就暂时不带他们了,简单做下火焰和水面的shader。

卡通火焰

火焰这部分其实也是老师参考油管的一个管主做的,放下他的个人博客和这次火焰的视频链接:

  • simon schreibt
  • Stylized VFX in RIME

主要思路就是应用两张自己做的贴图:
在这里插入图片描述

在这里插入图片描述

  • 左边那张图,也就是一大片蓝色的那张是有RGBA四个通道。

    • R通道是红色那圈,表示火焰的外焰。
    • G通道是绿色那圈,表示火焰的内焰。
    • B通道是透贴,后面是不显示的。
    • A通道是蒙版,用来控制我们扰动UV的区域,黑色的区域是不需要扰动的。
  • 右边那张图,是由两张不同的噪声纹理合成的,这两张噪声后后面用来扰动焰火贴图的UV的。两层noise有不同的tiling,本身形态也不同,混合的占比也可以调节,流动速度也不一样
    这样两个东西叠加就会有很大随机性。

    • R通道存放噪声1。
    • G通道存放噪声2。

在这里插入图片描述

  • 两个噪声叠加后再乘两个不同的蒙版,就能得到右图那张图,可以看到只有一部分区域是清晰可见的,这是我们要扰动的区域。

在这里插入图片描述

  • 如果用原始的UV去加上上面的那块可控区域,我们就能控制扰动UV的区域范围,这个会成为我们新的UV。

在这里插入图片描述

  • 然后用这个算出来的新UV去采样我们的火焰贴图。

在这里插入图片描述

  • 通过给R通道和G通道添加上颜色部分,就能分别控制内焰和外焰的颜色。

这里要说的是,shader里用到的贴图都是老师自己画的,我之前没有学过SD,一些操作不知道怎么调整,所以暂且用着,后面SD的学习得提上日程了。
在这里插入图片描述

代码

然后直接放上代码吧:

Shader "shader forge/L16_Fire"
{
    Properties
    {
        _Mask ("R:OuterFire  G:InsideFire  B:Ohter", 2D) = "white" {}
        _Noise ("R: Noise1  G: Noise2",2D) = "gray" {}
        _NoiseParam1 ("Noise1  X: Scale  Y: FlowSpeed  Z: Intensity",vector) = (1.0,0.2,0.2,1.0)
        _NoiseParam2 ("Noise2  X: Scale  Y: FlowSpeed  Z: Intensity",vector) = (1.0,0.2,0.2,1.0)
        _OuterColor ("Outer Color",Color) = (1.0,1.0,1.0,1.0)
        _InsideColor("Inside Color",Color) = (1.0,1.0,1.0,1.0)
        _BColor("Inside Color",Color) = (0.0,0.0,0.0,0.0)
    }
    SubShader
    {
        Tags {
            "RenderType"="Transparent" 
            "Queue" = "Transparent"
            "ForceNoShadowCasting" = "True"
            "IgnoreProjector" = "True"
        }

        Blend One OneMinusSrcAlpha		//混合方式用AB

        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv0 : TEXCOORD0; //Mask
                float2 uv1 : TEXCOORD1; //Noise1
                float2 uv2 : TEXCOORD2; //Noise2
                float4 vertex : SV_POSITION;
            };

            uniform sampler2D _Mask;
            uniform sampler2D _Noise;
            uniform half3 _NoiseParam1;
            uniform half3 _NoiseParam2;
            uniform half4 _OuterColor;
            uniform half4 _InsideColor;
            uniform half4 _BColor;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv0 = v.uv;
                //用_NoiseParam1的x参数,去控制UV的大小,然后加上_NoiseParam1的y参数去控制UV的流动方向和速度
                o.uv1 = v.uv * _NoiseParam1.x - float2(0.0,frac(_Time.x * _NoiseParam1.y));
                o.uv2 = v.uv * _NoiseParam2.x - float2(0.0,frac(_Time.x * _NoiseParam2.y));
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //单独取出渐变层
                half mask_b = tex2D(_Mask,i.uv0).b;
                // 采样出两个噪声贴图
                half noise1 = tex2D(_Noise, i.uv1).r;
                half noise2 = tex2D(_Noise, i.uv2).g; 
                //这个混合噪声是为了后续扰动UV而准备的
                half blendNoise = noise1 * _NoiseParam1.z + noise2 * _NoiseParam2.z;        
                //这是扭曲后的uv
                half2 wrapUV = i.uv0 - float2(0.0,blendNoise) * mask_b;      
				//通过扭曲的uv去采样遮罩
                half3 wrapMask = tex2D(_Mask, wrapUV);  
                //rg分别乘上不同的颜色,来控制颜色的变化
                wrapMask = wrapMask.r * _OuterColor.rgb + wrapMask.g * _InsideColor.rgb;
                //这里透明度只需要rg通道相加,因为我们只想看到火焰部分,不需要g通道,而rg通道只有火焰部分有值,其他都为黑色也就是0,所以除了火焰部分其他的值都为0.
                half opacity = wrapMask.r + wrapMask.g;
                //half3 noMask = tex2D(_Mask,i.uv0);
                return float4(wrapMask,opacity);
            }
            ENDCG
        }
    }
}

最后效果

在这里插入图片描述
因为用的AB的混合模式,所以可以看见背后的小人,颜色进行了混合:
在这里插入图片描述

水面

  • 水面道理其实和火焰差不多,不一样的是从只单纯控制一个V流速,变成了控制UV的流速。
  • 也是使用两个噪声对原始贴图进行UV扰动。
  • 原始贴图自己的UV也会动。

代码

Shader "shader forge/L16_Water"
{
    Properties
    {
    	//原始贴图
        _MainTex ("Texture", 2D) = "white" {}
        [Space]
        //用来控制原始贴图的uv流动
        _MainSpeed("Main Tex Speed  X:u_speed  Y:v_speed",vector) = (1.0,0.2,0.2,1.0)
        //扰动UV的贴图,该帖图有RG两个通道,存两张不同的噪声图。分别要缩放,XY的流速,扰动强度
        _WrapTex("Wrap Tex",2D) = "gray"{}
        _WrapParam1 ("Wrap1  X:Scale  Y:u_speed  Z:v_speed  W:Intensity",vector) = (1.0,0.2,0.2,1.0)
        _WrapParam2 ("Wrap2  X:Scale  Y:u_speed  Z:v_speed  W:Intensity",vector) = (1.0,0.2,0.2,1.0)
    }
    SubShader
    {
        Tags {            
            "RenderType"="Opaque" 
        }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag           

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv0 : TEXCOORD0;
                float2 uv1 : TEXCOORD1;
                float2 uv2 : TEXCOORD2;
                float4 vertex : SV_POSITION;
            };

            uniform sampler2D _MainTex;
            uniform half2  _MainSpeed;
            uniform float4 _MainTex_ST;
            uniform sampler2D _WrapTex;
            uniform half4 _WrapParam1;
            uniform half4 _WrapParam2;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv0 = v.uv - frac(_Time.x * _MainSpeed);
                //_WrapParam1的x通道存UV大小,yz通道分别存UV也就是XY的流动速度
                o.uv1 = v.uv * _WrapParam1.x - frac(_Time.x * _WrapParam1.yz);	
                o.uv2 = v.uv * _WrapParam2.x - frac(_Time.x * _WrapParam2.yz);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // 采样贴图,用之前定义好的两个不同的UV来采样,得到同一张贴图的不同UV表现形式。
                //这个采样出来的rg贴图结果,其实是两个被扰动后的UV,而b通道好像原来要做什么用,没用上。                
                half3 var_Wrap1 = tex2D(_WrapTex, i.uv1).rgb;
                half3 var_Wrap2 = tex2D(_WrapTex, i.uv2).rgb;
				//使用两个不同的强度混合两个不同的uv,得到扰动混合
                half2 wrap = (var_Wrap1.xy - 0.5) * _WrapParam1.w + (var_Wrap2.xy - 0.5) * _WrapParam2.w;
                //加上原来的UV,也就是扰动原来的UV
                half2 wrapUV = i.uv0 + wrap;
				//用扰动后的UV去采样原来的纹理贴图
                half3 var_MainTex = tex2D(_MainTex, wrapUV).rgb;
                
                return half4(var_MainTex,1.0);
            }
            ENDCG
        }
    }
}

最后效果

呃呃,毕竟是动画,不动起来看起来没什么变化,但条件限制,就这样吧。
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

FreeRTOS(队列)

队列 什么是队列? 队列又称消息队列,是一种常用于任务间通信的数据结构,队列可以在任务与任务间、中断和任 务间传递信息。 为什么不使用全局变量? 如果使用全局变量,兔子(任务1)修改了变量 a …

新版studio没法筛选Log

目录 方式一 简单粗暴,针对怀旧者,可切回原版log视图 方式二 学习新的log过滤方法 升级新版本AndroidStudio后,log日志变成以下样子,发现之前过滤error,infor的tab都不见了,瞬间不淡定了,查阅资料才发现…

DAY45:动态规划(五)背包问题:01背包理论基础+二维DP解决01背包问题

文章目录 背包问题大纲01背包01背包暴力解法01背包二维DP解法二维DP数组的解法DP数组含义递推公式初始化二维DP数组(比较重要)遍历顺序(比较重要) 二维DP数组完整版思路总结返回值为什么是二维数组最后一个元素DP推导过程与数组含…

selenium 根据期刊信息获取知网文献信息 pt.1

哈喽大家好,我是咸鱼 之前写过一篇获取知网文献信息的文章,看了下后台数据还挺不错 所以咸鱼决定再写一篇知网文献信息爬取的文章 需要注意的是文章只是针对某一特定期刊的爬取,希望小伙伴们把关注点放在如何分析网页以及如何定位元素上面…

python实现前后端学生管理系统(前后端不分离)

⭐作者介绍:大二本科网络工程专业在读,持续学习Java,努力输出优质文章 ⭐作者主页:逐梦苍穹 ⭐所属专栏:项目。 目录 1、前言2、简述实现内容首页注册登录管理员 3、详细代码3.1、项目目录3.2、templates3.2.1、testxz…

赛效:如何在线转换图片格式

1:点击左侧菜单栏里的“格式转换”,然后在转换格式菜单栏里点击上传按钮。 2:选择下方输出格式,点击右下角“开始转换”。 3:稍等片刻转换成功后,点击图片右下角的“下载”,将转换后的图片保存到…

UE5《Electric Dreams》项目PCG技术解析 之 PCGCustomNodes详解(四)ApplyHierarchy

继续解析《Electric Dreams》项目中的自定义节点和子图:ApplyHierarchy 文章目录 前导文章标准组合拳ApplyHierarchyExecute with ContextIteration Loop BodyPoint Loop Body应用场景 小结 前导文章 《UE5《Electric Dreams》项目PCG技术解析 之 理解Assembly&…

【Android】APT与JavaPoet学习与实战

PS:本文讲解的APT全称为Annotation Processing Tool,而非是Android Performance Tuner,这两种工具简称皆为APT,前者是“注释处理工具”,后者是“Android性能调试器”。 本文分别使用Java、kotlin 语言进行开发&#xf…

做一个游戏小项目有多简单?

认识一个朋友,学了很多年的 python, 还停留在 helloworld 阶段,每次拿起又放下,是不是很熟悉?每天都在想,我要学编程,我要学编程,但是又不知道从何学起,学了一点又不知道怎么用&…

java并发编程原理-----线程

目录 上下文切换 java代码创建线程的两种方式 线程的五个状态 线程join方法 多线程之间的影响 上下文切换 CPU的每一个核心同一时刻只能执行一个线程,但是我们会发现电脑同一时刻现实会进行几千个线程,这就是cpu在快速的切换执行线程,由…

Python中的迭代器

一、介绍 在Python中,迭代器是一种访问集合元素的方式,可以用于遍历数据集中的元素,而不需要事先知道集合的大小。迭代器可以被用于循环语句中,例如for循环,来遍历集合中的每个元素。 Python中的迭代器是一个实现了迭…

将Windows系统上的音频、视频通过iTunes传输到iPhone上

这个地方需要下载安装版的iTunes 下载地址: https://www.apple.com/itunes/download/win64 不要从Windows的APP Store中下载iTunes,不好使。 安装完成后,如果是导入一个文件夹中的资料,则点击 【文件】》【将文件夹添加到资料库】…

岛屿数量 (力扣) dfs + bfs (JAVA)

给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 此外,你可以假设该网格的…

在内卷化竞争时代,金龙鱼重返增长的关键到底是什么?

提到欧丽薇兰、胡姬花、香满园、海皇、金味、丰苑、锐龙洁劲100、丸庄酱油等品牌,多数消费者的第一反应是什么?多数消费者认为是某个不知名的新品牌。问题的重点不在产品,而在主品牌定位。 事实上,这都是金龙鱼母公司益海嘉里旗下的品牌。内行都知道益海…

“坏邻居”导致的kafka生产者TPS下降原因排查

背景: 今天测试了两种不同的场景下kafka producer的tps性能数据,两种场景下都是使用3个线程,每个线程都是对应一个kafka producer,测试发送到kafka集群的消息的量,两个场景的区别是场景A只发送kafka消息,场…

自定义类型

目录 什么是自定义类型 结构体 结构体的声明 常规结构体的声明形式 特殊的结构体声明形式 匿名结构体: 匿名结构体的重命名: 注意事项: 结构体的自引用 什么是结构体的自引用 结构体变量的定义与初始化 方法一: 方法…

总结python安装包(库)过程中的采坑

绝大数的包比如numpy、pandas可以用pip install或者conda install解决,使用pip时可以用pip -V命令看一下自己的pip安装在了哪个虚拟环境,一般pip安装在哪默认就把python包安装在哪。 pip -VC:\Users\20478>pip -V pip 23.1.2 from D:\Python\lib\sit…

Android Java代码与JNI交互 JNI子线程访问Java方法 (八)

🔥 Android Studio 版本 🔥 🔥 创建包含JNI的类 JNIInvokeMethod.java 🔥 package com.cmake.ndk1.jni;import com.cmake.ndk1.base.ICallbackMethod; import com.cmake.ndk1.base.IThreadCallback;public class JNIInvokeMethod {static {System.loadLibrary("…

VBA代码如何切换word和excel(3)

【分享成果,随喜正能量】人不能因为一件好事,高兴一整年,却能因为一个创伤,郁郁终生。痛苦给人的刺激,总是远远大于快乐。成年人的烦恼,和谁说都不合适,悲喜自渡,他人难悟。人最强大…

DDOS防御,阻止DDoS攻击的15个独家技巧

DDoS攻击可以使企业完全宕机数小时以上,而宕机的后果可能很严重,各种规模的企业和政府都可能受到影响。2021年,由于系统中断一小时导致销售额大幅下降,亚马逊为此遭受了约3400万美元的直接财务损失。而随后由于Fakebook的服务中断…