什么是Shader
Shader(着色器)是一段能够针对3D对象进行操作、并被GPU所执行的程序。Shader并不是一个统一的标准,不同的图形接口的Shader并不相同。OpenGL的着色语言是GLSL, NVidia开发了Cg,而微软的Direct3D使用高级着色器语言(HLSL)。而Unity的Shader 是将传统的图形接口的Shader(由 Cg / HLSL编写)嵌入到独有的描述性结构中而形成的一种代码生成框架,最终会自动生成各硬件平台自己的Shader,从而实现跨平台。所以说Shaer其实就是一门语言,用来控制渲染的语言。
Shader种类
OpenGL和Direct3D都提供了三类着色器:
- 顶点着色器:处理每个顶点,将顶点的空间位置投影在屏幕上,即计算顶点的二维坐标。同时,它也负责顶点的深度缓冲(Z-Buffer)的计算。顶点着色器可以掌控顶点的位置、颜色和纹理坐标等属性,但无法生成新的顶点。顶点着色器的输出传递到流水线的下一步。如果有之后定义了几何着色器,则几何着色器会处理顶点着色器的输出数据,否则,光栅化器继续流水线任务。
- 像素着色器(Direct3D),常常又称为片断着色器(OpenGL):处理来自光栅化器的数据。光栅化器已经将多边形填满并通过流水线传送至像素着色器,后者逐像素计算颜色。像素着色器常用来处理场景光照和与之相关的效果,如凸凹纹理映射和调色。名称片断着色器似乎更为准确,因为对于着色器的调用和屏幕上像素的显示并非一一对应。举个例子,对于一个像素,片断着色器可能会被调用若干次来决定它最终的颜色,那些被遮挡的物体也会被计算,直到最后的深度缓冲才将各物体前后排序。
- 几何着色器:可以从多边形网格中增删顶点。它能够执行对CPU来说过于繁重的生成几何结构和增加模型细节的工作。Direct3D版本10增加了支持几何着色器的API, 成为Shader Model 4.0的组成部分。OpenGL只可通过它的一个插件来使用几何着色器。
光栅化是将一个图元转变为一个二维图像的过程。二维图像上每个点都包含了颜色、深度和纹理数据。将该点和相关信息叫做一个片元(fragment)。
光栅化的目的,是找出一个几何单元(比如三角形)所覆盖的像素。
image.png
左侧有三个片元(三个顶点),将其光栅化,其实就是计算显示这个三角形需要多少个像素
Unity Shader 分为 表面着色器(Surface Shader)和 顶点片段着色器(Vertex And Fragment Shader):
- 表面着色器(Surface Shader)是Unity提出的一个概念。编写着色器与光照的交互是复杂的,光源有很多类型,不同的阴影选项,不同的渲染路径(正向和延时渲染),表面着色器将这一部分简化。Unity建议使用表面着色器来编写和光照有关的Shader。
- 顶点片段着色器(Vertex And Fragment Shader)和OpenGL,Direct3D中的顶点着色器和片段着色器没有什么区别。顶点片段着色器比表面着色器使用更自由也更强大,当然光照需要自行处理。Unity也允许在里面编写几何着色器,一般用得不多。
在Unity中,顶点着色器是使用最为广泛的一种
Shader的结构
![](https://img.haomeiwen.com/i112908/839e931124590ea4.png)
- 属性定义(Property Definition):定义Shader的输入,这些输入可以在材质编辑的时候指定
- 子着色器(SubShader):一个Shader可以有多个子着色器。这些子着色器互不相干且只有一个会在最终的平台运行。编写多个的目的是解决兼容性问题。Unity会自己选择兼容终端平台的Shader运行。
- 回滚(Fallback):如果子着色器在终端平台上都无法运行,那么使用Fallback指定的备用Shader,俗称备胎。
- Pass:一个Pass就是一次绘制。对于表面着色器,只能有一个Pass,所以不存在Pass节。顶点片段着色器可以有多个Pass。多次Pass可以实现很多特殊效果,例如当人物被环境遮挡时还可以看到人物轮廓就可以用多Pass来实现。
- Cg代码:每个Pass中都可以包含自定义的Cg代码,从CGPROGRAM开始到ENDCG结束。
//Shader语法:
Shader "name" { [Properties] Subshaders [Fallback] [CustomEditor] }
//Properties 语法
Properties { Property [Property ...] }
// Subshader 语法
Subshader { [Tags] [CommonState] Passdef [Passdef ...] }
// Pass 语法
Pass { [Name and Tags] [RenderSetup] }
// Fallback 语法
Fallback "name"
Shader示例
基本的表面着色器
Shader "Custom/NewShader" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType" = "Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
基本的顶点片段着色器
Shader "VertexInputSimple" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
fixed4 color : COLOR;
};
v2f vert (appdata_base v)
{
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.color.xyz = v.normal * 0.5 + 0.5;
o.color.w = 1.0;
return o;
}
fixed4 frag (v2f i) : SV_Target { return i.color; }
ENDCG
}
}
}
网友评论