• 【图形学】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"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

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

    			fixed4 _Color;
    			sampler2D _MainTex;
    			float4 _MainTex_ST;
    			fixed4 _Specular;
    			float _Gloss;
    
    • 1
    • 2
    • 3
    • 4
    • 5

      我们在 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;
    			};
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

      我们首先在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;
    			}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

      首先查看顶点着色器的代码,主要是第 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)
    
    • 1
    • 2

      实际上和我们写的基本是一样的…首先通过 _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);
    			}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

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

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

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

    			ENDCG
    		}
    	} 
    	FallBack "Specular"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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会比较生硬。

  • 相关阅读:
    MapReduce原理剖析(深入源码)
    大厂面试:如何用快排思想在O(n)内查找第K大元素?
    c# 字符串转化成语音合成,System.Speech
    Centos7 安装和配置 Redis 5 教程
    SQL注入
    LoRa Basics无线通信技术和应用案例详解
    C++初阶:C/C++内存管理
    Docker安装RabbitMQ
    类似于推箱子的小游戏 寻找 最短路径
    #define 宏定义看这一篇文章就够了
  • 原文地址:https://blog.csdn.net/qq_40891541/article/details/126225697