美文网首页
Unity Shader:Triplanar Mapping

Unity Shader:Triplanar Mapping

作者: Dragon_boy | 来源:发表于2020-09-22 23:54 被阅读0次

https://www.martinpalko.com/triplanar-mapping/

Triplanar Mapping,即三维映射,我们会沿X、Y、Z轴使用一个平面贴图映射一张纹理三次,然后使用与最少拉伸最匹配的三个采样点,基于面朝的角度在这三个采样中混合。理论上,我们不会用有一张拉伸的纹理或硬接缝,我们也不不需要在网格上进行UV映射。

为了实现三维映射,我们首先需要生成三个平面UV集,为此,我们使用片段的世界空间位置。如果我们使用片段的XZ世界空间坐标作为UV坐标来采样,这样就会得到一个从Y轴投影的平面贴图,同理还可以得到使用ZY坐标的X投影,XY坐标的Z投影。

我们还需要一个混合权重来决定使用哪一个UV集。我们根据世界空间顶点的法线来进行。我们可以使用每个轴的绝对值,举个例子,这样如果表面法线指向正向或负向Y轴,我们可以从Y轴投影平面来混合更多的纹理采样。

三维映射的一个缺点在于,因为法线映射依赖于由UV表示的网格的切线,并且由于我们使用世界空间坐标来表示我们的UV,这样网格的切线就会摇摆不定。我们仍可以使用法线贴图来进行光照,只是不正确。

同样,三维映射与传统的UV映射相比,没那么有效率。决定UV坐标和混合权重的计算并不那么耗能,但执行三次就不一样了。当我们只是采样漫反射纹理时这还能接受,但如果我们同样映射高光贴图、细节贴图等,我们就不得不去关注使用的采样点的数量了。

Unity中的实现:

Shader "Unlit/Triplanar Mapping"
{
    Properties
    {
        _AlbedoMap ("Albedo 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

            #include "UnityCG.cginc"

            struct vertexInput
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };

            struct vertexOutput
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 worldPos : TEXCOORD1;
                float3 worldNormal : TEXCOORD2;
            };

            sampler2D _AlbedoMap;
            float4 _AlbedoMap_ST;
            float _TextureScale;
            float _TriplanarBlendSharpness;

            vertexOutput vert (vertexInput i)
            {
                vertexOutput o;
                o.vertex = UnityObjectToClipPos(i.vertex);
                o.uv = TRANSFORM_TEX(i.uv, _AlbedoMap);
                o.worldPos = mul(unity_ObjectToWorld, i.vertex).xyz;
                o.worldNormal = UnityObjectToWorldNormal(i.normal);
                return o;
            }

            fixed4 frag (vertexOutput i) : SV_Target
            {
                // find our UVs for each axis based on world position of the fragment
                half2 yUV = i.worldPos.xz / _TextureScale;
                half2 xUV = i.worldPos.zy / _TextureScale;
                half2 zUV = i.worldPos.xy / _TextureScale;

                // do texture samples from our albedo mao with each of the 3 UV set's we've just made.
                half3 yDiff = tex2D(_AlbedoMap, yUV);
                half3 xDiff = tex2D(_AlbedoMap, xUV);
                half3 zDiff = tex2D(_AlbedoMap, zUV);

                // get the absolute value of the world normal.
                // put the blend weights to the power of BlendSharpness, the higher the value,
                // the sharpness the transition between the planar maps will be.
                half3 blendWeights = pow(abs(i.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.
                fixed4 col = fixed4(xDiff * blendWeights.x + yDiff * blendWeights.y + zDiff * blendWeights.z, 1.0);
                return col;
            }
            ENDCG
        }
    }
}

为对比效果,我创建了一个普通光照模型相同的shader,然后赋予到相同的模型上。

立方体:



看不出来区别,我们转换为球体:



可以明显看出三维映射时球面的混合情况,提升混合权重可以减弱这种混合不当的情况,但还是会看出三面。

圆柱:



可以发现三维映射的效果更好,排列的更规则,普通的纹理映射会有拉伸感(沿圆柱的纵轴)。

平面:



差别显而易见,普通的纹理映射如果想要达到同样的效果,需要调高拼贴参数。

接下来我们看看应用法线纹理会怎么样。这里就不贴出本人的代码了,因为用三维映射去实现法线贴图效果非常不好。在采样切线空间的法线时,我们需要使用三个面的UV分别去采样,由于涉及一个混合操作,同时混合系数与法线相关,因此会极其的不方便,得到的法线混合效果往往看上去是错误的,很难得到我们想要的效果,因此非常不推荐使用三维映射去采样法线贴图。

总结一下就是,可以使用三维映射去采样漫发射贴图,效果不错,可以避免接缝问题,不过非常不推荐使用三维映射去采样法线贴图进行光照计算,这里最好使用平常的平面映射去采样法线来进行计算。

相关文章

网友评论

      本文标题:Unity Shader:Triplanar Mapping

      本文链接:https://www.haomeiwen.com/subject/kjrgyktx.html