# 前言
Triplanar mapping 是一种三维贴图方式,分别计算 Z、Y、Z 三个方向的贴图贡献,然后混合。因此它相比一般的二维 UV 贴图方式,最大的好处大概就是:可以避免贴图拉伸。
而我也是基于这个好处,才想研究下它的。
对这个技术,在 Unity3D 官方文档上,有过一段提及,链接地址:https://docs.unity3d.com/Manual/SL-VertexFragmentShaderExamples.html
另外还有一个参考地址,比官方的说明可详细多了:
http://www.martinpalko.com/triplanar-mapping
当然示例主要是对模型,而对于地形来说,就不可能这样简单实现了,我也在网络上找过,相关中文资料基本上没有...... 英文资料... 就不多说了。而对于 Unity3D Terrain 相关的 Triplanar 资料,很抱歉,似乎根本就没有(当然,插件好像还是存在的)。
于是把官方的 Buildin Shader Download 下来,决定自己结合对于单独 Object 的资料进行处理下,建立一个适用于地形的 Triplanar Shader.
# 原理
更深层次的原理我也就没研究了,简单说下实现原理吧。
对于普通 Object 的实现,主要大概分为五步:
1. 取世界坐标 xy、xz、yz 作为三个轴向的 UV
2. 对 UV 通过 UVScale,或者说 TextureScale 进行缩放
3. 使用三个 UV 分别对贴图进行采样,计算出三个方向单独贴图采样
4. 计算法线方向,并对法线进行处理 (法线后面扮演了一个权重调整的角色,很重要)
5. 使用三个 UV 方向计算出来贴分别相乘法线的 X、Y、Z 并相加,取得最后的采样值,作为最终颜色
如果有法线的话,需要照此步骤依次对法线做同样的处理。
# 实现
OK, 现在轮到实现上来了。
首先,可以打开下载下来的 Buildin 地形 shader 看一下,主要大约就是 Standard-FirstPass.shader 了。
考虑到查看方便性问题.... 我决定还是贴一下代码:
Shader "Nature/Terrain/Standard" {
Properties {
// set by terrain engine
[HideInInspector] _Control ("Control (RGBA)", 2D) = "red" {}
[HideInInspector] _Splat3 ("Layer 3 (A)", 2D) = "white" {}
[HideInInspector] _Splat2 ("Layer 2 (B)", 2D) = "white" {}
[HideInInspector] _Splat1 ("Layer 1 (G)", 2D) = "white" {}
[HideInInspector] _Splat0 ("Layer 0 (R)", 2D) = "white" {}
[HideInInspector] _Normal3 ("Normal 3 (A)", 2D) = "bump" {}
[HideInInspector] _Normal2 ("Normal 2 (B)", 2D) = "bump" {}
[HideInInspector] _Normal1 ("Normal 1 (G)", 2D) = "bump" {}
[HideInInspector] _Normal0 ("Normal 0 (R)", 2D) = "bump" {}
[HideInInspector] [Gamma] _Metallic0 ("Metallic 0", Range(0.0, 1.0)) = 0.0
[HideInInspector] [Gamma] _Metallic1 ("Metallic 1", Range(0.0, 1.0)) = 0.0
[HideInInspector] [Gamma] _Metallic2 ("Metallic 2", Range(0.0, 1.0)) = 0.0
[HideInInspector] [Gamma] _Metallic3 ("Metallic 3", Range(0.0, 1.0)) = 0.0
[HideInInspector] _Smoothness0 ("Smoothness 0", Range(0.0, 1.0)) = 1.0
[HideInInspector] _Smoothness1 ("Smoothness 1", Range(0.0, 1.0)) = 1.0
[HideInInspector] _Smoothness2 ("Smoothness 2", Range(0.0, 1.0)) = 1.0
[HideInInspector] _Smoothness3 ("Smoothness 3", Range(0.0, 1.0)) = 1.0
// used in fallback on old cards & base map
[HideInInspector] _MainTex ("BaseMap (RGB)", 2D) = "white" {}
[HideInInspector] _Color ("Main Color", Color) = (1,1,1,1)
}
SubShader {
Tags {
"Queue" = "Geometry-100"
"RenderType" = "Opaque"
}
CGPROGRAM
#pragma surface surf Standard vertex:SplatmapVert finalcolor:SplatmapFinalColor finalgbuffer:SplatmapFinalGBuffer fullforwardshadows
#pragma multi_compile_fog
#pragma target 3.0
// needs more than 8 texcoords
#pragma exclude_renderers gles
#include "UnityPBSLighting.cginc"
#pragma multi_compile __ _TERRAIN_NORMAL_MAP
#define TERRAIN_STANDARD_SHADER
#define TERRAIN_SURFACE_OUTPUT SurfaceOutputStandard
#include "TerrainSplatmapCommon.cginc"
half _Metallic0;
half _Metallic1;
half _Metallic2;
half _Metallic3;
half _Smoothness0;
half _Smoothness1;
half _Smoothness2;
half _Smoothness3;
void surf (Input IN, inout SurfaceOutputStandard o) {
half4 splat_control;
half weight;
fixed4 mixedDiffuse;
half4 defaultSmoothness = half4(_Smoothness0, _Smoothness1, _Smoothness2, _Smoothness3);
SplatmapMix(IN, defaultSmoothness, splat_control, weight, mixedDiffuse, o.Normal);
o.Albedo = mixedDiffuse.rgb;
o.Alpha = weight;
o.Smoothness = mixedDiffuse.a;
o.Metallic = dot(splat_control, half4(_Metallic0, _Metallic1, _Metallic2, _Metallic3));
}
ENDCG
}
Dependency "AddPassShader" = "Hidden/TerrainEngine/Splatmap/Standard-AddPass"
Dependency "BaseMapShader" = "Hidden/TerrainEngine/Splatmap/Standard-Base"
Fallback "Nature/Terrain/Diffuse"
}
初一看,大概会觉得地形 shader “也不过如此”,参数很多,实现很少 —— 那就错了!
因为它引用到了 Unity 内置头文件 “TerrainSplatmapCommon.cginc”,好些个实现方法,都在那里面的。而影响地形最主要的一个,就是 “SplatmapMix” 方法,该方法,可以打开头文件 “TerrainSplatmapCommon.cginc” 进行查看。
而且,因为本身计算贴图就是这儿,所以我们需要修改的,也是这一块儿。
那么,稍微查看一下:
#ifdef TERRAIN_STANDARD_SHADER
void SplatmapMix(Input IN, half4 defaultAlpha, out half4 splat_control, out half weight, out fixed4 mixedDiffuse, inout fixed3 mixedNormal)
#else
void SplatmapMix(Input IN, out half4 splat_control, out half weight, out fixed4 mixedDiffuse, inout fixed3 mixedNormal)
#endif
{
splat_control = tex2D(_Control, IN.tc_Control);
weight = dot(splat_control, half4(1,1,1,1));
#if !defined(SHADER_API_MOBILE) && defined(TERRAIN_SPLAT_ADDPASS)
clip(weight == 0.0f ? -1 : 1);
#endif
// Normalize weights before lighting and restore weights in final modifier functions so that the overal
// lighting result can be correctly weighted.
splat_control /= (weight + 1e-3f);
mixedDiffuse = 0.0f;
#ifdef TERRAIN_STANDARD_SHADER
mixedDiffuse += splat_control.r * tex2D(_Splat0, IN.uv_Splat0) * half4(1.0, 1.0, 1.0, defaultAlpha.r);
mixedDiffuse += splat_control.g * tex2D(_Splat1, IN.uv_Splat1) * half4(1.0, 1.0, 1.0, defaultAlpha.g);
mixedDiffuse += splat_control.b * tex2D(_Splat2, IN.uv_Splat2) * half4(1.0, 1.0, 1.0, defaultAlpha.b);
mixedDiffuse += splat_control.a * tex2D(_Splat3, IN.uv_Splat3) * half4(1.0, 1.0, 1.0, defaultAlpha.a);
#else
mixedDiffuse += splat_control.r * tex2D(_Splat0, IN.uv_Splat0);
mixedDiffuse += splat_control.g * tex2D(_Splat1, IN.uv_Splat1);
mixedDiffuse += splat_control.b * tex2D(_Splat2, IN.uv_Splat2);
mixedDiffuse += splat_control.a * tex2D(_Splat3, IN.uv_Splat3);
#endif
#ifdef _TERRAIN_NORMAL_MAP
fixed4 nrm = 0.0f;
nrm += splat_control.r * tex2D(_Normal0, IN.uv_Splat0);
nrm += splat_control.g * tex2D(_Normal1, IN.uv_Splat1);
nrm += splat_control.b * tex2D(_Normal2, IN.uv_Splat2);
nrm += splat_control.a * tex2D(_Normal3, IN.uv_Splat3);
mixedNormal = UnpackNormal(nrm);
#endif
}
唔... 看起来信息量略大啊。
实际上,功能也就是计算四个通道的 Diffuse 贴图和法线贴图而已,只是因为有四个通道分别对应控制贴图的 R、G、B、A,所以看起来才一大团,理解就好。
而且,后面加入了对 triplanar 的计算之后....... 还得翻一番。
然后是顶点计算:
void SplatmapVert(inout appdata_full v, out Input data)
{
UNITY_INITIALIZE_OUTPUT(Input, data);
data.tc_Control = TRANSFORM_TEX(v.texcoord, _Control); // Need to manually transform uv here, as we choose not to use 'uv' prefix for this texcoord.
float4 pos = UnityObjectToClipPos(v.vertex);
UNITY_TRANSFER_FOG(data, pos);
#ifdef _TERRAIN_NORMAL_MAP
v.tangent.xyz = cross(v.normal, float3(0,0,1));
v.tangent.w = -1;
#endif
}
内置的地形 Shader 暂时就看到这里,那么在修改之前,首先,我们需要确定一件事儿:相对于默认地形 Shader,如果想把它修改成 triplanar, 差了哪些参数?
答:worldPos (世界坐标)、worldNormal (世界空间法线)
这两个参数都是从 Vertex 片段程序中计算,而看看上面那段 SplatmapVert 程序,明显没有计算 —— 因为一般使用也用不上。
既然如此,我们就得自食其力了。
将 CGPROGRAM 下一行的 #pragma 修改如下:
#pragma surface surf Standard vertex:vert finalcolor:SplatmapFinalColor finalprepass:SplatmapFinalPrepass finalgbuffer:SplatmapFinalGBuffer fullforwardshadows
然后新建一个我们自己的 Input Struct,用于将参数从顶点程序传入片段..... 噢,对于 Unity3D 的 Surface Shader,应该叫做 “surf” 处理程序 (嘛... 实际上就是被 Unity 自动嵌入片段程序中的一个方法而已)。
struct Input
{
float2 tc_Control : TEXCOORD4;
float3 wNormal;
float3 worldPos;
UNITY_FOG_COORDS(5)
};
因为只需要这么几个参数,使用 Triplanar 计算的地形是不需要贴图 UV 的,所以就这样了。
OK!然后是我们自己的顶点计算程序:
void vert(inout appdata_full v, out Input o)
{
UNITY_INITIALIZE_OUTPUT(Input, o);
o.tc_Control = TRANSFORM_TEX(v.texcoord, _Control);
float4 pos = mul(UNITY_MATRIX_MVP, v.vertex);
UNITY_TRANSFER_FOG(o, pos);
o.wNormal =normalize(mul(_Object2World, fixed4(v.normal, 0)).xyz);
#ifdef _TERRAIN_NORMAL_MAP
v.tangent.xyz = cross(v.normal, float3(0, 0, 1));
v.tangent.w = -1;
#endif
}
在这儿,一定、必定以及肯定:要注意一件事儿:UNITY_INITIALIZE_OUTPUT (Input, o)
这个 Unity 内置的宏将会初始化 Input 结构体中的变量,但是它是根据 “名字” 来的!所以,千万得跟内置的名字一样才行!比如说 “worldPos”,就一定得叫这个名字才行 —— 刚开始我就取名叫 “wPos”,结果坑的我一脸。最后浪费了好大一段时间,调试最终结果不正确的问题!
但是这个宏的实际定义方法,我却没在 Unity 的头文件中找到,若有人翻到了,希望提醒一声!
接下来,我们就可以在 Surf 方法中,具体处理了。
然后,稍微把 TerrainSplatmapCommon.cginc 中的 SplatmapMix 移过来一点:
splat_control = tex2D(_Control, IN.tc_Control);
weight = dot(splat_control, half4(1, 1, 1, 1));
splat_control /= (weight + 1e-3f);
按照上述几个步骤进进行处理:
//triplanar---------------<<<<<<<<<<<<
//计算权重
float3 N =normalize( IN.wNormal);
half3 blendWeights = pow(abs(IN.wNormal), _TriplanarBlendSharpness);
blendWeights /= dot(blendWeights, 1.0);
half2 xUV = IN.worldPos.zy;// / _TextureScale;
half2 yUV = IN.worldPos.xz;// / _TextureScale;
half2 zUV = IN.worldPos.xy;// / _TextureScale;
//通常 Triplanar实现,只是多了三张贴图处理
fixed4 tex0X = tex2D(_Splat0, (xUV*_Splat0_ST.xy + _Splat0_ST.zw)/ _TextureScale);
fixed4 tex0Y = tex2D(_Splat0, (yUV*_Splat0_ST.xy + _Splat0_ST.zw) / _TextureScale);
fixed4 tex0Z = tex2D(_Splat0, (zUV*_Splat0_ST.xy + _Splat0_ST.zw) / _TextureScale);
fixed4 tex1X = tex2D(_Splat1, (xUV*_Splat1_ST.xy + _Splat1_ST.zw) / _TextureScale);
fixed4 tex1Y = tex2D(_Splat1, (yUV*_Splat1_ST.xy + _Splat1_ST.zw) / _TextureScale);
fixed4 tex1Z = tex2D(_Splat1, (zUV*_Splat1_ST.xy + _Splat1_ST.zw) / _TextureScale);
fixed4 tex2X = tex2D(_Splat2, (xUV*_Splat2_ST.xy + _Splat2_ST.zw)/ _TextureScale);
fixed4 tex2Y = tex2D(_Splat2, (yUV*_Splat2_ST.xy + _Splat2_ST.zw)/ _TextureScale);
fixed4 tex2Z = tex2D(_Splat2, (zUV*_Splat2_ST.xy + _Splat2_ST.zw)/ _TextureScale);
fixed4 tex3X = tex2D(_Splat3, (xUV*_Splat3_ST.xy + _Splat3_ST.zw)/ _TextureScale);
fixed4 tex3Y = tex2D(_Splat3, (yUV*_Splat3_ST.xy + _Splat3_ST.zw)/ _TextureScale);
fixed4 tex3Z = tex2D(_Splat3, (zUV*_Splat3_ST.xy + _Splat3_ST.zw)/ _TextureScale);
fixed4 tex0 = tex0X*blendWeights.x + tex0Y * blendWeights.y + tex0Z * blendWeights.z;
fixed4 tex1 = tex1X*blendWeights.x + tex1Y * blendWeights.y + tex1Z * blendWeights.z;
fixed4 tex2 = tex2X*blendWeights.x + tex2Y * blendWeights.y + tex2Z * blendWeights.z;
fixed4 tex3 = tex3X*blendWeights.x + tex3Y * blendWeights.y + tex3Z * blendWeights.z;
//融合权重,添加高光
tex0 *= splat_control.r *half4(1.0, 1.0, 1.0, defaultSmoothness.r);
tex1 *= splat_control.g *half4(1.0, 1.0, 1.0, defaultSmoothness.g);
tex2 *= splat_control.b *half4(1.0, 1.0, 1.0, defaultSmoothness.b);
tex3 *= splat_control.a *half4(1.0, 1.0, 1.0, defaultSmoothness.a);
mixedDiffuse = tex0 + tex1 + tex2 + tex3;
Diffuse 颜色就是这样处理了,法线贴图与此一致,就不在这儿贴出来浪费空间了。
如果注意到对 Splat_ST 的计算,这个跟 TRANSFORM_TEX 宏的功能差不多,如果有兴趣,也可以在 UnityCG.cginc 头文件找到,主要就是重新计算被修改了缩放大小的贴图:
// Transforms 2D UV by scale/bias property
#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
# 效果
从图中可以非常明显地看出,Triplanar 技术对于贴图拉伸的补正作用。自带的地形 Shader 已经把贴图拉成线了,Triplanar 基本完全正常。
# 总结
OK,事情基本上到此为止了。
Triplanar 相对普通贴图方式,效果很大,但是还有一点需要注意:性能。
因为 Triplanar 会分别对每张贴图都采样三次,再加上其它计算的消耗,也就是是说它功能虽好,但是性能注定会比普通贴图方式更耗 —— 至少也是 2 倍吧。
所以,任何事情,都不是无偿的啊。
而我之所以研究这个,还是跟自己做的 Demo 有关,普通游戏也就罢了,如果处理好斜坡贴都和陡度的话,还是可以在一定程度上掩盖贴图拉伸问题的。不过我这次做的 Demo,因为想加入 “挖地形” 的功能,比如说挖矿什么真的可以挖个坑之类的。那这个问题就比较突出了 —— 如果一锄子下去,地形都 “花了”,那就.......... 所以,才需要这么个东西。
—— 嘛,我承认最近快要离开的项目也想用这技术,最后貌似是采用的一个插件了。
就是这样。
# 源码
源码.... 本来不想贴出来的,毕竟这么大一坨。但是断断续续会让看的人很困扰的吧?
所以,源码如下:
版本:2016.11.21
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
//2016.11.18 by cwhisme
//Note:在基于Unity Buildin Terrain Shader的基础上修改
//Unity 内置的地形shader可以在官方网站上进行下载
//加入了Triplanar计算,避免贴图拉伸问题
Shader "CWHISME/TriplannarTerrain"
{
Properties{
// set by terrain engine
[HideInInspector] _Control("Control (RGBA)", 2D) = "red" {}
[HideInInspector] _Splat3("Layer 3 (A)", 2D) = "white" {}
[HideInInspector] _Splat2("Layer 2 (B)", 2D) = "white" {}
[HideInInspector] _Splat1("Layer 1 (G)", 2D) = "white" {}
[HideInInspector] _Splat0("Layer 0 (R)", 2D) = "white" {}
[HideInInspector] _Normal3("Normal 3 (A)", 2D) = "bump" {}
[HideInInspector] _Normal2("Normal 2 (B)", 2D) = "bump" {}
[HideInInspector] _Normal1("Normal 1 (G)", 2D) = "bump" {}
[HideInInspector] _Normal0("Normal 0 (R)", 2D) = "bump" {}
[HideInInspector][Gamma] _Metallic0("Metallic 0", Range(0.0, 1.0)) = 0.0
[HideInInspector][Gamma] _Metallic1("Metallic 1", Range(0.0, 1.0)) = 0.0
[HideInInspector][Gamma] _Metallic2("Metallic 2", Range(0.0, 1.0)) = 0.0
[HideInInspector][Gamma] _Metallic3("Metallic 3", Range(0.0, 1.0)) = 0.0
[HideInInspector] _Smoothness0("Smoothness 0", Range(0.0, 1.0)) = 1.0
[HideInInspector] _Smoothness1("Smoothness 1", Range(0.0, 1.0)) = 1.0
[HideInInspector] _Smoothness2("Smoothness 2", Range(0.0, 1.0)) = 1.0
[HideInInspector] _Smoothness3("Smoothness 3", Range(0.0, 1.0)) = 1.0
// used in fallback on old cards & base map
[HideInInspector] _MainTex("BaseMap (RGB)", 2D) = "white" {}
[HideInInspector] _Color("Main Color", Color) = (1,1,1,1)
_TextureScale("Texture Scale",float) = 100
_TriplanarBlendSharpness("Triplanar Blend Sharpness",float) = 1
}
SubShader{
Tags{
"Queue" = "Geometry-100"
"RenderType" = "Opaque"
}
CGPROGRAM
#pragma surface surf Standard vertex:vert finalcolor:SplatmapFinalColor finalprepass:SplatmapFinalPrepass finalgbuffer:SplatmapFinalGBuffer fullforwardshadows
#pragma multi_compile_fog
#pragma target 3.0
// needs more than 8 texcoords
#pragma exclude_renderers gles
#include "UnityPBSLighting.cginc"
#pragma multi_compile __ _TERRAIN_NORMAL_MAP
//#define TERRAIN_STANDARD_SHADER
#define TERRAIN_SURFACE_OUTPUT SurfaceOutputStandard
//#include "TerrainSplatmapCommon.cginc"
sampler2D _Control;
float4 _Control_ST;
sampler2D _Splat0, _Splat1, _Splat2, _Splat3;
half4 _Splat0_ST, _Splat1_ST, _Splat2_ST, _Splat3_ST;
#ifdef _TERRAIN_NORMAL_MAP
sampler2D _Normal0, _Normal1, _Normal2, _Normal3;
#endif
half _Metallic0;
half _Metallic1;
half _Metallic2;
half _Metallic3;
half _Smoothness0;
half _Smoothness1;
half _Smoothness2;
half _Smoothness3;
float _TriplanarBlendSharpness;
float _TextureScale;
struct Input
{
float2 tc_Control : TEXCOORD4;
float3 wNormal;
float3 worldPos;
UNITY_FOG_COORDS(5)
};
void vert(inout appdata_full v, out Input o)
{
UNITY_INITIALIZE_OUTPUT(Input, o);
o.tc_Control = TRANSFORM_TEX(v.texcoord, _Control);
float4 pos = mul(UNITY_MATRIX_MVP, v.vertex);
UNITY_TRANSFER_FOG(o, pos);
o.wNormal =normalize(mul(unity_ObjectToWorld, fixed4(v.normal, 0)).xyz);
#ifdef _TERRAIN_NORMAL_MAP
v.tangent.xyz = cross(v.normal, float3(0, 0, 1));
v.tangent.w = -1;
#endif
}
void surf(Input IN, inout SurfaceOutputStandard o) {
half4 splat_control;
half weight;
fixed4 mixedDiffuse=0;
half4 defaultSmoothness = half4(_Smoothness0, _Smoothness1, _Smoothness2, _Smoothness3);
//SplatmapMix(IN, defaultSmoothness, splat_control, weight, mixedDiffuse, o.Normal);
//Custom begin=================================================<<<<<<<<<<<<<<<<<<<<<<<<<<<
splat_control = tex2D(_Control, IN.tc_Control);
weight = dot(splat_control, half4(1, 1, 1, 1));
splat_control /= (weight + 1e-3f);
//triplanar---------------<<<<<<<<<<<<
//计算权重
float3 N =normalize( IN.wNormal);
half3 blendWeights = pow(abs(IN.wNormal), _TriplanarBlendSharpness);
blendWeights /= dot(blendWeights, 1.0);
half2 xUV = IN.worldPos.zy;// / _TextureScale;
half2 yUV = IN.worldPos.xz;// / _TextureScale;
half2 zUV = IN.worldPos.xy;// / _TextureScale;
//通常 Triplanar实现,只是多了三张贴图处理
fixed4 tex0X = tex2D(_Splat0, (xUV*_Splat0_ST.xy + _Splat0_ST.zw)/ _TextureScale);
fixed4 tex0Y = tex2D(_Splat0, (yUV*_Splat0_ST.xy + _Splat0_ST.zw) / _TextureScale);
fixed4 tex0Z = tex2D(_Splat0, (zUV*_Splat0_ST.xy + _Splat0_ST.zw) / _TextureScale);
fixed4 tex1X = tex2D(_Splat1, (xUV*_Splat1_ST.xy + _Splat1_ST.zw) / _TextureScale);
fixed4 tex1Y = tex2D(_Splat1, (yUV*_Splat1_ST.xy + _Splat1_ST.zw) / _TextureScale);
fixed4 tex1Z = tex2D(_Splat1, (zUV*_Splat1_ST.xy + _Splat1_ST.zw) / _TextureScale);
fixed4 tex2X = tex2D(_Splat2, (xUV*_Splat2_ST.xy + _Splat2_ST.zw)/ _TextureScale);
fixed4 tex2Y = tex2D(_Splat2, (yUV*_Splat2_ST.xy + _Splat2_ST.zw)/ _TextureScale);
fixed4 tex2Z = tex2D(_Splat2, (zUV*_Splat2_ST.xy + _Splat2_ST.zw)/ _TextureScale);
fixed4 tex3X = tex2D(_Splat3, (xUV*_Splat3_ST.xy + _Splat3_ST.zw)/ _TextureScale);
fixed4 tex3Y = tex2D(_Splat3, (yUV*_Splat3_ST.xy + _Splat3_ST.zw)/ _TextureScale);
fixed4 tex3Z = tex2D(_Splat3, (zUV*_Splat3_ST.xy + _Splat3_ST.zw)/ _TextureScale);
fixed4 tex0 = tex0X*blendWeights.x + tex0Y * blendWeights.y + tex0Z * blendWeights.z;
fixed4 tex1 = tex1X*blendWeights.x + tex1Y * blendWeights.y + tex1Z * blendWeights.z;
fixed4 tex2 = tex2X*blendWeights.x + tex2Y * blendWeights.y + tex2Z * blendWeights.z;
fixed4 tex3 = tex3X*blendWeights.x + tex3Y * blendWeights.y + tex3Z * blendWeights.z;
//融合权重,添加高光
tex0 *= splat_control.r *half4(1.0, 1.0, 1.0, defaultSmoothness.r);
tex1 *= splat_control.g *half4(1.0, 1.0, 1.0, defaultSmoothness.g);
tex2 *= splat_control.b *half4(1.0, 1.0, 1.0, defaultSmoothness.b);
tex3 *= splat_control.a *half4(1.0, 1.0, 1.0, defaultSmoothness.a);
mixedDiffuse = tex0 + tex1 + tex2 + tex3;
//mixedDiffuse +=
//mixedDiffuse += splat_control.g * tex2D(_Splat1, IN.uv_Splat1) * half4(1.0, 1.0, 1.0, defaultSmoothness.g);
//mixedDiffuse += splat_control.b * tex2D(_Splat2, IN.uv_Splat2) * half4(1.0, 1.0, 1.0, defaultSmoothness.b);
//mixedDiffuse += splat_control.a * tex2D(_Splat3, IN.uv_Splat3) * half4(1.0, 1.0, 1.0, defaultSmoothness.a);
//---------法线---------<<<<<<<<<<<<<<<<<<<<<<<<<<<<
#ifdef _TERRAIN_NORMAL_MAP
fixed4 nrm = 0.0f;
fixed4 nm0X = tex2D(_Normal0, (xUV* _Splat0_ST.xy + _Splat0_ST.zw)/ _TextureScale);
fixed4 nm0Y = tex2D(_Normal0, (yUV* _Splat0_ST.xy + _Splat0_ST.zw) / _TextureScale);
fixed4 nm0Z = tex2D(_Normal0, (zUV* _Splat0_ST.xy + _Splat0_ST.zw) / _TextureScale);
fixed4 nm1X = tex2D(_Normal1, (xUV* _Splat1_ST.xy + _Splat1_ST.zw) / _TextureScale);
fixed4 nm1Y = tex2D(_Normal1, (yUV* _Splat1_ST.xy + _Splat1_ST.zw) / _TextureScale);
fixed4 nm1Z = tex2D(_Normal1, (zUV* _Splat1_ST.xy + _Splat1_ST.zw) / _TextureScale);
fixed4 nm2X = tex2D(_Normal2, (xUV* _Splat2_ST.xy + _Splat2_ST.zw)/ _TextureScale);
fixed4 nm2Y = tex2D(_Normal2, (yUV* _Splat2_ST.xy + _Splat2_ST.zw)/ _TextureScale);
fixed4 nm2Z = tex2D(_Normal2, (zUV* _Splat2_ST.xy + _Splat2_ST.zw)/ _TextureScale);
fixed4 nm3X = tex2D(_Normal3, (xUV* _Splat3_ST.xy + _Splat3_ST.zw)/ _TextureScale);
fixed4 nm3Y = tex2D(_Normal3, (yUV* _Splat3_ST.xy + _Splat3_ST.zw)/ _TextureScale);
fixed4 nm3Z = tex2D(_Normal3, (zUV* _Splat3_ST.xy + _Splat3_ST.zw)/ _TextureScale);
fixed4 nm0 = nm0X*blendWeights.x +nm0Y * blendWeights.y + nm0Z * blendWeights.z;
fixed4 nm1 = nm1X*blendWeights.x + nm1Y * blendWeights.y + nm1Z * blendWeights.z;
fixed4 nm2 = nm2X*blendWeights.x + nm2Y * blendWeights.y + nm2Z * blendWeights.z;
fixed4 nm3 = nm3X*blendWeights.x + nm3Y * blendWeights.y + nm3Z * blendWeights.z;
nm0 *= splat_control.r;
nm1 *= splat_control.g;
nm2 *= splat_control.b;
nm3 *= splat_control.a;
nrm = nm0 + nm1 + nm2 + nm3;
//nrm += splat_control.r * tex2D(_Normal0, IN.uv_Splat0);
//nrm += splat_control.g * tex2D(_Normal1, IN.uv_Splat1);
//nrm += splat_control.b * tex2D(_Normal2, IN.uv_Splat2);
//nrm += splat_control.a * tex2D(_Normal3, IN.uv_Splat3);
o.Normal = UnpackNormal(nrm);
#endif
//End Custom=================================================<<<<<<<<<<<<<<<<<<<<<<<<<<<
o.Albedo = max(fixed3(0.001, 0.001, 0.001), mixedDiffuse.rgb);
o.Alpha = weight;
o.Smoothness = mixedDiffuse.a;
o.Metallic = dot(splat_control, half4(_Metallic0, _Metallic1, _Metallic2, _Metallic3));
}
void SplatmapFinalColor(Input IN, TERRAIN_SURFACE_OUTPUT o, inout fixed4 color)
{
color *= o.Alpha;
#ifdef TERRAIN_SPLAT_ADDPASS
UNITY_APPLY_FOG_COLOR(IN.fogCoord, color, fixed4(0, 0, 0, 0));
#else
UNITY_APPLY_FOG(IN.fogCoord, color);
#endif
}
void SplatmapFinalPrepass(Input IN, TERRAIN_SURFACE_OUTPUT o, inout fixed4 normalSpec)
{
normalSpec *= o.Alpha;
}
void SplatmapFinalGBuffer(Input IN, TERRAIN_SURFACE_OUTPUT o, inout half4 diffuse, inout half4 specSmoothness, inout half4 normal, inout half4 emission)
{
diffuse.rgb *= o.Alpha;
specSmoothness *= o.Alpha;
normal.rgb *= o.Alpha;
emission *= o.Alpha;
}
ENDCG
}
Fallback "Nature/Terrain/Diffuse"
}