实现窗户特效的Unity Shader解析

news2024/11/16 19:23:43

        本文将详细介绍一种使用Unity Shader实现窗户特效的方法。通过分析代码,我们将解释每个关键部分的作用,以及如何将其组合在一起以实现逼真的窗户效果。希望本文能为Shader编程初学者和Unity开发者提供一些有用的指导。

 

引言:

        在游戏和虚拟现实应用中,窗户效果经常用于增强场景的真实感。通过使用Shader,我们可以模拟光线在玻璃上的折射和反射效果,让窗户看起来更加逼真。本文将介绍一种用Unity Shader实现窗户特效的方法,并对代码逐行解析。

Shader "Unlit/Window"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}  // 主纹理
_Size("Size",float) = 1  // 大小
_T("Time",float) = 1  // 时间
_Distortion("Distortion", range(-5, 5)) = 1  // 扭曲
_Blur("Blur",range(0, 1)) = 1  // 模糊程度
}
SubShader
{
Tags { "RenderType"="Opaque" "Queue"="Transparent"}  // 标签
LOD 100
    GrabPass{"_GrabTexture"}  // GrabPass命令将渲染窗口中的内容复制到纹理_GrabTexture中
    Pass
    {
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        // make fog work
        #pragma multi_compile_fog
        #define S(a, b, t) smoothstep(a,b,t)  // 定义了计算平滑插值的宏函数
        #include "UnityCG.cginc"  // Unity内置着色器代码宏

        struct appdata
        {
            float4 vertex : POSITION;  // 顶点位置
            float2 uv : TEXCOORD0;  // 纹理坐标
        };

        struct v2f
        {
            float2 uv : TEXCOORD0;  // 纹理坐标
            float4 grabUv : TEXCOORD1;  // 抓取窗口纹理坐标
            UNITY_FOG_COORDS(1)  // Unity内置的雾效相关宏
            float4 vertex : SV_POSITION;  // 顶点位置
        };

        sampler2D _MainTex,_GrabTexture;  // 主纹理和抓取窗口纹理
        float4 _MainTex_ST;  // 主纹理的平铺和偏移参数
        float _Size, _T , _Distortion, _Blur;  // 大小、时间、扭曲和模糊程度参数

        v2f vert (appdata v)
        {
            v2f o;
            o.vertex = UnityObjectToClipPos(v.vertex);  // 将顶点从对象空间转换到剪裁空间
            o.uv = TRANSFORM_TEX(v.uv, _MainTex);  // 对纹理坐标进行平铺和偏移变换
            o.grabUv = UNITY_PROJ_COORD(ComputeGrabScreenPos(o.vertex));  // 计算抓取窗口的纹理坐标

            UNITY_TRANSFER_FOG(o,o.vertex);  // 将雾效信息传递给片段着色器
            return o;
        }

        float N21(float2 p)
        {
            p = frac(p*float2(123.34,345.45));  // 将纹理坐标乘以固定值并取小数部分
            p += dot(p,p + 34.345);  // 点乘纹理坐标并加上一定值
            return frac(p.x*p.y);  // 返回取小数部分后的乘积
        }

        float3 Layer(float2 UV,float t)
        {
            float2 aspect = float2(2,1);  // 窗口宽高比
            float2 uv = UV*_Size * aspect;  // 对纹理坐标进行平铺和偏移变换
            uv.y += t * .25;  // 根据时间进行纹理坐标的偏移
            float2 gv = frac(uv)-.5;  // 将纹理坐标取小数部分后再减去0.5的值

            float2 id = floor(uv);  // 取纹理坐标的整数部分

            float n = N21(id);  // 计算噪声值
            t += n*6.2831;  // 根据噪声值进行时间的调整

            float w = UV .y * 10;  // 根据纹理坐标的y值进行宽度调整
            float x = (n - .5)*.8;  // 根据噪声值进行x的调整
            x += (.4-abs(x)) * sin(3*w)*pow(sin(w),6)*.45;  // 根据宽度和噪声进行x的进一步调整

            float y = -sin(t+sin(t+sin(t)*.5))*.45;  // 根据时间和噪声进行y的调整
            y -= (gv.x-x)*(gv.x-x);  // 根据x与纹理坐标的偏移进行y的进一步调整

            float2 dropPos = (gv-float2(x,y)) / aspect;  // 计算扭曲的纹理坐标
            float drop= S(.05,.03,length(dropPos));  // 计算扭曲的强度

            float2 trailPos = (gv-float2(x,t * .25)) / aspect;  // 计算轨迹的纹理坐标
            trailPos.y = (frac(trailPos.y * 8)-.5)/8;  // 计算纹理坐标的小数部分并进行调整
            float trail = S(.03,.01,length(trailPos));  // 计算轨迹的强度
            float fogTrail = S(-.05,.05,dropPos.y);  // 根据扭曲的纹理坐标的y值进行雾效的调整
            fogTrail *= S(.5, y, gv.y);  // 根据y和纹理坐标的y值再次进行雾效的调整
            trail *=fogTrail;  // 综合计算轨迹的强度

            fogTrail *= S(.05, .04, abs(dropPos.x));  // 根据扭曲的纹理坐标的x值进行雾效的进一步调整

            float2 offs = drop*dropPos + trail * trailPos;  // 计算扭曲和轨迹的综合效果

            return float3(offs,fogTrail);  // 返回扭曲、轨迹和雾效混合后的结果
        }

        fixed4 frag (v2f i) : SV_Target
        {
            float t = fmod(_Time.y +_T,7200);  // 计算时间

            float4 col = 0;  // 初始化颜色

            float3 drops = Layer(i.uv,t);  // 计算扭曲、轨迹和雾效
            drops += Layer(i.uv*1.23 + 7.54,t);  // 对纹理坐标进行平铺和偏移变换并再次计算扭曲、轨迹和雾效
            drops += Layer(i.uv*1.35 + 1.54,t);  // 对纹理坐标进行平铺和偏移变换并再次计算扭曲、轨迹和雾效
            drops += Layer(i.uv*1.57 - 7.54,t);  // 对纹理坐标进行平铺和偏移变换并再次计算扭曲、轨迹和雾效

            float fade = 1-saturate(fwidth(i.uv)*60);  // 根据纹理坐标的宽度计算淡化系数

            float blur = _Blur * 7 * (1-drops.z * fade);  // 根据淡化系数和扭曲、轨迹和雾效的深度计算模糊程度

            //col = tex2Dlod(_MainTex, float4(i.uv + drops.xy * _Distortion,0,blur));

            float2 projUv = i.grabUv.xy / i.grabUv.w;  // 根据抓取窗口的纹理坐标计算投影纹理坐标
            projUv += drops.xy * _Distortion * fade;  // 根据扭曲、轨迹和雾效的深度进行投影纹理坐标的调整
            blur *= .01;  // 调整模糊的程度

            const float numSamples = 32;  // 采样次数
            float a = N21(i.uv)*6.2831*0;  // 根据纹理坐标计算角度
            for(float i = 0; i < numSamples; i++)
            {
                float2 offs = float2(sin(a),cos(a))*blur;  // 根据角度和模糊程度计算偏移量
                float d = frac(sin((i+1)*546.)*5424.);  // 根据角度计算采样步长
                d = sqrt(d);  // 开平方根
                offs *= d;  // 根据采样步长调整偏移量
                col += tex2D(_GrabTexture,projUv + offs);  // 对抓取窗口纹理进行采样
                a++;
            }
            col /= numSamples;  // 对颜色进行平均

            //col *= 0; col += fade;

            return col*.9;  // 返回最终颜色
        }
        ENDCG
    }
}
}

