shader graph

With the release of Unity Editor 2019.1, the Shader Graph package officially came out of preview! Now, in 2019.2, we’re bringing even more features and functionality to Shader Graph.

随着Unity Editor 2019.1的发布,Shader Graph包正式退出预览! 现在,在2019.2中,我们将为Shader Graph带来更多功能。

2019年有何变化? (What’s Changed in 2019?)

自定义功能和子图升级 (Custom Function and Sub Graph Upgrades)

To maintain custom code inside of your Shader Graph, you can now use our new Custom Function node. This node allows you to define your own custom inputs and outputs, reorder them, and inject custom functions either directly into the node itself, or by referencing an external file.

要在“着色器图”中维护自定义代码,现在可以使用我们新的“自定义功能”节点。 该节点允许您定义自己的自定义输入和输出,对其进行重新排序,以及将自定义函数直接注入节点本身或通过引用外部文件。

Sub Graphs have also received an upgrade: you can now define your own outputs for Sub Graphs, with different types, custom names, and reorderable ports. Additionally, the Blackboard for Sub Graphs now supports all data types that the main graph supports.

子图也得到了升级:现在,您可以为子图定义自己的输出,具有不同的类型,自定义名称和可重新排序的端口。 此外,Blackboard for Sub Graphs现在支持主图支持的所有数据类型。

色彩模式和精度模式 (Color Modes and Precision Modes)

Using the Shader Graph to create powerful and optimized shaders just got a little easier. In 2019.2, you can now manually set the precision of calculations in your graph, either graph-wide or on a per-node basis. Our new Color Modes make it fast and easy to visualize the flow of Precision, the category of nodes, or display custom colors for your own use!

使用“ Shader Graph”创建功能强大且经过优化的着色器变得更加容易。 在2019.2中,您现在可以在图形范围内或在每个节点的基础上手动设置图形中的计算精度。 我们新的色彩模式使您可以快速,轻松地查看Precision的流程,节点类别或显示自定义颜色供您自己使用!

See the Shader Graph documentation for more information about these new features.

有关这些新功能的更多信息, 请参见 Shader Graph文档 。

样例项目 (Sample Project)

To help you get started with the new custom function workflow, we’ve created an example project together with step-by-step instructions. Download the project from our repository and follow along! This project will show you how to use the Custom Function node to write custom lighting shaders for the Lightweight Render Pipeline (LWRP). If you want to follow along using a fresh project, make sure you’re using the 2019.2 Editor and  LWRP package version 6.9.1 or higher.

为了帮助您开始使用新的自定义功能工作流程,我们创建了一个示例项目以及逐步说明。 从我们的资源库 下载项目 ,然后继续! 该项目将向您展示如何使用“自定义功能”节点为“轻量级渲染管道”(LWRP)编写自定义照明着色器。 如果要继续使用新项目,请确保使用的是2019.2编辑器和LWRP软件包6.9.1或更高版本。

从主光源获取数据 (Getting Data from the Main Light)

To get started, we need to get information from the main light in our Scene. Start by selecting Create > Shader > Unlit Graph to create a new Unlit Shader Graph. In the Create Node menu, locate the new Custom Function node, and click the gear icon on the top right to open the node menu.

首先,我们需要从场景中的主光源获取信息。 首先选择“ 创建”>“着色器”>“非照明图” 以创建一个新的“非照明着色器图”。 在“创建节点”菜单中,找到新的“自定义功能”节点,然后单击右上角的齿轮图标以打开节点菜单。

In this menu, you can add inputs and outputs. Add two output ports for Direction and Color, and select Vector 3 for both. If you see an “undeclared identifier” error flag, don’t be worried; this will go away when we start to add our code. In the Type dropdown menu, select String. Update your function name — in this example, we’re using “MainLight”. Now, we can start adding our custom code in the text box.

在此菜单中,您可以添加输入和输出。 为 DirectionColor 添加两个输出端口 ,然后 为两个都 选择 Vector 3 。 如果看到“未声明的标识符”错误标志,请不要担心。 当我们开始添加代码时,这将消失。 在 类型 下拉菜单中,选择 字符串 。 更新您的函数名称-在此示例中,我们使用“ MainLight”。 现在,我们可以开始在文本框中添加我们的自定义代码。

First, we’re going to use a flag called #ifdef SHADERGRAPH_PREVIEW. Because the preview boxes on nodes don’t have access to light data, we need to tell the node what to display on the in-graph preview boxes. #ifdef tells the compiler to use different code in different situations. Start by defining your fallback values for the output ports.

首先,我们将使用一个名为#ifdef SHADERGRAPH_PREVIEW的标志。 由于节点上的预览框无法访问光照数据,因此我们需要告诉节点在图形内预览框中显示的内容。 #ifdef告诉编译器在不同情况下使用不同的代码。 首先定义输出端口的后备值。

