基于高度的纹理混合shader

基于高度的纹理混合shader

原文:基于高度的纹理混合shader - 知乎 (zhihu.com) 

最近支持一个使用unity2021的项目,发现urp自带的Terrain/Lit shader已经自带高度混合了,看了下和我当初写的那个基本差不多,感觉稍微要比我的严谨一些,大家感兴趣的可以参考下,代码如下:

#ifdef _TERRAIN_BLEND_HEIGHT
void HeightBasedSplatModify(inout half4 splatControl, in half4 masks[4])
{
    // heights are in mask blue channel, we multiply by the splat Control weights to get combined height
    half4 splatHeight = half4(masks[0].b, masks[1].b, masks[2].b, masks[3].b) * splatControl.rgba;
    half maxHeight = max(splatHeight.r, max(splatHeight.g, max(splatHeight.b, splatHeight.a)));

    // Ensure that the transition height is not zero.
    half transition = max(_HeightTransition, 1e-5);

    // This sets the highest splat to "transition", and everything else to a lower value relative to that, clamping to zero
    // Then we clamp this to zero and normalize everything
    half4 weightedHeights = splatHeight + transition - maxHeight.xxxx;
    weightedHeights = max(0, weightedHeights);

    // We need to add an epsilon here for active layers (hence the blendMask again)
    // so that at least a layer shows up if everything's too low.
    weightedHeights = (weightedHeights + 1e-6) * splatControl;

    // Normalize (and clamp to epsilon to keep from dividing by zero)
    half sumHeight = max(dot(weightedHeights, half4(1, 1, 1, 1)), 1e-6);
    splatControl = weightedHeights / sumHeight.xxxx;
}
#endif


纹理混合(Texture Blend)是非常常见的着色器需求,在很多实时游戏中都需要它来实现复杂的地面纹理,参考了Advanced Terrain Texture Splatting这篇文章写了一个基于高度进行混合的shader,这里分享一下自己的理解,效果如下:

动图封面

