URP——着色器和材质——编写自定义着色器Writing custom shaders

Writing custom shaders

本节包含帮助你开始编写通用渲染管线(URP)着色器的指南。

本节包含以下主题:

与基本着色器的例子相比,每个例子都包含了一些额外的信息。如果你是用Unity的ShaderLab语言编写着色器的新手,可以考虑按照这个页面上出现的顺序来浏览这些章节。

Creating a sample scene

要跟随本节中的示例:

  1. 将URP安装到现有的Unity项目中,或者使用通用项目模板创建一个新项目。
  2. 在样例场景中,创建一个游戏对象来测试着色器;例如,胶囊。
  3. 创建一个新的材质,并分配它到胶囊。
  4. 创建一个新的着色器资产,并分配它到胶囊的材质。打开着色器资产编辑Unity着色器源文件。用示例中的代码替换源文件中的代码。

开始编写URP着色器,继续章节URP unlit基本着色器

URP unlit basic shader

这个例子展示了一个基本的URP兼容着色器。这个着色器用着色器代码中预定义的颜色填充网格形状。

查看运行中的着色器,复制并粘贴以下ShaderLab代码到着色器资产中。

// This shader fills the mesh shape with a color predefined in the code.
Shader "Example/URPUnlitShaderBasic"
{
    // The properties block of the Unity shader. In this example this block is empty
    // because the output color is predefined in the fragment shader code.
    Properties
    { }

    // The SubShader block containing the Shader code. 
    SubShader
    {
        // SubShader Tags define when and under which conditions a SubShader block or
        // a pass is executed.
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }

        Pass
        {
            // The HLSL code block. Unity SRP uses the HLSL language.
            HLSLPROGRAM
            // This line defines the name of the vertex shader. 
            #pragma vertex vert
            // This line defines the name of the fragment shader. 
            #pragma fragment frag

            // The Core.hlsl file contains definitions of frequently used HLSL
            // macros and functions, and also contains #include references to other
            // HLSL files (for example, Common.hlsl, SpaceTransforms.hlsl, etc.).
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"            

            // The structure definition defines which variables it contains.
            // This example uses the Attributes structure as an input structure in
            // the vertex shader.
            struct Attributes
            {
                // The positionOS variable contains the vertex positions in object
                // space.
                float4 positionOS   : POSITION;                 
            };

            struct Varyings
            {
                // The positions in this struct must have the SV_POSITION semantic.
                float4 positionHCS  : SV_POSITION;
            };            

            // The vertex shader definition with properties defined in the Varyings 
            // structure. The type of the vert function must match the type (struct)
            // that it returns.
            Varyings vert(Attributes IN)
            {
                // Declaring the output object (OUT) with the Varyings struct.
                Varyings OUT;
                // The TransformObjectToHClip function transforms vertex positions
                // from object space to homogenous clip space.
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                // Returning the output.
                return OUT;
            }

            // The fragment shader definition.            
            half4 frag() : SV_Target
            {
                // Defining the color variable and returning it.
                half4 customColor = half4(0.5, 0, 0, 1);
                return customColor;
            }
            ENDHLSL
        }
    }
}

fragment shader将游戏对象着色为暗红色(RGB值(0.5,0,0))。

下面的部分介绍了Unity着色器的基本结构。

Basic ShaderLab structure

Unity着色器是用一种称为ShaderLab的统一特定语言编写的。

Unity着色器在这个例子中有以下模块:

Shader block

ShaderLab代码从Shader声明开始。

Shader "Example/URPUnlitShaderBasic"

此声明中的路径决定了Unity着色器在材质着色器菜单中的显示名称和位置。着色器的方法Shader.Find也使用此路径。

Properties block

属性块包含用户可以在材质的检查器窗口中设置的属性的声明。

在这个例子中,属性块是空的,因为这个Unity着色器不公开任何用户可以定义的材质属性。

SubShader block

