UnityShader(五)

news2025/1/4 23:11:28

这次要用表面着色器实现一个水的特效。先翻到最下边看代码,看不懂再看下面的解释。

首先第一步要实现水的深浅判断,实现深水区和浅水区的区分。

这里需要用到深度图的概念。不去说太多概念,只去说怎么实现的,首先我们的水面是在地面上的,我们可以利用深度图,计算得到人眼到地面的距离depth,人眼到水面的距离记作IN.Proj.z

可以看的出来,当我们看深水区的时候,depth-IN.Proj.z的差值会比浅水区大,所以可以通过这个差值的大小去判断深水区和浅水区。这个IN就是我这个水面的其中某一块着色面片表面着色器的输入结构体值。depth可以通过调一系列api得到,里面涉及到很多空间变换,不仔细去讲了,只说大体思路。

然后,在判断完水的深浅以后,我们要通过设置一个深水色和一个浅水色,将我们的depth-IN.Proj.z映射到[0,1]中,这样通过对每个面片设置一个lerp差值,lerp(_WaterShallowColor,_WaterDeepColor, min(_DepthRange, deltaDepth)/_DepthRange),就可以得到整片水域的颜色深浅过度,而这个映射的过程,我们可以通过设置另外一个变量_DepthRange实现。

min(_DepthRange, deltaDepth)/_DepthRange,这个意思就是,当我水很深的时候,min(_DepthRange, deltaDepth)=_DepthRange,整体得1,所以在lerp函数中取的是深水色,当我水很浅的时候,min(_DepthRange, deltaDepth)=deltaDepth,lerp的第三个参数就成了deltaDepth/_DepthRange,因为水很浅,所以这个数接近0,颜色是接近浅水色,如果水的深浅介于深水和浅水之间,那么颜色也介于深水色和潜水色之间。但是这样深水和浅水虽然区分开了,但是都是纯色的,没有立体效果。

第二步通过对法线贴图采样,实现水的立体效果。

下面的代码是说,本来把法线贴图假设是第i,j个面片采样到我当前模型的面片,但是现在不是采样到我当前面片, 而是到了x方向加上_WaterSpeed * _Time.x这个值的面片,先不管_Time.x会动,因为uv的范围都是0-1,所以本质上是在0-1的范围内,采样的数量少了,因为是本来是法线0-1对应模型0-1,现在相当于同样一个点采样的距离大了,假设法线0-1对应模型0-1.4,模型1-1.4的不会显示,所以相当于是原来的0.6倍。

  float4 bumpOffset1 = tex2D(_NormalTex, IN.uv_NormalTex + float2(_WaterSpeed * _Time.x, 0));

剩下三个计算也是同理,只不过需要注意,_Time.x是会变的,这样子,本来法线贴图假设是第i,j个面片采样到模型的m,n个面片,一段时间后,就是对应到了模型第m + k,n个面片,所有面片都这样映射,相当于整体是移动了,也就是有了大波浪效果,汹涌澎湃。
  float4 bumpOffset2 = tex2D(_NormalTex, float2(1 - IN.uv_NormalTex.y, IN.uv_NormalTex.x) + float2(_WaterSpeed * _Time.x, 0));
  float4 offsetColor = (bumpOffset1 + bumpOffset2)/2;
  float2 offset = UnpackNormal(offsetColor).xy * _Refract;
  float4 bumpColor1 = tex2D(_NormalTex, IN.uv_NormalTex + offset + float2(_WaterSpeed * _Time.x, 0));
  float4 bumpColor2 = tex2D(_NormalTex, float2(1 - IN.uv_NormalTex.y, IN.uv_NormalTex.x) + offset + float2(_WaterSpeed * _Time.x, 0));


 第三步是要实现波纹,主要对浅水区,波纹的实现是要对WaveTex贴图进行采样。

WaveTex贴图就是一个黑白交替的图,白色表示有波纹,黑色表示没有,但它是0-1,对应模型0-1,但其实我们是需要将一个更大范围的x映射到一个更小范围的WaveTex贴图上的,此时就可以用到GPUGem里讲的正弦波模拟水面波纹了           

