[Shader] Unity CGPROGRAM / ENDCG

3.2.6. | CGPROGRAM / ENDCG.

我们上面回顾的所有部分都是用 ShaderLab 声明性语言编写的,我们在图形编程语言中的真正挑战从 CGPROGRAM 或 HLSLPROGRAM 声明开始。

默认情况下,我们可以看到Unity在我们的程序中包含了CG这个词。 对此,官方文档(2021.2版本)中表示:

Unity最初使用Cg语言,因此有一些单词和.cginc扩展名,但该软件不再使用这种语言,但是,这些单词仍然包含在内,出于兼容性原因,代码仍在编译中。

如上所述,在未来的版本中,我们的着色器很可能会以 HLSLPROGRAM 而不是 CGPROGRAM 的声明出现,因为 HLSL 目前是官方图形编程语言(事实上,Shader Graph 就是基于它的)。

无论如何,我们可以简单地通过将 HLSLPROGRAM 替换为 CGPROGRAM,将 ENDHLSL 替换为 ENDCG 来更新着色器,然后我们的程序将在Built-in RP 中进行编译,就像在Universal RP 和High Definition RP 中一样。

如果我们想将程序更新为 HLSL,我们将必须更改一些设置并向着色器添加一些路由,这将使理解变得更加复杂。

// CG version
Pass
{
	CGPROGRAM
	 …
	ENDCG
}
// HLSL version
Pass
{
	HLSLPROGRAM
	 …
	ENDHLSL
}

我们的着色器编译所需的所有函数都将写入 CGPROGRAM 和 ENDCG 字段内。 该片段之外的那些函数将被Unity作为ShaderLab属性,如果它们不对应于该语言,那么我们的程序将无法编译。

Shader "InspectorPath/shaderName"
{
	Properties {}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		Pass
		{
			CGPROGRAM
			// write all functions here
			ENDCG
		}
	}
}

3.2.7. | Data Types.

在继续定义着色器的属性和功能之前,我们必须查看数据类型,因为 Cg 和 HLSL 之间存在细微差别。

当我们在当前版本的Unity中创建默认着色器时,我们可以找到精度不同的浮点数,其中:

• float.
• half.
• fixed.

用 Cg 编写的着色器可以完美地编译这三种类型的精度,但是 HLSL 无法编译“fixed”数据类型,因此,如果我们使用这种语言,我们将不得不替换 这种类型要么有一half,要么有float。

“Float”(Cg 和 HLSL)是一种高精度数据类型,为 32 位,通常用于计算世界空间中的位置、纹理坐标 (UV) 或涉及三角函数或指数等复杂函数的标量计算。

“Half”(Cg 和 HLSL)是半精度,为 16 位,主要用于低幅度向量、方向、对象空间位置和高动态范围颜色的计算。

“Fixed”(Cg)精度较低,只有11位,主要用于简单运算(例如基本颜色存储)的计算。

在向量使用中经常出现的一个问题是,如果我们只对所有变量使用浮点数据类型(Float)会发生什么? 实际上这是可能的,但是我们必须考虑到float是一种高精度数据类型,这意味着它有更多的小数,因此,GPU将花费更长的时间来计算它,增加时间并产生热量。

使用所需数据类型的向量和/或变量至关重要,因此我们可以优化程序,减少 GPU 上的图形负载。

我们可以在两种语言中找到的其他广泛使用的数据类型是“Int、sampler2D 和samplerCube”。

“Sampler” 指的是纹理的采样状态。 在这种类型的数据中,我们可以存储纹理及其 UV 坐标。

一般来说,当我们想要在着色器中使用纹理时,我们必须使用“Texture2D”类型来存储纹理并创建一个“SamplerState”来采样。 数据类型“Sampler”允许我们将texture和 sampling status 存储在单个变量中。 为了详细了解采样器的功能,我们将执行以下操作:

// declare the _MainTex texture as a global variable
Texture2D _MainTex;
// declare the _MainTex sampler as a global variable
SamplerState sampler_MainTex;
// go to the fragment shader
half4 frag(v2f i) : SV_Target
{
	// inside the col vector sample the texture in UV coordinates.
	half4 col = _MainTex.Sample(sampler_MainTex, i.uv);
	// return the color of the texture
	return col;
}

只需使用“采样器”即可优化上述过程。

// declare the sampler for _MainTex
sampler2D _MainTex;
// go to the fragment shader stage
half4 frag(v2f i) : SV_Target
{
	// sampler the texture in UV coordinates using the function tex2D().
	half4 col = tex2D(_MainTex, i.uv)
	// return the color of the texture.
	retu rn col;
}

这两个示例都返回与我们在属性中生成的纹理相对应的相同值,并将从 Unity Inspector 分配。

我们可以在程序中使用标量值、向量和矩阵。

标量值是返回实数的值,可以是整数或小数(例如 1、0.4、4 等),要在着色器中声明它们,我们必须首先添加“数据类型”,然后添加“标量值名称” 最后初始化其默认值。

其语法如下:

float name = n; // e.g. float a = 0;
half name = n; // e.g. float b = 1.456;
fixed name = n; // e.g. float c = 2.5;

向量是那些返回多维值的向量(例如 XYZW),要在着色器中声明它们,我们必须首先添加“数据类型”,然后是“维度编号”,然后是“向量名称”,最后初始化其默认值 。

其语法如下:

float2 name = n; // e.g. float2 uv = float2(0.5, 0.5);
half3 name = n; // e.g. half3 position = float3(0, 0, 0);
fixed4 name = n; // e.g. fixed4 color = float4(1, 1, 1, 1);

矩阵是那些以行和列存储值的矩阵,它们具有多个维度,主要用于剪切、旋转和改变顶点位置。

要在着色器中声明矩阵,我们必须首先添加“数据类型”,然后是“维度数量与其自身相乘”,然后是“矩阵名称”,最后“初始化其默认值”,考虑到坐标 Xx、Yy &Zz 将等于 1。

其语法如下:

// three rows and three columns
float3x3 name = float3x3
(
	1, 0, 0,
	0, 1, 0,
	0, 0, 1
);
// two rows and two columns
half2x2 name = half2x2
(
	1, 0,
	0, 1
);
// four rows and four columns
fixed4x4 name = fixed4x4
(
	1, 0, 0, 0,
	0, 1, 0, 0,
	0, 0, 1, 0,
	0, 0, 0, 0
);

如果您刚开始接触 Unity 着色器的世界,您很可能无法完全理解所解释的内容。 别担心,在本章后面我们将详细回顾所有这些参数,并了解片段着色器阶段是如何工作的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值