Unity中URP下实现水体(水面反射)

文章目录

  • 前言
  • 一、原理
    • 1、法一:使用立方体纹理 CubeMap,作为反射纹理使用
    • 2、法二:使用反射探针生成环境反射图,所谓反射的采样纹理
  • 二、实现水面反射
  • 三、最终代码


前言

在上篇文章中,我们实现了水体的高光反射效果。

  • Unity中URP下实现水体(水面高光)

在这篇文章中,我们来实现水体的反射效果。


一、原理

以下两个方法,对反射纹理采样时,都是使用顶点的反射向量进行采样的。

1、法一:使用立方体纹理 CubeMap,作为反射纹理使用

  • Unity中Shader立方体纹理Cubemap

2、法二:使用反射探针生成环境反射图,所谓反射的采样纹理

  • Unity中Shader反射环境

二、实现水面反射

1、定义和申明CubeMap

  • 在属性面板定义一个 Cube 传入反射纹理

_ReflectionTex(“ReflectionTex”,Cube) = “white”{}

  • 在Pass中,申明纹理和采样器(注意:反射纹理为 TEXTURECUBE 类型)

TEXTURECUBE(_ReflectionTex);SAMPLER(sampler_ReflectionTex);

  • 我使用的CubeMap如下

在这里插入图片描述

2、反射向量需要什么

计算反射向量,需要知道 法线向量 N ⃗ \vec{N} N 和 摄像机指向顶点的入射向量 − V ⃗ -\vec{V} V

3、计算 N ⃗ \vec{N} N

得到世界空间下的 法线坐标 和 法线纹理 线性过渡的效果

  • 在顶点着色器中,转化得到世界空间下的法线坐标

o.normalWS = TransformObjectToWorldNormal(v.normalOS);

  • 在属性面板传入,需要使用的法线纹理

_NormalTex(“NormalTex”,2D) = “white”{}

  • 定义、申明控制过渡的值

[PowerSlider(3)]_NormalIntensity(“NormalIntensity”,Range(0,1)) = 0.5

  • 得到线性过渡的法线信息

half4 N = lerp(half4(i.normalWS,1),normalize(normalTex),_NormalIntensity);

4、计算 V ⃗ \vec{V} V

half3 V = normalize(_WorldSpaceCameraPos.xyz - i.positionWS.xyz);

5、计算反射向量 R ⃗ \vec{R} R

half3 reflectionUV = reflect(-V,N.xyz);

6、对CubeMap进行纹理采样

注意:

  1. 使用的采样方式为SAMPLE_TEXTURECUBE
  2. 采样UV为:反射向量

half4 reflectionTex = SAMPLE_TEXTURECUBE(_ReflectionTex,sampler_ReflectionTex,reflectionUV);

请添加图片描述

7、模拟出菲涅尔效果(近处反射弱,远处反射强)

在这里插入图片描述

  • fresnel = 1 - dot(N,V)

half fresnel = 1 - saturate(dot(i.normalWS,V));

  • 用 fresmel 和 reflectionTex 相乘得出拟真的反射效果

half4 reflection = reflectionTex * fresnel;

请添加图片描述

8、环境反射效果 与 高光反射效果 相乘,得到只有在高光处反射环境的效果

specular * reflection

请添加图片描述

9、用该效果与上篇文章的结果相加得出最终效果

half4 col = (foamColor + waterColor) * cameraOpaqueTex + (specular * reflection);

请添加图片描述
请添加图片描述


三、最终代码