首先要对波形图和噪声图进行采样,对波形图进行采样两次,是为了产生两道波纹,采样的时候使用噪声图是为了产生随机的效果,对波形图采样要采用上面的正弦波的方式去采,这样就有一个波浪的效果了

            fixed4 waveColor = tex2D(_WaveTex,float2(waveA + _WaveRange * sin(_Time.x * _WaveSpeed + noiseColor.r) , 1) + offset);
            fixed4 waveColor2 = tex2D(_WaveTex,float2(waveA + _WaveRange * sin(_Time.x * _WaveSpeed + _WaveDelta + noiseColor.r) , 1) + offset);

Shader "Custom/MyWater2"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _WaterShallowColor("WaterShallowColor", Color) = (1,1,1,1)
        _WaterDeepColor("WaterDeepColor", Color) = (1,1,1,1)
        _TranAmount("TranAmount", Range(0,100)) = 0.5
        _DepthRange("DepthRange",float) = 1
        _NormalTex("Normal",2D) = "bump"{}
        _WaterSpeed("WaterSpeed",float) = 5
        //控制法线的密集程度
        _Refract("Refract",float) = 0.5
        _Specular("Specular",float) = 1
        _Gloss("Gloss",float) = 0.5
        _SpecularColor("_SpecularColor", Color) = (1,1,1,1)
        //波浪效果需要先对波浪图采样,噪声图是为了产生随机效果
        _WaveTex("WaveTex",2D) = "white"{}
        _NoiseTex("NoiseTex",2D) = "white"{}
        _WaveSpeed("WaveSpeed",float) = 1
        _WaveRange("WaveRange",float) = 0.5
        _WaveRangeA("WaveRangeA",float) = 1
        _WaveDelta("WaveDelta", float) = 0.5
    } 
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue"="Transparent"  }
        LOD 200
        //水是透明的,所以关闭ZWrite
        ZWrite off
        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf WaterLight vertex:vert alpha noshadow

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0
        //加float是为了增加精度
        sampler2D_float _CameraDepthTexture;
        sampler2D _MainTex;
        fixed4 _WaterShallowColor;
        fixed4 _WaterDeepColor;
        half _TranAmount;
        half _DepthRange;
        sampler2D _NormalTex;
        half _WaterSpeed;
        sampler2D _WaveTex;
        sampler2D _NoiseTex;
        float _WaveSpeed;
        float _WaveRange;
        //这两个控制波浪的范围
        float _WaveRangeA;
        float _WaveDelta;

        struct Input
        {
            float2 uv_MainTex;
            float4 proj;
            float2 uv_NormalTex;
            float2 uv_WaveTex;
            float2 uv_NoiseTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
        half _Refract;
        half _Specular;
        half _Gloss;
        fixed4 _SpecularColor;

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)

        fixed4 LightingWaterLight(SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten)
        {
            float diffuseFactor = max(0,dot(normalize(lightDir),s.Normal));
            half3 halfDir = normalize(lightDir + viewDir);
            float nh = max(0,dot(halfDir,s.Normal));
            float spec = pow(nh, s.Specular * 128) * s.Gloss;
            fixed4 c;
            c.rgb = (s.Albedo * _LightColor0.rgb * diffuseFactor + _SpecularColor.rgb * spec * _LightColor0.rgb) * atten;
            c.a = s.Alpha + spec * _SpecularColor.a;
            return c;
        }

        void vert(inout appdata_full v,out Input i)
        {
            UNITY_INITIALIZE_OUTPUT(Input, i);
            i.proj = ComputeScreenPos(UnityObjectToClipPos(v.vertex));
            COMPUTE_EYEDEPTH(i.proj.z);
        }

        void surf (Input IN, inout SurfaceOutput o)
        {
        //关键点是深度图判断深水和浅水,法线移动实现波浪效果
            //tex2Dproj(_CameraDepthTexture, IN.proj)=tex2D(_CameraDepthTexture, IN.proj.xy/IN.proj.w)
            //对深度图采样,采样屏幕空间
            //它的uv表示当前模型在顶点着色器画到屏幕后的坐标
            //采样后的每个变量都是一样的,取r即可
            //越近深度图上对应的区域越红(深度值接近1),越远则越黑(深度值接近0)。也就是说深度值从0到1代表的是从远到近。
            half depth = LinearEyeDepth(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(IN.proj)).r);
            //depth 就相当于是这样计算出来的
            //SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(IN.proj));
            //UNITY_PROJ_COORD(IN.proj)是返回的采样后的纹理坐标,大多数平台就是返回的IN.proj,所以写不写区别不大
            //摄像机深度贴图的深度值减去地面模型面片的深度值
            half deltaDepth = depth - IN.proj.z;
            
            fixed4 c = lerp(_WaterShallowColor,_WaterDeepColor, min(_DepthRange, deltaDepth)/_DepthRange);



            //对法线贴图采样
            float4 bumpOffset1 = tex2D(_NormalTex, IN.uv_NormalTex + float2(_WaterSpeed * _Time.x, 0));
            float4 bumpOffset2 = tex2D(_NormalTex, float2(1 - IN.uv_NormalTex.y, IN.uv_NormalTex.x) + float2(_WaterSpeed * _Time.x, 0));
            float4 offsetColor = (bumpOffset1 + bumpOffset2)/2;
            float2 offset = UnpackNormal(offsetColor).xy * _Refract;
            float4 bumpColor1 = tex2D(_NormalTex, IN.uv_NormalTex + offset + float2(_WaterSpeed * _Time.x, 0));
            float4 bumpColor2 = tex2D(_NormalTex, float2(1 - IN.uv_NormalTex.y, IN.uv_NormalTex.x) + offset + float2(_WaterSpeed * _Time.x, 0));
            
            //波浪 跟水的深浅相反即可 min(_DepthRange, deltaDepth)/_DepthRange接近1的地方,应该无波浪
            //因为波浪要出现在水的边缘,min(_DepthRange, deltaDepth)/_DepthRange接近1的地方 表示deltadepth很大,水很深
            half waveA = 1 - min(_WaveRangeA,deltaDepth)/_WaveRangeA;
            fixed4 noiseColor = tex2D(_NoiseTex,IN.uv_NoiseTex);
            //sin函数来对波纹图采样,产生流动效果
            fixed4 waveColor = tex2D(_WaveTex,float2(waveA + _WaveRange * sin(_Time.x * _WaveSpeed + noiseColor.r) , 1) + offset);
            fixed4 waveColor2 = tex2D(_WaveTex,float2(waveA + _WaveRange * sin(_Time.x * _WaveSpeed + _WaveDelta + noiseColor.r) , 1) + offset);
            o.Albedo = c.rgb + (waveColor.rgb + waveColor2.rgb) * waveA;
            o.Normal = UnpackNormal((bumpColor1 + bumpColor2)/2).xyz;
            o.Gloss = _Gloss;
            o.Specular = _Specular;


            o.Alpha = min(_TranAmount,deltaDepth) / _TranAmount;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

