本文主要是介绍Unity3D实现指示灯亮灭效果,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
这周有个需求是实现控制指示灯亮灭的效果。实现起来很简单,但是找到这个方法还费了点时间。
先看效果。
原理是利用了标准着色器中的自发光属性,通过开关自发光属性来控制灯的亮灭。
具体看步骤:
1.创建一个自发光的材质球,合理设置主要颜色与自发光颜色,金属度Metallic与平滑度Smoothness自己看需求设置
2.再创建一个无自发光的材质球
3.如何用代码控制自发光的开关内?
若不想了解从哪儿找到控制的地方的,这一段可以跳过,直接看工程。
既然在编辑器能够勾选自发光,那一定有一个编辑器脚本可以控制这部分。那么这个编辑器脚本在哪儿呢?你需要从unity官网下载你对应版本的内置着色器源码,下载回来的文件夹中Editor文件夹下的StandardShaderGUI.cs即是标准着色器编辑器的源码。
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)using System;
using UnityEngine;namespace UnityEditor
{internal class StandardShaderGUI : ShaderGUI{private enum WorkflowMode{Specular,Metallic,Dielectric}public enum BlendMode{Opaque,Cutout,Fade, // Old school alpha-blending mode, fresnel does not affect amount of transparencyTransparent // Physically plausible transparency mode, implemented as alpha pre-multiply}public enum SmoothnessMapChannel{SpecularMetallicAlpha,AlbedoAlpha,}private static class Styles{public static GUIContent uvSetLabel = new GUIContent("UV Set");public static GUIContent albedoText = new GUIContent("Albedo", "Albedo (RGB) and Transparency (A)");public static GUIContent alphaCutoffText = new GUIContent("Alpha Cutoff", "Threshold for alpha cutoff");public static GUIContent specularMapText = new GUIContent("Specular", "Specular (RGB) and Smoothness (A)");public static GUIContent metallicMapText = new GUIContent("Metallic", "Metallic (R) and Smoothness (A)");public static GUIContent smoothnessText = new GUIContent("Smoothness", "Smoothness value");public static GUIContent smoothnessScaleText = new GUIContent("Smoothness", "Smoothness scale factor");public static GUIContent smoothnessMapChannelText = new GUIContent("Source", "Smoothness texture and channel");public static GUIContent highlightsText = new GUIContent("Specular Highlights", "Specular Highlights");public static GUIContent reflectionsText = new GUIContent("Reflections", "Glossy Reflections");public static GUIContent normalMapText = new GUIContent("Normal Map", "Normal Map");public static GUIContent heightMapText = new GUIContent("Height Map", "Height Map (G)");public static GUIContent occlusionText = new GUIContent("Occlusion", "Occlusion (G)");public static GUIContent emissionText = new GUIContent("Color", "Emission (RGB)");public static GUIContent detailMaskText = new GUIContent("Detail Mask", "Mask for Secondary Maps (A)");public static GUIContent detailAlbedoText = new GUIContent("Detail Albedo x2", "Albedo (RGB) multiplied by 2");public static GUIContent detailNormalMapText = new GUIContent("Normal Map", "Normal Map");public static string primaryMapsText = "Main Maps";public static string secondaryMapsText = "Secondary Maps";public static string forwardText = "Forward Rendering Options";public static string renderingMode = "Rendering Mode";public static string advancedText = "Advanced Options";public static GUIContent emissiveWarning = new GUIContent("Emissive value is animated but the material has not been configured to support emissive. Please make sure the material itself has some amount of emissive.");public static readonly string[] blendNames = Enum.GetNames(typeof(BlendMode));}MaterialProperty blendMode = null;MaterialProperty albedoMap = null;MaterialProperty albedoColor = null;MaterialProperty alphaCutoff = null;MaterialProperty specularMap = null;MaterialProperty specularColor = null;MaterialProperty metallicMap = null;MaterialProperty metallic = null;MaterialProperty smoothness = null;MaterialProperty smoothnessScale = null;MaterialProperty smoothnessMapChannel = null;MaterialProperty highlights = null;MaterialProperty reflections = null;MaterialProperty bumpScale = null;MaterialProperty bumpMap = null;MaterialProperty occlusionStrength = null;MaterialProperty occlusionMap = null;MaterialProperty heigtMapScale = null;MaterialProperty heightMap = null;MaterialProperty emissionColorForRendering = null;MaterialProperty emissionMap = null;MaterialProperty detailMask = null;MaterialProperty detailAlbedoMap = null;MaterialProperty detailNormalMapScale = null;MaterialProperty detailNormalMap = null;MaterialProperty uvSetSecondary = null;MaterialEditor m_MaterialEditor;WorkflowMode m_WorkflowMode = WorkflowMode.Specular;ColorPickerHDRConfig m_ColorPickerHDRConfig = new ColorPickerHDRConfig(0f, 99f, 1 / 99f, 3f);bool m_FirstTimeApply = true;public void FindProperties(MaterialProperty[] props){blendMode = FindProperty("_Mode", props);albedoMap = FindProperty("_MainTex", props);albedoColor = FindProperty("_Color", props);alphaCutoff = FindProperty("_Cutoff", props);specularMap = FindProperty("_SpecGlossMap", props, false);specularColor = FindProperty("_SpecColor", props, false);metallicMap = FindProperty("_MetallicGlossMap", props, false);metallic = FindProperty("_Metallic", props, false);if (specularMap != null && specularColor != null)m_WorkflowMode = WorkflowMode.Specular;else if (metallicMap != null && metallic != null)m_WorkflowMode = WorkflowMode.Metallic;elsem_WorkflowMode = WorkflowMode.Dielectric;smoothness = FindProperty("_Glossiness", props);smoothnessScale = FindProperty("_GlossMapScale", props, false);smoothnessMapChannel = FindProperty("_SmoothnessTextureChannel", props, false);highlights = FindProperty("_SpecularHighlights", props, false);reflections = FindProperty("_GlossyReflections", props, false);bumpScale = FindProperty("_BumpScale", props);bumpMap = FindProperty("_BumpMap", props);heigtMapScale = FindProperty("_Parallax", props);heightMap = FindProperty("_ParallaxMap", props);occlusionStrength = FindProperty("_OcclusionStrength", props);occlusionMap = FindProperty("_OcclusionMap", props);emissionColorForRendering = FindProperty("_EmissionColor", props);emissionMap = FindProperty("_EmissionMap", props);detailMask = FindProperty("_DetailMask", props);detailAlbedoMap = FindProperty("_DetailAlbedoMap", props);detailNormalMapScale = FindProperty("_DetailNormalMapScale", props);detailNormalMap = FindProperty("_DetailNormalMap", props);uvSetSecondary = FindProperty("_UVSec", props);}public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props){FindProperties(props); // MaterialProperties can be animated so we do not cache them but fetch them every event to ensure animated values are updated correctlym_MaterialEditor = materialEditor;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 standard shader.// Do this before any GUI code has been issued to prevent layout issues in subsequent GUILayout statements (case 780071)if (m_FirstTimeApply){MaterialChanged(material, m_WorkflowMode);m_FirstTimeApply = false;}ShaderPropertiesGUI(material);}public void ShaderPropertiesGUI(Material material){// Use default labelWidthEditorGUIUtility.labelWidth = 0f;// Detect any changes to the materialEditorGUI.BeginChangeCheck();{BlendModePopup();// Primary propertiesGUILayout.Label(Styles.primaryMapsText, EditorStyles.boldLabel);DoAlbedoArea(material);DoSpecularMetallicArea();m_MaterialEditor.TexturePropertySingleLine(Styles.normalMapText, bumpMap, bumpMap.textureValue != null ? bumpScale : null);m_MaterialEditor.TexturePropertySingleLine(Styles.heightMapText, heightMap, heightMap.textureValue != null ? heigtMapScale : null);m_MaterialEditor.TexturePropertySingleLine(Styles.occlusionText, occlusionMap, occlusionMap.textureValue != null ? occlusionStrength : null);m_MaterialEditor.TexturePropertySingleLine(Styles.detailMaskText, detailMask);DoEmissionArea(material);EditorGUI.BeginChangeCheck();m_MaterialEditor.TextureScaleOffsetProperty(albedoMap);if (EditorGUI.EndChangeCheck())emissionMap.textureScaleAndOffset = albedoMap.textureScaleAndOffset; // Apply the main texture scale and offset to the emission texture as well, for Enlighten's sakeEditorGUILayout.Space();// Secondary propertiesGUILayout.Label(Styles.secondaryMapsText, EditorStyles.boldLabel);m_MaterialEditor.TexturePropertySingleLine(Styles.detailAlbedoText, detailAlbedoMap);m_MaterialEditor.TexturePropertySingleLine(Styles.detailNormalMapText, detailNormalMap, detailNormalMapScale);m_MaterialEditor.TextureScaleOffsetProperty(detailAlbedoMap);m_MaterialEditor.ShaderProperty(uvSetSecondary, Styles.uvSetLabel.text);// Third propertiesGUILayout.Label(Styles.forwardText, EditorStyles.boldLabel);if (highlights != null)m_MaterialEditor.ShaderProperty(highlights, Styles.highlightsText);if (reflections != null)m_MaterialEditor.ShaderProperty(reflections, Styles.reflectionsText);}if (EditorGUI.EndChangeCheck()){foreach (var obj in blendMode.targets)MaterialChanged((Material)obj, m_WorkflowMode);}EditorGUILayout.Space();GUILayout.Label(Styles.advancedText, EditorStyles.boldLabel);m_MaterialEditor.RenderQueueField();m_MaterialEditor.EnableInstancingField();}internal void DetermineWorkflow(MaterialProperty[] props){if (FindProperty("_SpecGlossMap", props, false) != null && FindProperty("_SpecColor", props, false) != null)m_WorkflowMode = WorkflowMode.Specular;else if (FindProperty("_MetallicGlossMap", props, false) != null && FindProperty("_Metallic", props, false) != null)m_WorkflowMode = WorkflowMode.Metallic;elsem_WorkflowMode = WorkflowMode.Dielectric;}public override void AssignNewShaderToMaterial(Material material, Shader oldShader, Shader newShader){// _Emission property is lost after assigning Standard shader to the material// thus transfer it before assigning the new shaderif (material.HasProperty("_Emission")){material.SetColor("_EmissionColor", material.GetColor("_Emission"));}base.AssignNewShaderToMaterial(material, oldShader, newShader);if (oldShader == null || !oldShader.name.Contains("Legacy Shaders/")){SetupMaterialWithBlendMode(material, (BlendMode)material.GetFloat("_Mode"));return;}BlendMode blendMode = BlendMode.Opaque;if (oldShader.name.Contains("/Transparent/Cutout/")){blendMode = BlendMode.Cutout;}else if (oldShader.name.Contains("/Transparent/")){// NOTE: legacy shaders did not provide physically based transparency// therefore Fade modeblendMode = BlendMode.Fade;}material.SetFloat("_Mode", (float)blendMode);DetermineWorkflow(MaterialEditor.GetMaterialProperties(new Material[] { material }));MaterialChanged(material, m_WorkflowMode);}void BlendModePopup(){EditorGUI.showMixedValue = blendMode.hasMixedValue;var mode = (BlendMode)blendMode.floatValue;EditorGUI.BeginChangeCheck();mode = (BlendMode)EditorGUILayout.Popup(Styles.renderingMode, (int)mode, Styles.blendNames);if (EditorGUI.EndChangeCheck()){m_MaterialEditor.RegisterPropertyChangeUndo("Rendering Mode");blendMode.floatValue = (float)mode;}EditorGUI.showMixedValue = false;}void DoAlbedoArea(Material material){m_MaterialEditor.TexturePropertySingleLine(Styles.albedoText, albedoMap, albedoColor);if (((BlendMode)material.GetFloat("_Mode") == BlendMode.Cutout)){m_MaterialEditor.ShaderProperty(alphaCutoff, Styles.alphaCutoffText.text, MaterialEditor.kMiniTextureFieldLabelIndentLevel + 1);}}void DoEmissionArea(Material material){// Emission for GI?if (m_MaterialEditor.EmissionEnabledProperty()){bool hadEmissionTexture = emissionMap.textureValue != null;// Texture and HDR color controlsm_MaterialEditor.TexturePropertyWithHDRColor(Styles.emissionText, emissionMap, emissionColorForRendering, m_ColorPickerHDRConfig, false);// If texture was assigned and color was black set color to whitefloat brightness = emissionColorForRendering.colorValue.maxColorComponent;if (emissionMap.textureValue != null && !hadEmissionTexture && brightness <= 0f)emissionColorForRendering.colorValue = Color.white;// change the GI flag and fix it up with emissive as black if necessarym_MaterialEditor.LightmapEmissionFlagsProperty(MaterialEditor.kMiniTextureFieldLabelIndentLevel, true);}}void DoSpecularMetallicArea(){bool hasGlossMap = false;if (m_WorkflowMode == WorkflowMode.Specular){hasGlossMap = specularMap.textureValue != null;m_MaterialEditor.TexturePropertySingleLine(Styles.specularMapText, specularMap, hasGlossMap ? null : specularColor);}else if (m_WorkflowMode == WorkflowMode.Metallic){hasGlossMap = metallicMap.textureValue != null;m_MaterialEditor.TexturePropertySingleLine(Styles.metallicMapText, metallicMap, hasGlossMap ? null : metallic);}bool showSmoothnessScale = hasGlossMap;if (smoothnessMapChannel != null){int smoothnessChannel = (int)smoothnessMapChannel.floatValue;if (smoothnessChannel == (int)SmoothnessMapChannel.AlbedoAlpha)showSmoothnessScale = true;}int indentation = 2; // align with labels of texture propertiesm_MaterialEditor.ShaderProperty(showSmoothnessScale ? smoothnessScale : smoothness, showSmoothnessScale ? Styles.smoothnessScaleText : Styles.smoothnessText, indentation);++indentation;if (smoothnessMapChannel != null)m_MaterialEditor.ShaderProperty(smoothnessMapChannel, Styles.smoothnessMapChannelText, indentation);}public static void SetupMaterialWithBlendMode(Material material, BlendMode blendMode){switch (blendMode){case BlendMode.Opaque:material.SetOverrideTag("RenderType", "");material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);material.SetInt("_ZWrite", 1);material.DisableKeyword("_ALPHATEST_ON");material.DisableKeyword("_ALPHABLEND_ON");material.DisableKeyword("_ALPHAPREMULTIPLY_ON");material.renderQueue = -1;break;case BlendMode.Cutout:material.SetOverrideTag("RenderType", "TransparentCutout");material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);material.SetInt("_ZWrite", 1);material.EnableKeyword("_ALPHATEST_ON");material.DisableKeyword("_ALPHABLEND_ON");material.DisableKeyword("_ALPHAPREMULTIPLY_ON");material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest;break;case BlendMode.Fade:material.SetOverrideTag("RenderType", "Transparent");material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);material.SetInt("_ZWrite", 0);material.DisableKeyword("_ALPHATEST_ON");material.EnableKeyword("_ALPHABLEND_ON");material.DisableKeyword("_ALPHAPREMULTIPLY_ON");material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;break;case BlendMode.Transparent:material.SetOverrideTag("RenderType", "Transparent");material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);material.SetInt("_ZWrite", 0);material.DisableKeyword("_ALPHATEST_ON");material.DisableKeyword("_ALPHABLEND_ON");material.EnableKeyword("_ALPHAPREMULTIPLY_ON");material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;break;}}static SmoothnessMapChannel GetSmoothnessMapChannel(Material material){int ch = (int)material.GetFloat("_SmoothnessTextureChannel");if (ch == (int)SmoothnessMapChannel.AlbedoAlpha)return SmoothnessMapChannel.AlbedoAlpha;elsereturn SmoothnessMapChannel.SpecularMetallicAlpha;}static void SetMaterialKeywords(Material material, WorkflowMode workflowMode){// Note: keywords must be based on Material value not on MaterialProperty due to multi-edit & material animation// (MaterialProperty value might come from renderer material property block)SetKeyword(material, "_NORMALMAP", material.GetTexture("_BumpMap") || material.GetTexture("_DetailNormalMap"));if (workflowMode == WorkflowMode.Specular)SetKeyword(material, "_SPECGLOSSMAP", material.GetTexture("_SpecGlossMap"));else if (workflowMode == WorkflowMode.Metallic)SetKeyword(material, "_METALLICGLOSSMAP", material.GetTexture("_MetallicGlossMap"));SetKeyword(material, "_PARALLAXMAP", material.GetTexture("_ParallaxMap"));SetKeyword(material, "_DETAIL_MULX2", material.GetTexture("_DetailAlbedoMap") || material.GetTexture("_DetailNormalMap"));// A material's GI flag internally keeps track of whether emission is enabled at all, it's enabled but has no effect// or is enabled and may be modified at runtime. This state depends on the values of the current flag and emissive color.// The fixup routine makes sure that the material is in the correct state if/when changes are made to the mode or color.MaterialEditor.FixupEmissiveFlag(material);bool shouldEmissionBeEnabled = (material.globalIlluminationFlags & MaterialGlobalIlluminationFlags.EmissiveIsBlack) == 0;SetKeyword(material, "_EMISSION", shouldEmissionBeEnabled);if (material.HasProperty("_SmoothnessTextureChannel")){SetKeyword(material, "_SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A", GetSmoothnessMapChannel(material) == SmoothnessMapChannel.AlbedoAlpha);}}static void MaterialChanged(Material material, WorkflowMode workflowMode){SetupMaterialWithBlendMode(material, (BlendMode)material.GetFloat("_Mode"));SetMaterialKeywords(material, workflowMode);}static void SetKeyword(Material m, string keyword, bool state){if (state)m.EnableKeyword(keyword);elsem.DisableKeyword(keyword);}}
} // namespace UnityEditor
定位到上述脚本的393行(不同版本的脚本可能不太一样,看具体情况),即是设置自发光是否开启的部分。
其实就是使用material的EnableKeyword和DisableKeyword来控制_EMISSION属性的开关,知道原理之后写代码就简单了。
using UnityEngine;/// <summary>
/// 指示灯.
/// </summary>
public class HightLED : MonoBehaviour
{private Material mat;private void Awake(){mat = GetComponent<MeshRenderer>().material;}private void Update(){if (Input.GetKeyDown(KeyCode.F1)){SetEmission(mat, true);}if (Input.GetKeyDown(KeyCode.F2)){SetEmission(mat, false);}}private void SetEmission(Material mat, bool emissionOn){if (emissionOn){mat.EnableKeyword("_EMISSION");}else{mat.DisableKeyword("_EMISSION");}}
}
4.建议测试时把场景中内置的天空盒给删除掉,内置天空盒给的光太多,看不出效果。具体步骤见下图。
源码上传到GitHub了,见03.HightLight场景。
这篇关于Unity3D实现指示灯亮灭效果的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!