一个Unity着色器源文件包含一个或多个子着色器块。当渲染一个网格时,Unity选择与目标设备上的GPU兼容的第一个SubShader。

子着色块可以选择包含子着色标记块。使用关键字标签声明子着色标签块。

Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }

一个名为RenderPipeline的子着色器标签告诉Unity使用哪个渲染管线来渲染这个子着色器,而UniversalPipeline的值表示Unity应该使用URP的这个子着色器。

为了在不同的渲染管线中执行相同的着色器,创建多个SubShader块,使用不同的渲染管线标签值。要在HDRP中执行SubShader块,将RenderPipeline标记设置为HDRenderPipeline,要在内置的渲染管线中执行,将RenderPipeline设置为空值。

有关子着色器标签的更多信息,请参见ShaderLab: SubShader Tags

Pass block

在本例中,有一个包含HLSL程序代码的Pass块。有关通道块的更多信息,请参见ShaderLab: Pass

一个Pass块可以选择包含一个Pass标签块。有关更多信息,请参见URP ShaderLab Pass tags

HLSLPROGRAM block

此块包含HLSL程序代码。

注意:HLSL语言是URP着色器的首选语言。

注意:URP支持CG语言。如果你在着色器中添加CGPROGRAM/ENDCGPROGRAM块,Unity会自动从内置的渲染管线库中包含着色器。如果你从SRP着色器库包括着色器,一些SRP着色器宏和函数可能会与内置的渲染管线着色器函数冲突。着色器与CGPROGRAM块不兼容SRP批处理程序。

这个块包含带有对核心的引用的#include声明。hlsl文件。

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

Core.hlsl文件包含常用的hlsl宏和函数的定义,还包含对其他hlsl文件(例如Common.hlsl和SpaceTransforms.hlsl)。

例如,HLSL代码中的顶点着色器使用了来自SpaceTransforms.hlsl文件的TransformObjectToHClip函数。函数将顶点位置从对象空间转换为同质空间:

Varyings vert(Attributes IN)
{
    Varyings OUT;
    OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
    return OUT;
}

这个基本的HLSL代码中的fragment shader输出代码中预定义的单一颜色:

half4 frag() : SV_Target
{
    half4 customColor;
    customColor = half4(0.5, 0, 0, 1);
    return customColor;
}

URP unlit shader with color input(带有颜色输入的URP 无光照着色器)展示了如何在材质检查器窗口中添加可编辑的颜色属性。

URP unlit shader with color input

这个例子中的Unity着色器为材质添加了基础颜色属性。你可以选择颜色使用属性和着色器填充网格形状的颜色。

使用来自URP unlit基本着色器的Unity着色器源文件,对ShaderLab代码做如下更改:

1. 添加_BaseColor属性定义到属性块:

Properties
{ 
    _BaseColor("Base Color", Color) = (1, 1, 1, 1)
}

该声明将标签Base Color的_BaseColor属性添加到材质中:

_BaseColor属性名是一个保留名。当你用这个名字声明一个属性时,Unity使用这个属性作为材质的主颜色。

2. 在Properties块中声明属性时,还需要在HLSL代码中声明它。

注意:为了确保Unity着色器是SRP批处理兼容的,在一个CBUFFER块中声明所有的材质属性,命名为UnityPerMaterial。有关SRP批处理程序的更多信息,请参见页面脚本渲染管道(SRP)批处理程序Scriptable Render Pipeline (SRP) Batcher

在顶点着色器之前添加以下代码:

CBUFFER_START(UnityPerMaterial)
    half4 _BaseColor;            
CBUFFER_END

3. 更改片段着色器中的代码,使其返回_BaseColor属性。

half4 frag() : SV_Target
{
    return _BaseColor;
}

现在你可以在检查器窗口的基础颜色区域选择颜色。片段着色器用你选择的颜色填充网格。

下面是这个示例的完整的ShaderLab代码。