技术细节解析:

  1. Shader属性定义: 在这段代码中,我们定义了几个可在Inspector面板上调节的Shader属性。其中包括_MainTex(纹理)、_Size(大小)、_T(时间)、_Distortion(扭曲度)和_Blur(模糊度)等。这些属性可以根据需要进行调整,以获得最佳效果。

  2. 顶点函数(vert): 顶点函数是Shader的顶点转换函数。它将输入的顶点位置和纹理坐标进行转换和处理,并将输出传递给片段函数(frag)使用。

  3. 片段函数(frag): 片段函数是Shader的主要计算部分。在该函数中,我们使用一系列噪声函数和数学函数来模拟窗户特效的各个方面,比如光线折射、光影效果、水滴效果等。最后,我们通过采样_GrabTexture来获取最终的纹理颜色值。

  4. 主循环处理: 在frag函数中,我们通过多个Layer函数的叠加来模拟窗户上的各种效果。每个Layer函数都表示窗户上的一种特效,比如水滴、光线、模糊等。通过调节主循环中的参数可以控制各个特效的强度和效果。

结论:

        通过使用Unity Shader编写的这个窗户特效,可以使游戏场景更加逼真且具有交互性。通过对Shader属性的调整,我们可以轻松地定制窗户的效果,以适应不同的场景需求。

