Shader语法基础

Shader结构

Shader 后跟随的是该Shader的名称

Shader "Custom/DiffuseShader"{

}

Properties 块

  • 定义了可以在 Unity 编辑器中调整的变量,如颜色、纹理、浮点数等

  • 这些变量可以在材质面板中被修改,从而影响最终的渲染效果

[KeywordEnum(X, Y, Z)] _Faces ("Faces", Float) = 0

定义一个下拉列表,其中包含多个选项。XYZ 是下拉列表中的选项名称

当用户在材质编辑器中选择一个选项时,相应的关键字会被定义,可以在着色器代码中使用这些关键字来控制不同的代码路径

可以在 Unity 的材质编辑器中,你会看到一个名为 "Faces" 的下拉列表,其中包含三个选项:XYZ。用户可以选择其中一个选项,选择的选项会定义相应的关键字(_FACES_X_FACES_Y_FACES_Z),从而影响着色器代码的编译和运行。

Properties
{
 	_Color ("Main Color", Color) = (1,1,1,1)
	_MainTex ("Base (RGB)", 2D) = "white" {}
	[KeywordEnum(X, Y, Z)] _Faces ("Faces", Float) = 0
}

SubShader 块

  • 每个 Shader 至少需要一个 SubShader

  • SubShader 中包含了实际执行的着色器代码,包括顶点着色器(Vertex Shader)、片段着色器(Fragment Shader)、表面着色器(Surface Shader)等

  • 如果一个 Shader 在某些硬件上无法运行,Unity 会尝试使用下一个 SubShader

Tags标签

Tags 标签用于指定 Shader 的一些重要属性,这些属性可以帮助 Unity 更好地管理和优化渲染流程。Tags 标签通常放在 SubShader 块中,用于提供关于如何处理该 SubShader 的附加信息

  • RenderType:指定渲染类型,会影响渲顺序和混合模式

    • "Opaque": 当 RenderType 设置为 "Opaque" 时,Unity 会将使用该 Shader 的对象放入不透明渲染队列。不透明对象的渲染顺序通常是按对象的深度进行排序,从近到远(即从摄像机最近的对象开始渲染)

    • "Transparent":当 RenderType 设置为 "Transparent" 时,Unity 会将使用该 Shader 的对象放入透明渲染队列

Tags { "RenderType"="Opaque" }

LOD

LOD(Level of Detail,细节等级)用于控制 Shader 的复杂度和渲染性能。LOD 值可以用来决定在不同情况下使用哪些 SubShader 或 Pass。通过设置 LOD,你可以在性能和视觉效果之间找到一个平衡点。

  • 控制 Shader 的复杂度:LOD 值用于指定 Shader 的复杂度。较低的 LOD 值表示较低的复杂度,较高的 LOD 值表示较高的复杂度。

  • 优化性能

当LOD设置为200时,表示中等复杂度的 Shader,一般是漫反射。

LOD 200

CGPROGRAM和ENDCG

用于包围用 HLSL(High-Level Shading Language)编写的着色器代码块。这两个关键字告诉 Unity 编译器,这部分代码是用 HLSL 编写的,需要特殊处理。

#pragma 编译指令

它用于控制编译器的行为,它可以用于多种目的,包括指定着色器类型、优化编译选项、启用或禁用特定功能等

  • #pragma shader_feature:用于在编译时有条件地包含或排除代码块,你可以在 Unity 编辑器中通过材质属性或脚本来启用或禁用这些关键字

#pragma shader_feature _FACES_X _FACES_Y _FACES_Z

#if defined(_FACES_X)
	// 处理 X 面的逻辑
	o.cubeUV = v.color.yz * 255;
#elif defined(_FACES_Y)
	// 处理 Y 面的逻辑
	o.cubeUV = v.color.xz * 255;
#elif defined(_FACES_Z)
	// 处理 Z 面的逻辑
	o.cubeUV = v.color.xy * 255;

  • #pragma surface:定义一个表面着色器

语法:#pragma surface surf [lightModel] [additionalOptions] [vertex:vertexFunction]

  • surf:表面着色器的主函数名

  • lightModel:光照模型,例如 StandardLambertBlinnPhong

  • additionalOptions:额外的选项,例如 fullforwardshadowsaddshadow

    • fullforwardshadows启用前向渲染中的完整阴影,适用于需要高质量阴影的场景

  • vertex:vertexFunction:可选的顶点着色器函数名

#pragma surface surf Standard fullforwardshadows vertex:vert

// 顶点作色器函数名
void vert ()
{

}

// 表面着色器函数名
void surf ()
{

}

  • #pragma target:指定目标 HLSL 版本或特定的 GPU 特性

语法:#pragma target [version]

version:版本

  • 3.0:支持更多的高级特性,如几何着色器和双线性插值。

CG语法

变量类型

定义一个变量并使用,需要

1. 在 Shader 的 Properties 块中定义纹理属性

2. 在CG代码块中声明 sampler2D 变量。

sampler2D:二维纹理的采样器类型,它允许你在着色器中读取和采样纹理数据,使用tex2D进行采集纹理

顶点着色器函数

顶点着色器函数 vert 的参数用于传递和处理顶点数据

void vert (inout appdata_full v, out Input o)

参数说明

inout appdata_full v

  • inout 关键字表示这个参数既可以作为输入,也可以作为输出。也就是说,你可以在函数内部修改这个参数的值,这些修改会反映到调用者

  • appdata_full 是一个结构体,包含了顶点的所有属性,如位置、法线、切线、颜色、纹理坐标等

    • float4 vertex : POSITION:顶点的位置

    • float3 normal : NORMAL:顶点的法线

    • float4 tangent : TANGENT:顶点的切线

    • float4 color : COLOR:顶点的颜色

      • v.color.yz * 255 某个顶点的颜色的分离乘以255,这么做的目的是将顶点颜色的分量从【0,0】变为【0,255】将颜色值转换为更适合用作 UV 坐标的值

out Input o

  • out 关键字表示这个参数只能作为输出

  • Input 是一个用户定义的结构体,用于存储从顶点着色器传递到片段着色器的数据

表面着色器函数

void surf (Input IN, inout SurfaceOutputStandard o) {
	// 采集_MainTex对于uv的颜色,并进行混色
	fixed4 c = tex2D (_MainTex, IN.cubeUV) * _Color;
	// 采集的颜色应用到表面漫反射上
	o.Albedo = c.rgb;
	// 设置金属感,0为非金属,1为金属
	o.Metallic = _Metallic;
	// 设置光滑感,0为粗糙,1为光滑
	o.Smoothness = _Glossiness;
	// 设置透明度,0完全透明~1完全不透明
	o.Alpha = c.a;
}

Input IN

  • IN 是一个输入结构体,包含从顶点着色器传递过来的数据

inout SurfaceOutputStandard o

  • SurfaceOutputStandard 是 Unity 内置的结构体,包含多个表面属性

常用的宏

UNITY_INITIALIZE_OUTPUT(Input, o):这是一个宏,用于初始化 Input 结构体。确保所有字段都被正确初始化,避免未定义行为。让Input结构体里面的变量都初始化,并给o