// This shader fills the mesh shape with a color that a user can change using the
// Inspector window on a Material.
Shader "Example/URPUnlitShaderColor"
{    
    // The _BaseColor variable is visible in the Material's Inspector, as a field 
    // called Base Color. You can use it to select a custom color. This variable
    // has the default value (1, 1, 1, 1).
    Properties
    { 
        _BaseColor("Base Color", Color) = (1, 1, 1, 1)
    }

    SubShader
    {        
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }

        Pass
        {            
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"            

            struct Attributes
            {
                float4 positionOS   : POSITION;                 
            };

            struct Varyings
            {
                float4 positionHCS  : SV_POSITION;
            };

            // To make the Unity shader SRP Batcher compatible, declare all
            // properties related to a Material in a a single CBUFFER block with 
            // the name UnityPerMaterial.
            CBUFFER_START(UnityPerMaterial)
                // The following line declares the _BaseColor variable, so that you
                // can use it in the fragment shader.
                half4 _BaseColor;            
            CBUFFER_END

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                return OUT;
            }

            half4 frag() : SV_Target
            {
                // Returning the _BaseColor value.                
                return _BaseColor;
            }
            ENDHLSL
        }
    }
}

章节Drawing a texture演示了如何在网格上绘制纹理。

Drawing a texture

这个例子中的Unity着色器在网格上绘制纹理。

使用Unity shader源文件来自章节URP unlit shader with color input,并对ShaderLab代码做以下更改:

1. 在Properties块中,使用_BaseMap属性定义替换现有代码。

Properties
{ 
    _BaseMap("Base Map", 2D) = "white"
}

当你在属性块中声明纹理属性时,Unity会添加_BaseMap属性和标签基础贴图到材质中,并添加平铺和偏移量控件。

_BaseMap属性名是保留名。当你用这个名字声明一个属性时,Unity使用这个属性作为材质的主纹理(main texture)。

2. 在struct Attributes和struct Varyings中,为纹理上的uv坐标添加uv变量:

float2 uv           : TEXCOORD0;

3. 将纹理定义为2D纹理,并为它指定一个采样器。在CBUFFER块之前添加以下代码行:

TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);

TEXTURE2D和采样器宏定义在Core.hlsl中引用的一个文件中。

4. 为了使平铺和偏移工作,有必要在'CBUFFER'块中用_ST后缀声明纹理属性。_ST后缀是必要的,因为一些宏(例如TRANSFORM_TEX)使用它。

注意:为了确保Unity着色器是SRP批处理兼容的,在一个CBUFFER块中声明所有的材质属性,命名为UnityPerMaterial。有关SRP批处理程序的更多信息,请参见页面脚本渲染管道(SRP)批处理程序Scriptable Render Pipeline (SRP) Batcher

CBUFFER_START(UnityPerMaterial)
    float4 _BaseMap_ST;
CBUFFER_END

5. 要应用平铺和偏移变换,在顶点着色器中添加如下线条:

OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);

在Macros.hlsl文件中定义了TRANSFORM_TEX宏。#include声明包含对该文件的引用。

6. 在fragment shader中,使用SAMPLE_TEXTURE2D宏来采样纹理:

half4 frag(Varyings IN) : SV_Target
{
    half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
    return color;
}

现在你可以在检查器窗口的基础贴图中选择一个纹理。着色器在网格上绘制纹理。

下面是这个示例的完整的ShaderLab代码。

