Cg Programming/Unity/Textured Spheres纹理球体

本教程介绍了纹理映射。
这里写图片描述
这是Unity中Cg着色器关于纹理的一系列教程中的第一章。在本教程中,我们会从一个球体的简单纹理映射开始讲起。更具体地说,我们把一张地球的图像映射到了球体上。在此基础上,进一步的教程会涵盖诸如纹理表面光照、透明纹理、多重纹理、光泽映射等等。

纹理映射

这里写图片描述近似球面的三角形网格
”纹理映射“的基本思想是把一张图映射到一个三角网格上;换句话说,就是把一张平面图像放置到一个三维形状上。

为此,”纹理坐标“就被定义了,它简单指定了在纹理(也就是图像)中的位置。在OpenGL中,横坐标叫做S,纵坐标叫做T。但是,通常它们会被叫做x和y。在动画和建模工具中,纹理坐标通常被叫做U和V。

为了把纹理图像映射到一个网格上,网格的每个顶点都给出了一对纹理坐标。(这个过程 (和结果)有时被 称作“UV映射”,因为每个顶点会被映射到UV空间中的一点。)因此,每个顶点被映射到纹理图像上的一个点。这些顶点的纹理坐标随后会为三个顶点之间的任意三角形的每一个点作插值,因此网格所有三角形的每个点会有一对(经过插值的)纹理坐标。这些纹理坐标把网格的每个点映射到纹理贴图指定位置,从而颜色映射到该位置。因此,对于所有的可见顶点来说,绘制纹理映射的网格包含两个步骤:纹理坐标的插值和在由插值纹理坐标指定的位置上查找纹理贴图的颜色。

然而,当GPU要求查询(即用下面描述的“tex2D”指令)纹理贴图的像素(或叫“纹素”),它会在内部把纹理坐标映射到0到1之间,这取决于导入贴图时指定的“循环模式”:循环模式“repeat”基本上使用纹理坐标的小数部分来确定0到1之间的纹理坐标(译者注:在这种模式下,如果纹理坐标超过了1,那么它的整数部分会被丢弃,直接使用小数部分进行采样,这样纹理将会不断重复)。另一方面,循环模式“clamp”会把纹理坐标截取到这个范围。这些范围在0到1之间内部的纹理坐标随后会被用来确定纹理贴图中的位置:(0,0)指定了纹理贴图的左下角;(1,0)指定了右下角;(0,1)指定了左上角等等。

在Unity中映射球体

这里写图片描述
在Unity中为了把地球的贴图映射到球体上,你必须首先把贴图导入Unity。点击图像直到你得到一个更大的版本并把它(通常通过点击右键)保存到电脑。然后切换到Unity,从main menu选择Assets > Import New Asset…。选择这个图像文件并点击Import,这张贴图就会出现在Project Window中。(另外,你可以简单地拖拉这张图到Project Window中。)通过在那里选择它,关于导入的方式的详细信息出现在了Inspector Window中(并且可以更改)。

现在创建一个球体、一个材质以及一个着色器,然后如章节“最小型的着色器”中描述的把这个着色器挂载到材质上并且把材质挂载到球体上。着色器代码应该是这样的:

Shader "Cg shader with single texture" {
   Properties {
      _MainTex ("Texture Image", 2D) = "white" {} 
         // a 2D texture property that we call "_MainTex", which should
         // be labeled "Texture Image" in Unity's user interface.
         // By default we use the built-in texture "white"  
         // (alternatives: "black", "gray" and "bump").
   }
   SubShader {
      Pass {    
         CGPROGRAM

         #pragma vertex vert  
         #pragma fragment frag 

         uniform sampler2D _MainTex;    
            // a uniform variable refering to the property above
            // (in fact, this is just a small integer specifying a 
            // "texture unit", which has the texture image "bound" 
            // to it; Unity takes care of this).

         struct vertexInput {
            float4 vertex : POSITION;
            float4 texcoord : TEXCOORD0;
         };
         struct vertexOutput {
            float4 pos : SV_POSITION;
            float4 tex : TEXCOORD0;
         };

         vertexOutput vert(vertexInput input) 
         {
            vertexOutput output;

            output.tex = input.texcoord;
               // Unity provides default longitude-latitude-like 
               // texture coordinates at all vertices of a 
               // sphere mesh as the input parameter 
               // "input.texcoord" with semantic "TEXCOORD0".
            output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
            return output;
         }
         float4 frag(vertexOutput input) : COLOR
         {
            return tex2D(_MainTex, input.tex.xy);   
               // look up the color of the texture image specified by 
               // the uniform "_MainTex" at the position specified by 
               // "input.tex.x" and "input.tex.y" and return it

         }

         ENDCG
      }
   }
   Fallback "Unlit/Texture"
}

注意名字_MainTex被选择以确保备用着色器Unlit/Texture能够访问到它(查看章节“漫反射”中备用着色器的讨论)。

球体现在应该是白色的。如果它是灰色的,你应该检查着色器是否挂载到了材质上并且材质挂载到了球体上。如果球体是紫红色的,你应该检查Project Window中的着色器并且阅读Inspector Window中的错误信息。

