[译]Unity3D Shader教程(八)Planar Mapping

2021年10月17日 0 作者 老王

原文链接:
Shader Tutorials by Ronja


1. Summary

概要。
有时候我们的网格数据中并没有UV坐标,或者我们想让多个模型的纹理对齐,或者还有其他什么原因,我们需要动态生成UV坐标。那么接下来的教程,我们将以最简单的方法,二维平面映射,来生成我们的UV坐标。
本教程是在上一个图片着色器的基础上实现的。但是你也可以在任何表面着色器上使用这种方法。
Result

2.Basics

首先我们将输入结构体中的uv变量删除掉,因为我们打算通过脚本生成。

struct appdata{
    float vertex : POSITION;
};

因为片段着色器中的输入参数是由顶点着色器中的输出参数插值而得到的,因此我们选择在顶点着色器中计算新的uv值。首先我们将其顶点UV设置为顶点在模型坐标系下的x和z的值。这样足以让纹理出现在模型表面了,并且其效果看起来就好像是图片从上往下投影到模型表面一样。

v2f vert(appdata v){
    v2f o;
    o.position = UnityObjectToClipPos(v.vertex);
    o.uv = v.vertex.xz;
    return o;
}

3.Adjustable Tiling

前面并没有考虑图片的缩放,或者我们可能希望图片显示不跟随模型一起旋转。
图片缩放的问题可以通过TRANSFORM_TEX宏来执行UV变换,这样最终用于纹理采样的UV可以跟随纹理缩放而相应改变。

v2f vert(appdata v){
    v2f o;
    o.position = UnityObjectToClipPos(v.vertex);
    o.uv = TRANSFORM_TEX(v.vertex.xz, _MainTex);
    return o;
}

AdjustTilingOffset

4.Texture Coordinates based on World Position

为了消除模型位置、和旋转对UV坐标的影响,我们需要将顶点坐标转换到世界坐标系,前面的例子是使用模型坐标系中的顶点坐标来生成UV坐标的。计算世界坐标的方法很简单,只需要将顶点坐标乘以模型空间矩阵。在我们得到世界坐标后,使用其世界坐标来生成UV坐标。

v2f vert(appdata v){
    v2f o;
    //calculate the position in clip space to render the object
    o.position = UnityObjectToClipPos(v.vertex);
    //calculate world position of vertex
    float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
    //change UVs based on tiling and offset of texture
    o.uv = TRANSFORM_TEX(worldPos.xz, _MainTex);
    return o;
}

MoveSphere
从上面我们也可以看到基于世界坐标的二维平面映射有一些缺点,因为我们必须使用可重复纹理(tileable textures),否则无法覆盖整个空间区域。而且最终用渲染出来的纹理也会因为观察角度不同而发生扭曲。但是我们可以使用更牛的技术来改进,例如后面将会介绍的,三维平面映射。

Shader "Tutorial/008_Planar_Mapping"{
    //show values to edit in inspector
    Properties{
        _Color ("Tint", Color) = (0, 0, 0, 1)
        _MainTex ("Texture", 2D) = "white" {}
    }

    SubShader{
        //the material is completely non-transparent and is rendered at the same time as the other opaque geometry
        Tags{ "RenderType"="Opaque" "Queue"="Geometry"}

        Pass{
            CGPROGRAM

            #include "UnityCG.cginc"

            #pragma vertex vert
            #pragma fragment frag

            //texture and transforms of the texture
            sampler2D _MainTex;
            float4 _MainTex_ST;

            fixed4 _Color;

            struct appdata{
                float4 vertex : POSITION;
            };

            struct v2f{
                float4 position : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            v2f vert(appdata v){
                v2f o;
                //calculate the position in clip space to render the object
                o.position = UnityObjectToClipPos(v.vertex);
                //calculate world position of vertex
                float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
                //change UVs based on tiling and offset of texture
                o.uv = TRANSFORM_TEX(worldPos.xz, _MainTex);
                return o;
            }

            fixed4 frag(v2f i) : SV_TARGET{
                //read texture at uv position
                fixed4 col = tex2D(_MainTex, i.uv);
                //multiply texture color with tint color
                col *= _Color;
                return col;
            }

            ENDCG
        }
    }
    FallBack "Standard" //fallback adds a shadow pass so we get shadows on other objects
}

文中绝大部分的翻译来源于下文,感谢翻译作者。
https://tyson-wu.github.io/blogs/2021/07/02/Ronja_Planar_Mapping/