// This shader draws a texture on the mesh.
Shader "Example/URPUnlitShaderTexture"
{
    // The _BaseMap variable is visible in the Material's Inspector, as a field
    // called Base Map.
    Properties
    { 
        _BaseMap("Base Map", 2D) = "white"
    }

    SubShader
    {
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"            

            struct Attributes
            {
                float4 positionOS   : POSITION;
                // The uv variable contains the UV coordinate on the texture for the
                // given vertex.
                float2 uv           : TEXCOORD0;
            };

            struct Varyings
            {
                float4 positionHCS  : SV_POSITION;
                // The uv variable contains the UV coordinate on the texture for the
                // given vertex.
                float2 uv           : TEXCOORD0;
            };

            // This macro declares _BaseMap as a Texture2D object.
            TEXTURE2D(_BaseMap);
            // This macro declares the sampler for the _BaseMap texture.
            SAMPLER(sampler_BaseMap);

            CBUFFER_START(UnityPerMaterial)
                // The following line declares the _BaseMap_ST variable, so that you
                // can use the _BaseMap variable in the fragment shader. The _ST 
                // suffix is necessary for the tiling and offset function to work.
                float4 _BaseMap_ST;
            CBUFFER_END

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                // The TRANSFORM_TEX macro performs the tiling and offset
                // transformation.
                OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                // The SAMPLE_TEXTURE2D marco samples the texture with the given
                // sampler.
                half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
                return color;
            }
            ENDHLSL
        }
    }
}

Visualizing normal vectors法向量分段可视化展示了如何在网格上可视化法向量。

Visualizing normal vectors

这个例子中的Unity着色器可视化了网格上的法向量值。

使用来自URP unlit基本着色器的Unity着色器源文件,对ShaderLab代码做如下更改:

1. 在struct Attributes中,也就是本例中顶点着色器的输入结构,声明包含每个顶点的法向量的变量。

struct Attributes
{
    float4 positionOS   : POSITION;
    // Declaring the variable containing the normal vector for each vertex.
    half3 normal        : NORMAL;
};

2. 在struct Varyings中,也就是这个例子中片段着色器的输入结构,声明变量来存储每个片段的法向量值:

struct Varyings
{
    float4 positionHCS  : SV_POSITION;
    // The variable for storing the normal vector values.
    half3 normal        : TEXCOORD0;
};

这个例子使用法向量的三个组件作为每个片段的RGB颜色值。

3. 为了渲染网格上的法向量值,使用以下代码作为片段着色器:

half4 frag(Varyings IN) : SV_Target
{                
    half4 color = 0;
    color.rgb = IN.normal;
    return color;
}

4. Unity渲染网格上的法向量值:

胶囊的一部分是黑色的。这是因为在这些点上,法向量的三个分量都是负的。下一步将展示如何在这些区域中渲染值。

5. 要渲染负法向量分量,使用压缩技术。将正常分量值范围(-1..1)压缩为颜色值范围(0..1),更改如下行:

color.rgb = IN.normal;

到这行:

color.rgb = IN.normal * 0.5 + 0.5;

现在Unity在网格上渲染法向量值作为颜色。

下面是这个示例的完整的ShaderLab代码。

// This shader visuzlizes the normal vector values on the mesh.
Shader "Example/URPUnlitShaderNormal"
{    
    Properties
    { }

    SubShader
    {
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }

        Pass
        {            
            HLSLPROGRAM            
            #pragma vertex vert            
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"            

            struct Attributes
            {
                float4 positionOS   : POSITION;
                // Declaring the variable containing the normal vector for each
                // vertex.
                half3 normal        : NORMAL;
            };

            struct Varyings
            {
                float4 positionHCS  : SV_POSITION;
                half3 normal        : TEXCOORD0;
            };                                   

            Varyings vert(Attributes IN)
            {                
                Varyings OUT;                
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);       
                // Use the TransformObjectToWorldNormal function to transform the
                // normals from object to world space. This function is from the 
                // SpaceTransforms.hlsl file, which is referenced in Core.hlsl.
                OUT.normal = TransformObjectToWorldNormal(IN.normal);                
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {                
                half4 color = 0;
                // IN.normal is a 3D vector. Each vector component has the range
                // -1..1. To show all vector elements as color, including the
                // negative values, compress each value into the range 0..1.
                color.rgb = IN.normal * 0.5 + 0.5;                
                return color;
            }
            ENDHLSL
        }
    }
}

URP ShaderLab Pass tags

本节包含对特定于urp的ShaderLab Pass标记的描述。

