Unity中URP实现水体(水的焦散)
文章目录
- 前言
- 一、原理
- 1、 通过深度图,得到 对应像素 在 世界空间下的Z值
- 2、得到模型顶点在 观察空间 下的坐标
- 3、由以上两点得到 深度图像素 对应的 xyz 值
- 4、最后,转化到 模型本地空间下,用其对焦散纹理采样
- 二、实现
- 1、获取深度图
- 2、在顶点着色器中,转化 得到顶点在 观察空间下的坐标值
- 3、使用公式计算得到,深度图 z 对应的 xy 值
- 4、转化到模型的本地空间下
- 5、对定义和申明焦散纹理,并对其纹理采样
- 6、与之前文章的结果相加输出得出最终效果
- 三、最终代码
前言
在上篇文章中,我们实现了水面的反射。
- Unity中URP下实现水体(水面反射)
在这篇文章中,我们来实现一下水的焦散效果。
一、原理
和深度贴花使用的方法是一致的。
- Unity中URP下实现深度贴花
主要步骤:
1、 通过深度图,得到 对应像素 在 世界空间下的Z值
2、得到模型顶点在 观察空间 下的坐标
3、由以上两点得到 深度图像素 对应的 xyz 值
4、最后,转化到 模型本地空间下,用其对焦散纹理采样
二、实现
1、获取深度图
(因为要考虑之前的水下扭曲效果,需要把深度图扭曲一下)
- 获取 屏幕空间下的UV坐标
float2 screenUV = i.positionCS.xy / _ScreenParams.xy;
- 使用_Distort控制得到线性插值在 screenUV 和 normalTex之间的扭曲UV
(在之前的文章中,有完整过程)
float2 distortUV = lerp(screenUV,normalTex.xy,_Distort);
- 使用该UV采样得到,水下扭曲后的深度纹理
half4 depthDistortTex = SAMPLE_TEXTURE2D(_CameraDepthTexture,sampler_CameraDepthTexture,distortUV);
- 使深度图 转化到 观察空间下
half depthDistortScene = LinearEyeDepth(depthDistortTex.x,_ZBufferParams);
2、在顶点着色器中,转化 得到顶点在 观察空间下的坐标值
o.positionWS = TransformObjectToWorld(v.positionOS);
o.positionVS = TransformWorldToView(o.positionWS);
3、使用公式计算得到,深度图 z 对应的 xy 值
公式在贴花的那篇文章有推导
- W x = P x W z − P z W_x = \frac{P_xW_z}{-P_z} Wx=−PzPxWz
- W y = P y W z − P z W_y = \frac{P_yW_z}{-P_z} Wy=−PzPyWz
float4 depthVS = 1;
depthVS.xy = i.positionVS.xy * depthDistortScene / -i.positionVS.z;
depthVS.z = depthDistortScene;
4、转化到模型的本地空间下
float4 depthWS = mul(unity_CameraToWorld,depthVS);
float4 depthOS = mul(unity_WorldToObject,depthWS);
5、对定义和申明焦散纹理,并对其纹理采样
- 使用该本地空间坐标,计算得出两组方向不同的流动uv值
float2 uv1 = depthOS.xz * _CausticsTex_ST.xy + depthWS.y *0.1+ _Time.y * _WaterSpeed;
float2 uv2 = depthOS.xz * _CausticsTex_ST.xy + depthWS.y *0.1+ _Time.y * _WaterSpeed * float2(-1.1,1.3);
- 使用这两组 uv 分别进行 焦散纹理 的 纹理采样。
half4 causticsTex1 = SAMPLE_TEXTURE2D(_CausticsTex,sampler_CausticsTex,uv1);
half4 causticsTex2 = SAMPLE_TEXTURE2D(_CausticsTex,sampler_CausticsTex,uv2);
- 使用 min 函数制造随机混乱的效果
half4 causticsTex = min(causticsTex1,causticsTex2);
6、与之前文章的结果相加输出得出最终效果
half4 col = (foamColor + waterColor) * opaqueTex + (specular * reflection) + causticsTex;
三、最终代码
//水的深度
Shader "MyShader/URP/P4_8"
{Properties {[Header(Base)]_WaterColor1("WaterColor1",Color) = (1,1,1,1)_WaterColor2("WaterColor2",Color) = (1,1,1,1)[PowerSlider(3)]_WaterSpeed("WaterSpeed",Range(0,1)) = 0.1[Header(Foam)]_FoamTex("FoamTex",2D) = "white"{} _FoamColor("FoamColor",Color) = (1,1,1,1)_FoamRange("FoamRange",Range(0,5)) = 1_FoamNoise("FoamNoise",Range(0,3)) = 1[Header(Distort)]_NormalTex("NormalTex",2D) = "white"{}[PowerSlider(3)]_Distort("Distort",Range(0,0.3)) = 0[Header(Specular)]_SpecularColor("Specular Color",Color) = (1,1,1,1)[PowerSlider(3)]_SpecularIntensity("Specular Intensity",Range(0,1)) = 0.6_Smoothness("Smoothness",Range(0,10)) = 10[Header(Reflection)]_ReflectionTex("ReflectionTex",Cube) = "white"{}[PowerSlider(3)]_NormalIntensity("NormalIntensity",Range(0,1)) = 0.5[Header(Caustics)]_CausticsTex("CausticsTex",2D) = "white"{}}SubShader{Tags{//告诉引擎,该Shader只用于 URP 渲染管线"RenderPipeline"="UniversalPipeline"//渲染类型"RenderType"="Transparent"//渲染队列"Queue"="Transparent"}//Blend SrcAlpha OneMinusSrcAlphaZWrite OffPass{HLSLPROGRAM#pragma vertex vert#pragma fragment frag// Pragmas#pragma target 2.0// Includes#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"CBUFFER_START(UnityPerMaterial)half4 _WaterColor1;half4 _WaterColor2;half _WaterSpeed;half4 _FoamColor;half _FoamRange;half _FoamNoise;half4 _FoamTex_ST;half _Distort;half4 _NormalTex_ST;half4 _SpecularColor;half _SpecularIntensity;half _Smoothness;half _NormalIntensity;half4 _CausticsTex_ST;CBUFFER_ENDTEXTURE2D(_CameraDepthTexture);SAMPLER(sampler_CameraDepthTexture);TEXTURE2D(_FoamTex);SAMPLER(sampler_FoamTex);TEXTURE2D(_CameraOpaqueTexture);SAMPLER(sampler_CameraOpaqueTexture);TEXTURE2D(_NormalTex);SAMPLER(sampler_NormalTex);TEXTURECUBE(_ReflectionTex);SAMPLER(sampler_ReflectionTex);TEXTURE2D(_CausticsTex);SAMPLER(sampler_CausticsTex);//struct appdata//顶点着色器的输入struct Attributes{float3 positionOS : POSITION;float2 uv : TEXCOORD0;half3 normalOS : NORMAL;};//struct v2f//片元着色器的输入struct Varyings{float4 positionCS : SV_POSITION;float2 uv : TEXCOORD0;//foamUVfloat4 screenPos : TEXCOORD1;float3 positionVS : TEXCOORD2;float3 positionWS : TEXCOORD3;float3 normalWS : TEXCOORD4;float4 normalUV : TEXCOORD5;};//v2f vert(Attributes v)//顶点着色器Varyings vert(Attributes v){Varyings o = (Varyings)0;o.positionWS = TransformObjectToWorld(v.positionOS);o.positionVS = TransformWorldToView(o.positionWS);o.positionCS = TransformWViewToHClip(o.positionVS);o.screenPos = ComputeScreenPos(o.positionCS);//计算得到泡沫纹理采样需要的顶点世界空间下的坐标值的流动效果o.uv += o.positionWS.xz *_FoamTex_ST.xy + _Time.y * _WaterSpeed;//计算得到水下扭曲纹理的流动UVo.normalUV.xy = TRANSFORM_TEX(v.uv,_NormalTex) + _Time.y * _WaterSpeed;o.normalUV.zw = TRANSFORM_TEX(v.uv,_NormalTex) + _Time.y * _WaterSpeed * half2(-1.1,1.3);o.normalWS = TransformObjectToWorldNormal(v.normalOS);return o;}//fixed4 frag(v2f i) : SV_TARGET//片元着色器half4 frag(Varyings i) : SV_TARGET{//1、水的深度//获取屏幕空间下的 UV 坐标float2 screenUV = i.positionCS.xy / _ScreenParams.xy;half depthTex = SAMPLE_TEXTURE2D(_CameraDepthTexture,sampler_CameraDepthTexture,screenUV).x;//深度图转化到观察空间下float depthScene = LinearEyeDepth(depthTex,_ZBufferParams);//获取水面模型顶点在观察空间下的Z值(可以在顶点着色器中,对其直接进行转化得到顶点观察空间下的坐标)float4 depthWater = depthScene + i.positionVS.z;//2、水的颜色,线性插值得到水 和 接触物体的水的 颜色的过度half4 waterColor = lerp(_WaterColor1,_WaterColor2,depthWater);//3、水面泡沫//对泡沫纹理进行采样(这里使用顶点世界空间下的坐标进行纹理采样,防止水体缩放影响泡沫的平铺和重复方式)half4 foamTex = SAMPLE_TEXTURE2D(_FoamTex,sampler_FoamTex,i.uv.xy);foamTex = pow(abs(foamTex),_FoamNoise);//这里增加一个调整深度图范围的功能half4 foamRange = depthWater * _FoamRange;//使用泡沫纹理 和 泡沫范围 比较得到泡沫遮罩half4 foamMask = step(foamRange,foamTex);//给泡沫加上颜色half4 foamColor = foamMask * _FoamColor;//4、水下的扭曲half4 normalTex1 = SAMPLE_TEXTURE2D(_NormalTex,sampler_NormalTex,i.normalUV.xy);half4 normalTex2 = SAMPLE_TEXTURE2D(_NormalTex,sampler_NormalTex,i.normalUV.zw);half4 normalTex = normalTex1 * normalTex2;float2 distortUV = lerp(screenUV,normalTex.xy,_Distort);half4 depthDistortTex = SAMPLE_TEXTURE2D(_CameraDepthTexture,sampler_CameraDepthTexture,distortUV);half depthDistortScene = LinearEyeDepth(depthDistortTex.x,_ZBufferParams);half depthDistortWater = depthDistortScene + i.positionVS.z;float2 opaqueUV = distortUV;if(depthDistortWater<0) opaqueUV = screenUV;half4 opaqueTex = SAMPLE_TEXTURE2D(_CameraOpaqueTexture,sampler_CameraOpaqueTexture,opaqueUV);//5、水的高光//Specular = SpecularColor * Ks * pow(max(0,dot(N,H)), Shininess)Light light = GetMainLight();half3 L = light.direction;half3 V = normalize(_WorldSpaceCameraPos.xyz - i.positionWS.xyz);//修改法线实现,波光粼粼的效果half4 N = lerp(half4(i.normalWS,1),normalize(normalTex),_NormalIntensity);half3 H = normalize(L + V);half4 specular = _SpecularColor * _SpecularIntensity * pow(max(0,dot(N.xyz,H)),_Smoothness);//6、水的反射half3 reflectionUV = reflect(-V,N.xyz);half4 reflectionTex = SAMPLE_TEXTURECUBE(_ReflectionTex,sampler_ReflectionTex,reflectionUV);half fresnel = 1 - saturate(dot(i.normalWS,V));half4 reflection = reflectionTex * fresnel;//7、水的焦散float4 depthVS = 1;depthVS.xy = i.positionVS.xy * depthDistortScene / -i.positionVS.z;depthVS.z = depthDistortScene;float4 depthWS = mul(unity_CameraToWorld,depthVS);float4 depthOS = mul(unity_WorldToObject,depthWS);float2 uv1 = depthOS.xz * _CausticsTex_ST.xy + depthWS.y *0.1+ _Time.y * _WaterSpeed;float2 uv2 = depthOS.xz * _CausticsTex_ST.xy + depthWS.y *0.1+ _Time.y * _WaterSpeed * float2(-1.1,1.3);half4 causticsTex1 = SAMPLE_TEXTURE2D(_CausticsTex,sampler_CausticsTex,uv1);half4 causticsTex2 = SAMPLE_TEXTURE2D(_CausticsTex,sampler_CausticsTex,uv2);half4 causticsTex = min(causticsTex1,causticsTex2);half4 col = (foamColor + waterColor) * opaqueTex + (specular * reflection) + causticsTex;return col;}ENDHLSL}}FallBack "Hidden/Shader Graph/FallbackError"
}
相关文章:

Unity中URP实现水体(水的焦散)
文章目录 前言一、原理1、 通过深度图,得到 对应像素 在 世界空间下的Z值2、得到模型顶点在 观察空间 下的坐标3、由以上两点得到 深度图像素 对应的 xyz 值4、最后,转化到 模型本地空间下,用其对焦散纹理采样 二、实现1、获取深度图2、在顶点…...

vue构建版本
完整版:同时包含编译器和运行时的版本。 编译器:用来将模板字符串编译成为 JavaScript 渲染函数的代码。 运行时runtime:用来创建 Vue 实例、渲染并处理虚拟 DOM 等的代码。基本上就是除去编译器的其它一切。 UMD:UMD 版本可以…...
Docker挂载镜像到本地(日常记录)
Docker挂载镜像到本地 1、进入jar包文件夹 cd docker-publish/2、编写DockFile文件 #使用Jdk8环境作为基础镜像,如果镜像不在本地则会从DockerHub进行下载 #FROM openjdk:8-jdk-alpine FROM openjdk:11 #VOLUME 指定了临时文件目录为/tmp。其效果是在主机 /var/l…...
【Elasticsearch查询】精确查询
文章目录 复合查询constant_score querybool querydis_max queryfunction_score queryboosting query单层嵌套双层嵌套 词项查询term query(词项查询)数字的精确查询文本的精确查询查询优化 terms query(多词项查询)terms_set que…...

小狐狸chat2.7.2免授权修复版可用版
小狐狸chat2.7.2免授权修复版可用版 在网络上面找了好几个版本不能使用,今天发布这个仔细测试正常使用 主要功能:独立版无限多开支持分销会员充值自己APP打包小程序万能创作MJ绘图多个国内接口 国外很火的ChatGPT,这是一种基于人工智能技术…...

通过QScrollArea寻找最后一个弹簧并且设置弹簧大小
项目原因,最近需要通过QScrollArea寻找其中最后一个弹簧并且设置大小和策略,因为无法直接调用UI指针,所以只能用代码寻找。 直接上代码: if (m_scrollArea){int iScrollWidth m_labelSelectedTitle->width();m_scrollArea-&g…...
為什麼使用海外動態代理IP進行網路爬蟲?
網路爬蟲作為獲取網路數據的重要工具,其重要性不言而喻。但隨著網站反爬策略的日益嚴格,爬蟲任務變得愈發困難,不過海外動態代理IP可以很好地解決這一問題。本文將詳細闡釋動態代理IP在爬蟲中的應用,以及如何使用動態代理IP提升爬…...

LeetCode 热题100 刷题笔记
一:哈希表 一般哈希表都是用来快速判断一个元素是否出现集合里。 直白来讲其实数组就是一张哈希表,哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素。 1.两数之和 题目链接:. - 力扣(LeetCode…...
veridata安装
GoldenGate Veridata是GoldenGate中用于比较数据库间数据同步效果的一个对比软件。Veridata基于Web,支持大据量的数据对比,能够在不停止数据同步的情况下就可以比较数据。 1、安装veridata前我们都会先安装 middleware infrastructure 这时我们会添加几个…...

面试笔记系列三之spring基础知识点整理及常见面试题
目录 如何实现一个IOC容器? 说说你对Spring 的理解? 你觉得Spring的核心是什么? 说一下使用spring的优势? Spring是如何简化开发的? IOC 运行时序 prepareRefresh() 初始化上下文环境 obtainFreshBeanFactory() 创建并…...

面试笔记系列四之SpringBoot+SpringCloud+计算机网络基础知识点整理及常见面试题
目录 Spring Boot 什么是 Spring Boot? Spring Boot 有哪些优点? SpringBootApplication注解 Spring Boot 的启动流程 Spring Boot属性加载顺序 springboot自动配置原理是什么?(*) 如何理解springboot中的start…...
Kernel[Device Tree] - 1. 设备树的由来
内核代码中,arch文件夹下,是各个架构相关的代码,arm也在里面。 arm子文件夹下,有mach-xxx的目录,就是针对各个芯片类型的,比如mach-imx就是imx系列的芯片。 再里面就是具体的芯片或SOC,比如ma…...

第十四天-网络爬虫基础
目录 1.什么是爬虫 2.网络协议 OSI七层参考模型 TCP/IP模型 1.应用层 2.传输层 3.网络层 3.HTTP协议 1.介绍 2.http版本: 3.请求格式 4.请求方法 5.HTTP响应 状态码: 6.http如何连接 4.Python requests模块 1.安装 2.使用get/post 3.响…...

Linux系统安装
Linux系统安装 安装包链接 链接:https://pan.baidu.com/s/1FdP7TH90UvKUQuiL2yeGCA 提取码:c49n安装包内容 虚拟机执行文件 详细安装教程 虚拟机密钥 Ubuntu 安装步骤 先点击虚拟机的.EXE文件安装,打开安装教程,有详细的说明。...

springboot-基础-thymeleaf配置+YAML语法
备份笔记。所有代码都是2019年测试通过的,如有问题请自行搜索解决! 目录 配置thymeleafthymeleaf举例参数设置yaml基础知识YAML语法报错:Expecting a Mapping node but got 其他语法 spring boot不推荐使用jsp。thymeleaf是一个XML/XHTML/HTM…...

深入理解分库、分表、分库分表
前言 分库分表,是企业里面比较常见的针对高并发、数据量大的场景下的一种技术优化方案,所谓"分库分表",根本就不是一件事儿,而是三件事儿,他们要解决的问题也都不一样,这三个事儿分别是"只…...

Oracle中序列
1. Sequence 定义 在Oracle中可以用SEQUENCE生成自增字段。Sequence序列是Oracle中用于生成数字序列的对象,可以创建一个唯一的数字作为主键。 2. 为什么要用 Sequence 你可能有疑问为什么要使用序列? 不能使用一个存储主键的表并每次递增吗…...

蓝牙耳机和笔记本电脑配对连接上了,播放设备里没有显示蓝牙耳机这个设备,选不了输出设备
环境: WIN10 杂牌蓝牙耳机6s 问题描述: 蓝牙耳机和笔记本电脑配对连接上了,播放设备里没有显示蓝牙耳机这个设备,选不了输出设备 解决方案: 1.打开设备和打印机,找到这个设备 2.选中这个设备&#…...

Cadence Allegro PCB设计88问解析(三十四) 之 Allegro 中 DDR等长处理
一个学习信号完整性仿真的layout工程师 在进行PCB设计时 ,会遇到一些单端的信号要做等长处理,比如DDR的数据线,交换机之间的数据线之类的。这时需要我们建立match group,来做等长。下面简单介绍在Allegro中怎么做等长:…...
向爬虫而生---Redis 探究篇2<redis集群(1)>
前言: 经常会遇到这样的事,redis运行一段时间以后,就会出现迟钝和卡壳! 这时候,说明已经到了瓶颈期了,需要用到redis集群了! 那么,弄明白集群的几个概念是必要的,我用案例来讲,,, 正文: 当需要处理大量数据或提供高可用性和性能时,Redis集群是一种常见的解决方案。…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...

7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...

均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...