总结:

        本文详细介绍了如何使用Unity Shader实现窗户特效,并对代码进行了逐行解析。通过对Shader属性和函数的说明,读者可以更好地理解窗户特效的实现原理,并在自己的项目中进行应用。

提示:

        在实际写博客时,可以根据需要添加更多的细节和示例代码,以便读者更好地理解并自己尝试实现窗户特效。希望本文对学习Shader编程和Unity开发有所帮助。

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

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

相关文章

taro 小程序自定义地图选点功能

效果&#xff1a; 1、添加中心点 icon, 保证icon 处于地图中间 .map-box {width: 100vw;height: 36vh;position: relative;// 中心icon.center-icon-box {position: absolute;top: calc(50% - 30rpx); // 保证icon 处于地图中心点left: 50%;transform: translate(-50%, -50%)…

JavaScript内存管理和闭包

1 JavaScript内存管理 2 垃圾回收机制算法 3 闭包的概念理解 4 闭包的形成过程 5 闭包的内存泄漏 一个函数只有调用了外部的变量&#xff0c;才算是闭包。函数内和函数外会写成闭包。 深入JS闭包-闭包的访问过程 <!DOCTYPE html> <html lang"en"> &l…

Jenkins用户权限设置和运行节点配置实战

这里写目录标题 一、Jenkins用户权限设置实战1、用户权限配置2、用户权限分配 二、Jenkins运行节点配置实战1、增加运行节点的好处2、实战B-1:添加Jenkins运行节点实战1、相关字段说明&#xff1a;2、SSH连接方式 实战B-2:配置不同类型的节点-Python 节点实战B-3:配置不同类型的…

STM32F4 读/写 EEPROM【EEPROM、串口显示】

将姓名&#xff08;拼音&#xff09;学号写入EEPROM并在启动时通过串口显示 本篇博客将介绍如何使用STM32F4开发板将姓名&#xff08;拼音&#xff09;学号写入EEPROM&#xff0c;并在启动时通过串口显示。我们将使用核心实现函数EEPROM_WR_Test来完成这个功能。 准备工作 在…

Dubbo【Dubbo实战(用户更新业务消费者实现、用户删除业务消费者实现、复习内容) 】(六)-全面详解(学习总结---从入门到深化)

目录 Dubbo实战_用户更新业务消费者实现 Dubbo实战_用户删除业务消费者实现 复习内容&#xff1a; Dubbo实战_用户更新业务消费者实现 在Consumer中调用更新用户业务 /*** 根据用户id修改用户名字* param users* return*/Integer updateUsers(User users);/*** 根据用户id查…

Apollo分布式配置中心(二)

上一篇&#xff1a; 上一篇已经知道Apollo是什么东西了&#xff0c;接下来实践一下 目录 一、创建应用 1、 新增配置 2、创建Namespace ​3、同步配置 4、灰度发布 添加灰度配置项 ​编辑 配置灰度规则 二、删除应用、集群、appNamespace 三、springBoot整合Apollo …

JAVA设计模式介绍

一、什么是设计模式 设计模式&#xff08;Design pattern&#xff09;是一套被反复使用、多数人知晓的、经过分类编目的、代码设计…

前端Vue自定义手机号文本格式化组件手机号码文本转星号