URP Pass标签:LightMode

这个标记的值让管线决定在执行渲染管线的不同部分时使用哪个通道。

如果您没有在一个Pass中设置LightMode标记,URP将为该Pass使用SRPDefaultUnlit标记值。

在URP中,LightMode标签可以有以下值。

PropertyDescription
UniversalForward

The Pass renders object geometry and evaluates all light contributions. URP uses this tag value in the Forward Rendering Path.

Pass渲染对象的几何形状并评估所有的光照贡献。URP在正向渲染路径中使用这个标记值。

UniversalGBuffer

The Pass renders object geometry without evaluating any light contribution. URP uses this tag value in the Deferred Rendering Path.

通道渲染对象的几何形状而不评估任何光的贡献。URP在延迟渲染路径中使用此标记值。

UniversalForwardOnly

The Pass renders object geometry and evaluates all light contributions, similarly to when LightMode has the UniversalForward value. The difference from UniversalForward is that URP can use the Pass for both the Forward and the Deferred Rendering Paths.
Use this value if a certain Pass must render objects with the Forward Rendering Path when URP is using the Deferred Rendering Path. For example, use this tag if URP renders a Scene using the Deferred Rendering Path and the Scene contains objects with shader data that does not fit the GBuffer, such as Clear Coat normals.
If a shader must render in both the Forward and the Deferred Rendering Paths, declare two Passes with the UniversalForward and UniversalGBuffer tag values. If a shader must render using the Forward Rendering Path regardless of the Rendering Path that the URP Renderer uses, declare only a Pass with the LightMode tag set to UniversalForwardOnly.

通过渲染对象的几何形状和评估所有的光照贡献,类似于当LightMode有通用前向值时。与UniversalForward的不同之处在于URP可以使用前向和延迟渲染路径的通道。

当URP使用延迟渲染路径时,如果某个特定的通道必须以正向渲染路径渲染对象,则使用此值。例如,如果URP使用递延渲染路径渲染一个场景,并且场景中包含了不符合GBuffer的着色器数据的对象,如Clear Coat normals,则使用此标记。

如果着色器必须在前向和延迟渲染路径中进行渲染,则使用UniversalForward和UniversalGBuffer标记值声明两次通道。如果着色器必须使用正向渲染路径进行渲染,而不管URP渲染器使用的渲染路径是什么,只需声明一个Pass,并将LightMode标签设置为UniversalForwardOnly。

Universal2D

The Pass renders objects and evaluates 2D light contributions. URP uses this tag value in the 2D Renderer.

该流程渲染对象并评估2D光照贡献。URP在2D渲染器中使用这个标记值。

ShadowCaster

The Pass renders object depth from the perspective of lights into the Shadow map or a depth texture.

通道从光的角度渲染物体的深度到阴影贴图或深度纹理。

DepthOnly

The Pass renders only depth information from the perspective of a Camera into a depth texture.

Pass只从相机的角度渲染深度信息到深度纹理中。

Meta

Unity executes this Pass only when baking lightmaps in the Unity Editor. Unity strips this Pass from shaders when building a Player.

只有在Unity编辑器中烘焙光照贴图时,Unity才会执行此通道。当创建一个玩家时,Unity会从着色器中去除这个通道。

SRPDefaultUnlit

Use this LightMode tag value to draw an extra Pass when rendering objects. Application example: draw an object outline. This tag value is valid for both the Forward and the Deferred Rendering Paths.
URP uses this tag value as the default value when a Pass does not have a LightMode tag.

当渲染对象时,使用这个LightMode标签值来绘制一个额外的通道。应用程序示例:绘制对象轮廓。此标记值对正向和延迟渲染路径都有效。

当Pass没有LightMode标签时,URP使用这个标签值作为默认值。

注意:URP不支持以下LightMode标签:Always, ForwardAdd, PrepassBase, PrepassFinal, Vertex, VertexLMRGBM, VertexLM。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值