Перейти к содержанию

Пишем пиксельный шейдер


MasterGH

Рекомендуемые сообщения

Задали на работе написать пост эффект. Кому интересно, могут помочь.
 
Берем каждый пиксель за кадр рендренига и анализируем его яркость
 
- 30% темных по яркости делаем черными
- 30% -  сиреневым цветом
- след. 40% - белыми
 
Если это делать CPU, то это очень медленно. Поэтому надо через шейдер писельный.
 
Вот пример, когда цвета подбираются по функции distance(), а не по яркости
 

Shader "AlpacaSound/RetroPixel3"{	Properties	{		_Br0 ("Brightness Range 0", Range (0, 0.3)) = 0.3		_Br1 ("Brightness Range 1", Range (0.3, 0.6)) = 0.3		_Br2 ("Brightness Range 2", Range (0.6, 1.0)) = 0.6				_Color0 ("Color 0", Color) = (0.00, 0.00, 0.00, 1.0)			_Color1 ("Color 1", Color) = (0.14, 0.14, 0.14, 1.0)			_Color2 ("Color 2", Color) = (0.29, 0.29, 0.29, 1.0)				 	_MainTex ("", 2D) = "white" {}	}	 	SubShader	{		Lighting Off		ZTest Always		Cull Off		ZWrite Off		Fog { Mode Off }	 	 	Pass	 	{	  		CGPROGRAM	  		#pragma exclude_renderers flash	  		#pragma vertex vert_img	  		#pragma fragment frag			#pragma fragmentoption ARB_precision_hint_fastest	  		#include "UnityCG.cginc"	    			uniform fixed4 _Color0;			uniform fixed4 _Color1;			uniform fixed4 _Color2;	  		uniform sampler2D _MainTex;	    	  		fixed4 frag (v2f_img i) : COLOR	  		{	   			fixed3 original = tex2D (_MainTex, i.uv).rgb;	   			fixed dist0 = distance (original, _Color0.rgb);	   			fixed dist1 = distance (original, _Color1.rgb);	   			fixed dist2 = distance (original, _Color2.rgb);	   				   			fixed4 col = fixed4 (0,0,0,0);	   			fixed dist = 10.0;				if (dist0 < dist)				{					dist = dist0;					col = _Color0;				}				if (dist1 < dist)				{					dist = dist1;					col = _Color1;				}								if (dist2 < dist)				{					dist = dist2;					col = _Color2;				}								return col;	  		}	  			  		ENDCG	 	}	}		FallBack "Diffuse"}
using UnityEngine;using System.Collections;namespace Retro //Pixel{	[ExecuteInEditMode]	[RequireComponent (typeof(Camera))]	[AddComponentMenu("Image Effects/Custom/Retro Pixel")]	public class RetroPixel : MonoBehaviour	{		public bool isUseOriginalResolution = true;		public static readonly int MAX_NUM_COLORS = 8;		public int horizontalResolution = 160;		public int verticalResolution = 200;		public int numColors = MAX_NUM_COLORS;		int oldNumColors = 0;		public Color color0 = Color.black;		public Color color1 = Color.white;		public Color color2 = new Color32(255, 75, 75, 255);		public Color color3 = new Color32(255, 186, 19, 255);		public Color color4 = new Color32(234, 233, 0, 255);		public Color color5 = new Color32(132, 207, 69, 255);		public Color color6 = new Color32(0, 165, 202, 255);		public Color color7 = new Color32(192, 106, 194, 255);		Shader[] shaders = new Shader[MAX_NUM_COLORS];		Material m_material;		Material material		{			get			{				if (m_material == null)				{					for (int i = 1; i < MAX_NUM_COLORS; ++i)					{						string shaderName = "AlpacaSound/RetroPixel" + (i+1);						Shader shader = Shader.Find (shaderName);						if (shader == null)						{							Debug.LogError ("Shader \'" + shaderName + "\' not found. Was it deleted?");							enabled = false;							return null;						}												shaders[i] = shader;					}										m_material = new Material (shaders[1]);					m_material.hideFlags = HideFlags.DontSave;				}				return m_material;			} 		}		void Start ()		{			if(isUseOriginalResolution){				horizontalResolution = Screen.width;				verticalResolution = Screen.height;			}			if (!SystemInfo.supportsImageEffects)			{				enabled = false;				return;			}		}				public void OnRenderImage (RenderTexture src, RenderTexture dest)		{			horizontalResolution = Mathf.Clamp(horizontalResolution, 1, 2048);			verticalResolution = Mathf.Clamp(verticalResolution, 1, 2048);			numColors = Mathf.Clamp(numColors, 2, 8);			if (material)			{				if (oldNumColors != numColors)				{					material.shader = shaders[numColors-1];				}								material.SetColor ("_Color0", color0);				material.SetColor ("_Color1", color1);				if (numColors > 2) material.SetColor ("_Color2", color2);				if (numColors > 3) material.SetColor ("_Color3", color3);				if (numColors > 4) material.SetColor ("_Color4", color4);				if (numColors > 5) material.SetColor ("_Color5", color5);				if (numColors > 6) material.SetColor ("_Color6", color6);				if (numColors > 7) material.SetColor ("_Color7", color7);								RenderTexture scaled = RenderTexture.GetTemporary (horizontalResolution, verticalResolution);				scaled.filterMode = FilterMode.Point;				Graphics.Blit (src, scaled, material);				Graphics.Blit (scaled, dest);				RenderTexture.ReleaseTemporary (scaled);							}			else			{				Graphics.Blit (src, dest);			}		}		void OnDisable ()		{			if (m_material)			{				Material.DestroyImmediate (m_material);			}		}	}}


 
Я сегодня колупался с шейдерами, пока не решил. Вот такой интересной штукой я занимаюсь на работе.

Ссылка на комментарий
Поделиться на другие сайты

Вроде так
 



Вот увлечешься этими документациями на официальном сайте и подразделами, и уйдешь в такие дебри, что потом и не заметишь как уже второй час ночи и пора бы подкрепиться. Интересно, что завтра начальство скажет по результату.  Еще надо бы пару ползунков в гуи с тысячными долями прилепить, ладно днем прикручу.

Shader "AlpacaSound/RetroPixel3"{	Properties	{		_Br0 ("Brightness Range 0", Range (0, 1.0)) = 0.3		_Br1 ("Brightness Range 1", Range (0.0, 1.0)) = 0.6				_Color0 ("Color 0", Color) = (0.00, 0.00, 0.00, 1.0)			_Color1 ("Color 1", Color) = (0.14, 0.14, 0.14, 1.0)			_Color2 ("Color 2", Color) = (0.29, 0.29, 0.29, 1.0)				 	_MainTex ("", 2D) = "white" {}	}	 	SubShader	{		Lighting Off		ZTest Always		Cull Off		ZWrite Off		Fog { Mode Off }	 	 	Pass	 	{	  		CGPROGRAM	  		#pragma exclude_renderers flash	  		#pragma vertex vert_img	  		#pragma fragment frag			#pragma fragmentoption ARB_precision_hint_fastest	  		#include "UnityCG.cginc"    			uniform fixed4 _Color0;			uniform fixed4 _Color1;			uniform fixed4 _Color2;			uniform fixed _Br0;			uniform fixed _Br1;	  		uniform sampler2D _MainTex;	    	  		fixed4 frag (v2f_img i) : COLOR	  		{	   			fixed3 original = tex2D (_MainTex, i.uv).rgb;				fixed brightest = (original.r + original.g + original.b)/3.0;	   			fixed4 col = fixed4 (0,0,0,0);				if (brightest < _Br0)					col = _Color0;				else if (brightest > _Br0 && brightest < _Br1)					col = _Color1;				else if (brightest > _Br1)					col = _Color2;				return col;	  		}	  			  		ENDCG	 	}	}		FallBack "Diffuse"}
Ссылка на комментарий
Поделиться на другие сайты

Я не знаю деталей, но во всех официальных шейдрерах постэффектов есть установки

//...Subshader {Pass {   ZTest Always Cull Off ZWrite Off   Fog { Mode off }//...}//...

Официального объяснения я не нашел. На форумах пишут, что это нужно для работы Graphics.Blit, которая работает для пост эффекта в следующей функции
 

public void OnRenderImage (RenderTexture src, RenderTexture dest){//...	RenderTexture scaled = RenderTexture.GetTemporary (horizontalResolution, verticalResolution);	scaled.filterMode = FilterMode.Point;	Graphics.Blit (src, scaled, material);	Graphics.Blit (scaled, dest);	RenderTexture.ReleaseTemporary (scaled);}

ZTest может быть установлен в

Less | Greater | LEqual | GEqual | Equal | NotEqual | Always

ZTest позволяет рисовать такие примеры:
post-3-0-78762500-1424262015_thumb.png
post-3-0-64071000-1424262027_thumb.png

Для интересующихся шейдеры примеров можно найти здесь

Ссылка на комментарий
Поделиться на другие сайты

Именно. Потому я и спросил, зачем включать глубину для шейдера, который применяется к прирендеренному в 2д кадру, готовому к выводу на экран. Я так понял, что постэффект в твоем посте должен накладываться именно на готовый кадр, именно готовый к выводу на экран, то есть, не 3д кадр, а уже готовую 2д картинку. Если так, то работа с глубиной шейдеру нафиг не сдалась, ведь ее попросту нет, так как готовый к выводу на экран кадр - это прогсто растровая картинка, не имеющая третьего измерения.

Ссылка на комментарий
Поделиться на другие сайты

Я понял. Те, кто писали на форумах, если не использовать проверку глубины буфера, то возникает черный эркан. Возможно это имеет значения на разных девайсах на множестве камер или еще что-нибудь...

 

В официальных шейдерах потсэффектов пишут по дефолту.

 

ZTest Always Cull Off ZWrite Off

Ссылка на комментарий
Поделиться на другие сайты

Я кажись понял.

 

Если не писать буфер глубины, то он задается по умолчанию для всех типов шейдеров с параметром

 

LEqual

 

Поэтому надо прописывать по умолчанию совсем другое значение или вообще его не прописывать.

-------------

 

 

Тема с проблемой, не знаю зачем я её тут привожу, я не поленился и нашел её снова

 

Thanks very much, guys. The hint with the Greyscale Effect helped me fix my problem, which was very similar to 0xDEADCODE's. My blit with my custom material/shader returned black. My shader was already a fragment shader, but I forgot one more thing:

Code (csharp):

ZTest Always Cull Off ZWrite Off

Fog { Mode off }
Adding these lines (within Pass{}) made mine work (mostly because of the ZTest Always; I guess the ZTest just failed for every pixel earlier). Hope I can help some other people with the same issue.


Пост эффекты для рендерер текстуры, а они все через эту специальную текстуру идут скорее всего со своей спецификой для кода движка Unity3d.Кто-то писал даже, что это чуть ли не баг и надеялись, что этот баг поскорее бы исправили, чтобы в шейдерах не писать ZTest. 

 

 

 

Я бы не стал советовать это тестировать, т.к. мне это не нужно. Задача выполнена.

Ссылка на комментарий
Поделиться на другие сайты

×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.