跳转到内容

Cg 编程/Unity/置换贴图

来自维基教科书,开放世界开放书籍
像这样的不规则形状的陨石可以使用具有被置换贴图移动的顶点的球形网格创建。

本教程介绍了置换贴图,作为顶点着色器中纹理查找应用的示例。

本教程需要一些关于纹理映射的知识,如“纹理球体”部分中所述。

顶点着色器中的纹理查找

[编辑 | 编辑源代码]

本维基教科书中的大多数教程仅在片段着色器中使用纹理查找,因为顶点着色器中的纹理查找是 Shader Model 3.0 特性,即,并非所有 GPU 都支持它(参见Unity 对平台特定渲染差异的描述)。

然而,置换映射需要对每个顶点进行纹理值的查找,该值用于位移,即移动每个顶点到新的位置。出于技术原因,我们不能使用标准的tex2d命令。相反,我们必须使用命令tex2dlod,使用一个四维向量来指定纹理坐标,其中第四个分量为 0。(这确保我们始终从最精细的mipmap级别读取值,并且我们不会请求 mipmap 级别自动计算,这在顶点着色器中是不可能的。)该命令可能如下所示

      float4 dispTexColor = tex2Dlod(_DisplacementTex, float4(i.texcoord.xy, 0.0, 0.0));

其中i.texcoord.xy是两个纹理坐标。

顶点的位移

[编辑 | 编辑源代码]

下面的示例将得到的颜色dispTexColor转换为灰度值,并将其缩放为用户指定的统一_MaxDisplacement

      float displacement = dot(float3(0.21, 0.72, 0.07), dispTexColor.rgb) * _MaxDisplacement;

然后,此位移值用于沿其表面法线向量移动每个顶点,即,我们将对象坐标中的顶点位置(示例中的i.vertex)添加到与位移值相乘的表面法线向量(i.normal)。

      float4 newVertexPos = i.vertex + float4(i.normal * displacement, 0.0);

请注意,i.normal是一个三维向量;因此,我们必须附加一个 0.0 坐标以形成一个四维向量,然后才能将其添加到顶点位置。

着色器代码

[编辑 | 编辑源代码]

其余代码应用标准顶点变换,并使用无光照纹理贴图对表面进行着色。请确保将其用于具有相对较多顶点的网格上 - 否则位移表面将显得相当“块状”。

Shader "Vertex Displacement" {
   Properties {
      _MainTex ("Main Texture", 2D) = "white" {}
      _DisplacementTex ("Displacement Texture", 2D) = "white" {}
      _MaxDisplacement ("Max Displacement", Float) = 1.0
   }
   SubShader {
      Pass {    
         CGPROGRAM

         #pragma vertex vert
         #pragma fragment frag
   
         uniform sampler2D _MainTex;
         uniform sampler2D _DisplacementTex;
         uniform float _MaxDisplacement;
   
         struct vertexInput {
            float4 vertex : POSITION;
            float3 normal : NORMAL;
            float4 texcoord : TEXCOORD0;
         };
   
         struct vertexOutput {
            float4 position : SV_POSITION;
            float4 texcoord : TEXCOORD0;
         };
   
         vertexOutput vert(vertexInput i) {
            vertexOutput o;
            
            // get color from displacement map, and convert to float from 0 to _MaxDisplacement
            float4 dispTexColor = tex2Dlod(_DisplacementTex, float4(i.texcoord.xy, 0.0, 0.0));
            float displacement = dot(float3(0.21, 0.72, 0.07), dispTexColor.rgb) * _MaxDisplacement;
            
            // displace vertices along surface normal vector
            float4 newVertexPos = i.vertex + float4(i.normal * displacement, 0.0);   

            // output data            
            o.position = UnityObjectToClipPos(newVertexPos); 
            o.texcoord = i.texcoord;
            return o;
   
         }
   
         float4 frag(vertexOutput i) : COLOR
         {
            return tex2D(_MainTex, i.texcoord.xy);
         }
   
         ENDCG
      }
   }
}

请注意,如果纹理图像边缘的颜色不匹配,或者对应顶点的表面法线向量不匹配,则表面网格的边缘可能无法对齐。

恭喜你,你已经完成了本教程。你看到了

  • 如何在顶点着色器中使用纹理映射。
  • 如何沿表面法线向量移动顶点位置。

进一步阅读

[编辑 | 编辑源代码]

如果你还想了解更多

< Cg 编程/Unity

除非另有说明,否则本页上的所有示例源代码均授予公有领域。
华夏公益教科书