Shaders: ShaderLab and fixed function shaders
着色器:ShaderLab 和 固定管线着色器
本文档主要是对Unity官方手册的个人理解与总结(其实以翻译记录为主:>)
仅作为个人学习使用,不得作为商业用途,欢迎转载,并请注明出处。
文章中涉及到的操作都是基于Unity2018.2版本
参考链接:https://docs.unity3d.com/Manual/ShaderTut1.html
This tutorial teaches you the first steps of creating your own shaders, to help you control the look of your game and optimise the performance of the graphics.
本教程将教你创建自己的着色器的第一步,帮助您控制游戏的效果,并优化图形的性能。
Unity is equipped with a powerful shading and material language called ShaderLab. In style it is similar to CgFX and Direct3D Effects (.FX) languages - it describes everything needed to display a Material.
Unity配备了强大的着色和材质语言,叫做ShaderLab。在风格上,它类似于CgFX和Direct3D效果(.FX)语言——它描述了材料显示了需要的一切。
Shaders describe properties that are exposed in Unity’s Material Inspector and multiple shader implementations (SubShaders) targeted at different graphics hardware capabilities, each describing complete graphics hardware rendering state, and vertex/fragment programs to use. Shader programs are written in the high-level Cg/HLSL programming language.
着色器描述了在Unity的材质Inspector和多个着色器实现(SubShaders)中暴露的属性,它们针对不同的图形硬件功能,每一个都描述了完整的图形硬件呈现状态,以及要使用的vertex/fragment 程序。着色程序是用高级的Cg/HLSL 编程语言编写的。
In this tutorial we’ll describe how to write very simple shaders using so-called “fixed function” notation. In the next chapter we’ll introduce vertex and fragment shader programs. We assume that the reader has a basic understanding of OpenGL or Direct3D render states, and has some knowledge of HLSL, Cg, GLSL or Metal shader programming languages.
在本教程中,我们将描述如何使用所谓的“固定管线”标记来编写非常简单的着色器。在下一章中,我们将介绍顶点和片段着色程序。我们假设读者对OpenGL或Direct3D渲染状态有基本的了解,并且对HLSL、Cg、GLSL或金属着色程序编程语言有一定的了解。
Getting started
To create a new shader, either choose Assets > Create > Shader > Unlit Shader from the main menu, or duplicate an existing shader and work from that. The new shader can be edited by double-clicking it in the Project View.
要创建一个新的着色器,可以选择 Assets > Create > Shader > Unlit Shader,或者复制一个现有的着色器,并从中进行工作。新的着色器可以在Project 视图中双击它来编辑。
Unity has a way of writing very simple shaders in so-called “fixed-function” notation. We’ll start with this for simplicity. Internally the fixed function shaders are converted to regular vertex and fragment programs at shader import time.
Unity有一种方法可以在所谓的“固定管线”标记中编写非常简单的着色器。为了简单起见,我们先从这个开始。在内部,固定管线着色器在着色器导入时被转换为常规的顶点和片段程序。
We’ll start with a very basic shader:
Shader "Tutorial/Basic" {
Properties {
_Color ("Main Color", Color) = (1,0.5,0.5,1)
}
SubShader {
Pass {
Material {
Diffuse [_Color]
}
Lighting On
}
}
}
This simple shader demonstrates one of the most basic shaders possible. It defines a color property called Main Color and assigns it a default pink color (red=100% green=50% blue=50% alpha=100%). It then renders the object by invoking a Pass and in that pass setting the diffuse material component to the property _Color and turning on per-vertex lighting.
这个简单的着色器演示了一个最基本的着色器。它定义了一种称为Main Color的颜色属性,并赋予它一个默认的粉色(red=100% green=50% blue=50% alpha=100%)。然后,它通过调用一个Pass来呈现这个对象,并在该过程中将漫反射材质组件设置为属性颜色并打开顶点光照。
To test this shader, create a new material, select the shader from the drop-down menu (Tutorial->Basic) and assign the Material to some object. Tweak the color in the Material Inspector and watch the changes. Time to move onto more complex things!
要测试这个着色器,创建一个新材质,从下拉菜单中选择着色器(Tutorial->Basic),并将材质分配给某个对象。在材质检查器中调整颜色,观察变化。是时候去做更复杂的事情了!
Basic vertex lighting 基本顶点光照
If you open an existing complex shader, it can be a bit hard to get a good overview. To get you started, we will dissect the built-in VertexLit shader that ships with Unity. This shader uses fixed-function pipeline to do standard per-vertex lighting.
如果您打开一个现有的复杂着色器,那么就很难获得一个好的概述。为了让你开始,我们将仔细分析内置的VertexLit着色器。这个着色器使用固定管线来完成顶点光照。
Shader "VertexLit" {
Properties {
_Color ("Main Color", Color) = (1,1,1,0.5)
_SpecColor ("Spec Color", Color) = (1,1,1,1)
_Emission ("Emmisive Color", Color) = (0,0,0,0)
_Shininess ("Shininess", Range (0.01, 1)) = 0.7
_MainTex ("Base (RGB)", 2D) = "white" { }
}
SubShader {
Pass {
Material {
Diffuse [_Color]
Ambient [_Color]
Shininess [_Shininess]
Specular [_SpecColor]
Emission [_Emission]
}
Lighting On
SeparateSpecular On
SetTexture [_MainTex] {
constantColor [_Color]
Combine texture * primary DOUBLE, texture * constant
}
}
}
}
All shaders start with the keyword Shader followed by a string that represents the name of the shader. This is the name that is shown in the Inspector. All code for this shader must be put within the curly braces after it: { } (called a block).
所有的着色器都从关键字Shader 开始,然后是一个表示着色器名称的字符串。这是在检查器中显示的名称。这个着色器的所有代码都必须放在花括号里面:{ }(称为块)。
- The name should be short and descriptive. It does not have to match the .shader file name. 名称应该简短而有描述性。它不需要匹配.shader文件名。
- To put shaders in submenus in Unity, use slashes - e.g. MyShaders/Test would be shown as Test in a submenu called MyShaders, or MyShaders->Test. 为了在菜单栏放置着色器,使用斜杠——例如,MyShaders/Test将在名为MyShaders的子菜单中显示为Test,即MyShaders->Test。
The shader is composed of a Properties block followed by SubShader blocks. Each of these is described in sections below.
着色器由一个属性块组成,后面是 SubShader 块。下面的小节将介绍这些内容。
Properties
At the beginning of the shader block you can define any properties that artists can edit in the Material Inspector. In the VertexLit example the properties look like this:
在着色块的开始处,您可以定义美术可以在材质检查器中编辑的任何属性。在VertexLit的例子中,属性是这样的:
The properties are listed on separate lines within the Properties block. Each property starts with the internal name (Color, MainTex). After this in parentheses comes the name shown in the inspector and the type of the property. After that, the default value for this property is listed:
属性被列在属性块中的单独行中。每个属性从内部名称开始(Color, MainTex)。在括号中出现了检查器中显示的名称和属性的类型。在此之后,该属性的默认值列出:
The list of possible types are in the Properties Reference. The default value depends on the property type. In the example of a color, the default value should be a four component vector.
在Properties Reference 章节中列出了可能的类型。默认值取决于属性类型。在颜色的例子中,默认值应该是四个组合的向量。
We now have our properties defined, and are ready to start writing the actual shader.
我们现在已经定义了属性,并准备好开始编写实际的着色器。
The shader body
Before we move on, let’s define the basic structure of a shader file.
在我们继续之前,让我们定义一个着色器文件的基本结构。
Different graphic hardware has different capabilities. For example, some graphics cards support fragment programs and others don’t; some can lay down four textures per pass while the others can do only two or one; etc
. To allow you to make full use of whatever hardware your user has, a shader can contain multiple SubShaders. When Unity renders a shader, it will go over all subshaders and use the first one that the hardware supports.
不同的图形硬件有不同的功能。例如,一些图形卡支持片段程序,而另一些则不支持;有些可以在每个通道上放置四种纹理,而另一些则只能放两种或一种;等等。为了让您充分利用您的用户拥有的任何硬件,一个着色器可以包含多个子着色器(SubShaders)。当Unity呈现一个着色器时,它将遍历所有的子着色器,并使用硬件支持的第一个着色器。
Shader "Structure Example" {
Properties { /* ...shader properties... }
SubShader {
// ...subshader that requires DX11 / GLES3.1 hardware...
}
SubShader {
// ...subshader that might look worse but runs on anything :)
}
}
This system allows Unity to support all existing hardware and maximize the quality on each one. It does, however, result in some long shaders.
这个系统允许Unity支持所有现有的硬件,并最大化每个硬件的质量。然而,它确实会产生一些内容比较长的shaders。
Inside each SubShader block you set the rendering state shared by all passes; and define rendering passes themselves. A complete list of available commands can be found in the SubShader Reference.
在每个子材质块中,你设置所有通道的渲染状态并定义渲染通道本身。可以在SubShader Reference 章节中找到可用命令的完整列表。
Passes
Each subshader is a collection of passes. For each pass, the object geometry is rendered, so there must be at least one pass. Our VertexLit shader has just one pass:
每个子着色器都是一个通道的集合。对于每一通道,对象几何都被渲染出来,所以必须至少有一个通道。我们的VertexLit着色器只有一个通道:
// ...snip...
Pass {
Material {
Diffuse [_Color]
Ambient [_Color]
Shininess [_Shininess]
Specular [_SpecColor]
Emission [_Emission]
}
Lighting On
SeparateSpecular On
SetTexture [_MainTex] {
constantColor [_Color]
Combine texture * primary DOUBLE, texture * constant
}
}
// ...snip...
Any commands defined in a pass configures the graphics hardware to render the geometry in a specific way.
在pass中定义的任何命令都会配置图形硬件以一种特定的方式渲染几何图形。
In the example above we have a Material block that binds our property values to the fixed function lighting material settings. The command Lighting On turns on the standard vertex lighting, and SeparateSpecular On enables the use of a separate color for the specular highlight.
在上面的例子中,我们有一个材质块,它将我们的属性值绑定到固定管线光照材质设置。命令Lighting On开启了标准的顶点光照,并且SeparateSpecular开启了使用单独的颜色来显示高光。
All of these command so far map very directly to the fixed function OpenGL/Direct3D hardware model. Consult OpenGL red book for more information on this.
到目前为止,所有这些命令都直接映射到固定管线OpenGL/Direct3D硬件模型上。有关这方面的更多信息,请查阅OpenGL red book。
The next command, SetTexture, is very important. These commands define the textures we want to use and how to mix, combine and apply them in our rendering. SetTexture command is followed by the property name of the texture we would like to use (_MainTex here) This is followed by a combiner block that defines how the texture is applied. The commands in the combiner block are executed for each pixel that is rendered on screen.
下一个命令,SetTexture,是非常重要的。这些命令定义了我们想要使用的纹理,以及如何在渲染中混合、组合和应用它们。SetTexture命令后面跟着我们想要使用的纹理的属性名(这里是_MainTex),后面是一个组合块,它定义了纹理是如何应用的。组合块中的命令是为在屏幕上渲染的每个像素执行的。
Within this block we set a constant color value, namely the Color of the Material, _Color. We’ll use this constant color below.
在这个块中,我们设置了一个常量的颜色值,即材质的颜色,_Color。我们将在下面使用这个常量颜色。
In the next command we specify how to mix the texture with the color values. We do this with the Combine command that specifies how to blend the texture with another one or with a color. Generally it looks like this: Combine ColorPart, AlphaPart
在下一个命令中,我们指定如何将纹理与颜色值混合。我们使用Combine命令来实现这一点,它指定了如何将纹理与另一个或颜色混合。一般来说,它看起来是这样的:Combine ColorPart, AlphaPart
Here ColorPart and AlphaPart define blending of color (RGB) and alpha (A) components respectively. If AlphaPart is omitted, then it uses the same blending as ColorPart.
在这里,ColorPart和AlphaPart分别定义颜色(RGB)和alpha(A)组件的混合。如果省略了AlphaPart,那么它将使用与ColorPart相同的混合。
In our VertexLit example: Combine texture * primary DOUBLE, texture * constant
Here “texture” is the color coming from the current texture (here _MainTex). It is multiplied () with the primary vertex color. Primary color is the vertex lighting color, calculated from the Material values above. Finally, the result is multiplied by two to increase lighting intensity (DOUBLE). The alpha value (after the comma) is texture multiplied by constant value (set with constantColor above). Another often used combiner mode is called previous (not used in this shader). This is the result of any previous SetTexture step, and can be used to combine several textures and/or colors with each other.
这里的“纹理”是来自当前纹理的颜色(这里是_MainTex)。它是用主顶点颜色乘()的。主色是顶点光照的颜色,从上面的材质值计算出来。最后,结果乘以2以增加光照强度(加倍(DOUBLE))。alpha值(逗号后面)是纹理乘以常量值(上面设置了constantColor)。另一个经常使用的组合模式称为previous(在这个着色器中没有使用)。这是之前的SetTexture步骤的结果,可以用来将几种纹理和/或颜色相互组合。
Summary
Our VertexLit shader configures standard vertex lighting and sets up the texture combiners so that the rendered lighting intensity is doubled.
我们的VertexLit着色器配置标准的顶点光照,并设置纹理组合,使渲染的灯光强度加倍。
We could put more passes into the shader, they would get rendered one after the other. For now, though, that is not nessesary as we have the desired effect. We only need one SubShader as we make no use of any advanced features - this particular shader will work on any graphics card that Unity supports.
我们可以把更多的通道放到着色器里,它们会被一个接一个地渲染出来。然而,就目前而言,这并不是我们所期望的效果。我们只需要一个子着色器,因为我们不使用任何高级特性——这个特殊的着色器将在Unity支持的任何图形卡上工作。
The VertexLit shader is one of the most basic shaders that we can think of. We did not use any hardware specific operations, nor did we utilize any of the more special and cool commands that ShaderLab and Cg/HLSL has to offer.
VertexLit着色器是我们能想到的最基本的着色器之一。我们没有使用任何硬件特定的操作,我们也没有使用ShaderLab和Cg/HLSL提供的任何更特别、更酷的命令。
In the next chapter we’ll proceed by explaining how to write custom vertex & fragment programs using Cg/HLSL language.
在下一章中,我们将继续解释如何使用Cg/HLSL语言编写自定义顶点和片段程序。