天天看點

Shaders: vertex and fragment programs - Unity Shader Reference 番外2

Shaders: vertex and fragment programs

着色器:頂點與片斷程式

本文檔主要是對Unity官方手冊的個人了解與總結(其實以翻譯記錄為主:>)

僅作為個人學習使用,不得作為商業用途,歡迎轉載,并請注明出處。

文章中涉及到的操作都是基于Unity2018.2版本

參考連結:https://docs.unity3d.com/Manual/ShaderTut2.html

This tutorial will teach you the basics of how to write vertex and fragment programs in Unity shaders. For a basic introduction to ShaderLab see the Getting Started tutorial. If you want to write shaders that interact with lighting, read about Surface Shaders instead.

本教程将向您介紹如何在Unity着色器中編寫頂點和片段程式的基礎知識。對于ShaderLab的基本介紹,請參閱入門教程。如果你想要寫與燈光互動的着色器,那就去閱讀表面着色器吧。

Lets start with a small recap of the general structure of a shader:

Shader "MyShaderName"
{
    Properties
    {
        // material properties here
    }
    SubShader // subshader for graphics hardware A
    {
        Pass
        {
            // pass commands ...
        }
        // more passes if needed
    }
    // more subshaders if needed
    FallBack "VertexLit" // optional fallback
}
           

Here at the end we introduce a new command: FallBack “VertexLit”. The Fallback command can be used at the end of the shader; it tells which shader should be used if no SubShaders from the current shader can run on user’s graphics hardware. The effect is the same as including all SubShaders from the fallback shader at the end. For example, if you were to write a fancy normal-mapped shader, then instead of writing a very basic non-normal-mapped subshader for old graphics cards you can just fallback to built-in VertexLit shader.

在這裡,我們引入了一個新的指令:回退(FallBack) “VertexLit”。回退指令可以在着色器的末端使用;它告訴我們,如果目前着色器中沒有子材質可以在使用者的圖形硬體上運作,那麼應該使用哪個着色器。其效果在所有子着色器是一樣的被包含在最後的回退着色器中。例如,如果您要編寫一個漂亮的法線映射着色器,而不是為舊的圖形卡編寫一個非常基本的無法線映射的子着色器,您可以直接回退到内置的VertexLit着色器。

The basic building blocks of the shader are introduced in the first shader tutorial while the full documentation of Properties, SubShaders and Passes are also available.

着色器的基本建構塊是在第一個着色器教程中引入的,而屬性、SubShaders和Passes的完整文檔也是可用的。

A quick way of building SubShaders is to use passes defined in other shaders. The command UsePass does just that, so you can reuse shader code in a neat fashion. As an example the following command uses the pass with the name “FORWARD” from the built-in Specular shader: UsePass “Specular/FORWARD”.

建構子着色器的一種快速方法是使用在其他着色器中定義的passes。UsePass的指令就是這樣做的,是以您可以以一種整潔的方式重用着色器代碼。作為一個例子,下面的指令使用來自内置的高光着色器的“FORWARD”這個名稱:UsePass “Specular/FORWARD”.

In order for UsePass to work, a name must be given to the pass one wishes to use. The Name command inside the pass gives it a name: Name “MyPassName”.

為了讓UsePass工作,必須給希望被使用的pass提供一個名稱。pass内部的名稱指令給它起了一個名字:Name “MyPassName”。

Vertex and fragment programs

We described a pass that used just a single texture combine instruction in the first tutorial. Now it is time to demonstrate how we can use vertex and fragment programs in our pass.

我們描述了在第一個教程中隻使用一個紋理組合指令的pass。現在是示範如何在我們的pass中使用頂點和片段程式的時候了。

When you use vertex and fragment programs (the so called “programmable pipeline”), most of the hardcoded functionality (“fixed function pipeline”) in the graphics hardware is switched off. For example, using a vertex program turns off standard 3D transformations, lighting and texture coordinate generation completely. Similarly, using a fragment program replaces any texture combine modes that would be defined in SetTexture commands; thus SetTexture commands are not needed.

當您使用頂點和片段程式(所謂的“可程式設計管線”)時,圖形硬體中的大多數寫死功能(“固定管線”)都被關閉了。例如,使用頂點程式會完全關閉标準3D轉換、光照和紋理坐标的生成。類似地,使用片段程式替換在SetTexture指令中定義的任何紋理組合模式;是以SetTexture不再需要。

Writing vertex/fragment programs requires a thorough knowledge of 3D transformations, lighting and coordinate spaces - because you have to rewrite the fixed functionality that is built into APIs like OpenGL yourself. On the other hand, you can do much more than what’s built in!

編寫頂點/片斷程式需要對3D轉換、光照和坐标空間有深入的了解——因為你必須自己重寫像内置在APIs中的固定管線,比如OpenGL中的。另一方面,你可以做的比内置的要多得多!

