Triplanar笔记
这个笔记主要记录自CatlikeCoding-Triplanar 上面的文章很长而且有很多跟Triplanar无关的内容,其实triplanar和核心内容就是很简单的几行代码,所以看我这个笔记就可以了。
小学生都能看懂的视频版,求三连
什么是Triplanar?为什么要用Triplanar?
一般来说我们想要映射纹理到我们的模型上,可以用根据每个顶点的uv坐标来映射纹理。但这不是唯一的方法,而triplanar就是另一种映射贴图的方式。有时候,uv坐标映射的方式可能无法达到我们想要的效果(比如造成贴图拉伸)甚至根本没有uv坐标,这些情况大多在用代码生成模型时,动态改变模型时发生。Triplanar的原理就是用顶点位置作为uv坐标,用法线作为权重,来实现贴图的映射,因为映射已xyz三个平面分别映射,所以叫做tri-planar。
《A Short Hike》中的triplanar应用
先放源码
Shader "Custom/Triplanar" { Properties { _TopTex("TopTexture", 2D) = "white" {} _SideTex("SideTexture", 2D) = "white" {} _BlendOffset("BlendOffset",Range(0,0.5)) = 0.25 _BlendExponent ("Blend Exponent", Range(1, 8)) = 2 } SubShader { Tags { "RenderType"="Opaque" } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityStandardBRDF.cginc" #include "Assets/Plugins/GumouKit/cgincs/gfunc.cginc" struct appdata { float4 vertex : POSITION; float3 normal:NORMAL; }; struct v2f { float4 vertex : SV_POSITION; float3 normal:TEXCOORD1; float4 worldpos : TEXCOORD2; }; fixed _BlendOffset; half _BlendExponent; sampler2D _TopTex; sampler2D _SideTex; float4 _TopTex_ST,_SideTex_ST; struct TriUV{ float2 xUV,yUV,zUV; }; TriUV GetTriUV (float4 worldpos) { TriUV triUV; triUV.xUV = worldpos.zy; triUV.yUV = worldpos.xz; triUV.zUV = worldpos.xy; return triUV; } fixed3 GetTriWeights(fixed3 normal){ fixed3 weights = abs(normal); //因为normal可能有负数 weights = saturate(weights-_BlendOffset); //控制权重 weights = pow(weights,_BlendExponent); //进一步控制权重 return weights/(weights.x+ weights.y+ weights.z); //使xyz相加=1 } v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.normal = UnityObjectToWorldNormal(v.normal); o.worldpos = mul(unity_ObjectToWorld,v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { //triplanar TriUV triuv = GetTriUV(i.worldpos); fixed4 colx = tex2D(_SideTex,triuv.xUV * _SideTex_ST.xy + _SideTex_ST.zw); fixed4 coly = tex2D(_TopTex,triuv.yUV * _TopTex_ST.xy + _TopTex_ST.zw); fixed4 colz = tex2D(_SideTex,triuv.zUV * _SideTex_ST.xy + _SideTex_ST.zw); fixed3 weights = GetTriWeights(i.normal); fixed4 tricol = colx*weights.x + coly*weights.y +colz*weights.z; return tricol; } ENDCG } } }
用顶点位置作为uv坐标
先来映射一个面
fixed4 frag (v2f i) : SV_Target { fixed4 colx = tex2D(_SideTex,i.worldpos.zy); fixed4 coly = tex2D(_TopTex,i.worldpos.xz); fixed4 colz = tex2D(_SideTex,i.worldpos.xy); //return colx; //return colx; return colx; }
可以写成一个函数方便使用
struct TriUV{ float2 xUV,yUV,zUV; }; TriUV GetTriUV (float4 worldpos) { TriUV triUV; triUV.xUV = worldpos.zy; triUV.yUV = worldpos.xz; triUV.zUV = worldpos.xy; return triUV; } fixed4 frag (v2f i) : SV_Target { TriUV triuv = GetTriUV(i.worldpos); fixed4 colx = tex2D(_SideTex,triuv.xUV); fixed4 coly = tex2D(_TopTex,triuv.yUV); fixed4 colz = tex2D(_SideTex,triuv.zUV); }
合在一起的效果
fixed4 frag (v2f i) : SV_Target { TriUV triuv = GetTriUV(i.worldpos); fixed4 colx = tex2D(_SideTex,triuv.xUV); fixed4 coly = tex2D(_TopTex,triuv.yUV); fixed4 colz = tex2D(_SideTex,triuv.zUV); return (colx+coly+colz)/3; }
用法线作为权重
fixed3 GetTriWeights(fixed3 normal){ fixed3 weights = abs(normal); //因为normal可能有负数 return weights/(weights.x+ weights.y+ weights.z); //使xyz相加=1 } ...... fixed4 frag (v2f i) : SV_Target { ..... fixed3 weights = GetTriWeights(i.normal); fixed4 tricol = colx*weights.x + coly*weights.y +colz*weights.z; return tricol; }
控制权重
Properties { ..... _BlendOffset("BlendOffset",Range(0,0.5)) = 0.25 } ..... fixed _BlendOffset; .... fixed3 GetTriWeights(fixed3 normal){ fixed3 weights = abs(normal); //因为normal可能有负数 weights = saturate(weights-_BlendOffset); //控制权重 return weights/(weights.x+ weights.y+ weights.z); //使xyz相加=1 } .... fixed4 frag (v2f i) : SV_Target { ..... fixed3 weights = GetTriWeights(i.normal); fixed4 tricol = colx*weights.x + coly*weights.y +colz*weights.z; return tricol; }
进一步控制权重
Properties { ..... _BlendOffset("BlendOffset",Range(0,0.5)) = 0.25 _BlendExponent ("Blend Exponent", Range(1, 8)) = 2 } ..... fixed _BlendOffset; half _BlendExponent; .... fixed3 GetTriWeights(fixed3 normal){ fixed3 weights = abs(normal); //因为normal可能有负数 weights = saturate(weights-_BlendOffset); //控制权重 weights = pow(weights,_BlendExponent); //进一步控制权重 return weights/(weights.x+ weights.y+ weights.z); //使xyz相加=1 } .... fixed4 frag (v2f i) : SV_Target { ..... fixed3 weights = GetTriWeights(i.normal); fixed4 tricol = colx*weights.x + coly*weights.y +colz*weights.z; return tricol; }
接下来我们可以试试映射多张贴图,我们y轴用_TopTex草地贴图,x和z轴用 _MainTex石头贴图。
Properties { ..... _MainTex ("Texture", 2D) = "white" {} _TopTex("",2D) = "white"{} } ... fixed4 frag (v2f i) : SV_Target { fixed4 colx = tex2D(_MainTex,i.worldpos.zy); fixed4 coly = tex2D(_TopTex,i.worldpos.xz); fixed4 colz = tex2D(_MainTex,i.worldpos.xy); }
最后我们再实现一下纹理的缩放和位移
float4 _MainTex_ST,_SideTex_ST; ..... fixed4 frag (v2f i) : SV_Target { fixed4 colx = tex2D(_MainTex,i.worldpos.zy * _MainTex_ST.xy + _MainTex_ST.zw); fixed4 coly = tex2D(_TopTex,i.worldpos.xz * _SideTex_ST.xy + _SideTex_ST.zw); fixed4 colz = tex2D(_MainTex,i.worldpos.xy * _MainTex_ST.xy + _MainTex_ST.zw); ... }
把缩放设置为5看一下效果
暂无关于此日志的评论。