Unity Shader零基础入门4:纹理贴图与法线贴图

这篇教程详细介绍了Unity3D中纹理贴图和法线贴图的使用,包括它们如何影响模型的细节和外观。通过调整法线贴图的BumpScale参数,可以改变模型的细节程度。此外,还讲解了纹理贴图的Tiling和Offset属性,以及如何在Shader中应用这些概念。文章提供了素材包下载,帮助读者实践相关知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

纹理贴图:让模型拥有色彩、花纹等。

法线贴图:为减少资源消耗,模型本身的细节是比较少的。法线贴图可以增加模型的细节,让模型的表面出现更多的细节、阴影等。法线贴图一般是在切线空间下的,以便适合不同模型。如果是在模型空间下的,则只能配合对应的模型使用。切线空间的法线贴图看起来主要是蓝色,而模型空间的看起来五颜六色。

uv坐标: 纹理坐标又称为uv坐标。u、v分别代表横轴、纵轴。贴图左下为原点,范围为0-1。比如贴图右上角为(1,1)。

贴图的平铺和偏移:

纹理贴图和法线贴图的属性:Tiling-平铺;Offset-平移;

Tiling能让贴图变为重复的多份。比如设置为x=1,y=2,则x方向不变,y方向变成重复平铺的两份,各占一半高度。如果要使用Tiling让贴图重复平铺,需要贴图文件的Wrap Mode设置为Repeat。

Offset自然是让贴图平移偏离原位置。

本文所需要的素材包下载:

链接:https://pan.baidu.com/s/1JOSWNaT9APrw25C8geYwhA
提取码:wwic

 下载后导入到工程中。之后用包内的预制体在场景中生成一个模型。

 然后我们新建一个材质并给模型替换上新材质。

之后创建一个Shader文件(Standard Surface Shader),并修改内容如下:

Shader "Tutorial/04Texture"{
	Properties{
		//调整纹理贴图的颜色用
		_Color("Color",Color)=(1,1,1,1)
		//纹理贴图
		_MainTex("MainTex",2D)="white"{}
		//法线贴图。这里不能用"white"而要用"bump"表示没有法线贴图时默认使用模型本身的法线
		_NormalMap("NormalMap",2D)="bump"{}
		//控制使用法线贴图的法线的比例。如果为1则完全使用法线贴图。如果为0则完全使用模型本身的法线。
		//如果在0~1则法线贴图和模型法线按比例产生影响。如果大于1则会变得比完全使用法线贴图更加夸张。
		_BumpScale("BumpScale",Range(0,10))=1
	}

	SubShader{
		pass{
			Tags{
				"Lighting"="ForwardBase"
			}
			CGPROGRAM	
			#include "Lighting.cginc"
			#pragma vertex vert 
			#pragma fragment frag 
			fixed4 _Color;
			//纹理贴图
			sampler2D _MainTex;
			//_MainTex_ST的前二维代表纹理贴图的平铺数,后二维代表纹理贴图的偏移数。
			//这个变量必须命名为纹理贴图变量名+"_ST"
			//ST表示scale 和 transition
			float4 _MainTex_ST;
			//法线贴图
			sampler2D _NormalMap;
			//法线贴图的平铺与偏移
			//与纹理贴图的情况类似,命名必须为法线贴图名+"_ST"
			float4 _NormalMap_ST;
			float _BumpScale;

			struct a2v{
				//模型空间下的顶点坐标
				float4 vertex:POSITION;
				//模型的法线
				//之后要用切线空间,切线空间是通过模型的法线和切线确定的。所以要获得法线和切线然后传输到片元函数。
				float3 normal:normal;
				//模型的切线
				//这里的方向不再用float3而用float4,因为需要第4维用来确定切线空间中坐标轴的方向
				float4 tangent:TANGENT;
				//通过语义:TEXCOORD 获得了uv坐标
				float2 texcoord:TEXCOORD;
			};
			struct v2f{
				//剪切空间下的顶点坐标
				float4 svPos:SV_POSITION;
				//存储切线空间下平行光的方向
				//用TEXCOORD、COLOR解释时,并不代表只能存储纹理坐标、颜色。这里存了方向。
				float3 lightDir:TEXCOORD0;
				//世界空间下的顶点坐标
				float4 worldVertex:TEXCOORD1;
				//存放uv坐标用,以便传递到片元函数中。xy存储纹理贴图坐标,zw存储法线贴图坐标。xyzw分别代表第1、2、3、4维。
				float4 uv:TEXCOORD3;
			};
			v2f vert(a2v v){
				v2f f;
				f.svPos=mul(UNITY_MATRIX_MVP,v.vertex);
				//v.texcoord即uv坐标在这里表示现在需要使用纹理/法线贴图的哪个位置
				//对现在需要使用纹理贴图的哪个位置的坐标进行缩放和平移
				//*_MainTex_ST.xy表示乘上平铺数,+_MainTex_ST.zw则表示加上偏移
				f.uv.xy=v.texcoord*_MainTex_ST.xy+_MainTex_ST.zw;
				//对法线贴图也进行类似操作
				f.uv.zw=v.texcoord*_NormalMap_ST.xy+_NormalMap_ST.zw;
				//调用一个宏得到一个矩阵rotation用来把模型空间下的方向转换到切线空间下。
				//用这个宏必须配合v、f、normal、tangent这几个固定的变量名才能正常使用。
				//如果没有用这几个变量名或用来表示其它内容就可能报错。
				TANGENT_SPACE_ROTATION;
				//rotation就是之前得到的矩阵,用来把模型空间下的方向转换到切线空间的下。
				//ObjSpaceLightDir(v.vertex)表示得到该顶点的模型空间下的光源方向
				f.lightDir= mul(rotation,ObjSpaceLightDir(v.vertex));

				return f;
			}
			//因为从法线贴图取得的法线方向在切线空间下,所以把所有跟法线方向有关的运算都放在切线空间下进行。
			fixed4 frag(v2f f):SV_TARGET{
				//读取法线贴图的颜色
				//tex2D(贴图,坐标)就表示读取该贴图的相应坐标的颜色
				fixed4 normalColor=tex2D(_NormalMap,f.uv.zw);
				//使用unity自带的方法通过法线贴图的颜色得到切线空间下的法线
				fixed3 tangentNormal=UnpackNormal(normalColor);
				//切线空间下的法线的z轴实际就是模型空间下模型的法线
				//_BumpScale为使用法线贴图的比例,只需要将从法线贴图得到的切线空间下的法线的xy乘这个系数而不用管z
				//当_BumpScale为0时该向量只剩z轴自然就成了模型空间下模型的法线。
				tangentNormal.xy=tangentNormal.xy*_BumpScale;
				tangentNormal=normalize(tangentNormal);
				//顶点函数中已经将光源方向转换到了切线空间
				fixed3 lightDir=normalize(f.lightDir);
				//获得纹理贴图上对应的颜色
				fixed4 texcolor= tex2D(_MainTex,f.uv.xy)*_Color;
				//将纹理贴图的颜色融入到漫反射颜色中。
				//同时法线也影响着该点的明暗,从而在一些区域形成阴影以增加细节。
				fixed3 diffuse=_LightColor0.rgb*texcolor.rgb*max(0,dot(tangentNormal,lightDir));
				//通过漫反射颜色和环境光叠加得到最终颜色。这里让环境光颜色也融合了纹理贴图颜色。
				fixed3 tempColor=diffuse+UNITY_LIGHTMODEL_AMBIENT.rgb*texcolor;
				
				return fixed4(tempColor,1);
			}
			ENDCG
		}
	}
	Fallback "Diffuss"
}

