首页 > 其他分享 >【Unity3D】表面着色器

【Unity3D】表面着色器

时间:2023-03-21 09:55:20浏览次数:70  
标签:Unity3D 法线 MainTex uv CG 纹理 表面 着色器

1 前言

固定管线着色器一固定管线着色器二 中介绍了 ShaderLib 的基本用法,本文将接着讲解表面着色器(Surface Shader)的用法。固定管线着色器基于 ShaderLib 命令实现,表面着色器基于 CG 语言实现。目前主流的 Shader 编程语言主要有 GLSL、HLSL、CG,如下:

  • GLSL:OpenGL Shading Language,基于 OpenGL 接口,跨操作系统,依赖硬件,可以在 Windows 、Linux、Mac、Web、移动端跑,这种跨平台是由于 OpenGL 没有提供编译器,由显卡驱动完成着色器的编译工作,即显卡驱动支持 OpenGL,它就可以运行;
  • HLSL:High Level Shading Language,基于 DirectX 接口,由微软研发,不依赖硬件,就算有不同的硬件,同一个着色器编译结果是一样的;
  • CG:C for Graphic,由 NVIDIA(英伟达)研发,跨操作系统,不依赖硬件,它会根据平台的不同编译成相应的中间语言,CG 语言的跨平台性很大程度取决于与微软的合作,这也导致 CG 和 HLSL 非常相似,CG 可以无缝移植到 HLSL 平台上,但是缺点是无法完全发挥 OpenGL 的新特性。

2 CG 语言基础

1)基本数据类型

float // 32位浮点数, 精度: 小数点后6位, 适用: 模型坐标、纹理uv坐标
half // 16位浮点数, 精度: 小数点后3位, 适用: 模型坐标、纹理uv坐标
fixed // 12位浮点数, 精度: 1/256, 适用: 光照计算、颜色
int // 32位整数
bool // 8位布尔数
string // 字符串
sampler1D, sampler2D, sampler3D, samplerCUBE, samplerRECT // 纹理对象

​ 补充:对于 float、half、int、fixed、bool 类型,可以在其后添加 2、3、4,以扩充维度,如:float2、int3、fixed4。

2)Input

struct Input
{
	float3 worldPos; // 世界坐标
	float3 screenPos; // 屏幕坐标
	half3 viewDir; // 观察方向, 顶点指向相机
	float4 color : COLOR; // 顶点颜色, 后面的COLOR是语义绑定
	fixed2 uv_MainTex; // 纹理uv坐标(命名方式必须是uv_纹理变量名称)
	fixed2 uv_NormTex; // 法线纹理uv坐标(命名方式必须是uv_法线纹理变量名称)
};

​ 说明:Input 结构体需要自定义,里面的变量可以根据需要添加。

3)SurfaceOutput

struct SurfaceOutput
{
	half3 Albedo; // 反射率, 即颜色纹理rgb
	half3 Normal; // 法向量
	half3 Emission; // 自发光颜色rgb
	half Alpha; // 透明度
	half Specular; // 镜面反射度
	half Gloss; // 光泽度
};

​ 说明:SurfaceOutput 结构体是系统自定义的结构体,不需要用户定义。

4)常用函数

saturate(var, min, max) // 将变量var约束在min和max之间, 超过边界就取边界值
dot(vec1, vec2) // 向量点乘
cross(vec1, vec2) // 向量叉乘
normalize(vec) // 向量归一化
tex2D(sampler2D, uv_Tex) // 查询纹理坐标对应的纹理值
UnpackNormal(color) // 根据法线纹理解析法线向量

3 光照

​ 在 Assets 窗口右键,依次选择【Create→Shader→Standard Surface Shader】创建 Shader 脚本,实现光照效果代码如下:

​ SurfaceShader.shader

Shader "MyShader/SurfaceShaderTest" {
	Properties
	{
		// 属性名 ("面板显示名称", 类型) = 默认值
		_DiffuseColor ("漫反射颜色", Color) = (1, 0, 0, 0.5)
	}

	SubShader
	{
		CGPROGRAM // CG语言的开始
		// 编译指令 着色器类型 函数名称 光照模型 开启透明度
		#pragma surface surf Lambert Alpha
		// 声明属性变量, 必须与外部属性变量名称一致
		fixed4 _DiffuseColor;

		struct Input
		{
			float4 color : COLOR; // 顶点颜色, 后面的COLOR是语义绑定
		};

		void surf (Input IN, inout SurfaceOutput o) // 表面着色器函数
		{
			o.Albedo = _DiffuseColor.rgb; // 像素颜色
			o.Alpha = _DiffuseColor.a; // 像素透明度
		}

		ENDCG // CG语言的结束
	}

	FallBack "Diffuse"
}