//水的深度
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
        
    }
    
    SubShader
    {
        Tags
        {
            //告诉引擎,该Shader只用于 URP 渲染管线
            "RenderPipeline"="UniversalPipeline"
            //渲染类型
            "RenderType"="Transparent"
            //渲染队列
            "Queue"="Transparent"
        }
        //Blend SrcAlpha OneMinusSrcAlpha
        ZWrite Off
        Pass
        {
          
            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;
            CBUFFER_END

            
            TEXTURE2D(_CameraDepthTexture);SAMPLER(sampler_CameraDepthTexture);
            TEXTURE2D(_FoamTex);SAMPLER(sampler_FoamTex);
            TEXTURE2D(_CameraOpaqueTexture);SAMPLER(sampler_CameraOpaqueTexture);
            TEXTURE2D(_NormalTex);SAMPLER(sampler_NormalTex);
            TEXTURECUBE(_ReflectionTex);SAMPLER(sampler_ReflectionTex);
            //struct appdata
            //顶点着色器的输入
            struct Attributes
            {
                float3 positionOS : POSITION;
                float2 uv : TEXCOORD0;
                half3 normalOS : NORMAL;
            };
            //struct v2f
            //片元着色器的输入
            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float2 uv : TEXCOORD0;//foamUV
                float4 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;
                //计算得到水下扭曲纹理的流动UV
                o.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 cameraOpaqueTex = SAMPLE_TEXTURE2D(_CameraOpaqueTexture,sampler_CameraOpaqueTexture,distortUV);
                
                //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);
                
                //水的反射
                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;
                
                //水的焦散


                half4 col = (foamColor + waterColor) * cameraOpaqueTex + (specular * reflection);
                
                return col;
            }
            ENDHLSL
        }
    }
    FallBack "Hidden/Shader Graph/FallbackError"
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux服务器调度数据库的存储过程

1、需要安装数据库的客户端 2、安装sqlplus 3、编写sh脚本 脚本内容如下: 4、设置调度任务

React UI框架Antd 以及 如何按需引入css样式配置(以及过程中各种错误处理方案)

一、react UI框架Antd使用 1.下载模块 npm install antd -S 2.引入antd的样式 import ../node_modules/antd/dist/reset.css; 3.局部使用antd组件 import {Button, Calendar} from antd; import {PieChartTwoTone} from ant-design/icons; {/* 组件汉化配置 */} import l…

[机器视觉]halcon应用实例 边缘检测2

一个学习找边的实例2,用于练习相关算子,这个版本比较简单,少了一些对图像的处理。所以主要是用于练习思路和相关算子。只有用了,练了你在脑子里心才有收获。 边缘检测的步骤图解 代码 *直线找边 *清空屏幕,显式控制图…

express+mysql+vue,从零搭建一个商城管理系统5--用户注册

提示:学习express,搭建管理系统 文章目录 前言一、新建user表二、安装bcryptjs、MD5、body-parser三、修改config/db.js四、新建config/bcrypt.js五、新建models文件夹和models/user.js五、index.js引入使用body-parser六、修改routes/user.js七、启动项…

Linux------进程地址空间

目录 一、进程地址空间 二、地址空间本质 三、什么是区域划分 四、为什么要有地址空间 1.让进程以统一的视角看到内存 2.进程访问内存的安全检查 3.将进程管理与内存管理进行解耦 一、进程地址空间 在我们学习C/C的时候,一定经常听到数据存放在堆区、栈区、…

Linux shell:补充命令的使用