如果球体是白色的,在Hierarchy Window或Scene View中选择球体,并且在Inspector Window中查看它的信息。你的材质应该在Mesh Renderer下面显示并且在它下面应该有个标签Texture Image。(否则点击material 让它显示出来。)标签Texture Image”跟我们在着色器代码中为我们着色器属性指定_MainTex是一样的。在标签的右边有一个空的方框。点击方框上小的选择按钮并且选择导入的纹理贴图,或者可以从Project Window拖拉纹理贴图到这个空方框上。

如果上面的步骤都做对了,这个纹理贴图应该会显示在球体上了。恭喜你!

它是如何工作的

既然好多技术都用了纹理映射,那么了解这里发生的事情是很有好处的。因此,让我们回顾一下这个着色器代码:

Unity球体的顶点伴随着在带有语义TEXCOORD0的顶点输入参数texcoord中每个顶点的纹理坐标。这些坐标跟经纬度(但范围从0到1)是相似的。跟带有语义POSITION的顶点输入参数类似,它指定对象空间中的位置,除了texcoord 指定纹理贴图空间中的纹理坐标。

随后顶点着色器会把每个顶点的纹理坐标写入到顶点输出参数output.tex。对于每个三角形的片元(也就是每个被覆盖到的像素),在三个三角形顶点的输出参数的值被插值(参考章节“光栅化”中的描述),并且被插值的纹理坐标会作为输出参数传递到片元着色器中。片元着色器随后用它们查找纹理空间中被插值的位置上,由uniform _MainTex指定的纹理贴图颜色,并且作为片元输出参数返回这个颜色,它随后会被写入帧缓冲并显示到屏幕上。

至关重要的是,为了理解以后教程中更复杂的纹理映射技术,你对这些步骤要有一个很好的了解。

重复和移动纹理

这里写图片描述
(译者注:随便打开一个材质,就可以看到Tiling和Offset)
对于上面的着色器在Unity的界面中,你可能会注意到参数Tiling和Offset,每个都有x和y分量。在内置着色器中,这些参数允许你把纹理重复(通过在纹理坐标空间中缩小纹理图像)以及在表面移动纹理贴图(通过在纹理坐标空间偏移纹理图像)。为了与这种行为保持一致,另一个uniform必须被定义成:

uniform float4 _MainTex_ST;//_MainTex属性的Tiling和Offset参数

对于每个纹理属性,Unity提供了这么一个以“_ST”结尾的float4 uniform。(记住:“S”和“T”是纹理坐标的正式名字,它们通常被叫做“U”和“V”, 或“x”和“y”。)这个uniform包含了在_MainTex_ST.x 和_MainTex_ST.y中Tiling参数的x和y分量。uniform应该这么使用:

return tex2D(_MainTex, _MainTex_ST.xy * input.tex.xy + _MainTex_ST.zw);

(译者注:_MainTex_ST的ST是应该是SamplerTexture的意思 ,就是声明_MainTex是一张采样图,也就是会进行UV运算。 如果没有这句话,是不能进行TRANSFORM_TEX的运算的。其中Tiling的xy值会传给_MainTex_ST的xy,而Offset的xy值会传给_MainTex_ST的zw)

这个使得着色器表现得像内置着色器。在其它的教程中,为了保持着色器代码更简洁,这个特性通常不会被实现。

只是为了完整,这里给出带有这个特性的完整着色器代码:

Shader "Cg shader with single texture" {
   Properties {
      _MainTex ("Texture Image", 2D) = "white" {} 
         // a 2D texture property that we call "_MainTex", which should
         // be labeled "Texture Image" in Unity's user interface.
         // By default we use the built-in texture "white"  
         // (alternatives: "black", "gray" and "bump").
   }
   SubShader {
      Pass {    
         CGPROGRAM

         #pragma vertex vert  
         #pragma fragment frag 

         uniform sampler2D _MainTex;    
         uniform float4 _MainTex_ST; 
            // tiling and offset parameters of property

         struct vertexInput {
            float4 vertex : POSITION;
            float4 texcoord : TEXCOORD0;
         };
         struct vertexOutput {
            float4 pos : SV_POSITION;
            float4 tex : TEXCOORD0;
         };

         vertexOutput vert(vertexInput input) 
         {
            vertexOutput output;

            output.tex = input.texcoord;
            output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
            return output;
         }

         float4 frag(vertexOutput input) : COLOR
         {
            return tex2D(_MainTex, _MainTex_ST.xy * input.tex.xy + _MainTex_ST.zw); 
               // 纹理坐标乘以tiling参数,然后和offset参数相加
         }

         ENDCG
      }
   }
   Fallback "Unlit/Texture"
}

总结

你已经到达了最重要的教程之一的结尾。我们看到了:

  • 如何导入一张纹理贴图以及如何把它挂载到着色器的纹理属性上。
  • 顶点着色器和片元着色器是如何配合工作映射一张纹理贴图到网格上去的。
  • Unity中关于贴图的tiling和offset参数是如何工作的以及如何实现它们。

扩展阅读

如果你想知道得更多:

  • 关于顶点着色器和片元着色器进出的数据流(也就是顶点输入和输出参数等),你应该阅读章节“可编程图形管线”中的描述。
  • 关于片元着色器的顶点输出参数的插值,你应该阅读章节“光栅化”。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值