​ 创建一个Material,并将 ShaderTest 绑定到该 Material 上,如下:

img

​ 将该 Material 拖拽到一个 Cube 和 Sphere 游戏对象上。选中绑定的 Material,在 Inspector 窗口调整 Shader 中光照颜色,显示效果如下:

img

4 贴图

​ SurfaceShader.shader

Shader "MyShader/SurfaceShaderTest" {
	Properties
	{
		// 属性名 ("面板显示名称", 类型) = 默认值
		_MainTex ("2阶贴图", 2D) = "white" {}
	}

	SubShader
	{
		CGPROGRAM // CG语言的开始
		// 编译指令 着色器类型 函数名称 光照模型
		#pragma surface surf Lambert
		// 声明属性变量, 必须与外部属性变量名称一致
		sampler2D _MainTex;

		struct Input
		{
			fixed2 uv_MainTex; // 纹理uv坐标(命名方式必须是uv_纹理变量名称)
		};

		void surf (Input IN, inout SurfaceOutput o) // 表面着色器函数
		{
			o.Albedo = tex2D(_MainTex, IN.uv_MainTex); // 像素颜色
		}

		ENDCG // CG语言的结束
	}

	FallBack "Diffuse"
}

​ 选中绑定的 Material,在 Inspector 窗口选择贴图图片,显示效果如下:

img

5 法线贴图

​ 光照的漫反射、镜面反射都是通过法线向量计算得到,法线向量经 PackNormal 函数处理后,值域映射到 [0, 1],由于颜色与处理后的法向量维度(3维)和值域([0, 1])一样,因此可以用颜色描述法线向量,即用一张法线图描述原图片每个像素位置的法线,取出法线图中某位置的颜色,再通过 UnpackNormal 函数映射,即可得到对应像素位置的法线向量。

​ 如下,是一张原图和对应的法线图,可以看到,原图和法线图的轮廓很相似,因此美工可以通过调整原图的色相得到法线图。法线的 (x, y, z) 分量对应颜色的 (r, g, b) 分量,z 值越大,法线越偏向观察方向,法线图越偏向蓝色,这也是正视图一般偏向蓝色的原因。Unity3D 中,用户可以通过修改图片的 Texture Type 为 Normal map 生成法线图。

img 原图与法线图

​ SurfaceShader.shader

Shader "MyShader/SurfaceShaderTest" {
	Properties
	{
		// 属性名 ("面板显示名称", 类型) = 默认值
		_MainTex ("2阶贴图", 2D) = "white" {}
		_NormTex ("法线贴图", 2D) = "white" {}
	}

	SubShader
	{
		CGPROGRAM // CG语言的开始
		// 编译指令 着色器类型 函数名称 光照模型
		#pragma surface surf Lambert

		// 声明属性变量, 必须与外部属性变量名称一致
		sampler2D _MainTex;
		sampler2D _NormTex;

		struct Input
		{
			fixed2 uv_MainTex; // 纹理uv坐标(命名方式必须是uv_纹理变量名称)
			fixed2 uv_NormTex; // 法线纹理uv坐标(命名方式必须是uv_法线纹理变量名称)
		};

		void surf (Input IN, inout SurfaceOutput o) // 表面着色器函数
		{
			o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb; // 像素颜色
			o.Normal = UnpackNormal(tex2D(_NormTex, IN.uv_NormTex)); // 像素法线
		}

		ENDCG // CG语言的结束
	}

	FallBack "Diffuse"
}

​ 选中绑定的 Material,在 Inspector 窗口选择贴图和法线图,并将该 Material 拖拽到一个 Cube 对象上,再新建一个 Cube 对象(作为对比),绑定普通的 Material,显示效果如下:

img

​ 说明:左边是法线贴图,右边是普通贴图,左边图片明显比右边更有质感。

6 自发光

​ 本节在法线贴图的基础上,在物体凹凸边缘添加高亮效果。物体凹凸边缘是指物体表面法线与观察方向近似垂直的地方。

​ SurfaceShader.shader

