Shader "Custom/Palette Shader" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} } SubShader { Pass { ZTest Always Cull Off ZWrite Off Fog { Mode off } CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma fragmentoption ARB_precision_hint_fastest #include "UnityCG.cginc" #pragma target 3.0 struct v2f { float4 pos : POSITION; float2 uv : TEXCOORD0; }; uniform sampler2D _MainTex; v2f vert(appdata_img v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = MultiplyUV(UNITY_MATRIX_TEXTURE0, v.texcoord); return o; } half4 frag(v2f i): COLOR { half4 color = tex2D(_MainTex, i.uv); // , half4 rc = color; return rc; } ENDCG } } FallBack "Diffuse" }
using UnityEngine; [ExecuteInEditMode] [RequireComponent(typeof(Camera))] public class PaletteShader : MonoBehaviour { public Shader shader; private Material _material; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected Material material { get { if (_material == null) { _material = new Material(shader); _material.hideFlags = HideFlags.HideAndDontSave; } return _material; } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private void OnRenderImage(RenderTexture source, RenderTexture destination) { if (shader == null) return; Material mat = material; Graphics.Blit(source, destination, mat); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void OnDisable() { if (_material) DestroyImmediate(_material); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// }
R*0.21 + G*0.72 + B*0.07
. Will call this parameter "brightness."
const int depth = 3; // - const float f_depth = 1.0f / (1.0f * depth - 1.0f); Color[] palette = new Color[depth*depth*depth]; float[] palette_grey = new float[depth*depth*depth]; // for (int r = 0; r < depth; r++) { for (int g = 0; g < depth; g++) { for (int b = 0; b < depth; b++) { Color c = new Color(r * f_depth / 2, g * f_depth, b * f_depth, 1); int n = r*depth*depth + g*depth + b; palette[n] = c; palette_grey[n] = cr*0.21f + cg*0.72f + cb*0.07f; } } }
const int dim = 16; // - const float f_dim = 1.0f / (1.0f * dim - 1.0f); Texture3D tex = new Texture3D(dim, dim, dim, TextureFormat.RGB565, false); tex.filterMode = FilterMode.Point; // ! tex.wrapMode = TextureWrapMode.Clamp; Color[] t = new Color[dim*dim*dim]; // for (int r = 0; r < dim; r++) { for (int g = 0; g < dim; g++) { for (int b = 0; b < dim; b++) { float grey = (r * 0.21f + g * 0.72f + b * 0.07f) * f_dim; // int idx = 0; float min_d = grey; for (int i = 1; i < palette_grey.Length; i++) { float d = Mathf.Abs(palette_grey[i] - grey); if (d < min_d) { min_d = d; idx = i; } } t[r * dim * dim + g * dim + b] = palette[idx]; // } } } tex.SetPixels(t); tex.Apply();
half4 color = tex2D(_MainTex, i.uv); half4 rc = tex3D(_PaletteTex, color.rgb * _Br); float d = abs(Luminance(color) - Luminance(rc)); if ((d < 0.15) || (_Br == 1)) rc = color; return rc;
if
. The second condition is obviously - “if brightness == 1, then we return the original color intact.” But the first is a kind of condition that “when the color from the palette is quite close (within 15%) to the resulting one, then also leave the original color. This is done in order to reduce some unnecessary "chatter" of flowers. A kind of "snapping", if you please. And that is why you can see that some elements on our screen become their color before the final phase. Otherwise, they would not be of their own color to the last, but as close as possible from the palette. What would look bad for dark colors.
Shader "Custom/Palette Shader" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _Br("Brightness", Float) = 0 _PaletteTex ("Pelette texture", 3D) = "white" {} } SubShader { Pass { ZTest Always Cull Off ZWrite Off Fog { Mode off } CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma fragmentoption ARB_precision_hint_fastest #include "UnityCG.cginc" #pragma target 3.0 struct v2f { float4 pos : POSITION; float2 uv : TEXCOORD0; }; uniform sampler2D _MainTex; uniform sampler3D _PaletteTex; uniform float _Br; v2f vert(appdata_img v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = MultiplyUV(UNITY_MATRIX_TEXTURE0, v.texcoord); return o; } half4 frag(v2f i): COLOR { half4 color = tex2D(_MainTex, i.uv); half4 rc = tex3D(_PaletteTex, color.rgb * _Br); float d = abs(Luminance(color) - Luminance(rc)); if ((d < 0.15) || (_Br == 1)) rc = color; return rc; } ENDCG } } FallBack "Diffuse" }
using UnityEngine; [ExecuteInEditMode] [RequireComponent(typeof(Camera))] public class PaletteShader : MonoBehaviour { public Shader shader; private Material _material; [Range(0, 1)] public float brightness = 0.0f; [Range(0, 1)] public float random = 1f; private float _r = 0f; private Texture3D _tex; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected Material material { get { if (_material == null) { _material = new Material(shader); _material.hideFlags = HideFlags.HideAndDontSave; } return _material; } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private Texture3D GeneratePaletteTexture() { const int dim = 16; // - const int depth = 3; // - const float f_dim = 1.0f / (1.0f * dim - 1.0f); const float f_depth = 1.0f / (1.0f * depth - 1.0f); Texture3D tex = new Texture3D(dim, dim, dim, TextureFormat.RGB565, false); tex.filterMode = FilterMode.Point; tex.wrapMode = TextureWrapMode.Clamp; Color[] palette = new Color[depth*depth*depth]; float[] palette_grey = new float[depth*depth*depth]; // for (int r = 0; r < depth; r++) { for (int g = 0; g < depth; g++) { for (int b = 0; b < depth; b++) { Color c = new Color(r * f_depth / 2, g * f_depth, b * f_depth, 1); int n = r*depth*depth + g*depth + b; palette[n] = c; palette_grey[n] = cr*0.21f + cg*0.72f + cb*0.07f; } } } Color[] t = new Color[dim*dim*dim]; // for (int r = 0; r < dim; r++) { for (int g = 0; g < dim; g++) { for (int b = 0; b < dim; b++) { float grey = (r * 0.21f + g * 0.72f + b * 0.07f) * f_dim; // int idx = 0; float min_d = grey; for (int i = 1; i < palette_grey.Length; i++) { float d = Mathf.Abs(palette_grey[i] - grey); if (d < min_d) { min_d = d; idx = i; } } t[r * dim * dim + g * dim + b] = palette[idx]; // } } } tex.SetPixels(t); tex.Apply(); return tex; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private void OnRenderImage(RenderTexture source, RenderTexture destination) { if (shader == null) return; Material mat = material; mat.SetFloat("_Br", brightness); if (_tex == null) _tex = GeneratePaletteTexture(); if (random != _r) { _r = random; _tex = GeneratePaletteTexture(); } mat.SetTexture("_PaletteTex", _tex); Graphics.Blit(source, destination, mat); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void OnDisable() { if (_material) DestroyImmediate(_material); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// }
Source: https://habr.com/ru/post/213253/