Using Cg/HLSL in ShaderLab

Shaders in ShaderLab are usually written in Cg/HLSL programming language. Cg and DX9-style HLSL are for all practical purposes one and the same language, so we’ll be using Cg and HLSL interchangeably (see this page for details).

ShaderLab的着色器通常是用Cg/HLSL程式設計語言編寫的。Cg和DX9-style的HLSL是所有實際用途的一種和相同的語言,是以我們将會交替使用Cg和HLSL(請參閱這一頁了解詳細資訊)。

Shader code is written by embedding “Cg/HLSL snippets” in the shader text. Snippets are compiled into low-level shader assembly by the Unity editor, and the final shader that is included in your game’s data files only contains this low-level assembly or bytecode, that is platform specific. When you select a shader in the Project View

, the Inspector has a button to show compiled shader code, which might help as a debugging aid. Unity automatically compiles Cg snippets for all relevant platforms (Direct3D 9, OpenGL, Direct3D 11, OpenGL ES and so on). Note that because Cg/HLSL code is compiled by the editor, you can’t create shaders from scripts

at runtime.

Shader代碼是通過在着色器文本中嵌入“Cg/HLSL片段”來編寫的。片段被Unity編輯器編譯成低級着色彙編,而在您的遊戲資料檔案中包含的最終着色器隻包含這個低級彙編或位元組碼,這是特定于平台的。當你在項目視圖中選擇一個着色器時,檢查器有一個按鈕來顯示編譯後的着色代碼,這可能有助于調試幫助。Unity自動為所有相關平台(Direct3D 9、OpenGL、Direct3D 11、OpenGL ES等)編譯Cg片段。注意,因為Cg/HLSL代碼已被編譯,你不能在運作時建立shader。

In general, snippets are placed inside Pass blocks. They look like this:

一般來說,代碼片段被放置在Pass塊中。它們是這樣的:

Pass {
    // ... the usual pass state setup ...

    CGPROGRAM
    // compilation directives for this snippet, e.g.:
    #pragma vertex vert
    #pragma fragment frag

    // the Cg/HLSL code itself

    ENDCG
    // ... the rest of pass setup ...
}
           

The following example demonstrates a complete shader that renders object normals as colors:

面的例子示範了一個完整的着色器,它将對象法線作為顔色渲染:

Shader "Tutorial/Display Normals" {
    SubShader {
        Pass {

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f {
                float4 pos : SV_POSITION;
                fixed3 color : COLOR0;
            };

            v2f vert (appdata_base v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.color = v.normal * 0.5 + 0.5;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return fixed4 (i.color, 1);
            }
            ENDCG

        }
    }
}
           

When applied on an object it will result in an image like this:

當應用到一個物體上時,它會産生這樣的圖像:

Shaders: vertex and fragment programs - Unity Shader Reference 番外2

Our “Display Normals” shader does not have any properties, contains a single SubShader with a single Pass that is empty except for the Cg/HLSL code. Let’s dissect the code part by part:

我們的“顯示法線”着色器沒有任何屬性,隻包含一個帶有一個通道的子着色器,通道中隻有Cg/HLSL代碼。讓我們逐個分析代碼:

CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// ...
ENDCG
           

The whole snippet is written between CGPROGRAM and ENDCG keywords. At the start compilation directives are given as #pragma statements:

整個代碼片段是在CGPROGRAM和ENDCG關鍵字之間編寫的。在開始編譯指令給出了#pragma語句:

  • #pragma vertex name tells that the code contains a vertex program in the given function (vert here). 代碼在給定的函數中包含一個頂點程式。
  • #pragma fragment name tells that the code contains a fragment program in the given function (frag here). 代碼在給定的函數中包含一個片斷程式。

Following the compilation directives is just plain Cg/HLSL code. We start by including a built-in include file:

編譯指令接下來的隻是普通的Cg/HLSL代碼。我們開始包括一個内置包括檔案:

#include "UnityCG.cginc"
           

The UnityCG.cginc file contains commonly used declarations and functions so that the shaders can be kept smaller (see shader include files page for details). Here we’ll use appdata_base structure from that file. We could just define them directly in the shader and not include the file of course.

UnityCG.cginc檔案包含常用的聲明和函數,使着色器可以保持更小(詳情見着色器包括檔案頁)。在這裡,我們将使用來自該檔案的appdata_base結構。我們可以直接在着色器中定義它們,當然這樣就可以不包括檔案。

Next we define a “vertex to fragment” structure (here named v2f) - what information is passed from the vertex to the fragment program. We pass the position and color parameters. The color will be computed in the vertex program and just output in the fragment program.

接下來,我們定義一個“頂點到片段”結構體(這裡稱為v2f)——從頂點到片段程式傳遞了什麼資訊。我們傳遞位置和顔色參數。顔色将在頂點程式中計算,并在片段程式中輸出。