Shader "MyShader/SurfaceShaderTest" {
	Properties
	{
		// 属性名 ("面板显示名称", 类型) = 默认值
		_MainTex ("2阶贴图", 2D) = "white" {}
		_NormTex ("法线贴图", 2D) = "white" {}
		_RimColor ("自发光颜色", Color) = (1, 1, 1, 1)
		_RimStrength ("自发光强度", Range(0, 5)) = 0.5
	}

	SubShader
	{
		CGPROGRAM // CG语言的开始
		// 编译指令 着色器类型 函数名称 光照模型
		#pragma surface surf Lambert

		// 声明属性变量, 必须与外部属性变量名称一致
		sampler2D _MainTex;
		sampler2D _NormTex;
		fixed4 _RimColor;
		half _RimStrength;

		struct Input
		{
			fixed2 uv_MainTex; // 纹理uv坐标(命名方式必须是uv_纹理变量名称)
			fixed2 uv_NormTex; // 法线纹理uv坐标(命名方式必须是uv_法线纹理变量名称)
			half3 viewDir; // 观察方向, 顶点指向相机
		};

		void surf (Input IN, inout SurfaceOutput o) // 表面着色器函数
		{
			o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb; // 像素颜色
			o.Normal = UnpackNormal(tex2D(_NormTex, IN.uv_NormTex)); // 像素法线
			half rimPower = 1 - saturate(dot(normalize(IN.viewDir), normalize(o.Normal))); // 计算自发光系数
			// o.Emission = _RimColor * _RimStrength * rimPower;
			o.Emission = _RimColor * pow(rimPower, _RimStrength); // 自发光颜色
		}

		ENDCG // CG语言的结束
	}

	FallBack "Diffuse"
}

​ 选中绑定的 Material,在 Inspector 窗口选择贴图和法线图,并调整自发光颜色和自发光强度,将该 Material 拖拽到一个 Cube 对象上,显示效果如下:

img

​ 声明:本文转自【Unity3D】表面着色器

标签:Unity3D,法线,MainTex,uv,CG,纹理,表面,着色器
From: https://www.cnblogs.com/zhyan8/p/17237931.html

相关文章

  • 【Unity3D】AudioSource组件
    1简介​1)AudioSource与AudioListener简介​AudioSource(音频源)组件用于控制播放AudioClip(音频片段),能够控制2D和3D(距离越远,声音越小)声音播放,它一般挂在产......
  • 【Unity3D】场景切换、全屏_恢复切换、退出游戏、截屏
    1前言​1)场景切换​场景切换可以使用SceneManager的LoadScene和LoadSceneAsync方法,如下:publicstaticvoidLoadScene(stringsceneName)publicstatic......
  • 【Unity3D】GUI控件
    1前言​Unity3D提供了GUI、NGUI、UGUI等图形系统,以增强玩家与游戏的交互性。GUI在编译时不能可视化,在运行时才能可视化。GUI代码需要在OnGUI函数中调用才能......
  • 【Unity3D】相机跟随
    1前言​相机跟随是相机指始终跟随特定游戏对象,有以下2种跟随效果:位置跟随:相机指向目标游戏对象的向量始终不变位置和姿态跟随:相机在目标游戏对象的坐标系下的坐......
  • 【Unity3D】UGUI之Text
    1Text简介​UGUI概述中介绍了Canvas渲染模式、RectTransform组件、锚点(Anchor)等,本文将介绍UGUI中的Text控件。​在Hierarchy窗口右键,选择UI列表里......
  • 【Unity3D】UGUI概述
    1UGUI与GUI区别​GUI控件在编译时不能可视化,并且界面不太美观,在实际应用中使用的较少。UGUI在编译时可视化,界面美观,实际应用较广泛。2Canvas渲染模式(Render......
  • 【Unity3D】UGUI之Button
    1Button属性面板​在Hierarchy窗口右键,选择UI列表里的Button控件,即可创建Button控件,选中创建的Button控件,按键盘【T】键,可以调整Button控件的大小和位置......
  • 【Unity3D】UGUI之Image和RawImage
    1纹理(Texture)​Image控件和RawImage控件都是承载渲染图片的控件,都需要指定一个纹理(Texture)图片。在Assets窗口选中一张图片,在Inspector窗口的参数设置面板可......
  • 【Unity3D】UGUI之Slider
    1Slider属性面板​在Hierarchy窗口右键,选择UI列表里的Slider控件,即可创建Slider控件,选中创建的Slider控件,按键盘【T】键,可以调整Slider控件的大小和位置......
  • 【Unity3D】UGUI之Toggle
    1Toggle属性面板​在Hierarchy窗口右键,选择UI列表里的Toggle控件,即可创建Toggle控件,选中创建的Toggle控件,按键盘【T】键,可以调整Toggle控件的大小和位置......