You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
421 lines
15 KiB
421 lines
15 KiB
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Text.RegularExpressions;
|
|
using UnityEngine;
|
|
|
|
namespace Flockaroo
|
|
{
|
|
[ExecuteInEditMode]
|
|
[RequireComponent(typeof (Camera))]
|
|
//[AddComponentMenu("Image Effects/Artistic/OilPainting")]
|
|
public class OilPaintingEffect : MonoBehaviour {
|
|
|
|
List <string> bufferOrder = new List <string>();
|
|
Dictionary<string, RenderTexture> buffers = new Dictionary<string, RenderTexture>();
|
|
Dictionary<string, Material> shaders = new Dictionary<string, Material>();
|
|
Dictionary<string, Texture> textures = new Dictionary<string, Texture>();
|
|
Dictionary<string, Dictionary <int,string>> textureCh = new Dictionary<string, Dictionary <int,string>>();
|
|
Dictionary<string, Dictionary <int,bool>> textureDemandsMip = new Dictionary<string, Dictionary <int,bool>>();
|
|
Dictionary<string, List<Mesh>> meshes = new Dictionary<string, List<Mesh>>();
|
|
RenderTexture mainTex = null;
|
|
RenderTexture mainMip = null;
|
|
Regex refRegex = new Regex(@"Ref:([^:]+):Tex([0-9]+)");
|
|
private int actWidth=0;
|
|
private int actHeight=0;
|
|
private int NumTriangles=0;
|
|
|
|
//FIXME: automate useMipOnMain - activate when needed
|
|
private bool useMipOnMain = false;
|
|
|
|
[Header("Input/Output")]
|
|
|
|
[Tooltip("take a texture as input instead of the camera")]
|
|
public Texture inputTexture;
|
|
[Tooltip("render to a texture instead of the screen")]
|
|
public bool renderToTexture;
|
|
[Tooltip("texture being rendered to if above is checked")]
|
|
public RenderTexture outputTexture;
|
|
[Tooltip("generate mipmap for output texture")]
|
|
public bool outputMipmap;
|
|
|
|
[Header("Common")]
|
|
|
|
[Range(0.0f,1.0f)]
|
|
public float EffectFade = 0.0f;
|
|
[Range(0.0f,1.0f)]
|
|
public float PanFade = 0.0f;
|
|
|
|
[Header("Source")]
|
|
|
|
[Range(0.0f,2.0f)]
|
|
public float SrcBright = 1.0f;
|
|
[Range(0.0f,2.0f)]
|
|
public float SrcContrast = 1.4f;
|
|
[Range(0.0f,4.0f)]
|
|
public float SrcColor = 1.0f;
|
|
[Range(0.0f,1.0f)]
|
|
public float SrcBlur = 0.0f;
|
|
|
|
[Header("Effect")]
|
|
|
|
[Range(0.0f,1.0f)]
|
|
public float BrushDetail = 0.1f;
|
|
[Range(0.0f,1.0f)]
|
|
public float BrushFill = 0.5f;
|
|
[Range(5000,200000)]
|
|
public int NumStrokes=0x8000;
|
|
[Range(0.5f,0.95f)]
|
|
public float LayerScale = 0.8f;
|
|
[Range(0.0f,1.0f)]
|
|
public float Canvas = 0.4f;
|
|
[Range(0.0f,100.0f)]
|
|
public float FlickerFreq = 15.0f;
|
|
[Range(0.0f,1.0f)]
|
|
public float FlickerStrength = 0.0f;
|
|
[Range(0.0f,360.0f)]
|
|
public float LightAng = 135.0f;
|
|
[Range(0.0f,90.0f)]
|
|
public float LightOffs = 60.0f;
|
|
[Range(0.0f,1.0f)]
|
|
public float PaintDiff = 0.15f;
|
|
[Range(0.0f,1.0f)]
|
|
public float PaintSpec = 0.15f;
|
|
[Range(0.0f,1.0f)]
|
|
public float PaintShiny = 0.5f;
|
|
[Range(0.0f,2.0f)]
|
|
public float ColorSpread = 0.0f;
|
|
[Range(0.0f,120.0f)]
|
|
public float ScreenFOV = 0.0f;
|
|
[Range(0.0f,360.0f)]
|
|
public float StrokeAng = 0.0f;
|
|
[Range(-1.0f,1.0f)]
|
|
public float StrokeBend = -1.0f;
|
|
[Range(0.0f,1.0f)]
|
|
public float StrokeDir = 0.0f;
|
|
[Range(0.0f,1.0f)]
|
|
public float StrokeContour = 1.0f;
|
|
[Range(0.0f,16.0f)]
|
|
public float MultiStroke = 6.0f;
|
|
[Range(0.0f,100000.0f)]
|
|
public float strokeSeed = 10.0f;
|
|
[Range(0.0f,1.0f)]
|
|
public float Vignette = 1.0f;
|
|
[Range(0.0f,1.0f)]
|
|
public float CanvasBg = 0.5f;
|
|
public Color CanvasTint = new Color(1.0f,0.97f,0.85f);
|
|
//###PublicVars
|
|
[Header("Other")]
|
|
public bool flipY=false;
|
|
public bool geomFlipY=false;
|
|
|
|
Material createShader(string resname)
|
|
{
|
|
Shader shader = Resources.Load<Shader>(resname);
|
|
Material mat = new Material(shader);
|
|
mat.hideFlags = HideFlags.HideAndDontSave;
|
|
return mat;
|
|
}
|
|
|
|
RenderTexture createRenderTex(int w = -1, int h = -1, bool mip = false, int aa = 1)
|
|
{
|
|
RenderTexture rt;
|
|
//if(w==-1) w=Screen.width;
|
|
//if(h==-1) h=Screen.height;
|
|
if(w==-1) w=actWidth;
|
|
if(h==-1) h=actHeight;
|
|
rt = new RenderTexture(w, h,0,RenderTextureFormat.ARGBFloat);
|
|
rt.antiAliasing=aa; // must be 1 for mipmapping to work!!
|
|
rt.useMipMap=mip;
|
|
if(mip)
|
|
rt.filterMode=FilterMode.Trilinear;
|
|
return rt;
|
|
}
|
|
|
|
Texture2D createRandTex(int w, int h)
|
|
{
|
|
//if (RandTex == null)
|
|
// RandTex = Resources.Load<Texture2D>("rand256");
|
|
Texture2D RandTex;
|
|
{
|
|
RandTex = new Texture2D(w, h, TextureFormat.RGBAFloat, true);
|
|
//RandTex = new Texture2D(w, h, TextureFormat.RGBAHalf, true);
|
|
//RandTex = new Texture2D(w, h, TextureFormat.RGBA32, true);
|
|
|
|
for (int x = 0; x < RandTex.width; x++)
|
|
{
|
|
for (int y = 0; y < RandTex.height; y++)
|
|
{
|
|
float r = Random.Range(0.0f, 1.0f);
|
|
float g = Random.Range(0.0f, 1.0f);
|
|
float b = Random.Range(0.0f, 1.0f);
|
|
float a = Random.Range(0.0f, 1.0f);
|
|
RandTex.SetPixel(x, y, new Color(r, g, b, a) );
|
|
}
|
|
}
|
|
|
|
RandTex.Apply();
|
|
}
|
|
RandTex.filterMode=FilterMode.Trilinear;
|
|
return RandTex;
|
|
}
|
|
|
|
List<Mesh> createMesh(int trinum = 0x10000)
|
|
{
|
|
List<Mesh> meshes = new List<Mesh>();
|
|
int maxMeshSize = 0x10000/3*3;
|
|
int mnum = (trinum*3+maxMeshSize-1)/maxMeshSize;
|
|
for(int j=0;j<mnum;j++)
|
|
{
|
|
Mesh mesh = new Mesh();
|
|
meshes.Add(mesh);
|
|
mesh.Clear();
|
|
int vnum = maxMeshSize;
|
|
Vector3[] verts = new Vector3 [vnum];
|
|
int[] tris = new int [vnum];
|
|
for(int i=0;i<vnum;i++)
|
|
{
|
|
verts[i].x=i+j*maxMeshSize;
|
|
verts[i].y=1;
|
|
verts[i].z=2;
|
|
tris[i]=i;
|
|
}
|
|
mesh.vertices = verts;
|
|
mesh.triangles = tris;
|
|
}
|
|
return meshes;
|
|
}
|
|
|
|
void initMainMipmapRenderTexture(RenderTexture src)
|
|
{
|
|
if(mainMip == null)
|
|
{
|
|
mainMip = new RenderTexture(src.width, src.height,0,RenderTextureFormat.ARGB32);
|
|
mainMip.antiAliasing=1; // must be for mipmapping to work!!
|
|
mainMip.useMipMap=true;
|
|
mainMip.filterMode=FilterMode.Trilinear;
|
|
#if UNITY_5_5_OR_NEWER
|
|
//rtmip.autoGenerateMips=false;
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
void initAll(int width, int height)
|
|
{
|
|
if(renderToTexture)
|
|
{
|
|
outputTexture = new RenderTexture(width, height, 0, RenderTextureFormat.ARGBFloat);
|
|
if(outputMipmap)
|
|
{
|
|
outputTexture.antiAliasing=1; // must be for mipmapping to work!!
|
|
outputTexture.useMipMap=true;
|
|
outputTexture.filterMode=FilterMode.Trilinear;
|
|
}
|
|
}
|
|
else
|
|
outputTexture = null;
|
|
|
|
actWidth=width;
|
|
actHeight=height;
|
|
bufferOrder.Clear();
|
|
textureCh.Clear();
|
|
buffers.Clear();
|
|
shaders.Clear();
|
|
meshes.Clear();
|
|
if(!textures.ContainsKey("rand256")) textures["rand256"] = createRandTex(256,256);
|
|
if(!textures.ContainsKey("rand64")) textures["rand64"] = createRandTex(64,64);
|
|
|
|
bufferOrder.Add("Buff_A");
|
|
buffers["Buff_A"] = createRenderTex();
|
|
textureCh["Buff_A"] = new Dictionary <int,string> ();
|
|
textureDemandsMip["Buff_A"] = new Dictionary <int,bool> ();
|
|
shaders["Buff_A"] = createShader("flockaroo_OilPainting/Buff_A");
|
|
textureCh["Buff_A"][0] = "none";
|
|
textureCh["Buff_A"][1] = "rand256";
|
|
textureCh["Buff_A"][2] = "Buff_A";
|
|
buffers["Buff_A"].width=512;
|
|
buffers["Buff_A"].height=512;
|
|
buffers["Buff_A"].useMipMap=true;
|
|
buffers["Buff_A"].filterMode=FilterMode.Trilinear;
|
|
bufferOrder.Add("Geom_A");
|
|
buffers["Geom_A"] = createRenderTex();
|
|
buffers["Geom_A"].depth = 24;
|
|
textureCh["Geom_A"] = new Dictionary <int,string> ();
|
|
textureDemandsMip["Geom_A"] = new Dictionary <int,bool> ();
|
|
shaders["Geom_A"] = createShader("flockaroo_OilPainting/Geom_A");
|
|
meshes["Geom_A"] = createMesh(NumStrokes*2);
|
|
NumTriangles=NumStrokes*2;
|
|
textureCh["Geom_A"][0] = "https://ak1.picdn.net/shutterstock/videos/15325111/preview/stock-footage-beautiful-young-woman-enjoying-her-vacation-on-santorini-happy-tourist-is-wearing-sunhat.webm";
|
|
textureCh["Geom_A"][1] = "rand256";
|
|
textureCh["Geom_A"][2] = "Buff_A";
|
|
textureDemandsMip["Geom_A"][0]=true;
|
|
bufferOrder.Add("Image");
|
|
buffers["Image"] = createRenderTex();
|
|
textureCh["Image"] = new Dictionary <int,string> ();
|
|
textureDemandsMip["Image"] = new Dictionary <int,bool> ();
|
|
shaders["Image"] = createShader("flockaroo_OilPainting/Image");
|
|
textureCh["Image"][0] = "Geom_A";
|
|
textureCh["Image"][1] = "rand256";
|
|
textureCh["Image"][2] = "Buff_A";
|
|
textureCh["Image"][3] = "https://ak1.picdn.net/shutterstock/videos/15325111/preview/stock-footage-beautiful-young-woman-enjoying-her-vacation-on-santorini-happy-tourist-is-wearing-sunhat.webm";
|
|
|
|
//###InitMarker
|
|
}
|
|
|
|
void Start () {
|
|
//initAll(Screen.width,Screen.height);
|
|
}
|
|
|
|
// Update is called once per frame
|
|
void Update () {
|
|
|
|
}
|
|
|
|
Texture getTexture(string name)
|
|
{
|
|
if(name.StartsWith("Ref:")) {
|
|
Match match = refRegex.Match(name);
|
|
if (match.Success)
|
|
{
|
|
string buff = match.Groups[1].Value;
|
|
int chan = int.Parse(match.Groups[2].Value);
|
|
return getTexture(textureCh[buff][chan]);
|
|
}
|
|
return null;
|
|
}
|
|
if(buffers.ContainsKey(name)) return buffers[name];
|
|
if(textures.ContainsKey(name)) return textures[name];
|
|
if(name.EndsWith(".mp4")) return mainTex;
|
|
if(name.EndsWith(".webm")) return mainTex;
|
|
return mainTex;
|
|
// FIXME: alloc textures if not present
|
|
//return textures["rand256"];
|
|
}
|
|
|
|
private void OnRenderImage(RenderTexture src, RenderTexture dest) {
|
|
mainTex=src;
|
|
bool reinit=false;
|
|
|
|
if(inputTexture)
|
|
{
|
|
mainTex = new RenderTexture(inputTexture.width, inputTexture.height, 0, RenderTextureFormat.ARGBFloat);
|
|
Graphics.Blit(inputTexture, mainTex);
|
|
}
|
|
|
|
if (renderToTexture && outputTexture==null) { reinit=true; }
|
|
if (!renderToTexture && outputTexture!=null) { reinit=true; }
|
|
|
|
if( mainTex.width!=actWidth || mainTex.height!=actHeight || reinit )
|
|
{
|
|
Debug.Log("OilPainting 1st init (or Resolution changed)");
|
|
initAll(mainTex.width,mainTex.height);
|
|
}
|
|
|
|
if(NumTriangles!=NumStrokes*2)
|
|
{
|
|
meshes["Geom_A"] = createMesh(NumStrokes*2);
|
|
NumTriangles=NumStrokes*2;
|
|
}
|
|
|
|
if(useMipOnMain)
|
|
{
|
|
initMainMipmapRenderTexture(mainTex);
|
|
Graphics.Blit(mainTex, mainMip);
|
|
mainTex = mainMip;
|
|
}
|
|
|
|
foreach( string buffName in bufferOrder )
|
|
{
|
|
Material mat = null;
|
|
if(shaders.ContainsKey(buffName)) mat = shaders[buffName];
|
|
if(mat==null) { continue; }
|
|
|
|
mat.SetFloat("geomFlipY", geomFlipY?1.0f:0.0f);
|
|
mat.SetFloat("flipY", flipY?1.0f:0.0f);
|
|
mat.SetInt("_FrameCount", Time.frameCount);
|
|
|
|
mat.SetFloat("BrushDetail",(BrushDetail*0.8f+0.2f)*(BrushDetail*0.8f+0.2f));
|
|
mat.SetFloat("BrushSize",BrushFill*2.0f+0.25f);
|
|
mat.SetFloat("Canvas",Canvas);
|
|
mat.SetFloat("EffectFade",EffectFade);
|
|
mat.SetFloat("FlickerFreq",FlickerFreq);
|
|
mat.SetFloat("FlickerStrength",FlickerStrength);
|
|
mat.SetFloat("halfFOV",ScreenFOV/180.0f*3.14159265359f*0.5f);
|
|
mat.SetFloat("LayerScale",LayerScale);
|
|
mat.SetFloat("LightAng",LightAng/180.0f*3.14159265359f);
|
|
mat.SetFloat("LightOffs",LightOffs/180.0f*3.14159265359f);
|
|
mat.SetFloat("NumTriangles",NumStrokes*2);
|
|
mat.SetFloat("PaintDiff",PaintDiff);
|
|
mat.SetFloat("PaintSpec",PaintSpec);
|
|
mat.SetFloat("PaintShiny",PaintShiny);
|
|
mat.SetFloat("PanFade",PanFade);
|
|
mat.SetFloat("SrcBlur",SrcBlur);
|
|
mat.SetFloat("SrcBright",SrcBright);
|
|
mat.SetFloat("SrcContrast",SrcContrast);
|
|
mat.SetFloat("SrcColor",SrcColor);
|
|
mat.SetFloat("ColorSpread",ColorSpread);
|
|
mat.SetFloat("StrokeAng",StrokeAng/180.0f*3.14159265359f);
|
|
mat.SetFloat("StrokeBend",StrokeBend);
|
|
mat.SetFloat("StrokeContour",StrokeContour);
|
|
mat.SetFloat("StrokeDir",StrokeDir);
|
|
mat.SetVector("strokeNumXY",new Vector4(MultiStroke,MultiStroke,0.0f,0.0f));
|
|
mat.SetFloat("strokeSeed",strokeSeed);
|
|
mat.SetFloat("Vignette",Vignette);
|
|
mat.SetColor("CanvasTint", CanvasTint);
|
|
mat.SetFloat("CanvasBg", CanvasBg);
|
|
//###MatParams
|
|
|
|
for(int i=0;i<8;i++)
|
|
{
|
|
if(textureCh.ContainsKey(buffName) &&
|
|
textureCh[buffName].ContainsKey(i))
|
|
{
|
|
Texture tex = getTexture(textureCh[buffName][i]);
|
|
if(mat!=null) mat.SetTexture("iChannel"+i, tex);
|
|
if(textureDemandsMip.ContainsKey(buffName) &&
|
|
textureDemandsMip[buffName].ContainsKey(i) &&
|
|
textureDemandsMip[buffName][i])
|
|
{
|
|
if(tex==mainTex) useMipOnMain=true;
|
|
else if(tex is RenderTexture) ((RenderTexture)tex).useMipMap=true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(meshes.ContainsKey(buffName))
|
|
{
|
|
Graphics.SetRenderTarget(buffers[buffName]);
|
|
GL.Clear(true, true, Color.clear);
|
|
if(mat!=null) mat.SetPass(0);
|
|
foreach(Mesh mesh in meshes[buffName])
|
|
{
|
|
Graphics.DrawMeshNow(mesh, Vector3.zero, Quaternion.identity);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(buffName=="Image")
|
|
{
|
|
if(mat!=null)
|
|
if(outputTexture)
|
|
{
|
|
Graphics.Blit(mainTex, outputTexture, mat);
|
|
// default blit of screen - no effect
|
|
Graphics.Blit(src, dest);
|
|
}
|
|
else
|
|
Graphics.Blit(mainTex, dest, mat);
|
|
}
|
|
else
|
|
{
|
|
if(mat!=null) Graphics.Blit(mainTex, buffers[buffName], mat);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
/*public void OnPostRender() {
|
|
}*/
|
|
|
|
}
|
|
} |