前端Vue自定义手机号文本格式化组件, 下载完整代码请访问uni-app插件市场地址&#xff1a;https://ext.dcloud.net.cn/plugin?id13231 效果图如下: # cc-format-phone #### 使用方法 使用方法 <!-- phone:手机号 isStar: 是否转星号 --> <cc-format-phone :phon…

2023.7.2

#include <iostream>using namespace std;class Complex { private:int age;int num; public://无参构造Complex(){}//有参构造Complex(int age,int num){this->age age;this->num num;}void show(){cout << "age" << age << "…

chatgpt赋能python:用Python根据IP地址查找具体地址:一个实用的SEO技巧

用Python根据IP地址查找具体地址&#xff1a;一个实用的SEO技巧 在数字化时代&#xff0c;IP地址是一个重要的数字标识符。使用IP地址来发现目标受众的位置是网络营销和搜索引擎优化策略的重要组成部分。在本文中&#xff0c;我们将介绍用Python编程语言根据IP地址查找具体地址…

4. MySQL多表查询练习题

数据库表 CREATE TABLE departments (dept_no CHAR(4) PRIMARY KEY COMMENT 部门编码,dept_name VARCHAR(40) NOT NULL UNIQUE COMMENT 部门名称 ) COMMENT 部门表;CREATE TABLE dept_emp (emp_no INT NOT NULL COMMENT 部门编码,dept_no …

vue路由配置公共布局layout

本篇实现三段式界面 公共布局文件 首先在src下新建layoutPc文件夹&#xff0c;再给layoutPc新建组件 header 、bottm、main三个文件基本确定了一个页面的基本架子&#xff0c;然后再新建一个index.vue文件 comment/AppMain.vue <template><section class"ap…

卷积神经网络狗猫数据集的分类

卷积神经网络狗猫数据集的分类 环境搭建 安装Anaconda 具体安装过程&#xff0c;请自行百度 配置TensorFlow、Keras 创建虚拟环境 输入下面命令&#xff1a; conda create -n ppqppl_tfl python3.6 #tf1是自己为创建虚拟环境取的名字&#xff0c;后面python的版本可以根据自己需…

MyBatis-Plus教程,不敢说是最详细的

一、MyBatis-Plus 1.简介 MyBatis-Plus (opens new window)&#xff08;简称 MP&#xff09;是一个 MyBatis (opens new window)的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 我们的愿景是成为 MyBatis 最好的搭档&…

基于SpringBoot+vue的在线动漫信息平台设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

SAM 在遥感图像分割上的应用

文章目录 导读方法SAM 模型SAM 的实例分割扩展 RSPrompter概述特征聚合器锚点式Prompter查询式Prompter 实验在 WHU 上的结果在NWPU上的结果在SSDD上的结果 总结参考 Title: RSPrompter: Learning to Prompt for Remote Sensing Instance Segmentation based on Visual Foundat…

Spring Boot 整合视图层技术 FreeMarker

大家好&#xff01;我是今越。简单记录一下在 Spring Boot 框架中如何整合 Freemarker 及使用。 FreeMarker 简介 FreeMarker 是一款模板引擎&#xff1a;即一种基于模板和要改变的数据&#xff0c;并用来生成输出文本&#xff08; HTML 网页&#xff0c;电子邮件&#xff0c;…

Python3 实例(二) | 菜鸟教程(二十)

目录 一、Python 将列表中的头尾两个元素对调 &#xff08;一&#xff09;定义一个列表&#xff0c;并将列表中的头尾两个元素对调。 &#xff08;二&#xff09;实例1 &#xff08;三&#xff09;实例2 &#xff08;四&#xff09;实例3 二、Python 将列表中的指定位置的…

新一代版本依赖管理Vesion Catalog的探索与实践

前言 前段时间使用新版本的Android Studio创建了个项目想测试点东西&#xff0c;项目创建后发现整个Gradle的依赖管理发生了巨大的变化。 先说一下我使用的Android Studio的版本如下。 创建项目后主要变化如下&#xff1a; 原本的.gradle变成了.gradle.kts&#xff0c;也就是…