目录 一.导读 二.正文 三.结语 一.导读 上一篇介绍了脚本的简单概念以及使用,现在补充一些命令。 二.正文 目前处于全局目录,通过mkdir创建名我为day01的文件。 通过cd命令day01 切换至day01文件当中。 使用vim文本编辑器文件名(firstdir&…

Dledger部署RocketMQ高可用集群(9节点集群)

文章目录 🔊博主介绍🥤本文内容规划集群准备工作节点0配置(ip地址为192.168.80.101的机器)节点1配置(ip地址为192.168.80.102的机器)节点2配置(ip地址为192.168.80.103的机器)在所有…

基于springboot实现鞋类商品购物商城系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现鞋类商品购物商城系统演示 摘要 时代的变化速度实在超出人类的所料,21世纪,计算机已经发展到各行各业,各个地区,它的载体媒介-计算机,大众称之为的电脑,是一种特高速的科学仪器&#xf…

05|Mysql锁分类

1. 锁分类 1.1根据性能 乐观锁 ● 版本号 ● 读多场景 ● 第二次循环需要读取到最新的数据统计 示例 while{ // 1.调用方法获取当前版本号 getCurrentBalanceAndVersion(accountId); // 2.Java运算newBalance Balance 500; updateAccountBalance(account…

LVS+Keepalived高可用群集

一、Keepalived简介 Keepalived 软件起初是专为LVS负载均衡软件设计的,用来管理并监控LVS集群系统中各个服务节点的状态,后来又加入了可以实现高可用的VRRP功能。因此,Keepalived除了能够管理LVS软件外,还可以作为其他服务&…

低代码平台与MES:智能制造的新篇章

随着工业4.0和智能制造的兴起,企业对于生产过程的数字化、智能化需求日益迫切。传统的MES系统实施周期长、成本高,成为许多企业数字化转型的瓶颈。而低代码开发平台的出现为这一问题提供了新的解决思路。 一、万界星空科技低代码平台的优势: …

【项目管理】CMMI-质量保证过程

质量保证过程(PQA):通过质量保证活动,确保过程与产品满足过程、规程及相应的要求,确保问题得到关注与解决,使工作人员和管理者能够客观地了解过程与相关的工作产品。QA工程师应实施质量保证策划活动,客观地…

Leetcode—64. 最小路径和【中等】

2024每日刷题&#xff08;116&#xff09; Leetcode—64. 最小路径和 实现代码 class Solution { public:int minPathSum(vector<vector<int>>& grid) {int m grid.size();int n grid[0].size();vector<vector<int>> dp(m 1, vector<int&g…

代理ip并发是不是越大越好?为何对代理IP做出并发请求限制?

随着网络技术的不断发展&#xff0c;代理IP已成为许多人在上网时保护隐私和突破地域限制的重要手段。然而&#xff0c;在选择和使用代理IP时&#xff0c;有一个问题常常被人们提及&#xff1a;代理IP的并发数是不是越大越好&#xff1f;为何对代理IP做出并发请求限制&#xff1…

C++ 高频考点

1. C/C内存有哪几种类型&#xff1f; C中&#xff0c;内存分为5个区&#xff1a;堆(malloc)、栈(如局部变量、函数参数)、程序代码区&#xff08;存放二进制代码&#xff09;、全局/静态存储区&#xff08;全局变量、static变量&#xff09;和常量存储区&#xff08;常量&…

言语理解与表达-郭熙-01

1、听课小贴上 2、 言语理解与表达-题型分类&#xff08;章就是类&#xff09; 国考&#xff1a;40道题 时间建议&#xff1a;30分钟 目标正确率&#xff1a;80% 3、课程安排 4、第一章-片段阅读 看问题---> 带着问题读文段----> 确定选项 4.1 中心理解题 关联词&…

QT摄像头采集

主界面为显示框&#xff0c;两个下拉框&#xff0c;一个是所有相机&#xff0c;一个是相机支持的分辨率 系统根据UI界面自动生成的部分不再描述&#xff0c;以下为其他部分源码 widget.h #include <QWidget> #include <QMouseEvent> class QCamera; class QCamer…

osi模型,tcp/ip模型(名字由来+各层介绍+中间设备介绍)

目录 网络协议如何分层 引入 osi模型 tcp/ip模型 引入 命名由来 介绍 物理层 数据链路层 网络层 传输层 应用层 中间设备 网络协议如何分层 引入 我们已经知道了网络协议是层状结构,接下来就来了解了解下网络协议如何分层 常见的网络协议分层模型是OSI模型 和 …

Sentinel实战(待完善)

目录 服务雪崩 什么是服务雪崩 服务不可用原因 解决方案 技术选型对比 Sentinel 介绍 优点 核心概念 资源 规则 代码实战 API实现 SentinelResource注解埋点实现 服务雪崩 什么是服务雪崩 在服务调用链路中, 服务提供者不可用, 导致服务调用者不可用, 间接让上上游…

C++——类的6个默认成员函数

目录 类中的6个默认成员函数 构造函数 构造函数的特点 初始化列表 隐式类型转换 析构函数 拷贝构造函数 赋值重载 运算符重载 赋值重载 取地址重载 类中的6个默认成员函数 类中的6个默认成员函数根据不同的作用可以分为&#xff1a; 初始化和使用后清理&#xff1a;…