(动图请点这里:https://pic4.zhimg.com/v2-5ebb57038515b3659a7b50c7976fe7e3_b.gif)

说到贴图混合,也许你已经听说过Texture Splatting技术了,这个术语是Charles Bloom创造的,他在http://www.cbloom.com/3d/techdocs/splatting.txt里对这个技术进行了阐述;

混合的最简单做法就是,用一个通道对贴图进行插值 ,效果如下

核心代码如下

void surf (Input IN, inout SurfaceOutput o) {
	fixed4 splat_control = tex2D (_Control, IN.uv_Control).rgba;	
	fixed3 lay1 = tex2D (_Splat0, IN.uv_Splat0);
	fixed3 lay2 = tex2D (_Splat1, IN.uv_Splat1);
	fixed3 lay3 = tex2D (_Splat2, IN.uv_Splat2);
	fixed3 lay4 = tex2D (_Splat3, IN.uv_Splat3);
	o.Alpha = 0.0;
	o.Albedo.rgb = (lay1 * splat_control.r + lay2 * splat_control.g + lay3 * splat_control.b+ lay4 * splat_control.a);

} 


这端代码很好理解,通过splat_control这张贴图的四个通道控制_Splat0~_Splat3这四张贴图的混合,如果splat_control对应通道的值为1,那么这个通道对应的贴图就完全显示,为0则完全不显示,通过修改splat_control贴图就可以实现想要的混合效果了;

这种技术在Unity3D的标准地形编辑器中有使用。

如你所见,过渡很平滑,但不太自然。石头看起来就好像被沙子污染了,但在现实世界中这是不可能发生的情况。沙子不会粘着石头,相反地,沙子会落下来,填补到石头之间的缝隙里,而石头表面仍是干净的。

我们希望沙子会更多的在缝隙里面出现,而石头越高的地方沙子应该要越少,那么我们需要知道每一张贴图的深度信息,这里我把贴图对应的高度图保存在每张贴图的alpha通道。通过对比每张贴图的高度差,就可以知道应该显示哪张贴图了,为了简化,我们先计算两张贴图混合的情况,代码如下:

float3 blend(float3 lay1, float3 lay2)
{
	 return lay1.a > lay2.a ? lay1.rgb : lay2.rgb;
}

`得到的是这么样的效果

其中用于混合的两张贴图和他们的透明通道分别是这样的:

我们加上splat_control 贴图的影响试试

float3 blend(float3 lay1, float3 lay2, float4 splat_control)
{
	 return lay1.a * splat_control > lay2.a *splat_control ? lay1.rgb : lay2.rgb;
} 

得到这样的效果:


相比原来的线性混合,现在看起来已经自然很多了,沙子落在石砖路的缝隙里,并慢慢减少;但因为目前只是单纯的判断显示那个贴图,所以边缘看起来太硬了,人工痕迹比较明显,

为了改进效果,我们给边缘增加一点过渡。

float3 blend(float3 lay1, float3 lay2, float4 splat_control)
{
        float b1 = lay1.a * splat_control.r;
        float b2 = lay2.a * splat_control.g;
        float ma = max(b1,b2);
        b1 = max(b1 - (ma – 0.3), 0) * splat_control.r;
        b2 = max(b2 - (ma – 0.3), 0) * splat_control.g;

        return (lay1.rgb * b1 + lay2.rgb * b2)/(b1 + b2);
}

解释一下这段代码,先对比两张贴图的高度,高度差超过0.3的会被舍弃掉,为了防止在边缘以外的地方也被保留下来了,所以后面再乘一次splat_control,最后做一个标准化处理,把他们按比例缩放到0-1这个区间。

于是,我们就得到了下面的这个效果

看起来非常自然,沙子慢慢过渡到石砖路,砖面上的沙子比较少,缝隙里的沙子更多 。

我们把这个算法拓展到4张贴图,并通过一个值来控制混合的权重,完整代码如下:

Shader "mya/terrainTextrueBlend" {
    Properties {
    	_Splat0 ("Layer 1(RGBA)", 2D) = "white" {}
    	_Splat1 ("Layer 2(RGBA)", 2D) = "white" {}
    	_Splat2 ("Layer 3(RGBA)", 2D) = "white" {}
    	_Splat3 ("Layer 4(RGBA)", 2D) = "white" {}
        _Tiling3("_Tiling4 x/y", Vector)=(1,1,0,0)
    	_Control ("Control (RGBA)", 2D) = "white" {}
        _Weight("Blend Weight" , Range(0.001,1)) = 0.2
        
    }
                    
    SubShader {
    	Tags 
    	{
			"RenderType"="Opaque"
			"Queue"="Geometry"
    	}
    CGPROGRAM
    #pragma surface surf BlinnPhong
    #pragma target 3.0

    struct Input 
    {
    	float2 uv_Control : TEXCOORD0;
    	float2 uv_Splat0 : TEXCOORD1;
    	float2 uv_Splat1 : TEXCOORD2;
    	float2 uv_Splat2 : TEXCOORD3;
    	//float2 uv_Splat3 : TEXCOORD4;
    };
     
    sampler2D _Control;
    sampler2D _Splat0,_Splat1,_Splat2,_Splat3;
    float _Weight;
    float4 _Tiling3;
    inline half4 Blend(half high1 ,half high2,half high3,half high4 , half4 control) 
    {
        half4 blend = half4(high1, high2, high3, high4) * control;
        half ma = max(blend.r, max(blend.g, max(blend.b, blend.a)));
        //与权重最大的通道进行对比,高度差在_Weight范围内的将会保留,_Weight不可以为0
        blend = max(blend - ma +_Weight , 0) * control;
        return blend/(blend.r + blend.g + blend.b + blend.a);
    }

    void surf (Input IN, inout SurfaceOutput o) {
    	half4 splat_control = tex2D (_Control, IN.uv_Control).rgba;
    		
    	half4 lay1 = tex2D (_Splat0, IN.uv_Splat0);
    	half4 lay2 = tex2D (_Splat1, IN.uv_Splat1);
    	half4 lay3 = tex2D (_Splat2, IN.uv_Splat2);
    	half4 lay4 = tex2D (_Splat3, IN.uv_Control*_Tiling3.xy);


        //纯色测试代码
        //lay1.rgb = fixed3(1,0,0);
        //lay2.rgb = fixed3(0,1,0);
        //lay3.rgb = fixed3(0,0,1);
        //lay4.rgb = fixed3(0,0,0);

        half4 blend = Blend(lay1.a,lay2.a,lay3.a,lay4.a,splat_control);
    	o.Alpha = 0.0;
        o.Albedo.rgb = blend.r * lay1 + blend.g * lay2 + blend.b * lay3 + blend.a * lay4;//混合


    }
    ENDCG 
    }
    FallBack "Diffuse"
}

最终效果:

左边混合权重为0.2,右边为1,混合权重为1的时候其实就是普通的线性混合了。

加上法线和高光的效果

最后附上文中所用的贴图:链接:http://pan.baidu.com/s/1cKodFg 密码:5kvi

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

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

相关文章

BOOT0 和BOOT1 直接接地还是经过电阻接地

STM32F103 配置BOOT0 和BOOT1 直接接地还是经过电阻接地? 问:我的STM32F103RET6 挂了,原因是BOOT0 和BOOT1 直接接地导致的,很是奇怪,大家有没有遇到啊? 答:STM32 的硬件设计指导也是建议BOOT0、…

Vue2中的render函数

Vue 推荐在绝大多数情况下使用模板来创建你的 HTML。然而在一些场景中,你真的需要 JavaScript 的完全编程的能力。这时你可以用render渲染函数,它比模板更接近编译器 。 在Vue2中,render函数是一个可选的、用于生成虚拟DOM的特殊函数。它是Vu…

Leetcdoe-Day19-代码随想录-栈与队列-1047-150

1047. 删除字符串中的所有相邻重复项 题目链接 题解&#xff1a;简单题&#xff0c;最后需要注意反转字符串即可。 class Solution { public:string removeDuplicates(string s) {stack<char> z;for(int i0;i<s.size();i){if(!z.empty()){int topz.top();if(tops[i]…

VTable导出当前页和导出所有页数据

表格导出的是当前显示的表格&#xff0c;如果是分页表格想导出全部的数据话。有两种方法可以实现 表格先显示的全量数据&#xff0c;导出后再恢复当前页。新建一个隐藏的表格实例显示全量数据导出这个隐藏的表格实例。 下面是全量代码&#xff1a; <template><div&…

使用North自部署图床服务

图床 图床可以把图片转为链接&#xff0c;从而方便我们书写、分享博客&#xff0c;目前图床主要分为以下几类: 利用 Git 仓库存储对象存储&#xff08;OSS、COS、七牛云等&#xff09;免费公共图床&#xff08;SM.MS、聚合图床、ImgTP、Postimage等&#xff09; 但上述图床都…

2024软件设计师笔记之考点版(一考就过):11-25

软件设计师之一考就过:成绩版 考点11:防火墙、入侵检测 真题1:(专家系统、模型检测、简单匹配)属于入侵检测;而漏洞扫描不属于。 真题2:防火墙特性包括(控制进出网络的数据包和数据流向、提供流量信息的日志和审计、隐藏内部IP以及网络结构细节),但不包括提供漏洞扫…

恋爱脑?No,爱情保镖还得靠AI!

本文由 ChatMoney团队出品 你是否曾经想过&#xff0c;为什么我们会在恋爱中变得如此“上头”&#xff0c;仿佛整个世界都围绕着那个TA旋转? 恋爱脑&#xff0c;通常是指一个人在恋爱中过度投入、过度依赖对方&#xff0c;甚至无法自拔的心理状态。 你会发现自己时时刻刻都在…

屏幕翻译下载哪个软件好?好用的屏幕翻译推荐

想象一下&#xff0c;当我们在阅读外文文档或是观看外语电影时&#xff0c;如果能有一款翻译工具同步提供译文&#xff0c;那将是多么令人愉悦的体验&#xff01; 如果这种翻译服务能够在不影响其他应用的情况下进行&#xff0c;那就是double快乐了。 其实&#xff0c;现在要…

分享一套基于SSM的美食推荐管理系统(源码+文档+部署)

大家好&#xff0c;今天给大家分享一套基于SSM的美食推荐管理系统 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringSpringMvcMyBatis 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 博主介绍&#xff1a; 一名Java全栈工程师&#xff0c;专注于Jav…

各大广告商竞相厮杀下,诞生了一个偏门的副业方式

前段时间&#xff0c;想买摩托车&#xff0c;但是媳妇不让买&#xff0c;所以我打算偷偷买&#xff0c;然后萌生了去摆摊赚钱的想法&#xff0c;但是还没有实施就在网上接触到了“某赚”APP&#xff0c;于是一发不可收拾&#xff0c;用我的话来说&#xff0c;我做的不是副业&am…

腾讯云 轻量应用服务器 部署私有化大模型

1. 进入控制台后,找到我们购买的服务器,然后点击登录 服务器 - 轻量云 - 控制台 (tencent.com) 2. 安装系统 面板输入 curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sudo bash quick_start.sh 命令解释: 从F…

解密城市酷选为何异军突起!打造消费新潮流的排队免单模式

一、城市酷选平台简介 在数字化浪潮席卷全球的今天&#xff0c;城市酷选作为一个前沿的消费平台&#xff0c;凭借其独特的排队免单模式&#xff0c;成功吸引了众多消费者和商家的目光。该平台不仅整合了线上线下的资源&#xff0c;更通过数字化手段&#xff0c;为消费者提供了…

一文读懂RSTP流媒体传输原理

什么是流&#xff1f; 流&#xff08;Streaming&#xff09;&#xff1a;近年在Internet上出现的新概念&#xff0c;定义非常广泛&#xff0c;主要是指通过网络传输多媒体数据的技术总称。 流式传输分为两种&#xff1a;顺序流式传输 (Progressive Streaming)和实时流式传输 (R…

408数据结构-图的应用1-最小生成树 自学知识点整理

前置知识&#xff1a;图的遍历 图的应用是408初试历年考查的重点。不过一般而言&#xff0c;这部分内容直接以算法设计题形式考查的可能性极小&#xff0c;更多的是结合图的实例来考查算法的具体操作过程&#xff0c;要求掌握的是手推模拟给定图的各个算法执行过程。此外&#…

华为Pura70支持5G功能吗?看完你就清楚了

随着 5G 技术的普及&#xff0c;现在智能手机市场中的大部分新品都已经支持 5G 网络。相较于 4G&#xff0c;5G 不仅带来了更快的网速&#xff0c;更为用户带来了全新的使用体验。 然而&#xff0c;华为作为智能手机市场的佼佼者&#xff0c;其产品线中的部分手机在配置上却有…

程序运行包与源码的区别

在软件开发和信息技术领域&#xff0c;程序运行包&#xff08;Executable Package&#xff09;与源码&#xff08;Source Code&#xff09;是两个至关重要的概念。它们各自在软件开发、部署和维护过程中扮演着不同的角色&#xff0c;且有着显著的区别。本文旨在深入探讨程序运行…

数字图像分析(第二部分)

文章目录 第8章 图像分割图像分割定义阈值分割依赖像素的阈值选取Otsus方法依赖区域的阈值选取依赖坐标的阈值选取变化阈值法区域生长法分裂合并方法分水岭算法聚类分割算法K-meansAP算法Graph cut第9章 图像特征表达基于全局特征的图像表达直方图GIST基于局部特征的图像表达简…

【ppt技巧】有哪些方法可以实现?PPT转换为图片!

将ppt文件转换为图片都有哪些方法可以实现&#xff1f;其实很简单&#xff0c;一起来看一下如何操作吧&#xff01; 方法一&#xff1a; 使用格式转换器&#xff0c;有些文件格式转换器&#xff0c;支持ppt转换为图片。 方法二&#xff1a; 不需要转换器&#xff0c;直接在…

最长上升子序列模型——AcWing 272. 最长公共上升子序列

最长上升子序列模型 定义 给定一个序列&#xff0c;如整数序列或字符序列&#xff0c;最长上升子序列是指其中最长的子序列&#xff0c;满足子序列中的元素依次递增。最长上升子序列模型是一种在给定序列中寻找最长上升子序列的问题模型。 运用情况 该模型常用于解决与序列…

44岁过气港姐晚晚熬通宵开直播,情路坎坷生两胎老公身份成迷

曾经的「9料」港姐冠军杨思琦近年将工作重心转向内地&#xff0c;狠心抛下一儿一女在香港&#xff0c;只身一人定居广州靠当主播维持生计。 相信有不少网友都留意到&#xff0c;杨思琦几乎晚晚都通宵直播&#xff0c;睡觉前看她在卖力劲歌热舞与其他直播主PK赚钱&#xff0c;一…