基于高度的纹理混合shader

news2024/10/7 5:52:14

基于高度的纹理混合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

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

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

相关文章

刷代码随想录有感(118):动态规划——打家劫舍II

题干&#xff1a; 代码&#xff1a; class Solution { public:int solve(vector<int>& nums, int start, int end){if(start end)return nums[start];if(nums.size() 0)return 0;if(nums.size() 1)return nums[0];vector<int>dp(nums.size(), 0);dp[start]…

vscode连接SSH

1、安装Remote-SSH插件 2、点击左下角&#xff0c;选择SSH 3、点击连接到主机后&#xff0c;添加新的SSH主机&#xff0c;示例ssh 用户ip 4、点击服务器&#xff0c;输入密码登录服务器 5、可在远程资源管理器选项卡中查看 6、可以在ssh设置中打开ssh配置文件 config中的文件…

Bad attr `class` with message: unexpected ```

小程序不支持模板字符串吗&#xff1f; | 微信开放社区 小程序不支持模版字符串。。。

一个项目学习Vue3---创建一个 Vue 应用

步骤1&#xff1a;安装符合要求的node版本 目前官网要求使用的node.js版本为18.3及其以上 所以我们要安装node.js 18.3及其以上版本 NVM安装教程&#xff1a;一个项目学习Vue3---NVM和NPM安装-CSDN博客 若不想安装NVM&#xff0c;可以直接下载适合自己的node版本Node.js — …

3D资产爆发,轻量化需求再度冲高,见证下一代3D崛起!

数字经济不断发展&#xff0c;3D资产和实体经济迎来深度融合的窗口期&#xff0c;3D资产应用外延催生大量新场景、新业态&#xff0c;一个3D资产构建的数字世界正出现在我们眼前。 数字经济不断发展&#xff0c;3D资产和实体经济迎来深度融合的窗口期&#xff0c;3D资产应用外…

django restframework 多对多模型 —— python

模型 图书和作者是多对多关系 class Book(models.Model):book_namemodels.CharField(max_length40)pricemodels.DecimalField(max_digits4,decimal_places2)publishmodels.ForeignKey(to"Publish",on_deletemodels.CASCADE,related_name"publish")authorm…

odoo 去掉在线聊天的删除和编辑内容

描述 odoo在线聊天功能中,在原有的聊天记录中是可以进行编辑和删除的 这使得产生很多不可控原因,乱改,乱删等 所以要进行屏蔽此功能 优化前 优化后 升级 mail 模块刷新即可。 <Dropdown t-if="messageActions.actions.length gt quickActionCount"onStateCha…

英伟达连跌3日,传字节与博通合作研发AI芯片,讯飞星火V4.0周四发布

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 更多资源欢迎关注 每日行业新闻 1、英伟达单日下跌6.7%&#xff0c;拖累标普500指数和纳指走低 昨夜&#xff0c;英伟达暴跌6.7%&#xff0c;创两个月以来最大单日跌幅&#xff0c;为连续三个交易日下跌&#xff0c;市…

Spring的jar包下载(最新版6.0版本)

1.在Spring官网的projects下面点击spring-framework 2.进入github官网 3.进入github后往下滑&#xff0c;点击Spring Framework Artifacts 4.往下滑找到 点击 5.在左边先点击Artifacts&#xff0c;在右边找到 libs-snapshot&#xff0c;展开libs-snapshot&#xff08;之前其他…

1变3裂变营销,七星创客模式,推三返一模式解析

推三返一模式的出现让我们看到&#xff0c;在商业竞争中&#xff0c;创新的商业模式与良好的产品服务相结合&#xff0c;才能真正赢得市场和消费者的心。 推三返一又称为“三三循环”模式&#xff0c;是一种简单粗暴的营销模式&#xff0c;消费者通过直接推荐三个新用户到平台消…

AI软件革新文本操作体验:从自动粘贴文本到一键提取保存手机号码

在当今数字化时代&#xff0c;AI技术的快速发展为各行各业带来了革命性的变革。特别是在文本处理领域&#xff0c;AI软件通过其强大的自动粘贴文本功能以及一键提取并保存手机号码的便捷操作&#xff0c;极大地提高了工作效率&#xff0c;为用户带来了全新的体验。本文将深入探…

【探索Linux】P.35(传输层 —— UDP协议)

阅读导航 引言一、UDP协议端格式二、UDP的特点三、UDP的缓冲区四、基于UDP的应用层协议温馨提示 引言 在上一篇文章中&#xff0c;我们深入探讨了网络协议的应用层&#xff0c;揭示了各种协议如何协同工作以确保信息在网络中正确、高效地传递。从HTTP到FTP&#xff0c;每一层协…

在 PMP 考试中,项目管理经验不足怎么办?

在项目管理的专业成长之路上&#xff0c;PMP认证如同一块里程碑&#xff0c;标志着从业者的专业水平达到了国际公认的标准。然而&#xff0c;对于那些项目管理经验尚浅的考生来说&#xff0c;这座里程碑似乎显得有些遥不可及。那么&#xff0c;在PMP考试准备中&#xff0c;项目…

树形结构的勾选、取消勾选、删除、清空已选、回显、禁用

树形结构的勾选、取消勾选、删除、清空已选、回显、禁用 基本页面&#xff1a; 分为上传文件和编辑的页面 代码实现要点&#xff1a; 上传文件页面&#xff1a; 点开选择范围弹窗&#xff0c;三个radio单选框都为可选状态&#xff0c;默认显示的是第一个单选框&#xff08;按…

邮件推送怎么设计吸引邮件内容?有何策略?

邮件推送在营销策略中的作用&#xff1f;如何评估邮件推送效果&#xff1f; 设计吸引人的邮件内容不仅可以提高打开率&#xff0c;还能促进用户的积极参与和转换。AokSend将探讨如何设计吸引人的邮件推送内容&#xff0c;以确保您的邮件不被忽略&#xff0c;并能够有效传达您的…

MES的实施难点与解决方案

随着智能制造的发展&#xff0c;制造执行系统&#xff08;MES&#xff09;已成为诸多制造企业争相引入的宝贵资产&#xff0c;它能有效衔接上层ERP系统与底层自动化设备&#xff0c;实现生产过程的实时监控与优化。然而&#xff0c;MES系统的成功实施并非易事&#xff0c;不当的…

Java毕设服务工作室

Java毕设服务工作室&#xff1a;专注提供高质量Java代码解决方案 在Java编程领域&#xff0c;毕业设计&#xff08;毕设&#xff09;项目往往需要大量的代码编写和调试。为了让同学们能够更专注于项目的核心逻辑和技术实现&#xff0c;Java毕设服务工作室应运而生&#xff0c;…

OpenAI停止对中国服务:背后原因与影响深度解析

引言 近日&#xff0c;OpenAI宣布停止对中国地区的服务&#xff0c;引发了广泛关注与讨论。许多开发者和企业用户收到了OpenAI的官方邮件&#xff0c;通知他们从7月9日开始&#xff0c;OpenAI将采取额外措施阻止来自不支持地区的API流量。这一决定对中国地区的开发者和企业意味…

GO sync包——读写锁

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

OpenCV-Python——环境配置及图像基本操作

一、环境配置 1、在虚拟环境中安装以下库&#xff1a; opencv-python3.4.1.15 opencv-contrib-python3.4.1.15 建议安装3.4.1的版本&#xff0c;3.4.2之后的版本做了专利保护&#xff0c;有的功能不能实现。 虚拟环境的安装以及安装库的问题见PyQt5专栏中的第一节。 2、安…