【图形学】20 基础纹理(一、单张纹理)

来源:《UNITY SHADER入门精要》

1、单张纹理

Shader "Unity Shaders Book/Chapter 7/Single Texture" {
	Properties {
		_Color ("Color Tint", Color) = (1, 1, 1, 1)
		_MainTex ("Main Tex", 2D) = "white" {}
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(8.0, 256)) = 20
	}
	SubShader {		
		Pass { 
			Tags { "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag

			#include "Lighting.cginc"

  以上都是基础的操作了。第 4 行,用 2D 的属性声明了纹理。我们使用了一个字符串后跟一个花括号作为它的初始值:“white” 就是它内置纹理的名字。

			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed4 _Specular;
			float _Gloss;

  我们在 CG 代码中,声明了与 Properties语义块相匹配的变量,以便和材质简历联系。可以注意第 2 行、第 3 行,都是对应 _MainTex 属性来声明的类型变量,在规定纹理名的后面加上 _ST,S 代表 Scale,T 代表 Translation,它一定是一个 float4。如 _MainTex_ST_MainTex_ST.xy 存储的是缩放值, _MainTex_ST.zw 存储的是偏移值。

image-20220621113044750
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 texcoord : TEXCOORD0;
			};

			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				float2 uv : TEXCOORD2;
			};

  我们首先在a2v结构体中使用TEXCOORD0语义声明了一个新的变量texcoord,这样Unity就会将模型的第一组纹理坐标存储到该变量中。然后,我们在v2f结构体中添加了用于存储纹理坐标的变量,以便在片元着色器中使用该坐标进行纹理采样。

			v2f vert(a2v v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				
				o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
				// Or just call the built-in function
				//o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
				
				return o;
			}

  首先查看顶点着色器的代码,主要是第 9 行–第 11 行,我们的UV值采样。这一句也可以采用Unity的内置带参宏 TRANSFORM_TEX() 来解决,但是,其实效果是一样的。因为我们在UnityCG,cginc可以看到它的定义。

// Transforms 2D UV by scale/bias property
#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)

  实际上和我们写的基本是一样的…首先通过 _MainTex_ST.xy 来对顶点纹理坐标进行缩放,然后再通过 _MainTex_ST.zw 对结果进行偏移。

			fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				
				// Use the texture to sample the diffuse color
				fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
				
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				
				fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
				
				fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
				fixed3 halfDir = normalize(worldLightDir + viewDir);
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);	
				
				return fixed4(ambient + diffuse + specular, 1.0);
			}

  我们再看片元着色器中,首先,第2、3行,我们计算了世界空间中的 法线方向 和光照方向。
  第 6 行,我们通过纹理坐标来采样,我们使用官方的函数 tex2D(sampler2D x, float2 v)

float4 tex2D(sampler2D x, float2 v)    { return x.t.Sample(x.s, v); }

  第一个参数是需要被采样的纹理,第二个参数是一个 float2 类型的纹理坐标。它将返回计算得到的 纹素值(texel):指每一个像素的纹理值。再乘以颜色属性 _Color 的乘积作为材质的 albedo(反射率)。
  第 8 行,我们使用环境光的颜色乘以 材质的反射率(albedo)。同样的,后面计算漫反射和镜面反射都会乘以这个 反射率(albedo)。

			ENDCG
		}
	} 
	FallBack "Specular"
}

2、纹理的属性

  在Unity默认的拖动图片生成纹理的选项中,有一些这样的属性:

①Wrap Mode

  Wrap Mode有多种模式:Repeat、Clamp、Mirror、Mirror Once、Per-axis。
  Repeat模式:纹理会不断重复。Clamp模式:超过范围的部分会截取到边界值,形成一个条形结构。Mirror模式:无限镜像。Mirror Once模式:只镜像一次。

②Filter Mode

  Filter Mode决定了当纹理由于产生拉伸时将采用哪种滤波模式,它有三种模式:Point、Bilinear、Trilinear。它们的滤波效果从左到右依次提升,但需要耗费的性能也依次增大。

③纹理缩小

  纹理缩小的过程比放大更加复杂一些,此时原纹理中的多个像素将会对应一个目标像素。纹理缩放更加复杂的原因在于我们往往需要处理抗锯齿的问题,一个最常使用的方法就是多级渐远纹理(mipmapping)
  在材质的Inspector,Texture Type 是 Default 的情况下,

3、生成高度图

  当我们把纹理设置成Nromal map后,能够找到一个复选框 Create from Grayscale,生成一张高度图,白色表示更高,黑色标识相对更低。
  勾选了Create from Grayscale 之后,出现新的两个选项——Bumpiness 和 Filtering。Bumpiness 用于控制凹凸程度,而 Filtering 决定我们使用哪种方式来计算凹凸程度:Smooth会比较平滑,Sharp会比较生硬。

猜你喜欢

转载自blog.csdn.net/qq_40891541/article/details/126225697