1. Introdução
Olá, Habr. Hoje, quero falar um pouco sobre como você pode, rápida e sem esforço (quase) começar a escrever sombreadores de
texto clássicos no Unity usando o
Lightweight Rendering Pipeline (LWRP) - um dos exemplos do
pipeline Scriptable Rendering Pipeline (SRP) .
Mas e o gráfico Shader?
O Shader Graph é uma ferramenta conveniente e rápida para criar protótipos ou escrever efeitos simples. No entanto, às vezes, é necessário escrever algo complicado e complexo, e então - o número de nós, funções personalizadas, sub-gráficos está aumentando incrivelmente, e é por isso que até o programador gráfico mais experiente começa a ficar confuso com toda essa bagunça. Todos nós entendemos que o código gerado automaticamente a priori não pode ser melhor do que o escrito manualmente - você não precisa ir muito longe para obter exemplos, porque qualquer erro no layout dos nós pode levar ao fato de que o resultado do cálculo já conhecido no sombreador de vértice será contado repetidamente no fragmento. Existem pessoas que se sentem mais à vontade trabalhando com
código , em vez de com nós. Os motivos podem ser diferentes, mas a essência é a mesma - com os nós, viva o código!

Edição
Então, qual é o problema de sentar e escrever um sombreador de texto comum em LWRP? E o problema é que os Shaders de
superfície padrão favoritos de todos não são suportados no LWRP.
Ao tentar usá-lo, obtemos o seguinte:

Código de sombreadorShader "Custom/NewSurfaceShader" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Glossiness ("Smoothness", Range(0,1)) = 0.5 _Metallic ("Metallic", Range(0,1)) = 0.0 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM
Então, lembre-se de tentar escrever um shader anlite comum com uma parte de vértice e fragmento. E, felizmente, tudo funciona:

Código de sombreador Shader "Unlit/NewUnlitShader" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag
No entanto, como você pode não ficar triste - parecíamos ficar nus à margem sem luz, sombras, mapas de luz e
PBR amado, sem os quais a vida não era agradável.
Claro, você pode escrever tudo à mão:
Que haja luz!
Código de sombreador Shader "TheProxor/Simple Lit" { Properties { _MainTex("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType" = "Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag
Tudo parece funcionar, mas isso é apenas iluminação difusa. O que fazer depois? Você pode continuar devolvendo tudo manualmente, mas é longo e sombrio, e o PBR não pode ser devolvido de forma alguma, e perdemos todos os chips LWRP. Portanto, não temos escolha a não ser escolher o LWRP para retornar tudo com um toque mágico.
Solução
Portanto, como você sabe, o LWRP é construído com base no
Forward-renderer , o que significa que ele possui seu próprio LitForwardPass, que deve ser descrito em algum lugar. Obviamente, em algum lugar existe um CGInclude no qual tudo isso é descrito. É aqui que partimos para uma divertida aventura em:
% localappdata% \ Unity \ cache \ packages \ packages.unity.com \ com.unity.render-pipelines.lightweight @ (versão LWRP) \
Na verdade, tendo chegado ao endereço especificado e entrado na pasta
Shaders , podemos encontrar um shader interessante chamado
Lit.shader . Na verdade, podemos dizer que nossa busca terminou, aqui está - o cobiçado shader. Indo para dentro - encontramos o seguinte conteúdo:
Lit.shader Shader "Lightweight Render Pipeline/Lit" { Properties {
Resta apenas expandi-lo para facilitar a edição, livrando-se do include-s. Bem, modifique um pouco à sua maneira.
Temos algo parecido com isto:

Uber shader final Shader "TheProxor/LWRP/Dissolve + Vertex Offset" { Properties {
Primeiro de tudo, você precisa entender o que há no shader resultante. Em primeiro lugar, estamos interessados na linha:
CustomEditor "UnityEditor.Rendering.LWRP.ShaderGUI.LitShaderEditor"
Que, como lembramos, no original era o seguinte:
CustomEditor "UnityEditor.Rendering.LWRP.ShaderGUI.LitShader"
Então, o que é e por que é? A resposta a essa pergunta é bastante simples - se você observar atentamente, o shader possui um grande número de tipos diferentes de definições, que, curiosamente, precisam ser ativadas e desativadas, e isso, por um momento, precisa ser feito a partir do código, e é por isso que precisamos de um inspetor personalizado. Além disso, nosso inspetor personalizado deve nos dar a oportunidade de editar não apenas as propriedades internas, mas também aquelas que podemos precisar em nossos shaders. , :
%localappdata%\Unity\cache\packages\packages.unity.com\com.unity.render-pipelines.lightweight@6.9.2\Editor\ShaderGUI\Shaders\
, LitShader.cs, BaseShaderGUI:
internal class LitShader : BaseShaderGUI { ... }
BaseShaderGUI.cs, :
public abstract class BaseShaderGUI : ShaderGUI { ... }
Editor ( — , , UnityEditor ). , , -
SavedBool , bool. . , .
:
SavedBool m_SurfaceOptionsFoldout; SavedBool m_SurfaceInputsFoldout; SavedBool m_AdvancedFoldout;
:
AnimatedValues.AnimBool m_SurfaceOptionsFoldout; AnimatedValues.AnimBool m_SurfaceInputsFoldout; AnimatedValues.AnimBool m_AdvancedFoldout;
:
AnimatedValues.AnimBool m_OtherFoldout;
, :
protected class Styles {
. ,
HideInInspector , , . , , ? , ! , -. :
... [MainColor][HideInInspector] _BaseColor("Color", Color) = (0.5,0.5,0.5,1) [MainTexture][HideInInspector] _BaseMap("Albedo", 2D) = "white" {} [HideInInspector]_Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5 [HideInInspector]_Smoothness("Smoothness", Range(0.0, 1.0)) = 0.5 [HideInInspector]_GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0 [HideInInspector]_SmoothnessTextureChannel("Smoothness texture channel", Float) = 0 ...
:
public override void OnGUI(...) { ... m_OtherFoldout.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_OtherFoldout.value, Styles.OtherOptions); if (m_OtherFoldout.value) { base.OnGUI(materialEditorIn, properties); EditorGUILayout.Space(); } EditorGUILayout.EndFoldoutHeaderGroup(); ... }
:

:
BaseShaderEditor.cs using System; using UnityEngine; using UnityEngine.Rendering; using UnityEditor.Rendering; namespace UnityEditor { public abstract class BaseShaderEditor : ShaderGUI { #region EnumsAndClasses public enum SurfaceType { Opaque, Transparent } public enum BlendMode { Alpha, // Old school alpha-blending mode, fresnel does not affect amount of transparency Premultiply, // Physically plausible transparency mode, implemented as alpha pre-multiply Additive, Multiply } public enum SmoothnessSource { BaseAlpha, SpecularAlpha } public enum RenderFace { Front = 2, Back = 1, Both = 0 } protected class Styles { // Catergories public static readonly GUIContent SurfaceOptions = new GUIContent("Surface Options", "Controls how LWRP renders the Material on a screen."); public static readonly GUIContent SurfaceInputs = new GUIContent("Surface Inputs", "These settings describe the look and feel of the surface itself."); public static readonly GUIContent AdvancedLabel = new GUIContent("Advanced", "These settings affect behind-the-scenes rendering and underlying calculations."); public static readonly GUIContent surfaceType = new GUIContent("Surface Type", "Select a surface type for your texture. Choose between Opaque or Transparent."); public static readonly GUIContent blendingMode = new GUIContent("Blending Mode", "Controls how the color of the Transparent surface blends with the Material color in the background."); public static readonly GUIContent cullingText = new GUIContent("Render Face", "Specifies which faces to cull from your geometry. Front culls front faces. Back culls backfaces. None means that both sides are rendered."); public static readonly GUIContent alphaClipText = new GUIContent("Alpha Clipping", "Makes your Material act like a Cutout shader. Use this to create a transparent effect with hard edges between opaque and transparent areas."); public static readonly GUIContent alphaClipThresholdText = new GUIContent("Threshold", "Sets where the Alpha Clipping starts. The higher the value is, the brighter the effect is when clipping starts."); public static readonly GUIContent receiveShadowText = new GUIContent("Receive Shadows", "When enabled, other GameObjects can cast shadows onto this GameObject."); public static readonly GUIContent baseMap = new GUIContent("Base Map", "Specifies the base Material and/or Color of the surface. If you've selected Transparent or Alpha Clipping under Surface Options, your Material uses the Texture's alpha channel or color."); public static readonly GUIContent emissionMap = new GUIContent("Emission Map", "Sets a Texture map to use for emission. You can also select a color with the color picker. Colors are multiplied over the Texture."); public static readonly GUIContent normalMapText = new GUIContent("Normal Map", "Assigns a tangent-space normal map."); public static readonly GUIContent bumpScaleNotSupported = new GUIContent("Bump scale is not supported on mobile platforms"); public static readonly GUIContent fixNormalNow = new GUIContent("Fix now", "Converts the assigned texture to be a normal map format."); public static readonly GUIContent queueSlider = new GUIContent("Priority", "Determines the chronological rendering order for a Material. High values are rendered first."); public static readonly GUIContent OtherOptions = new GUIContent("Your own options", "You own custom options"); } #endregion #region Variables protected MaterialEditor materialEditor { get; set; } protected MaterialProperty surfaceTypeProp { get; set; } protected MaterialProperty blendModeProp { get; set; } protected MaterialProperty cullingProp { get; set; } protected MaterialProperty alphaClipProp { get; set; } protected MaterialProperty alphaCutoffProp { get; set; } protected MaterialProperty receiveShadowsProp { get; set; } // Common Surface Input properties protected MaterialProperty baseMapProp { get; set; } protected MaterialProperty baseColorProp { get; set; } protected MaterialProperty emissionMapProp { get; set; } protected MaterialProperty emissionColorProp { get; set; } protected MaterialProperty queueOffsetProp { get; set; } public bool m_FirstTimeApply = true; private const string k_KeyPrefix = "LightweightRP:Material:UI_State:"; private string m_HeaderStateKey = null; // Header foldout states AnimatedValues.AnimBool m_SurfaceOptionsFoldout; AnimatedValues.AnimBool m_SurfaceInputsFoldout; AnimatedValues.AnimBool m_AdvancedFoldout; AnimatedValues.AnimBool m_OtherFoldout; #endregion private const int queueOffsetRange = 50; //////////////////////////////////// // General Functions // //////////////////////////////////// #region GeneralFunctions public abstract void MaterialChanged(Material material); public virtual void FindProperties(MaterialProperty[] properties) { surfaceTypeProp = FindProperty("_Surface", properties); blendModeProp = FindProperty("_Blend", properties); cullingProp = FindProperty("_Cull", properties); alphaClipProp = FindProperty("_AlphaClip", properties); alphaCutoffProp = FindProperty("_Cutoff", properties); receiveShadowsProp = FindProperty("_ReceiveShadows", properties, false); baseMapProp = FindProperty("_BaseMap", properties, false); baseColorProp = FindProperty("_BaseColor", properties, false); emissionMapProp = FindProperty("_EmissionMap", properties, false); emissionColorProp = FindProperty("_EmissionColor", properties, false); queueOffsetProp = FindProperty("_QueueOffset", properties, false); } public override void OnGUI(MaterialEditor materialEditorIn, MaterialProperty[] properties) { if (materialEditorIn == null) throw new ArgumentNullException("materialEditorIn"); FindProperties(properties); // MaterialProperties can be animated so we do not cache them but fetch them every event to ensure animated values are updated correctly materialEditor = materialEditorIn; Material material = materialEditor.target as Material; // Make sure that needed setup (ie keywords/renderqueue) are set up if we're switching some existing // material to a lightweight shader. if (m_FirstTimeApply) { OnOpenGUI(material, materialEditorIn); m_FirstTimeApply = false; } ShaderPropertiesGUI(material); m_OtherFoldout.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_OtherFoldout.value, Styles.OtherOptions); if (m_OtherFoldout.value) { base.OnGUI(materialEditorIn, properties); EditorGUILayout.Space(); } EditorGUILayout.EndFoldoutHeaderGroup(); foreach (var obj in materialEditor.targets) MaterialChanged((Material)obj); } public virtual void OnOpenGUI(Material material, MaterialEditor materialEditor) { // Foldout states m_HeaderStateKey = k_KeyPrefix + material.shader.name; // Create key string for editor prefs m_SurfaceOptionsFoldout = new AnimatedValues.AnimBool(true); m_SurfaceInputsFoldout = new AnimatedValues.AnimBool(true); m_AdvancedFoldout = new AnimatedValues.AnimBool(true); m_OtherFoldout = new AnimatedValues.AnimBool(true); foreach (var obj in materialEditor.targets) MaterialChanged((Material)obj); } public void ShaderPropertiesGUI(Material material) { if (material == null) throw new ArgumentNullException("material"); EditorGUI.BeginChangeCheck(); m_SurfaceOptionsFoldout.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SurfaceOptionsFoldout.value, Styles.SurfaceOptions); if (m_SurfaceOptionsFoldout.value) { DrawSurfaceOptions(material); EditorGUILayout.Space(); } EditorGUILayout.EndFoldoutHeaderGroup(); m_SurfaceInputsFoldout.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_SurfaceInputsFoldout.value, Styles.SurfaceInputs); if (m_SurfaceInputsFoldout.value) { DrawSurfaceInputs(material); EditorGUILayout.Space(); } EditorGUILayout.EndFoldoutHeaderGroup(); m_AdvancedFoldout.value = EditorGUILayout.BeginFoldoutHeaderGroup(m_AdvancedFoldout.value, Styles.AdvancedLabel); if (m_AdvancedFoldout.value) { DrawAdvancedOptions(material); EditorGUILayout.Space(); } EditorGUILayout.EndFoldoutHeaderGroup(); DrawAdditionalFoldouts(material); if (EditorGUI.EndChangeCheck()) { foreach (var obj in materialEditor.targets) MaterialChanged((Material)obj); } } #endregion //////////////////////////////////// // Drawing Functions // //////////////////////////////////// #region DrawingFunctions public virtual void DrawSurfaceOptions(Material material) { DoPopup(Styles.surfaceType, surfaceTypeProp, Enum.GetNames(typeof(SurfaceType))); if ((SurfaceType)material.GetFloat("_Surface") == SurfaceType.Transparent) DoPopup(Styles.blendingMode, blendModeProp, Enum.GetNames(typeof(BlendMode))); EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = cullingProp.hasMixedValue; var culling = (RenderFace)cullingProp.floatValue; culling = (RenderFace)EditorGUILayout.EnumPopup(Styles.cullingText, culling); if (EditorGUI.EndChangeCheck()) { materialEditor.RegisterPropertyChangeUndo(Styles.cullingText.text); cullingProp.floatValue = (float)culling; material.doubleSidedGI = (RenderFace)cullingProp.floatValue != RenderFace.Front; } EditorGUI.showMixedValue = false; EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = alphaClipProp.hasMixedValue; var alphaClipEnabled = EditorGUILayout.Toggle(Styles.alphaClipText, alphaClipProp.floatValue == 1); if (EditorGUI.EndChangeCheck()) alphaClipProp.floatValue = alphaClipEnabled ? 1 : 0; EditorGUI.showMixedValue = false; if (alphaClipProp.floatValue == 1) materialEditor.ShaderProperty(alphaCutoffProp, Styles.alphaClipThresholdText, 1); if (receiveShadowsProp != null) { EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = receiveShadowsProp.hasMixedValue; var receiveShadows = EditorGUILayout.Toggle(Styles.receiveShadowText, receiveShadowsProp.floatValue == 1.0f); if (EditorGUI.EndChangeCheck()) receiveShadowsProp.floatValue = receiveShadows ? 1.0f : 0.0f; EditorGUI.showMixedValue = false; } } public virtual void DrawSurfaceInputs(Material material) { DrawBaseProperties(material); } public virtual void DrawAdvancedOptions(Material material) { materialEditor.EnableInstancingField(); if (queueOffsetProp != null) { EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = queueOffsetProp.hasMixedValue; var queue = EditorGUILayout.IntSlider(Styles.queueSlider, (int)queueOffsetProp.floatValue, -queueOffsetRange, queueOffsetRange); if (EditorGUI.EndChangeCheck()) queueOffsetProp.floatValue = queue; EditorGUI.showMixedValue = false; } } public virtual void DrawAdditionalFoldouts(Material material) { } public virtual void DrawBaseProperties(Material material) { if (baseMapProp != null && baseColorProp != null) // Draw the baseMap, most shader will have at least a baseMap { materialEditor.TexturePropertySingleLine(Styles.baseMap, baseMapProp, baseColorProp); // TODO Temporary fix for lightmapping, to be replaced with attribute tag. if (material.HasProperty("_MainTex")) { material.SetTexture("_MainTex", baseMapProp.textureValue); var baseMapTiling = baseMapProp.textureScaleAndOffset; material.SetTextureScale("_MainTex", new Vector2(baseMapTiling.x, baseMapTiling.y)); material.SetTextureOffset("_MainTex", new Vector2(baseMapTiling.z, baseMapTiling.w)); } } } protected virtual void DrawEmissionProperties(Material material, bool keyword) { var emissive = true; var hadEmissionTexture = emissionMapProp.textureValue != null; if (!keyword) { materialEditor.TexturePropertyWithHDRColor(Styles.emissionMap, emissionMapProp, emissionColorProp, false); } else { // Emission for GI? emissive = materialEditor.EmissionEnabledProperty(); EditorGUI.BeginDisabledGroup(!emissive); { // Texture and HDR color controls materialEditor.TexturePropertyWithHDRColor(Styles.emissionMap, emissionMapProp, emissionColorProp, false); } EditorGUI.EndDisabledGroup(); } // If texture was assigned and color was black set color to white var brightness = emissionColorProp.colorValue.maxColorComponent; if (emissionMapProp.textureValue != null && !hadEmissionTexture && brightness <= 0f) emissionColorProp.colorValue = Color.white; // LW does not support RealtimeEmissive. We set it to bake emissive and handle the emissive is black right. if (emissive) { material.globalIlluminationFlags = MaterialGlobalIlluminationFlags.BakedEmissive; if (brightness <= 0f) material.globalIlluminationFlags |= MaterialGlobalIlluminationFlags.EmissiveIsBlack; } } public static void DrawNormalArea(MaterialEditor materialEditor, MaterialProperty bumpMap, MaterialProperty bumpMapScale = null) { if (bumpMapScale != null) { materialEditor.TexturePropertySingleLine(Styles.normalMapText, bumpMap, bumpMap.textureValue != null ? bumpMapScale : null); if (bumpMapScale.floatValue != 1 && UnityEditorInternal.InternalEditorUtility.IsMobilePlatform( EditorUserBuildSettings.activeBuildTarget)) if (materialEditor.HelpBoxWithButton(Styles.bumpScaleNotSupported, Styles.fixNormalNow)) bumpMapScale.floatValue = 1; } else { materialEditor.TexturePropertySingleLine(Styles.normalMapText, bumpMap); } } protected static void DrawTileOffset(MaterialEditor materialEditor, MaterialProperty textureProp) { materialEditor.TextureScaleOffsetProperty(textureProp); } #endregion //////////////////////////////////// // Material Data Functions // //////////////////////////////////// #region MaterialDataFunctions public static void SetMaterialKeywords(Material material, Action<Material> shadingModelFunc = null, Action<Material> shaderFunc = null) { // Clear all keywords for fresh start material.shaderKeywords = null; // Setup blending - consistent across all LWRP shaders SetupMaterialBlendMode(material); // Receive Shadows if (material.HasProperty("_ReceiveShadows")) CoreUtils.SetKeyword(material, "_RECEIVE_SHADOWS_OFF", material.GetFloat("_ReceiveShadows") == 0.0f); // Emission if (material.HasProperty("_EmissionColor")) MaterialEditor.FixupEmissiveFlag(material); bool shouldEmissionBeEnabled = (material.globalIlluminationFlags & MaterialGlobalIlluminationFlags.EmissiveIsBlack) == 0; if (material.HasProperty("_EmissionEnabled") && !shouldEmissionBeEnabled) shouldEmissionBeEnabled = material.GetFloat("_EmissionEnabled") >= 0.5f; CoreUtils.SetKeyword(material, "_EMISSION", shouldEmissionBeEnabled); // Normal Map if (material.HasProperty("_BumpMap")) CoreUtils.SetKeyword(material, "_NORMALMAP", material.GetTexture("_BumpMap")); // Shader specific keyword functions shadingModelFunc?.Invoke(material); shaderFunc?.Invoke(material); } public static void SetupMaterialBlendMode(Material material) { if (material == null) throw new ArgumentNullException("material"); bool alphaClip = material.GetFloat("_AlphaClip") == 1; if (alphaClip) { material.EnableKeyword("_ALPHATEST_ON"); } else { material.DisableKeyword("_ALPHATEST_ON"); } var queueOffset = 0; // queueOffsetRange; if (material.HasProperty("_QueueOffset")) queueOffset = queueOffsetRange - (int)material.GetFloat("_QueueOffset"); SurfaceType surfaceType = (SurfaceType)material.GetFloat("_Surface"); if (surfaceType == SurfaceType.Opaque) { if (alphaClip) { material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest; material.SetOverrideTag("RenderType", "TransparentCutout"); } else { material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Geometry; material.SetOverrideTag("RenderType", "Opaque"); } material.renderQueue += queueOffset; material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero); material.SetInt("_ZWrite", 1); material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); material.SetShaderPassEnabled("ShadowCaster", true); } else { BlendMode blendMode = (BlendMode)material.GetFloat("_Blend"); var queue = (int)UnityEngine.Rendering.RenderQueue.Transparent; // Specific Transparent Mode Settings switch (blendMode) { case BlendMode.Alpha: material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); break; case BlendMode.Premultiply: material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); material.EnableKeyword("_ALPHAPREMULTIPLY_ON"); break; case BlendMode.Additive: material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.One); material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); break; case BlendMode.Multiply: material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.DstColor); material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero); material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); material.EnableKeyword("_ALPHAMODULATE_ON"); break; } // General Transparent Material Settings material.SetOverrideTag("RenderType", "Transparent"); material.SetInt("_ZWrite", 0); material.renderQueue = queue + queueOffset; material.SetShaderPassEnabled("ShadowCaster", false); } } #endregion //////////////////////////////////// // Helper Functions // //////////////////////////////////// #region HelperFunctions public static void TwoFloatSingleLine(GUIContent title, MaterialProperty prop1, GUIContent prop1Label, MaterialProperty prop2, GUIContent prop2Label, MaterialEditor materialEditor, float labelWidth = 30f) { EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = prop1.hasMixedValue || prop2.hasMixedValue; Rect rect = EditorGUILayout.GetControlRect(); EditorGUI.PrefixLabel(rect, title); var indent = EditorGUI.indentLevel; var preLabelWidth = EditorGUIUtility.labelWidth; EditorGUI.indentLevel = 0; EditorGUIUtility.labelWidth = labelWidth; Rect propRect1 = new Rect(rect.x + preLabelWidth, rect.y, (rect.width - preLabelWidth) * 0.5f, EditorGUIUtility.singleLineHeight); var prop1val = EditorGUI.FloatField(propRect1, prop1Label, prop1.floatValue); Rect propRect2 = new Rect(propRect1.x + propRect1.width, rect.y, propRect1.width, EditorGUIUtility.singleLineHeight); var prop2val = EditorGUI.FloatField(propRect2, prop2Label, prop2.floatValue); EditorGUI.indentLevel = indent; EditorGUIUtility.labelWidth = preLabelWidth; if (EditorGUI.EndChangeCheck()) { materialEditor.RegisterPropertyChangeUndo(title.text); prop1.floatValue = prop1val; prop2.floatValue = prop2val; } EditorGUI.showMixedValue = false; } public void DoPopup(GUIContent label, MaterialProperty property, string[] options) { DoPopup(label, property, options, materialEditor); } public static void DoPopup(GUIContent label, MaterialProperty property, string[] options, MaterialEditor materialEditor) { if (property == null) throw new ArgumentNullException("property"); EditorGUI.showMixedValue = property.hasMixedValue; var mode = property.floatValue; EditorGUI.BeginChangeCheck(); mode = EditorGUILayout.Popup(label, (int)mode, options); if (EditorGUI.EndChangeCheck()) { materialEditor.RegisterPropertyChangeUndo(label.text); property.floatValue = mode; } EditorGUI.showMixedValue = false; } // Helper to show texture and color properties public static Rect TextureColorProps(MaterialEditor materialEditor, GUIContent label, MaterialProperty textureProp, MaterialProperty colorProp, bool hdr = false) { Rect rect = EditorGUILayout.GetControlRect(); EditorGUI.showMixedValue = textureProp.hasMixedValue; materialEditor.TexturePropertyMiniThumbnail(rect, textureProp, label.text, label.tooltip); EditorGUI.showMixedValue = false; if (colorProp != null) { EditorGUI.BeginChangeCheck(); EditorGUI.showMixedValue = colorProp.hasMixedValue; int indentLevel = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; Rect rectAfterLabel = new Rect(rect.x + EditorGUIUtility.labelWidth, rect.y, EditorGUIUtility.fieldWidth, EditorGUIUtility.singleLineHeight); var col = EditorGUI.ColorField(rectAfterLabel, GUIContent.none, colorProp.colorValue, true, false, hdr); EditorGUI.indentLevel = indentLevel; if (EditorGUI.EndChangeCheck()) { materialEditor.RegisterPropertyChangeUndo(colorProp.displayName); colorProp.colorValue = col; } EditorGUI.showMixedValue = false; } return rect; } // Copied from shaderGUI as it is a protected function in an abstract class, unavailable to others public new static MaterialProperty FindProperty(string propertyName, MaterialProperty[] properties) { return FindProperty(propertyName, properties, true); } // Copied from shaderGUI as it is a protected function in an abstract class, unavailable to others public new static MaterialProperty FindProperty(string propertyName, MaterialProperty[] properties, bool propertyIsMandatory) { for (int index = 0; index < properties.Length; ++index) { if (properties[index] != null && properties[index].name == propertyName) return properties[index]; } if (propertyIsMandatory) throw new ArgumentException("Could not find MaterialProperty: '" + propertyName + "', Num properties: " + (object)properties.Length); return null; } #endregion } }
LitShaderEditor.cs using System; using UnityEngine; using UnityEngine.Rendering; using UnityEditor.Rendering.LWRP; namespace UnityEditor.Rendering.LWRP.ShaderGUI { internal class LitShaderEditor : BaseShaderEditor {
.
— , . :
- ForwardLit: Forward , , PBR .
- ShadowCaster: .
- DepthOnly: (Z-Buffer).
- Meta: ().
- Lightweight2D: UI.
, Unity -. SRP HLSLcc. , API OpenGL ES (, Android), HLSLcc .
:
#pragma prefer_hlslcc gles
DirectX 9 , :
#pragma exclude_renderers d3d11_9x
— , CG, HLSL, :
HLSLPROGRAM ... ENDHLSL
-
_Time ,
_ScreenParams _WorldSpaceCameraPos . ,
UnityCG.cginc . , ,
UnityObjectToClipPos(POSITION) TransformWorldToHClip(POSITION) , —
UNITY_APPLY_FOG(fogCoord, color) MixFog(color, fogCoord) .
, Core.hlsl:
%localappdata%\Unity\cache\packages\packages.unity.com\com.unity.render-pipelines.lightweight@( LWRP)\ShaderLibrary\Core.hlsl
.
(CBUFFER) UnityPerMaterial. , GPU, .
CBUFFER_START CBUFFER_END :
CBUFFER_START(UnityPerMaterial) float4 _BaseMap_ST; half4 _BaseColor; half4 _SpecColor; half4 _EmissionColor; half _Cutoff; half _Smoothness; half _Metallic; half _BumpScale; half _OcclusionStrength; CBUFFER_END
- ( , ) - .
LWRP —
UnityPerObject UnityPerMaterial . , . , , setpass . , , LWRP .
, — , .
, , ,
.
, SurfaceData:
SurfaceData surfaceData;
, -
PBR Master ShaderGraph .
Exemplo
, ,
! -
Vertex Displacement Dissolve Effect , .

, .
:
Properties { ... _DissolveMap("Dissolve Map", 2D) = "white" {} _DissolveFactor("Dissolve Factor", Range(0, 1)) = 0.0 _DissolveWidth("Dissolve Width", Range(0, 1)) = 0.0 [HDR]_DissolveColor("Color", Color) = (1,1,0) }
:

:
CBUFFER_START(UnityPerMaterial) ... float4 _DissolveMap_ST;
:
Varyings LitPassVertex(Attributes input) { ... input.positionOS.xyz += normalize(input.positionOS.xyz) * sin(input.positionOS.x) * sin(_Time.x * 100); ... }
:
Varyings ShadowPassVertex(Attributes input) { ... input.positionOS.xyz += normalize(input.positionOS.xyz) * sin(input.positionOS.x) * sin(_Time.x * 100); ... }
:
Varyings DepthOnlyVertex(Attributes input) { ... input.position.xyz += normalize(input.position.xyz) * sin(input.position.x) * sin(_Time.x * 100); ... }
Dissolve :
half4 LitPassFragment(Varyings input) : SV_Target { ...
Dissolve , ,
Shader Graph , — .
half4 ShadowPassFragment(Varyings input) : SV_TARGET { ... float4 mask = SAMPLE_TEXTURE2D(_DissolveMap, sampler_DissolveMap, input.uv); if (mask.r > _DissolveFactor) discard; ... }
:
SimpleDissolve Shader "TheProxor/LWRP/Dissolve + Vertex Offset" { Properties {
Conclusão
, . , LWRP , , , , . ,
Standard Surface Shader- , - LWRP HDRP, - .
:
,LWRP