为模型选择新写的shader后,在工程面板中添加纹理贴图和法线贴图。先在工程面板中设置一下图片的类型,纹理贴图需要是Texture,法线贴图是Normal map。

然后给模型的这个Shader添加纹理贴图和法线贴图:

 

 ​​之后可以调整BumpScale,当为0时完全未使用法线贴图,模型的细节就比较少。当为1时完全使用法线贴图,模型表面的细节增加了很多。当大于1时就比较夸张了。

<think>好的,用户是一位Unity开发者,只会C#,现在想从零开始学习HLSL语法。我需要从基础开始教他,并且要考虑到他已有的C#背景,可能需要对比两者差异,帮助他理解。首先,我应该先介绍HLSL是什么,在Unity中的作用,比如用于编写Shader,实现图形效果。然后,可能需要讲解HLSL的基本语法结构,变量类型,以及C#的不同之处,比如HLSL更注重数据并行处理图形管线中的特定阶段。 接下来,用户可能需要知道如何开始写一个简单的Shader,比如表面着色器或顶点/片元着色器。需要举例说明,比如创建一个Unlit Shader,然后逐步解析代码结构,包括Properties块、SubShader、Pass块等。然后进入HLSL部分,比如CGPROGRAM中的顶点片元着色器函数。 然后,可能需要讲解HLSL中的数据类型,比如float、half、fixed的区别,矩阵类型,以及语义(semantics)如SV_POSITION、TEXCOORD0的作用。同时,要强调语义在着色器中的重要性,因为它们在管线中传递数据。 另外,用户可能对如何从C#传递数据到Shader感兴趣,比如使用Material.SetFloat或Shader.SetGlobalTexture,需要说明HLSL如何通过cbuffer或直接声明变量来接收这些数据。 还需要涵盖常用的内置函数,比如mul用于矩阵乘法,tex2D用于采样纹理,以及如何编写自定义函数。同时,流程控制语句如if、for循环可能C#类似,但要注意在Shader中的性能影响。 用户可能遇到的常见问题包括语法错误、变量未正确绑定、语义缺失等,需要给出调试建议,比如使用Frame Debugger或Visual Studio的Shader调试工具。 最后,建议学习资源,如微软的HLSL文档、Unity手册中的Shader部分,以及示例项目供练习。可能需要提醒用户学习Shader数学知识,如向量、矩阵运算光照模型,以更好地应用HLSL。 在整个过程中,需要通过示例代码对比C#HLSL的差异,帮助用户迁移已有的编程思维到着色器编写中。同时,强调实践的重要性,建议从修改现有Shader开始,逐步构建复杂效果。</think>好的!我将从零开始带你学习HLSL(High-Level Shading Language)。作为Unity开发者,你已经有C#基础,理解编程逻辑会更容易。我们从基础概念到实际代码逐步深入,结合Unity中的使用场景。 --- ### **一、HLSL是什么?** - **用途**:用于编写GPU运行的着色器代码(Shader),控制图形渲染流程(顶点处理、光照、像素颜色等)。 - **C#的区别**:HLSL是数据并行的(每个像素/顶点独立处理),没有类、循环谨慎使用(避免性能问题),语法类似C,但有GPU特有的概念(如语义、纹理采样)。 --- ### **二、基础语法快速入门** #### 1. **基本结构(Unity Shader示例)** ```hlsl Shader "Unlit/MyFirstShader" { Properties { _MainTex ("Texture", 2D) = "white" {} _Color ("Color", Color) = (1,1,1,1) } SubShader { Pass { CGPROGRAM #pragma vertex vert // 声明顶点着色器函数 #pragma fragment frag // 声明片元着色器函数 // HLSL代码从这里开始 #include "UnityCG.cginc" // 引用Unity内置函数 struct appdata { float4 vertex : POSITION; // 输入顶点位置(语义绑定) float2 uv : TEXCOORD0; // 输入UV坐标 }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; // 输出裁剪空间位置 }; sampler2D _MainTex; float4 _Color; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); // 转换到裁剪空间 o.uv = v.uv; return o; } float4 frag (v2f i) : SV_Target { float4 col = tex2D(_MainTex, i.uv) * _Color; // 采样纹理 return col; } ENDCG } } } ``` --- #### 2. **数据类型** - **标量**:`float`(32位)、`half`(16位,适合颜色)、`fixed`(低精度) - **向量**:`float3`(xyz/rgb)、`float4`(xyzw/rgba) - **矩阵**:`float4x4`(4x4矩阵,用于变换) - **纹理**:`sampler2D`(2D纹理)、`samplerCUBE`(立方体贴图) --- #### 3. **语义(Semantics)** - 用于标记数据的用途(如顶点位置、UV坐标): ```hlsl float4 vertex : POSITION; // 输入顶点位置 float4 pos : SV_POSITION; // 输出裁剪空间位置 float4 color : COLOR0; // 颜色数据 float2 uv : TEXCOORD0; // 纹理坐标 ``` --- #### 4. **常用内置函数** - **数学**:`sin`, `cos`, `pow`, `sqrt`, `dot`(点积), `cross`(叉积) - **矩阵**:`mul(matrix, vector)` 矩阵乘法 - **纹理采样**:`tex2D(sampler2D, uv)` - **空间转换**: - `UnityObjectToClipPos(v)`:模型空间→裁剪空间 - `WorldSpaceViewDir(v)`:获取世界空间视角方向 --- ### **三、C#的交互** 在C#中通过`Material`或`Shader`类传递数据到HLSL: ```csharp Material mat = GetComponent<Renderer>().material; mat.SetFloat("_MyFloat", 1.0f); // 对应HLSL中的 float _MyFloat; mat.SetVector("_MyVector", new Vector4(1,0,0,1)); mat.SetTexture("_MainTex", myTexture); ``` --- ### **四、核心概念:着色器阶段** 1. **顶点着色器(Vertex Shader)**: - 处理每个顶点的位置、法线、UV等。 - 输出裁剪空间位置(必须赋给带`SV_POSITION`语义的变量)。 2. **片元着色器(Fragment Shader)**: - 计算每个像素的最终颜色。 - 返回`float4`(RGBA颜色),需绑定`SV_Target`语义。 --- ### **五、实战练习:渐变颜色效果** ```hlsl // 在片元着色器中添加以下代码 float4 frag (v2f i) : SV_Target { // 根据UV的x值做渐变(从红色到蓝色) float3 color = lerp(float3(1,0,0), float3(0,0,1), i.uv.x); return float4(color, 1); } ``` --- ### **六、调试技巧** - 用`return float4(i.uv.x, i.uv.y, 0, 1);` 可视化UV坐标。 - 在Unity编辑器中使用**Frame Debugger**(Window → Analysis → Frame Debugger)逐步查看渲染过程。 --- ### **七、学习资源推荐** 1. **官方文档**: - [Microsoft HLSL文档](https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl) - [Unity Shader手册](https://docs.unity3d.com/Manual/SL-Reference.html) 2. **书籍**: - 《Unity Shader入门精要》(适合中文读者) - 《The Book of Shaders》(可视化学习网站) 3. **实践**: - 修改Unity Standard Shader,观察效果变化。 - 在ShaderToy(https://www.shadertoy.com/)学习高级效果。 --- ### **下一步建议** 1. 从修改简单参数(颜色、UV偏移)开始。 2. 实现基础光照模型(如Lambert漫反射)。 3. 学习法线贴图、边缘光等常见效果。 如果有具体问题或想深入某个方向,随时告诉我!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值