效果图:
效果图1,地形:左边为三平面式贴图效果,右边为一般贴图效果。
效果图2,树干:右边为三平面式贴图效果,左边为一般贴图效果。
效果图3,球体:左边为三平面式贴图效果,右边为一般贴图效果。
1,世界坐标贴图 World Space UV-mapping
在一般的贴图方法中,在片段着色器内使用tex2D(texture,uv);对2D材质进行采样时,片段着色器将会根据顶点着色器将会传入的顶点UV值插值出当前像素点的uv值。在World Space UV-mapping中,uv值使用当前像素的世界坐标,tex2D(texture,worldPos_xy);。由于uv值只可以取0-1,因此一般面积大于1的2D平面会出现一种repeat的效果,可以将uv值除以一个变量进行缩放,tex2D(texture,worldPos_xy/textureScale);。这种贴图方法的优势是物体向xy平面方向放大时贴图不会出现拉伸,缺点时当物体移动或旋转时,贴图不会随着物体移动,而是根据新的坐标重新进行材质采样。
效果图4:平面以worldPos_xz进行tex2D()
2,三平面式贴图 Triplanar Mapping
以世界坐标贴图方法为基础,将像素的世界坐标(x,y,z)取值为三组UV,xy,xz,yz;根据三组UV值分别对材质进行采样, 然后根据法线方形对采样的颜色进行混合。这样当像素的法线面向y轴时,它会显示由xz采样的颜色,当面向x轴时,将会显示yz采样的颜色,在面向xy轴45度夹角时将会显示由xz,yz采样出的颜色的混合颜色。以此种方式对物体所有非平滑处进行混合过渡。对不规则复杂形状物体,如地形,树木用此方式贴图视觉效果更好。缺点是计算量相对普通贴图要更大。
3,代码
surface shader:
Shader "TriplanarTutorial/Triplanar_Final"
{
Properties
{
_DiffuseMap ("Diffuse Map ", 2D) = "white" {}
_TextureScale ("Texture Scale",float) = 1
_TriplanarBlendSharpness ("Blend Sharpness",float) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma target 3.0
#pragma surface surf Lambert
sampler2D _DiffuseMap;
float _TextureScale;
float _TriplanarBlendSharpness;
struct Input
{
float3 worldPos;
float3 worldNormal;
};
void surf (Input IN, inout SurfaceOutput o)
{
// Find our UVs for each axis based on world position of the fragment.
half2 yUV = IN.worldPos.xz / _TextureScale;
half2 xUV = IN.worldPos.zy / _TextureScale;
half2 zUV = IN.worldPos.xy / _TextureScale;
// Now do texture samples from our diffuse map with each of the 3 UV set's we've just made.
half3 yDiff = tex2D (_DiffuseMap, yUV);
half3 xDiff = tex2D (_DiffuseMap, xUV);
half3 zDiff = tex2D (_DiffuseMap, zUV);
// Get the absolute value of the world normal.
// Put the blend weights to the power of BlendSharpness, the higher the value,
// the sharper the transition between the planar maps will be.
half3 blendWeights = pow (abs(IN.worldNormal), _TriplanarBlendSharpness);
// Divide our blend mask by the sum of it's components, this will make x+y+z=1
blendWeights = blendWeights / (blendWeights.x + blendWeights.y + blendWeights.z);
// Finally, blend together all three samples based on the blend mask.
o.Albedo = xDiff * blendWeights.x + yDiff * blendWeights.y + zDiff * blendWeights.z;
}
ENDCG
}
}
vertex shader:
Shader "Unlit/Tri-Planar"
{
Properties
{
_DiffuseMap ("Diffuse Map ", 2D) = "white" {}
_TextureScale ("Texture Scale",float) = 1
_TriplanarBlendSharpness ("Blend Sharpness",float) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
float3 normal:NORMAL;
float4 w_Vertex : FLOAT;
};
float _TextureScale;
sampler2D _DiffuseMap;
float4 _MainTex_ST;
float _TriplanarBlendSharpness;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
o.normal=mul(unity_ObjectToWorld,v.normal);
o.w_Vertex=mul(unity_ObjectToWorld,v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
float3 blending=abs(i.normal);
blending=normalize(pow(blending,_TriplanarBlendSharpness));
float b=(blending.x+blending.y+blending.z);
blending/=float3(b,b,b);
float4 xaxis=tex2D(_DiffuseMap,i.w_Vertex.yz/_TextureScale);
float4 yaxis=tex2D(_DiffuseMap,i.w_Vertex.xz/_TextureScale);
float4 zaxis=tex2D(_DiffuseMap,i.w_Vertex.xy/_TextureScale);
fixed4 tex=xaxis*blending.x+yaxis*blending.y+zaxis*blending.z;
// fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, tex);
return tex;
}
ENDCG
}
}
}
参考:
Use Tri-Planar Texture Mapping for Better Terrain –Brent Owens
https://gamedevelopment.tutsplus.com/articles/use-tri-planar-texture-mapping-for-better-terrain–gamedev-13821
Triplanar Mapping –MARTIN PALKO
http://www.martinpalko.com/triplanar-mapping/