We proceed by defining the vertex program - vert function. Here we compute the position and output input normal as a color: o.color = v.normal * 0.5 + 0.5;

我們繼續定義頂點程式-vert函數。這裡我們計算位置和輸出輸入法線作為顔色: o.color = v.normal * 0.5 + 0.5;

Normal components are in –1…1 range, while colors are in 0…1 range, so we scale and bias the normal in the code above. Next we define a fragment program - frag function that just outputs the calculated color and 1 as the alpha component:

法線分量在-1 …1範圍,而顔色在0…1範圍,是以我們在上面的代碼中縮放和偏置法線。接下來我們定義一個片段程式- frag函數,它隻輸出計算好的顔色和作為alpha分量的1:

fixed4 frag (v2f i) : SV_Target
{
    return fixed4 (i.color, 1);
}
           

That’s it, our shader is finished! Even this simple shader is very useful to visualize mesh normals.

就這樣,我們的着色器完成了!即使是這個簡單的着色器也非常有助于可視化網格法線。

Of course, this shader does not respond to lights at all, and that’s where things get a bit more interesting; read about Surface Shaders for details.

當然,這個着色器對光照完全沒有反應,這就是事情變得更有趣的地方;閱讀關于表面着色器的細節。

Using shader properties in Cg/HLSL code

When you define properties in the shader, you give them a name like _Color or _MainTex. To use them in Cg/HLSL you just have to define a variable of a matching name and type. See properties in shader programs page for details.

當您在着色器中定義屬性時,您會給它們一個名稱,比如_Color或_MainTex。要在Cg/HLSL中使用它們,隻需定義一個比對名稱和類型的變量。有關詳細資訊,請參閱着色程式頁面中的屬性。

Here is a complete shader that displays a texture modulated by a color. Of course, you could easily do the same in a texture combiner call, but the point here is just to show how to use properties in Cg:

這是一個完整的着色器,顯示由顔色調整的紋理。當然,你可以在一個紋理組合器調用中做同樣的事情,但是這裡的重點是展示如何在Cg中使用屬性:

Shader "Tutorial/Textured Colored" {
    Properties {
        _Color ("Main Color", Color) = (1,1,1,0.5)
        _MainTex ("Texture", 2D) = "white" { }
    }
    SubShader {
        Pass {

        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag

        #include "UnityCG.cginc"

        fixed4 _Color;
        sampler2D _MainTex;

        struct v2f {
            float4 pos : SV_POSITION;
            float2 uv : TEXCOORD0;
        };

        float4 _MainTex_ST;

        v2f vert (appdata_base v)
        {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
            return o;
        }

        fixed4 frag (v2f i) : SV_Target
        {
            fixed4 texcol = tex2D (_MainTex, i.uv);
            return texcol * _Color;
        }
        ENDCG

        }
    }
}
           

The structure of this shader is the same as in the previous example. Here we define two properties, namely _Color and _MainTex. Inside Cg/HLSL code we define corresponding variables:

這個着色器的結構體與前面的示例相同。這裡我們定義了兩個屬性,即_Color和_MainTex。在Cg/HLSL代碼中,我們定義了相應的變量:

fixed4 _Color;
sampler2D _MainTex;
           

See Accessing Shader Properties in Cg/HLSL for more information.

有關更多資訊,請參閱Cg/HLSL中通路着色器屬性。

The vertex and fragment programs here don’t do anything fancy; vertex program uses the TRANSFORM_TEX macro from UnityCG.cginc to make sure texture scale and offset is applied correctly, and fragment program just samples the texture and multiplies by the color property.

這裡的頂點和片段程式沒有任何花哨的功能;頂點程式使用UnityCG.cginc中的TRANSFORM_TEX宏來確定紋理縮放和偏移的應用是正确的,而片段程式隻是采樣紋理并乘以顔色屬性。

Summary

We have shown how custom shader programs can be written in a few easy steps. While the examples shown here are very simple, there’s nothing preventing you to write arbitrarily complex shader programs! This can help you to take the full advantage of Unity and achieve optimal rendering results.

我們已經展示了如何在幾個簡單的步驟中編寫自定義着色器程式。雖然這裡展示的例子非常簡單,但沒有什麼可以阻止你編寫任意複雜的着色程式!這可以幫助您充分利用Unity,實作最佳的呈現結果。

The complete ShaderLab reference manual is here, and more examples in vertex and fragment shader examples page. We also have a forum for shaders at forum.unity3d.com so go there to get help with your shaders! Happy programming, and enjoy the power of Unity and ShaderLab.

完整的ShaderLab參考手冊在這裡,更多的例子在頂點和片斷着色器例子頁。我們也有一個論壇的着色器在forum.unity3d.com, 是以去那裡得到幫助你的着色器!快樂程式設計,享受Unity和ShaderLab的力量。