Unity Shader学习笔记 (三)—— C for graphics 专题(一)

概述

前面的一些博客里展示的一些Untiy Shader中,实际的顶点着色器和片元着色器代码被编写到了一个语法块CGPROGRAMENDCG中(例如这篇博客)。这个语法块告诉我们,在这里面编写的代码遵从CG的语法。
CG = C for graphics。顾名思义,可以理解为针对图形编程的C语言。实际上,Cg是NVIDIA为GPU编程设计开发的一门新的语言,但它保留了C语言大部分的语义,同时又考虑GPU的体系结构添加了一些新的语义和特性,让开发者能够方便地在图形编程领域进行开发。
从这篇文章开始,我学习的CG都是Untiy Shader中使用的一些语法,Unity Shader 的 CG和实际的CG有小小的区别,虽影响不大,但是仍需注意

语言特性

数据类型

按照维基百科上的介绍,CG总共有六种数据大类,其中一些是针对GPU专门是设计的:

  • float - 32bit浮点数
  • half - 16bit浮点数
  • int - 32bit整数
  • fixed - 12bit定点数
  • bool - 布尔值
  • sampler* - 代表纹理物件

有时候,我们会看到这样的类型定义float4,这其实是一种语法糖,相当于声明一个四元数组float [4]。由于在图形学编程中,四元及其以下的数组会经常用到,所以CG专门定义这些额外的类型。

pragma关键字

看一下这段代码

// 代码片段 1.0
#pragma vertex vert
#pragma fragment frag

float4 vert(float4 v:POSITION):SV_POSITION{
	return mul(UNITY_MATRIX_MVP, v);
}
fixed4 frag():SV_Target{
	return fixed4(1.0,1.0,1.0,1.0);
}

这段代码片段中,#pragma vertex用来表示指定顶点着色器,而#pragma fragment用来指定片元着色器。即在实际的渲染流程中,顶点着色器的实现代码就是vert()函数。片元着色器同理。

语义

通常,语义被放在一个冒号之后。如,在代码片段1.0中,POSITIONSV_POSITIONSV_Target都是语义。每种语义表示的含义不同。当一个语义被放在一个变量之后,通常表示这个变量和GPU存储器(显存)中的一些数据相互关联。(关于显存中保存着什么数据,可以参考这篇博客里的一张图)。我们知道,CPU在调用DrawCall通知GPU渲染一个模型之前,会将一些渲染相关的数据从内存中拷贝到显存中。这些数据包括但不限于顶点坐标,顶点颜色,顶点对应的纹理坐标和纹理,等等)。

在上述代码片段中,POSITION语义表示将v与显存中的顶点坐标相关联。由于这里是函数的输入变量,于是每次调用该顶点着色器时,GPU会将对应的顶点坐标填充到这个变量v中。由于一个顶点坐标是四元数组(为什么是四元,请参考关键词齐次坐标系),如果v是float3类型的,那么会将该顶点坐标前三个数填充到变量中。float2和float同理。

而SV_POSITION表示顶点着色器vert()的输出是裁剪空间中的顶点坐标。在这篇博客中,我们知道顶点着色器之后还有一个曲面细分着色器,几何着色器等等。顶点着色器在输出顶点变换后的坐标后,还要经过曲面细分着色器和几何着色器的改动才会得到真正的裁剪空间下的顶点坐标。但是SV_POSITION语义指定顶点坐标系输出的顶点将不需要经过这些步骤,直接就是裁剪空间下的坐标,同时直接进入下一步的光栅化处理。

SV_Target 表示片元着色器frag()的输出直接输出到一个渲染目标(render target)中,这里的渲染目标是指GPU中的帧缓存。GPU提供给开发者的帧缓存一般有八个,我们可以改为使用SV_TargetN( 0 <= N < 8)来指定输出到哪个帧缓存。在这种情况下,SV_Target0和SV_Target是一样的效果。编写片元着色器代码时,一定要将片元着色器的输出关联到一个SV_Target中)

现在,再来看一个更复杂的例子

// 代码片段 2.0
#pragma vertex vert
#pragma fragment frag

struct a2v{
	float4 vertex: POSITION;
	float3 normal: NORMAL;
	float4 texcoord: TEXCOORD0;
};

float4 vert(a2v v): SV_POSITION{
	return mul(UNITY_MATRIX_MVP, v.vertex);
}
fixed4 frag(): SV_Target{
	return fixed4(1.0,1.0,1.0,1.0);
}

在这个例子中,vert的输入变量 v 并没有指定语义,那么顶点着色器怎么知道从哪里拿数据去填充这个 v 呢?注意,这个 v 实际上是我们定义的结构体 a2v 。而在a2v里面,已经定义了一个顶点变量,并且关联了语义POSITION。所以实际上这个 v 是有数据的。而且是三个数据的封装,除了顶点数据,NORMAL语义表示将模型空间的法线方向填充normal变量。TEXCOORD0表示用模型的第一套纹理坐标填充texcoord变量。

语义总结
  1. 顶点着色器输入(从应用阶段传递模型数据给顶点着色器)相关语义
语义描述
POSITION模型空间的顶点坐标,通常是float4类型
NORMAL顶点法线,通常是float3类型
TANGENT顶点切线,通常是float4类型
TEXCOORDn该顶点的纹理坐标,TEXCOORD0表示第一组纹理坐标,依此类推
COLOR顶点颜色,通常是fixed4或float4类型
  1. 顶点着色器输出/片元着色器输入(从顶点着色器传递给片元着色器)
语义描述
SV_POSITION裁剪空间中的顶点坐标,片元着色器的输入必须包含一个这样语义修饰的变量
COLOR0顶点着色器输出第一组顶点颜色,通常不是必须的
COLOR1顶点着色器输出第二组顶点颜色,通常不是必须的
TEXCOORDn,n∈[0,7]顶点着色器输出纹理坐标,通常不是必须的
  1. 片元着色器输出
语义描述
SV_Target输出值将会存储到一个渲染目标中。

现在,我们已经知道如何表示顶点着色器的输入和输出,现在就可以进入正式的Unity Shader编程了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值