#if SHADERGRAPH_PREVIEW Direction = half3(0.5, 0.5, 0); Color = 1; #if SHADERGRAPH_PREVIEW Direction = half3(0.5, 0.5, 0); Color = 1;

1

2
3

#if SHADERGRAPH_PREVIEW
Direction = half3(0.5, 0.5, 0);
Color = 1;

1

2
3

#if SHADERGRAPH_PREVIEW
Direction = half3 ( 0.5 , 0.5 , 0 ) ;
Color = 1 ;

Next, we’ll use #else to tell the compiler what to do when not in a preview. This is where we actually get our light data. Use the built-in function GetMainLight() from the LWRP package. We can use this information to assign the Direction and Color outputs. Your custom function should now look like this:

接下来,我们将使用#else告诉编译器不在预览时的操作。 这是我们实际获取灯光数据的地方。 使用LWRP包中的内置函数GetMainLight() 。 我们可以使用此信息来分配“ 方向” 和“ 颜色” 输出。 您的自定义函数现在应如下所示:

#if SHADERGRAPH_PREVIEW Direction = half3(0.5, 0.5, 0); Color = 1; #else Light light = GetMainLight(); Direction = light.direction; Color = light.color; #endif #if SHADERGRAPH_PREVIEW Direction = half3(0.5, 0.5, 0); Color = 1; #else Light light = GetMainLight(); Direction = light.direction; Color = light.color; #endif

1

2
3
4
5
6
7
8

#if SHADERGRAPH_PREVIEW
Direction = half3(0.5, 0.5, 0);
Color = 1;
#else
Light light = GetMainLight();
Direction = light.direction;
Color = light.color;
#endif

1

2
3
4
5
6
7
8

#if SHADERGRAPH_PREVIEW
Direction = half3 ( 0.5 , 0.5 , 0 ) ;
Color = 1 ;
#else
Light light = GetMainLight ( ) ;
Direction = light . direction ;
Color = light . color ;
#endif

Now, it’s a good idea to add this node to a group so that you can mark down what it’s doing. Right-click the node, select Create Group from Selection, and then rename the group title to describe what your node is doing. Here we’ve entered “Get Main Light”.

现在,将这个节点添加到组中是一个好主意,以便您可以记下它在做什么。 右键单击该节点,选择“ 从Selection中创建组” ,然后重命名组标题以描述您的节点正在做什么。 在这里,我们输入了“获取主光源”。

Now that we have our light data, we can calculate some shading. We’re going to start with a standard Lambertian lighting, so let’s take the dot product of the world normal vector and the light direction. Pass it into a Saturate node, and multiply it by the light color. Plug this into the Color port of the Unlit Master node, and your preview should update with some custom shading!

现在我们有了光照数据,我们可以计算一些阴影了。 我们将从标准的Lambertian照明开始,让我们以世界法线向量和光方向的点积为例。 将其传递到饱和节点,然后将其乘以浅色。 将其插入 Unlit Master节点 的 Color 端口,您的预览应该使用一些自定义阴影进行更新!

使用自定义功能文件模式 (Using the Custom Function File Mode)

Since we now know how to get light data using the Custom Function node, we can expand on our function. Our next function gets attenuation values from the main light in addition to the direction and color.

由于现在我们知道如何使用“自定义功能”节点获取灯光数据,因此可以扩展功能。 我们的下一个功能除了获得方向和颜色之外,还从主光源获取衰减值。

As this is a more complicated function, let’s switch to file mode, and use an HLSL include file. This lets you author more complicated functions in a proper code editor before injecting it into the graph. This also means that we have one unified location to debug the code from.

由于这是一个更复杂的功能,让我们切换到文件模式,并使用HLSL包含文件。 这使您可以在将适当的代码编辑器注入图形之前,编写更复杂的函数。 这也意味着我们有一个统一的位置可以从中调试代码。

Start by opening the CustomLighting include file in the Assets > Include folder of the project. For now, we’ll only focus on the MainLight_half function. The function looks like this:

首先打开 项目 的 Assets> Include 文件夹中的 CustomLighting包含文件 。 现在,我们仅关注MainLight_half函数。 该函数如下所示:

void MainLight_half(float3 WorldPos, out half3 Direction, out half3 Color, out half DistanceAtten, out half ShadowAtten) { #if SHADERGRAPH_PREVIEW Direction = half3(0.5, 0.5, 0); Color = 1; DistanceAtten = 1; ShadowAtten = 1; #else #if SHADOWS_SCREEN half4 clipPos = TransformWorldToHClip(WorldPos); half4 shadowCoord = ComputeScreenPos(clipPos); #else half4 shadowCoord = TransformWorldToShadowCoord(WorldPos); #endif Light mainLight = GetMainLight(shadowCoord); Direction = mainLight.direction; Color = mainLight.color; DistanceAtten = mainLight.distanceAttenuation; ShadowAtten = mainLight.shadowAttenuation; #endif } void MainLight_half(float3 WorldPos, out half3 Direction, out half3 Color, out half DistanceAtten, out half ShadowAtten) { #if SHADERGRAPH_PREVIEW Direction = half3(0.5, 0.5, 0); Color = 1; DistanceAtten = 1; ShadowAtten = 1; #else #if SHADOWS_SCREEN half4 clipPos = TransformWorldToHClip(WorldPos); half4 shadowCoord = ComputeScreenPos(clipPos); #else half4 shadowCoord = TransformWorldToShadowCoord(WorldPos); #endif Light mainLight = GetMainLight(shadowCoord); Direction = mainLight.direction; Color = mainLight.color; DistanceAtten = mainLight.distanceAttenuation; ShadowAtten = mainLight.shadowAttenuation; #endif }

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

void MainLight_half(float3 WorldPos, out half3 Direction, out half3 Color, out half DistanceAtten, out half ShadowAtten)
{
#if SHADERGRAPH_PREVIEW
Direction = half3(0.5, 0.5, 0);
Color = 1;
DistanceAtten = 1;
ShadowAtten = 1;
#else
#if SHADOWS_SCREEN
half4 clipPos = TransformWorldToHClip(WorldPos);
half4 shadowCoord = ComputeScreenPos(clipPos);
#else
half4 shadowCoord = TransformWorldToShadowCoord(WorldPos);
#endif
Light mainLight = GetMainLight(shadowCoord);
Direction = mainLight.direction;
Color = mainLight.color;
DistanceAtten = mainLight.distanceAttenuation;
ShadowAtten = mainLight.shadowAttenuation;
#endif
}

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

void MainLight_half ( float3 WorldPos , out half3 Direction , out half3 Color , out half DistanceAtten , out half ShadowAtten )
{
#if SHADERGRAPH_PREVIEW
Direction = half3 ( 0.5 , 0.5 , 0 ) ;
Color = 1 ;
DistanceAtten = 1 ;
ShadowAtten = 1 ;
#else
#if SHADOWS_SCREEN
half4 clipPos = TransformWorldToHClip ( WorldPos ) ;
half4 shadowCoord = ComputeScreenPos ( clipPos ) ;
#else
half4 shadowCoord = TransformWorldToShadowCoord ( WorldPos ) ;
#endif
Light mainLight = GetMainLight ( shadowCoord ) ;
Direction = mainLight . direction ;
Color = mainLight . color ;
DistanceAtten = mainLight . distanceAttenuation ;
ShadowAtten = mainLight . shadowAttenuation ;
#endif
}

This function includes some new input and output data, so let’s go back to our Custom Function node and add them. Add two new outputs for DistanceAtten (distance attenuation) and ShadowAtten (shadow attenuation). Then, add the new input for WorldPos (world position). Now that we have our inputs and outputs, we can reference the include file. Change the Type dropdown to File. In the Source input, navigate to the include file, and select the Asset to reference. Now, we need to tell the node which function to use. In the Name box, we’ve entered “MainLight”.

该函数包括一些新的输入和输出数据,因此让我们回到“自定义函数”节点并添加它们。 为 DistanceAtten (距离衰减)和 ShadowAtten (阴影衰减) 添加两个新的输出 。 然后,为 WorldPos 添加新的输入 (世界位置)。 现在我们有了输入和输出,我们可以引用包含文件了。 将 类型 下拉列表 更改 为 文件 。 在源输入中,导航到包含文件,然后选择要引用的资产。 现在,我们需要告诉节点要使用哪个函数。 在“ 名称” 框中,我们输入了“ MainLight”。

You’ll notice that the include file has _half at the end of the function name, but our name option doesn’t. This is because the Shader Graph compiler appends the precision format to each function name. Since we’re defining our own function, we need the source code to tell the compiler which precision format our function uses. In the node, however, we only need to reference the main function name. You can create a duplicate of the function that uses ‘float’ values to compile in float precision mode. The ‘Precision’ Color Mode lets you easily track the precision set for each node in the graph, with blue representing float and red representing half.

您会注意到,包含文件的函数名称末尾有_half ,但我们的name选项没有。 这是因为Shader Graph编译器将精度格式附加到每个函数名称。 由于我们正在定义自己的函数,因此需要源代码来告诉编译器函数使用哪种精度格式。 但是,在节点中,我们只需要引用主函数名称。 您可以创建使用“ float”值在浮点精度模式下编译的函数的副本。 “精确度”颜色模式使您可以轻松跟踪图中每个节点的精确度设置,蓝色代表浮点数,红色代表一半。

We’ll probably want to use this function again somewhere else, and the easiest way to make this Custom Function reusable is to wrap it in a Sub Graph. Select the node and its group, and then right-click to find Convert to Sub-graph. We’ve called ours “Get Main Light”. In the Sub Graph, simply add the required output ports to the Sub Graph output node, and plug the node’s output into the Sub Graph output. Next, we’ll add a world position node to plug into the input.

我们可能想在其他地方再次使用此函数,并使此自定义函数可重用的最简单方法是将其包装在Sub Graph中。 选择节点及其组,然后右键单击以找到“ 转换为子图形” 。 我们称其为“获取主灯”。 在Sub Graph中,只需将所需的输出端口添加到Sub Graph输出节点,然后将节点的输出插入Sub Graph输出。 接下来,我们将添加一个世界位置节点以插入输入中。

Save the Sub Graph, and go back to our unlit graph. We’re going to add two new multiply nodes to our existing logic. First, multiply the two attenuation outputs together. Then, multiply that output by the light color. We can multiply this by NdotL from earlier to properly calculate attenuation in our basic shading.

保存子图,然后返回到我们未打开的图。 我们将向现有逻辑中添加两个新的乘法节点。 首先,将两个衰减输出相乘。 然后,将输出乘以浅色。 我们可以将其乘以 较早 的 NdotL 来正确计算基本阴影中的衰减。

创建直接镜面着色器 (Creating a Direct Specular Shader)

The shader we’ve made is great for matte objects, but what if we want some shine? We can add our own specular calculations to our shader! For this step, we’ll use another Custom Function node wrapped in a Sub Graph, called Direct Specular. Take a look at the CustomLighting include file again, and see that we’re now referencing another function from the same file:

我们制作的着色器非常适合遮罩对象,但是如果我们想要一些光泽,该怎么办? 我们可以将自己的镜面反射计算添加到我们的着色器中! 在此步骤中,我们将使用包裹在子图中的另一个“自定义功能”节点,称为“ 直接高光” 。 再次查看CustomLighting包含文件,看看我们现在正在引用同一文件中的另一个函数:

void DirectSpecular_half(half3 Specular, half Smoothness, half3 Direction, half3 Color, half3 WorldNormal, half3 WorldView, out half3 Out) { #if SHADERGRAPH_PREVIEW Out = 0; #else Smoothness = exp2(10 * Smoothness + 1); WorldNormal = normalize(WorldNormal); WorldView = SafeNormalize(WorldView); Out = LightingSpecular(Color, Direction, WorldNormal, WorldView, half4(Specular, 0), Smoothness); #endif } void DirectSpecular_half(half3 Specular, half Smoothness, half3 Direction, half3 Color, half3 WorldNormal, half3 WorldView, out half3 Out) { #if SHADERGRAPH_PREVIEW Out = 0; #else Smoothness = exp2(10 * Smoothness + 1); WorldNormal = normalize(WorldNormal); WorldView = SafeNormalize(WorldView); Out = LightingSpecular(Color, Direction, WorldNormal, WorldView, half4(Specular, 0), Smoothness); #endif }

1

2
3
4
5
6
7
8
9
10
11

void DirectSpecular_half(half3 Specular, half Smoothness, half3 Direction, half3 Color, half3 WorldNormal, half3 WorldView, out half3 Out)
{
#if SHADERGRAPH_PREVIEW
Out = 0;
#else
Smoothness = exp2(10 * Smoothness + 1);
WorldNormal = normalize(WorldNormal);
WorldView = SafeNormalize(WorldView);
Out = LightingSpecular(Color, Direction, WorldNormal, WorldView, half4(Specular, 0), Smoothness);
#endif
}

1

2
3
4
5
6
7
8
9
10
11

void DirectSpecular_half ( half3 Specular , half Smoothness , half3 Direction , half3 Color , half3 WorldNormal , half3 WorldView , out half3 Out )
{
#if SHADERGRAPH_PREVIEW
Out = 0 ;
#else
Smoothness = exp2 ( 10 * Smoothness + 1 ) ;
WorldNormal = normalize ( WorldNormal ) ;
WorldView = SafeNormalize ( WorldView ) ;
Out = LightingSpecular ( Color , Direction , WorldNormal , WorldView , half4 ( Specular , 0 ) , Smoothness ) ;
#endif
}

This function performs some simple specular calculations, and if you’re curious, you can read more about them here. The Sub Graph for this function also includes some inputs on the Blackboard:

此函数执行一些简单的镜面反射计算,如果您好奇的话,可以 在此处 阅读有关它们的更多信息 。 此功能的子图还包括黑板上的一些输入:

Make sure that your new node has all the appropriate input and output ports to match the function. Adding properties to the Blackboard is simple; just click the Add (+) icon on the top right, and select the data type. Double-click the pill to rename the input, and drag and drop the pill to add it to the graph. Lastly, update the output port for your Sub Graph, and save it.

确保新节点具有所有适当的输入和输出端口以匹配该功能。 向Blackboard添加属性很简单。 只需单击右上角的 添加(+) 图标,然后选择数据类型。 双击药丸以重命名输入,然后拖放药丸以将其添加到图形中。 最后,更新子图的输出端口,并保存它。

Now that specular calculation is set up, we can go back to the unlit graph, and add it through the Create Node menu. Connect the Attenuation output to the Color input of the Direct Specular Sub Graph. Next, connect the Direction output from the Get Main Light function to the Direction input of the specular Sub Graph. Add the result of NdotL*Attenuation to the output of the Direct Specular Sub Graph, and plug this in the Color output.

既然已经设置了镜面反射计算,我们可以返回到未点亮的图形,然后通过“创建节点”菜单将其添加。 将“ 衰减” 输出 连接 到 “ 直接高 光子图” 的“ 颜色” 输入 。 接下来,将“ 获取主光源”功能 的“ 方向” 输出 连接 到 高光子图 的“ 方向” 输入。 将NdotL * Attenuation的结果添加到 Direct Specular Sub Graph 的输出中 ,并将其插入 Color 输出中。

Now we’ve got a bit of shine!

现在,我们有了一点光彩!

使用多个光源 (Working with Multiple Lights)

The LWRP’s main light refers to the brightest directional light relative to the object, which is usually the sun. To improve performance on lower end hardware, the LWRP calculates the main light and any additional lights separately. To make sure our shader calculates correctly for all lights in the Scene, and not just the brightest directional light, you need to create a loop in your function.

LWRP的主光源是相对于物体的最亮的定向光,通常是太阳。 为了提高低端硬件的性能,LWRP分别计算主光源和任何其他光源。 为了确保我们的着色器能够正确计算场景中的所有灯光,而不仅仅是最明亮的定向灯光,您需要在函数中创建一个循环。

To get the additional light data, we used a new Sub Graph to wrap a new Custom Function node. Take a look at the AdditionalLight_float function in the CustomLighting include file:

为了获得更多的灯光数据,我们使用了新的子图来包装新的“自定义函数”节点。 看一下CustomLighting包含文件中的AdditionalLight_float函数:

void AdditionalLights_half(half3 SpecColor, half Smoothness, half3 WorldPosition, half3 WorldNormal, half3 WorldView, out half3 Diffuse, out half3 Specular) { half3 diffuseColor = 0; half3 specularColor = 0; #ifndef SHADERGRAPH_PREVIEW Smoothness = exp2(10 * Smoothness + 1); WorldNormal = normalize(WorldNormal); WorldView = SafeNormalize(WorldView); int pixelLightCount = GetAdditionalLightsCount(); for (int i = 0; i < pixelLightCount; ++i) { Light light = GetAdditionalLight(i, WorldPosition); half3 attenuatedLightColor = light.color * (light.distanceAttenuation * light.shadowAttenuation); diffuseColor += LightingLambert(attenuatedLightColor, light.direction, WorldNormal); specularColor += LightingSpecular(attenuatedLightColor, light.direction, WorldNormal, WorldView, half4(SpecColor, 0), Smoothness); } #endif Diffuse = diffuseColor; Specular = specularColor; } void AdditionalLights_half(half3 SpecColor, half Smoothness, half3 WorldPosition, half3 WorldNormal, half3 WorldView, out half3 Diffuse, out half3 Specular) { half3 diffuseColor = 0; half3 specularColor = 0; #ifndef SHADERGRAPH_PREVIEW Smoothness = exp2(10 * Smoothness + 1); WorldNormal = normalize(WorldNormal); WorldView = SafeNormalize(WorldView); int pixelLightCount = GetAdditionalLightsCount(); for (int i = 0; i < pixelLightCount; ++i) { Light light = GetAdditionalLight(i, WorldPosition); half3 attenuatedLightColor = light.color * (light.distanceAttenuation * light.shadowAttenuation); diffuseColor += LightingLambert(attenuatedLightColor, light.direction, WorldNormal); specularColor += LightingSpecular(attenuatedLightColor, light.direction, WorldNormal, WorldView, half4(SpecColor, 0), Smoothness); } #endif Diffuse = diffuseColor; Specular = specularColor; }

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

void AdditionalLights_half(half3 SpecColor, half Smoothness, half3 WorldPosition, half3 WorldNormal, half3 WorldView, out half3 Diffuse, out half3 Specular)
{
half3 diffuseColor = 0;
half3 specularColor = 0;
#ifndef SHADERGRAPH_PREVIEW
Smoothness = exp2(10 * Smoothness + 1);
WorldNormal = normalize(WorldNormal);
WorldView = SafeNormalize(WorldView);
int pixelLightCount = GetAdditionalLightsCount();
for (int i = 0; i < pixelLightCount; ++i)
{
Light light = GetAdditionalLight(i, WorldPosition);
half3 attenuatedLightColor = light.color * (light.distanceAttenuation * light.shadowAttenuation);
diffuseColor += LightingLambert(attenuatedLightColor, light.direction, WorldNormal);
specularColor += LightingSpecular(attenuatedLightColor, light.direction, WorldNormal, WorldView, half4(SpecColor, 0), Smoothness);
}
#endif
Diffuse = diffuseColor;
Specular = specularColor;
}

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

void AdditionalLights_half ( half3 SpecColor , half Smoothness , half3 WorldPosition , half3 WorldNormal , half3 WorldView , out half3 Diffuse , out half3 Specular )
{
half3 diffuseColor = 0 ;
half3 specularColor = 0 ;
#ifndef SHADERGRAPH_PREVIEW
Smoothness = exp2 ( 10 * Smoothness + 1 ) ;
WorldNormal = normalize ( WorldNormal ) ;
WorldView = SafeNormalize ( WorldView ) ;
int pixelLightCount = GetAdditionalLightsCount ( ) ;
for ( int i = 0 ; i < pixelLightCount ; ++ i )
{
Light light = GetAdditionalLight ( i , WorldPosition ) ;
half3 attenuatedLightColor = light . color * ( light . distanceAttenuation * light . shadowAttenuation ) ;
diffuseColor += LightingLambert ( attenuatedLightColor , light . direction , WorldNormal ) ;
specularColor += LightingSpecular ( attenuatedLightColor , light . direction , WorldNormal , WorldView , half4 ( SpecColor , 0 ) , Smoothness ) ;
}
#endif
Diffuse = diffuseColor ;
Specular = specularColor ;
}

Like before, use the AdditionalLights function in the file reference of the Custom Function node, and ensure that you’ve created all the proper inputs and outputs. Make sure to expose Specular Color and Specular Smoothness on the Blackboard of the Sub Graph in which the node is wrapped. Use the Position, Normal Vector, and View Direction nodes to plug in the World Position, World Normal, and World Space View Direction in the Sub Graph.

与之前一样,在“自定义函数”节点的文件引用中使用AdditionalLights函数,并确保已创建所有正确的输入和输出。 确保 在包裹节点的子图的黑板上 显示“ 镜面反射颜色” 和“ 镜面反射平滑度 ”。 使用“位置”,“法线向量”和“视图方向”节点 在子图中 插入“ 世界位置” ,“ 世界法线 ”和“ 世界空间视图方向 ”。

After you’ve set up the function, use it! First, take your main Unlit graph from the previous step, and collapse it to a Sub Graph. Select the nodes, and right-click Convert to Sub-graph. Remove the last Add node, and plug the outputs into the output ports of the Sub Graph. We recommend that you also create input properties for Specular and Smoothness.

设置功能后,请使用它! 首先,从上一步中获取您的主Unlit图,并将其折叠为Sub Graph。 选择节点,然后右键单击“ 转换为子图” 。 删除最后一个“添加”节点,然后将输出插入子图的输出端口。 我们建议您还为“ 镜面反射” 和“ 平滑度” 创建输入属性 。

Now you can combine your main light calculations and your additional light calculations together. In the main Unlit graph, create a new node for the Additional Light calculations to go alongside the Main Light calculations. Add the Diffuse and Specular outputs from Main Light and Additional Lights together. Pretty simple!

现在,您可以将主要的光照计算和其他的光照计算结合在一起。 在主“未照明”图中,为“其他照明”计算创建一个新节点,使其与“主照明”计算一起进行。 将“ 主光源”和“附加光源” 的“ 漫反射” 和“ 镜面反射” 输出相加。 很简单!

创建一个简单的卡通着色器 (Creating a Simple Toon Shader)

Now you know how to get the data from all lights in a Scene for an LWRP project, but what can you do with it? One of the most common uses for custom lighting in shaders is a classic toon shader!

现在,您知道如何从LWRP项目的“场景”中的所有灯光中获取数据,但是您将如何处理呢? 着色器中自定义照明的最常见用途之一是经典的卡通着色器!

With all of the light data, creating a toon shader is pretty simple. First, take all the light calculations you’ve done so far, and wrap them in a Sub Graph one more time. This will help with readability in the final shader. Don’t forget to remove the final Add node, and feed Diffuse and Specular into separate output ports on the Sub Graph output node.

利用所有的灯光数据,创建卡通着色器非常简单。 首先,进行到目前为止的所有光照计算,然后再将它们包装在“子图中”。 这将有助于最终着色器的可读性。 不要忘记删除最终的“添加”节点,并将“ 漫反射” 和“ 镜面 反射” 馈送 到“子图”输出节点上的单独输出端口。

There are lots of methods to create toon shading, but in this example, well use light intensity to look up colors from a Ramp Texture. This technique is usually called Ramp Lighting.  We’ve included some examples of the kind of Texture Asset needed for Ramp Lighting in the sample project. You can also sample a gradient to use dynamic ramps in Ramp Lighting.

有很多创建卡通阴影的方法,但是在此示例中,很好地利用光强度从“渐变纹理”中查找颜色。 此技术通常称为“斜坡照明”。 我们在示例项目中提供了一些示例示例,说明了“坡道照明”所需的纹理资产类型。 您也可以在渐变照明中采样渐变以使用动态渐变。

The first step is to convert the intensity of Diffuse and Specular from RGB values to HSV values. This lets us use the intensity of the light color (the HSV values) to determine the brightness on the shader, and helps us sample the Texture at different spots along the horizontal axis of the Asset. Use a static value for the Y channel of the UV to determine, from top to bottom, what part of the image should be sampled. You c

第一步是将“ 漫反射” 和“ 镜面 反射” 的强度 从RGB值转换为HSV值。 这使我们能够使用浅色的强度(HSV值)来确定着色器上的亮度,并帮助我们在沿着资源的水平轴的不同点处对纹理进行采样。 对UV的Y通道使用静态值,从上到下确定应该对图像的哪一部分进行采样。 你c

an use this static value as an index to reference multiple lighting ramps for the project in a single Texture Asset.

使用此静态值作为索引来引用单个Texture Asset中该项目的多个照明坡度 。

Once you’ve set the UV values, use a Sample Texture 2D LOD node to sample the Ramp Texture. The Sample LOD is important; if we use a regular Sample Texture 2D node, the ramp is automatically mipped in a Scene, and objects further away will have different lighting behaviors. Using a Sample Texture 2D LOD node allows us to manually determine the mip level. Additionally, since the Ramp Texture is only 2 pixels high, we created our own Sampler State for the Textures.  To make sure that the Texture is sampled correctly, we set the Filter to Point, and the Wrap to Clamp. We exposed this as a property on the Blackboard so that you can change the settings if the Texture Asset changes.

设置UV值后,使用“采样纹理2D LOD”节点采样“渐变纹理”。 样本LOD很重要; 如果我们使用常规的Sample Texture 2D(采样纹理2D)节点,则渐变将自动在场景中进行剪切,并且较远的对象将具有不同的照明行为。 使用“采样纹理2D LOD”节点可以使我们手动确定Mip级别。 另外,由于斜坡纹理只有2个像素高,因此我们为纹理创建了自己的采样器状态。 为了确保正确采样纹理,我们将“滤镜”设置为“点”,将“环绕”设置为“钳位”。 我们将此属性公开为Blackboard上的一个属性,以便在Texture Asset更改时可以更改设置。

Finally, we multiply the ramp sample from the diffuse calculations by a color property, Diffuse, so that we can change the object’s colors. Add the ramp sample from the specular calculations to the Diffuse output, and plug the final color into the Master node.

最后,我们将来自漫反射计算的渐变样本乘以颜色属性 Diffuse ,以便可以更改对象的颜色。 将镜面反射计算中的渐变样本添加到“漫反射”输出中,然后将最终颜色插入“主”节点中。

扩展定制照明 (Expanding Custom Lighting)

This simple custom lighting setup can be expanded and applied to a wide variety of use cases in all kinds of Scenes. In our example project, we’ve included a full Scene configured with shaders that use our custom lighting setup. It also contains vertex animation, a simple subsurface scattering approximation, as well as refractions and coloring that use depth. Download the project, and check out our Example Assets to explore more advanced methods!

这种简单的自定义照明设置可以扩展并应用于各种场景中的各种使用案例。 在示例项目中,我们包括一个完整的场景,该场景配置了使用自定义照明设置的着色器。 它还包含顶点动画,简单的地下散射近似值以及使用深度的折射和着色。 下载项目,并查看我们的示例资产以探索更高级的方法!

保持学习! (Keep Learning!)

If you want to discuss Shader Graph, and the shaders you can make with it, come hang out in our brand new forum space! You can also find community members and (sometimes) a few developers hanging out in the community Discord!

如果您想讨论Shader Graph及其可以使用的着色器,请在我们全新的 论坛空间中 闲逛 ! 您还可以在 社区Discord中 找到社区成员和(有时)一些开发人员 。

Don’t forget to keep an eye out for recordings of our SIGGRAPH 2019 sessions, where we go into even more detail about using Shader Graph for custom lighting!

别忘了留意SIGGRAPH 2019会议的录音,在此我们将详细介绍如何使用Shader Graph进行自定义照明!

翻译自: https://blogs.unity3d.com/2019/07/31/custom-lighting-in-shader-graph-expanding-your-graphs-in-2019/

shader graph

shader graph_Shader Graph中的自定义照明:在2019年扩展图形相关推荐

  1. shader graph_在Shader Graph中使用表面梯度框架进行法线贴图合成

    shader graph A recent Unity Labs paper introduces a new framework for blending normal maps that is e ...

  2. 从一个简洁的进度刻度绘制中了解自定义 View 的思路流程

    先看效果(原谅我的渣像素),进度的刻度.宽度.颜色可以随意设定: 项目github地址: https://github.com/zhangke301... 实现起来并不难,通过本文,我们可以学到: 1 ...

  3. 在ASP.NET Core中创建自定义端点可视化图

    在上篇文章中,我为构建自定义端点可视化图奠定了基础,正如我在第一篇文章中展示的那样.该图显示了端点路由的不同部分:文字值,参数,动词约束和产生结果的端点: 在本文中,我将展示如何通过创建一个自定义的D ...

  4. UE | Shader | 在UE中添加全局Shader

    UE | Shader | 在UE中添加全局Shader .usf(Unreal Shader Files) 和.usf的使用 .usf文件编写 .usf文件绑定.cpp和.h文件 添加控制台变量 D ...

  5. Unity Shader 学习笔记(5)Shader变体、Shader属性定义技巧、自定义材质面板

    写在之前 Shader变体.Shader属性定义技巧.自定义材质面板,这三个知识点任何一个单拿出来都是一套知识体系,不能一概而论,本文章目的在于将学习和实际工作中遇见的问题进行总结,类似于网络笔记之用 ...

  6. 从一个简洁的进度刻度绘制中了解自定义View的思路流程

    先看效果(原谅我的渣像素),进度的刻度.宽度.颜色可以随意设定: [项目github地址: https://github.com/zhangke3016/CircleLoading] 实现起来并不难, ...

  7. echarts graph关系图自定义线条label颜色

    echarts graph关系图自定义线条label颜色 一.按条件匹配自定义颜色 echarts 关系图自定义线条label颜色 自定义label颜色:通过配置edgeLabel中的rich和for ...

  8. 自主数据类型:在TVM中启用自定义数据类型探索

    自主数据类型:在TVM中启用自定义数据类型探索 介绍 在设计加速器时,一个重要的决定是如何在硬件中近似地表示实数.这个问题有一个长期的行业标准解决方案:IEEE 754浮点标准.1.然而,当试图通过构 ...

  9. [Flash开发笔记] 如何在as2.0中使用自定义类事件

    as2编程中,我们通常要处理一些异步加载的数据,有点类似ajax中的callback,即我们不知道何时数据才会返回,并且只有当数据返回时,执行我们定义的操作.     在flash6及以前,我们会常常 ...

最新文章

  1. 自定义Adapter中实现startActivityForResult的分析
  2. 一维随机变量及其概率分布
  3. Elasticsearch中如何进行日期(数值)范围查询
  4. javaone_JavaOne和OOW 2015总结
  5. exit(0)和exit(1)区别
  6. 2015年《大数据》高被引论文 Top10
  7. JVM源码分析--ClassLoader类加载器
  8. linux 开放端口
  9. 拷贝网页内容增加版权信息的 JavaScript 代码示例
  10. Understanding Growth
  11. SpringSecurity安全框架的笔记
  12. 最优算法-LQR-离散时间有限边界
  13. POJ-3764 01-Trie
  14. 教你如何卷积操作进行边缘检测,基础必备
  15. 鳄克斯系列服务器,英雄联盟:FPX冠军庆典活动进行中 一半宝石入手海科克斯系列皮肤...
  16. Unity Shader - Specular mode: Specular parameter 高光模式中的高光参数
  17. 如何防止局域网病毒春风吹又生--之一
  18. Unity之ASE从入门到精通 目录
  19. 视频会议室预定小程序_6个最佳免费视频会议应用程序
  20. HaaS轻应用(JavaScript)低功耗蓝牙案例

热门文章

  1. svn服务器现存的库文件导入,svn导入版本库及相关知识
  2. maven 多模块项目,打包其中一个项目,Could not find artifact org.javaboy:commons:pom:1.0-SNAPSHOT
  3. javascript开发HTML5游戏--斗地主(单机模式part2)
  4. 如何format格式化ftl模板
  5. 基于Vue+Element Plus实现快速导航
  6. EPUB电子书阅读与制作
  7. HTTP协议漫谈 - HTTP协议历史和报文结构
  8. 男神网络红人莫小涛2022生活百度图片大全
  9. RNA-seq流程学习笔记(18)- Heatmap图
  10. [笔]蔚来汽车网络笔/面试题汇总解答