法线贴图,噪声图,波纹图网盘链接

链接:https://pan.baidu.com/s/1Qdm3ly2YW9vq9lNqYvZnbA?pwd=jk3l 
提取码:jk3l

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

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

相关文章

宠物商店系统《宠物之家》,巨完善

源码下载地址 支持:远程部署/安装/调试、讲解、二次开发/修改/定制 系统分为用户端和管理员端。 截图中有些图片加载失败,是因为没有上传图片,登录管理员账号上传图片后,图片显示会变成正常。 web的宠物商城系统《宠物之家》。系…

什么是DDD领域驱动设计

一、DDD背景 2003 年埃里克埃文斯(Eric Evans)发表了《领域驱动设计》(Domain-Driven Design –Tackling Complexity in the Heart of Software)这本书,从此领域驱动设计(Domain Driven Design&#xff0c…

SpringCloud 微服务全栈体系(十二)

第十一章 分布式搜索引擎 elasticsearch 一、初识 elasticsearch 1. 了解 ES 1.1 elasticsearch 的作用 elasticsearch 是一款非常强大的开源搜索引擎,具备非常多强大功能,可以帮助我们从海量数据中快速找到需要的内容 例如: 在 GitHub 搜…

Linux Shell和权限

目录 Shell命令及运行原理 权限 1.文件基本属性 2.文件权限值的表示方法 3.文件访问权限的相关设置方法 3.(1)chmod 组名修改 3.(2)chmod 二进制修改 3.(3)chown 3.(4)chgrp 3.(5)umask 4.目录权限 Shell命令及运行原理 Linux的操作系统,狭义上是…

C++常用格式化输出转换

在C语言中可以用printf以一定的格式打印字符,C当然也可以。 输入输出及命名空间还不太了解的小伙伴可以看一看C入门讲解第一篇。  在C中,可以用流操作符(stream manipulators)控制数据的输出格式,这些流操作符定义在2…

提升家庭自动化级别:使用HomeAssistant添加HACS插件,实现对米家、HomeKit等智能家居设备的公网控制

提升家庭自动化级别:使用HomeAssistant添加HACS插件,实现对米家、HomeKit等智能家居设备的公网控制 文章目录 提升家庭自动化级别:使用HomeAssistant添加HACS插件,实现对米家、HomeKit等智能家居设备的公网控制基本条件一、下载HA…

游戏、电影、动画、电视广播还是现场娱乐,xsens都会随时随地为您提供准确的动作捕捉

MVN 动画释放你的创造力 无论是游戏、电影、动画、电视广播还是现场娱乐,MVN Animate都会随时随地为您提供准确的动作捕捉。其先进的惯性技术为您在工作室内外提供精准的生产数据! 认识 MVN Animate 实时传输所有数据 MVN动画的专有动作捕捉软件可以直…

上海:竹云董事长董宁受邀在第三届“双区驱动,打造全球经济新引擎”国际合作论坛发言

作为中国最具活力的两大重要经济带,粤港澳和长三角两大湾区2022年GDP总量超过42万亿,占全国GDP总量的35%,对中国经济的重要性举足轻重。中国国际进口博览会是我国主动向世界开放市场的重大举措,是一个推动两地开放合作&#xff0c…

电脑自动重启是什么原因?教你快速定位问题

电脑是我们日常生活和工作中不可或缺的工具,但有时它们会出现问题,其中之一是自动重启。这种情况可能会影响您的工作效率和数据的安全。可是您知道电脑自动重启是什么原因吗?在本文中,我们将深入研究电脑自动重启的可能原因&#…

Spring IoCDI入门

一:Spring IoC&DI概念 (1)Spring概念 💗Spring是包含了众多工具方法的IoC容器,是一个开源框架,让我们的开发更加简单 🌟Spring的两大核心和特点:IoC和AOP (2)IoC的介绍 1.概念 💗IoC: Inv…

【算法与数据结构】77、LeetCode组合

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析:如果k是固定的,最直接的方法就是建立k个for循环,将结果全部压入result容器中。…

Verilog刷题[hdlbits] :Always if2

题目:Always if2 A common source of errors: How to avoid making latches 一个常见的错误来源:如何避免产生latches When designing circuits, you must think first in terms of circuits: 在设计电路时,必须首先考虑电路: I want this logic gate…

【Spring】c命名和p命名空间注入

p命名空间注入 导入p名称空间 xmlns:p"http://www.springframework.org/schema/p"直接输入p就会有相关的属性弹出 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xml…

若依分离版——定时调度

定时调度操作简单&#xff0c;只需在已有的service方法添加component&#xff0c;并在定时任务中添加任务即可。 一、在已有service类上加上Component("TestTask") Component("TestTask") //定时调度任务 public class SysTestServiceImpl implements …

学生用的台灯护眼的哪种比较好?专业的护眼台灯分享

对于学生而言&#xff0c;台灯是必不可少的一盏桌面照明灯具&#xff0c;尤其是在夜间不管是读书、写字还是使用观看电子屏幕&#xff0c;都需要一个光源稳定、柔和的台灯。而这些需要考虑到照度、色温、蓝光、频闪等参数&#xff0c;所以挑选一盏台灯时千万不能马虎&#xff0…

python 视频硬字幕去除 内嵌字幕去除工具 vsr

项目简介 开源地址&#xff1a;https://github.com/YaoFANGUK/video-subtitle-remover Video-subtitle-remover (VSR) 是一款基于AI技术&#xff0c;将视频中的硬字幕去除的软件。 主要实现了以下功能&#xff1a; 无损分辨率将视频中的硬字幕去除&#xff0c;生成去除字幕后…

AI:66-基于机器学习房价预测

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

【代码扫描修复】不安全的反序列化攻击(高危)

目录 一、漏洞描述1.1 摘要&#xff1a;1.2 漏洞解释&#xff1a;1.3 修复建议 二、知识补充2.1 反序列化的历史2.2 什么是序列化、反序列化&#xff1f;补充&#xff1a;Java 对象序列化为二进制 2.3 序列化/反序列化库2.4 反序列化漏洞 三、漏洞复现&#xff1a;攻击链13.1 依…

jacoco和sonar

目录 jacoco 引入依赖 构建配置修改 单元测试 生成报告 查看报告 报告说明 1. Instructions 2. Branches 3. Cyclomatic Complexity 4. Lines 5. Methods 6. Classes sonar7.7 基础环境 需要下载软件 解压文件并配置 运行启动 jacoco 引入依赖 <dep…