private void DrawDitheringSettingsGUI(MaterialEditor materialEditor) { // Hack: Get Palette and Pattern objects. // Because it's not possible to save references to other objects within a shader or material object, // This hack saves the instance id's in shader properties, by converting the exact bit setup to vectors. _palette = BeffioDitherEditorUtilities.GetAssetFromVector(_paletteRefProperty.vectorValue) as Palette; _pattern = BeffioDitherEditorUtilities.GetAssetFromVector(_patternRefProperty.vectorValue) as Pattern; if (_palette == null) { _palette = AssetDatabase.LoadAssetAtPath <Palette>(_defaultPalettePath); } if (_pattern == null) { _pattern = AssetDatabase.LoadAssetAtPath <Pattern>(_defaultPatternPath); } bool hasPatternTexture = (_hasPatternTextureProperty.floatValue > 0.5f); EditorGUI.indentLevel = 2; // Palette _palette = EditorGUILayout.ObjectField(ContentText.paletteProperty, _palette, typeof(Palette), false) as Palette; if (_palette != null) { _colorCountProperty.floatValue = _palette.MixedColorCount; _paletteHeightProperty.floatValue = _palette.Texture.height; _paletteTexProperty.textureValue = _palette.Texture; _paletteRefProperty.vectorValue = BeffioDitherEditorUtilities.AssetToVector4GUID(_palette); } else { _colorCountProperty.floatValue = 0; _paletteHeightProperty.floatValue = Texture2D.whiteTexture.height; _paletteTexProperty.textureValue = Texture2D.whiteTexture; _paletteRefProperty.vectorValue = Vector4.zero; } // Pattern Asset if (!hasPatternTexture) { EditorGUI.BeginChangeCheck(); { _pattern = EditorGUILayout.ObjectField(ContentText.patternProperty, _pattern, typeof(Pattern), false) as Pattern; if (EditorGUI.EndChangeCheck()) { if (_pattern != null) { _patternSizeProperty.floatValue = _pattern.Texture.height; _patternTexProperty.textureValue = _pattern.Texture; _patternRefProperty.vectorValue = BeffioDitherEditorUtilities.AssetToVector4GUID(_pattern); } else { _patternSizeProperty.floatValue = 0; _patternTexProperty.textureValue = null; _patternRefProperty.vectorValue = Vector4.zero; } _hasPatternTextureProperty.floatValue = 0; } } } // Pattern Texture if (_pattern == null) { EditorGUI.BeginChangeCheck(); { EditorGUI.indentLevel = 0; materialEditor.TexturePropertySingleLine(ContentText.patternTextureProperty, _patternTexProperty); if (EditorGUI.EndChangeCheck()) { if (_patternTexProperty.textureValue) { _patternSizeProperty.floatValue = _patternTexProperty.textureValue.height; } else { _patternSizeProperty.floatValue = 0; } _hasPatternTextureProperty.floatValue = (_patternTexProperty.textureValue == null ? 0 : 1); } } } EditorGUI.indentLevel = 2; // Pattern Scale materialEditor.FloatProperty(_patternScaleProperty, ContentText.patternScaleProperty.text); // Screen Space Dithering EditorGUI.BeginChangeCheck(); { bool enabled = EditorGUILayout.Toggle(ContentText.screenSpaceProperty, _targetMat.IsKeywordEnabled("_SCREENSPACEDITHER")); if (EditorGUI.EndChangeCheck()) { EnableKeyword("_SCREENSPACEDITHER", enabled, ContentText.screenSpaceUndo); } } // Alpha Cutout if (_targetMat.HasProperty("_Cutout")) { materialEditor.ShaderProperty(_cutoutProperty, ContentText.cutoutProperty.text); } }
/* SETUP */ #region Setup private void OnEnable() { m_Grain = AddComponent(new GrainComp()); cam = GetComponent <Camera>(); mat = new Material(Shader.Find("Unlit/Texture")); #if UNITY_EDITOR string _noiseTexturePath = "Assets/Stylizer/Resources/Shaders/NoiseAndGrain.png"; string _defaultPalettePath = "Assets/Stylizer/Styles/Palette/Palette_1.asset"; string _defaultNoisePath = "Assets/Stylizer/Styles/Pattern/Pattern_1.asset"; string _profilePath = "Assets/Stylizer/Resources/Scripts/UnityGrain/Grain.asset"; string _ubPath = "Assets/Stylizer/Resources/Scripts/UnityGrain/Shaders/Build materials/ub.mat"; string _gmPath = "Assets/Stylizer/Resources/Scripts/UnityGrain/Shaders/Build materials/gm.mat"; if (profile == null) { profile = AssetDatabase.LoadAssetAtPath <PostProf>(_profilePath); if (profile == null) { Debug.LogWarningFormat("No grain profile found.", _profilePath); } } if (ub == null) { ub = AssetDatabase.LoadAssetAtPath <Material>(_ubPath); if (ub == null) { Debug.LogWarningFormat("No ub material found.", _ubPath); } } if (gm == null) { gm = AssetDatabase.LoadAssetAtPath <Material>(_gmPath); if (gm == null) { Debug.LogWarningFormat("No ub material found.", _gmPath); } } if (_palette == null) { _palette = AssetDatabase.LoadAssetAtPath <Palette>(_defaultPalettePath); if (_palette == null) { Debug.LogWarningFormat("No default palette found.", _defaultPalettePath); } } if (_pattern == null) { _pattern = AssetDatabase.LoadAssetAtPath <Pattern>(_defaultNoisePath); if (_pattern == null) { Debug.LogWarningFormat("No default pattern found.", _defaultNoisePath); } } if (noiseTexture == null) { noiseTexture = AssetDatabase.LoadAssetAtPath <Texture2D>(_noiseTexturePath); if (noiseTexture == null) { Debug.LogWarningFormat("No branding logo texture found at {0}, please make sure a texture is available at that path, or change the path in the variable _brandingLogoPath in this script.", _noiseTexturePath); } } #endif checkSupport(); }
/* PALETTE GENERATION */ #region Palette generation /// <summary> /// Creates and saves a palette image from the unique colors in another image. /// /// The palette image structure: /// /// | | | | | | | | row of 16 16x16 squares with the third mix color /// |__|__|__|__|__|__|__|__ /// | | | | | | | | row of 16 16x16 squares with the second mix color /// |__|__|__|__|__|__|__|__ /// | | | | | | | | row of 16 16x16 squares with the first mix color /// |__|__|__|__|__|__|__|__ /// /// Each horizontal square has a fixed Red component: 0/15 to 15/15. /// The blue component increases from 0 to 1 horizontally over a square. /// The green component increases from 0 to 1 vertically over a square. /// /// The palette image is used in the shaders to convert a truecolor to several dithered colors. /// The truecolor RGB components points to N colors in the palette, one color per row of squares. /// The N colors are mixed together in a dithering pattern to produce a close approximation of the original truecolor. /// /// The steps to create the palette image: /// /// 1. Load the color image to a Texture2D /// 2. Create a list of all the unique colors in the color image /// 3. Create the palette image /// 4. a) Loop through each pixel in the palette image and determine the truecolor for that pixel /// b) Device a mixing plan to achieve the truecolor /// c) Save the N colors in the square column /// 5. Save the palette image /// </summary> private void GeneratePaletteTexture(Palette palette) { if (palette.Colors.Count == 0) { Debug.LogError(ContentText.emptyColorListError); return; } uint[] paletteColors = ConvertColorListToPaletteList(palette.Colors); MixingPlanner mixingPlanner = new MixingPlanner(paletteColors); // Create the palette image int height = (int)Math.Pow(2, Math.Ceiling(Math.Log(_colorSquares * CurrentPalette.MixedColorCount - 1) / Math.Log(2))); if (palette.Texture == null) { palette.Texture = new Texture2D(_colorSquares * _colorSquares, height, TextureFormat.RGB24, false); palette.Texture.name = "Palette Texture"; palette.Texture.filterMode = FilterMode.Point; palette.Texture.wrapMode = TextureWrapMode.Repeat; AssetDatabase.AddObjectToAsset(palette.Texture, palette); AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(palette.Texture)); } else if (palette.Texture.height != height) { palette.Texture.Resize(_colorSquares * _colorSquares, height); } // Fill empty rows with magenta int x = 0, y = 0; for (int i = 0; i < palette.Texture.height * palette.Texture.width; i++) { x = i / palette.Texture.height; y = i % palette.Texture.width; palette.Texture.SetPixel(x, y, Color.magenta); } // Loop through each pixel in the palette image and determine the target color for that pixel for (x = 0; x < _colorSquares * _colorSquares; x++) { for (y = 0; y < _colorSquares; y++) { byte r = (byte)((float)(x / _colorSquares) / (_colorSquares - 1) * 255); byte g = (byte)((float)y / (_colorSquares - 1) * 255); byte b = (byte)(((float)x % _colorSquares) / (_colorSquares - 1) * 255); uint targetColor = (uint)((r << 16) + (g << 8) + b); // Device a mixing plan to achieve the truecolor uint[] mixingPlan = mixingPlanner.DeviseBestMixingPlan(targetColor, (uint)CurrentPalette.MixedColorCount); // Save the N colors in the square column for (int c = 0; c < CurrentPalette.MixedColorCount; c++) { palette.Texture.SetPixel(x, y + (CurrentPalette.MixedColorCount - c - 1) * _colorSquares, palette.Colors[(int)mixingPlan[c]]); } } } palette.Texture.Apply(); palette.HasTexture = true; // Save the palette image // SaveTexture(texture); }