FFTによるグレアフィルター
環境
Unity2021.2.18f1
概要
FFTを用いたグレアフィルターです。
今回も参考リンクのページのコードを実装してみた感じです。
画像も同じのを使わせていただいています。

GenerateStarBurst2Window.cs
using System.IO;
using UnityEditor;
using UnityEngine;
public class GenerateStarBurst2Window : EditorWindow
{
[MenuItem("Tools/Generate StarBurst2")]
private static void Create()
{
var window = GetWindow<GenerateStarBurst2Window>( "GenerateStarBurst2" );
window.Show();
}
private GenerateStarBurst2 _obj = null;
private void OnEnable()
{
var ms = MonoScript.FromScriptableObject( this );
var path = AssetDatabase.GetAssetPath( ms );
path = path.Replace(Path.GetFileName( path ), "" );
path = path + "GenerateStarBurst2.asset";
_obj = AssetDatabase.LoadAssetAtPath<GenerateStarBurst2>( path );
if(_obj == null)
{
var assets = AssetDatabase.LoadAllAssetsAtPath(path);
if(assets.Length > 0)
_obj = assets[0] as GenerateStarBurst2;
}
if( _obj == null )
{
_obj = ScriptableObject.CreateInstance<GenerateStarBurst2>();
AssetDatabase.CreateAsset( _obj, path );
AssetDatabase.ImportAsset( path, ImportAssetOptions.ForceUpdate | ImportAssetOptions.ImportRecursive );
}
}
private void OnDisable()
{
if( _obj == null )
return;
EditorUtility.SetDirty( _obj );
AssetDatabase.ImportAsset( AssetDatabase.GetAssetPath( _obj ), ImportAssetOptions.ForceUpdate | ImportAssetOptions.ImportRecursive );
}
private void OnGUI()
{
_obj.OnGUI();
Repaint();
}
}
GenerateStarBurst2.cs
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
public class GenerateStarBurst2 : ScriptableObject
{
[SerializeField] private ComputeShader _computeShader = null;
[SerializeField] private Texture2D _targetTexture = null;
[SerializeField] private Texture2D _apertureTexture = null;
private const int m_texheight = 512; //computeShaderも変更する
private float m_glareintensity = 20.0f;
private float m_threshold = 0.96f;
private RenderTexture[] m_RWfullsizeTex = null;
private RenderTexture[] m_RWmaxminTex = null;
private RenderTexture[] m_RWlineInnerTex = null;
private RenderTexture m_RClearTex = null;
private struct ComputeParameters
{
public float lambdaR;
public float lambdaG;
public float lambdaB;
public float glareintensity;
public float threshold;
};
private GraphicsBuffer _gsConstantParam = null;
private void OnDestroy()
{
if(_gsConstantParam != null)
_gsConstantParam.Dispose();
if(m_RWfullsizeTex != null)
{
for(int i = 0; i < m_RWfullsizeTex.Length; i++)
m_RWfullsizeTex[i]?.Release();
}
m_RWfullsizeTex = null;
if(m_RWmaxminTex != null)
{
for(int i = 0; i < m_RWmaxminTex.Length; i++)
m_RWmaxminTex[i]?.Release();
}
m_RWmaxminTex = null;
if(m_RWlineInnerTex != null)
{
for(int i = 0; i < m_RWlineInnerTex.Length; i++)
m_RWlineInnerTex[i]?.Release();
}
m_RWlineInnerTex = null;
m_RClearTex?.Release();
m_RClearTex = null;
}
private static string LabeledTextField(string label, string text, float labelFieldWidth = 100, float fieldWidth = 30)
{
using (var horizontalScope = new GUILayout.HorizontalScope())
{
EditorGUILayout.LabelField(label, GUILayout.Width(labelFieldWidth));
var str = EditorGUILayout.TextField(text, GUILayout.Width(fieldWidth));
return str;
}
}
public void OnGUI()
{
var str = LabeledTextField("Glare Intensity", m_glareintensity.ToString(), 100, 60);
m_glareintensity = (float)Convert.ToDouble(str);
str = LabeledTextField("Threshold", m_threshold.ToString(), 100, 60);
m_threshold = (float)Convert.ToDouble(str);
if(GUILayout.Button( "Generate Glare" ) == true)
{
GenerateGlare();
}
if(GUILayout.Button( "Generate" ) == true)
{
Generate();
}
var baseRect = GUILayoutUtility.GetLastRect();
var rect = baseRect;
rect.position = new Vector2(rect.position.x, rect.position.y + rect.size.y);
rect.size = new Vector2(128, 128);
if(m_RWfullsizeTex != null)
{
for(int i = 0; i < m_RWfullsizeTex.Length; i++)
{
if(m_RWfullsizeTex[i] != null)
EditorGUI.DrawPreviewTexture(rect, m_RWfullsizeTex[i]);
rect.position = new Vector2(rect.position.x + rect.size.x, rect.position.y);
}
}
rect.position = new Vector2(baseRect.position.x, rect.position.y + rect.size.y);
if(m_RWmaxminTex != null)
{
for(int i = 0; i < m_RWmaxminTex.Length; i++)
{
if(m_RWmaxminTex[i] != null)
EditorGUI.DrawPreviewTexture(rect, m_RWmaxminTex[i]);
rect.position = new Vector2(rect.position.x + rect.size.x, rect.position.y);
}
}
rect.position = new Vector2(baseRect.position.x, rect.position.y + rect.size.y);
if(m_RWlineInnerTex != null)
{
for(int i = 0; i < m_RWlineInnerTex.Length; i++)
{
if(m_RWlineInnerTex[i] != null)
EditorGUI.DrawPreviewTexture(rect, m_RWlineInnerTex[i]);
rect.position = new Vector2(rect.position.x + rect.size.x, rect.position.y);
}
}
rect.position = new Vector2(baseRect.position.x, rect.position.y + rect.size.y);
if(m_RClearTex != null)
{
EditorGUI.DrawPreviewTexture(rect, m_RClearTex);
rect.position = new Vector2(rect.position.x + rect.size.x, rect.position.y);
}
}
private void CreateClearTex()
{
if(m_RClearTex != null)
return;
m_RClearTex = new RenderTexture(m_texheight, m_texheight, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Default);
m_RClearTex.useMipMap = false;
m_RClearTex.enableRandomWrite = true;
ExecuteClearCommand(m_RClearTex);
}
private unsafe void GenerateGlare()
{
var cp = new ComputeParameters();
cp.lambdaR = 633e-9f;
cp.lambdaG = 532e-9f;
cp.lambdaB = 466e-9f;
cp.glareintensity = m_glareintensity;
cp.threshold = m_threshold;
if(_gsConstantParam == null)
_gsConstantParam = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 1, sizeof(ComputeParameters));
_gsConstantParam.SetData(new ComputeParameters[]{cp});
if(m_RWfullsizeTex == null)
{
m_RWfullsizeTex = new RenderTexture[9];
for (int i = 0; i < m_RWfullsizeTex.Length; i++)
{
m_RWfullsizeTex[i] = new RenderTexture(m_texheight, m_texheight, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Default);
m_RWfullsizeTex[i].useMipMap = false;
m_RWfullsizeTex[i].enableRandomWrite = true;
}
}
if(m_RWmaxminTex == null)
{
m_RWmaxminTex = new RenderTexture[2];
for (int i = 0; i < m_RWmaxminTex.Length; i++)
{
m_RWmaxminTex[i] = new RenderTexture(m_texheight, m_texheight, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Default);
m_RWmaxminTex[i].useMipMap = false;
m_RWmaxminTex[i].enableRandomWrite = true;
}
}
if(m_RWlineInnerTex == null)
{
m_RWlineInnerTex = new RenderTexture[2];
for (int i = 0; i < m_RWlineInnerTex.Length; i++)
{
m_RWlineInnerTex[i] = new RenderTexture(m_texheight, m_texheight, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Default);
m_RWlineInnerTex[i].useMipMap = false;
m_RWlineInnerTex[i].enableRandomWrite = true;
}
}
CreateClearTex();
ExecuteFFTCommand(_apertureTexture, m_RClearTex, m_RWfullsizeTex[3], m_RWfullsizeTex[4]);//グレア生成
ExecuteCalcurateAmplitudeCommand(m_RWfullsizeTex[3], m_RWfullsizeTex[4], m_RWfullsizeTex[5], m_RWfullsizeTex[6]);
ExecuteCalcMaxMinCommand(m_RWfullsizeTex[5], m_RWmaxminTex[0], m_RWmaxminTex[1]);
ExecuteDivideMaxAmpCommand(m_RWmaxminTex[0], m_RWmaxminTex[1], m_RWfullsizeTex[5], m_RWfullsizeTex[6], m_RWfullsizeTex[3], m_RWfullsizeTex[4]);
ExecuteRaiseRICommand(m_RWfullsizeTex[3], m_RWfullsizeTex[4], m_RWfullsizeTex[5], m_RWfullsizeTex[6]);//グレアの輝度を底上げ
ExecuteSpectrumScalingCommand(m_RWfullsizeTex[5], m_RWfullsizeTex[6], m_RWfullsizeTex[3], m_RWfullsizeTex[4]);//波長スケーリング
ExecuteCopyCommand(m_RWfullsizeTex[3], m_RWfullsizeTex[7]);
ExecuteCopyCommand(m_RWfullsizeTex[4], m_RWfullsizeTex[8]);
SaveRenderTexture(m_RWfullsizeTex[7], "GlareReal.png");
SaveRenderTexture(m_RWfullsizeTex[8], "GlareImage.png");
}
private unsafe void Generate()
{
if(m_RWfullsizeTex == null)
GenerateGlare();
CreateClearTex();
ExecuteBinaryThresholdCommand(_targetTexture, m_RWfullsizeTex[1]);
ExecuteConvolutionCommand(m_RWfullsizeTex[1], m_RClearTex, m_RWfullsizeTex[1], m_RWfullsizeTex[2], m_RWfullsizeTex[7], m_RWfullsizeTex[8], m_RWfullsizeTex[3], m_RWfullsizeTex[4], m_RWfullsizeTex[5], m_RWfullsizeTex[6]);
ExecuteCalcurateAmplitudeCommand(m_RWfullsizeTex[5], m_RWfullsizeTex[6], m_RWfullsizeTex[0], m_RWfullsizeTex[1]);
ExecuteCalcMaxMinCommand(m_RWfullsizeTex[0], m_RWmaxminTex[0], m_RWmaxminTex[1]);
ExecuteDivideMaxAmpCommand(m_RWmaxminTex[0], m_RWmaxminTex[1], m_RWfullsizeTex[0], m_RWfullsizeTex[1], m_RWfullsizeTex[3], m_RWfullsizeTex[4]);
ExecuteAddCommand(_targetTexture, m_RWfullsizeTex[3], m_RWfullsizeTex[1]);
SaveRenderTexture(m_RWfullsizeTex[1], "Final.png");
}
private void SaveRenderTexture(RenderTexture renderTex, string path)
{
var prevRt = RenderTexture.active;
Texture2D tex = new Texture2D(renderTex.width, renderTex.height, renderTex.graphicsFormat, TextureCreationFlags.None);
RenderTexture.active = renderTex;
tex.ReadPixels(new Rect(0, 0, renderTex.width, renderTex.height), 0, 0);
tex.Apply();
RenderTexture.active = prevRt;
var bytes = tex.EncodeToPNG();
File.WriteAllBytes(Path.Combine($"{Application.dataPath}", path), bytes);
AssetDatabase.Refresh();
}
private Dictionary<string, string> _nameDict = null;
private int FindKernel(string name)
{
if(_nameDict == null)
{
_nameDict = new Dictionary<string, string>();
_nameDict.Add("mulCS", "mainMULTIPLY");
_nameDict.Add("fftCS_ROW", "mainFFT_ROW");
_nameDict.Add("fftCS_COL", "mainFFT_COL");
_nameDict.Add("ifftCS_ROW", "mainFFT_ROW_INV");
_nameDict.Add("ifftCS_COL", "mainFFT_COL_INV");
_nameDict.Add("ampCS", "mainAMPLITUDE");
_nameDict.Add("divByMaxAMPCS", "mainDivByMaxAMP");
_nameDict.Add("AddCS", "mainAdd");
_nameDict.Add("BTCS", "mainBinaryThreshold");
_nameDict.Add("copyCS", "mainCopy");
_nameDict.Add("clearCS", "mainClear");
_nameDict.Add("spectrumScalingCS", "mainSpectrumScaling");
_nameDict.Add("raiseRICS", "mainRaiseBottomRealImage");
_nameDict.Add("maxminfirstCS", "mainMAXMINfirst");
_nameDict.Add("maxminsecondCS", "mainMAXMINsecond");
}
return _computeShader.FindKernel(_nameDict[name]);
}
private static string[] _csNames = new string[]
{
"computeConstants", //0
"sourceImageR", //1
"sourceImageI", //2
"destinationImageR", //3
"destinationImageI", //4
"destinationImageR1", //5
"destinationImageI1", //6
};
private string GetCsName(int i)
{
return _csNames[i];
}
private void ExecuteCopyCommand(Texture In, Texture Out)
{
var kernelIndex = FindKernel("copyCS");
_computeShader.SetBuffer(kernelIndex, GetCsName(0), _gsConstantParam);
_computeShader.SetTexture(kernelIndex, GetCsName(1), In);
_computeShader.SetTexture(kernelIndex, GetCsName(3), Out);
_computeShader.Dispatch(kernelIndex, 1, m_texheight, 1);
}
private void ExecuteBinaryThresholdCommand(Texture In, Texture Out)
{
var kernelIndex = FindKernel("BTCS");
_computeShader.SetBuffer(kernelIndex, GetCsName(0), _gsConstantParam);
_computeShader.SetTexture(kernelIndex, GetCsName(1), In);
_computeShader.SetTexture(kernelIndex, GetCsName(3), Out);
_computeShader.Dispatch(kernelIndex, 1, m_texheight, 1);
}
private void ExecuteClearCommand(Texture Tex)
{
var kernelIndex = FindKernel("clearCS");
_computeShader.SetBuffer(kernelIndex, GetCsName(0), _gsConstantParam);
_computeShader.SetTexture(kernelIndex, GetCsName(3), Tex);
_computeShader.Dispatch(kernelIndex, 1, m_texheight, 1);
}
private void ExecuteFFTCommand(Texture Real, Texture Image)
{
ExecuteFFTCommand(Real, Image, Real, Image);
}
private void ExecuteFFTCommand(Texture Real, Texture Image, Texture OutReal, Texture OutImage)
{
//縦方向
var kernelIndex = FindKernel("fftCS_ROW");
_computeShader.SetBuffer(kernelIndex, GetCsName(0), _gsConstantParam);
_computeShader.SetTexture(kernelIndex, GetCsName(1), Real);
_computeShader.SetTexture(kernelIndex, GetCsName(2), Image);
_computeShader.SetTexture(kernelIndex, GetCsName(3), m_RWlineInnerTex[0]);
_computeShader.SetTexture(kernelIndex, GetCsName(4), m_RWlineInnerTex[1]);
_computeShader.Dispatch(kernelIndex, 1, m_texheight, 1);
//横方向
kernelIndex = FindKernel("fftCS_COL");
_computeShader.SetBuffer(kernelIndex, GetCsName(0), _gsConstantParam);
_computeShader.SetTexture(kernelIndex, GetCsName(1), m_RWlineInnerTex[0]);
_computeShader.SetTexture(kernelIndex, GetCsName(2), m_RWlineInnerTex[1]);
_computeShader.SetTexture(kernelIndex, GetCsName(3), OutReal);
_computeShader.SetTexture(kernelIndex, GetCsName(4), OutImage);
_computeShader.Dispatch(kernelIndex, 1, m_texheight, 1);
}
private void ExecuteIFFTCommand(Texture Real, Texture Image)
{
//縦方向
var kernelIndex = FindKernel("ifftCS_ROW");
_computeShader.SetBuffer(kernelIndex, GetCsName(0), _gsConstantParam);
_computeShader.SetTexture(kernelIndex, GetCsName(1), Real);
_computeShader.SetTexture(kernelIndex, GetCsName(2), Image);
_computeShader.SetTexture(kernelIndex, GetCsName(3), m_RWlineInnerTex[0]);
_computeShader.SetTexture(kernelIndex, GetCsName(4), m_RWlineInnerTex[1]);
_computeShader.Dispatch(kernelIndex, 1, m_texheight, 1);
//横方向
kernelIndex = FindKernel("ifftCS_COL");
_computeShader.SetBuffer(kernelIndex, GetCsName(0), _gsConstantParam);
_computeShader.SetTexture(kernelIndex, GetCsName(1), m_RWlineInnerTex[0]);
_computeShader.SetTexture(kernelIndex, GetCsName(2), m_RWlineInnerTex[1]);
_computeShader.SetTexture(kernelIndex, GetCsName(3), Real);
_computeShader.SetTexture(kernelIndex, GetCsName(4), Image);
_computeShader.Dispatch(kernelIndex, 1, m_texheight, 1);
}
private void ExecuteCalcurateAmplitudeCommand(Texture InReal, Texture InImage, Texture OutReal, Texture OutImage)
{
var kernelIndex = FindKernel("ampCS");
_computeShader.SetBuffer(kernelIndex, GetCsName(0), _gsConstantParam);
_computeShader.SetTexture(kernelIndex, GetCsName(1), InReal);
_computeShader.SetTexture(kernelIndex, GetCsName(2), InImage);
_computeShader.SetTexture(kernelIndex, GetCsName(3), OutReal);
_computeShader.SetTexture(kernelIndex, GetCsName(4), OutImage);
_computeShader.Dispatch(kernelIndex, 1, m_texheight, 1);
}
private void ExecuteCalcMaxMinCommand(Texture Tex, Texture OutOnePixReal_MAX, Texture OutOnePixImage_MIN)
{
//最大値最小値の計算
var kernelIndex = FindKernel("maxminfirstCS");
_computeShader.SetBuffer(kernelIndex, GetCsName(0), _gsConstantParam);
_computeShader.SetTexture(kernelIndex, GetCsName(1), Tex);
_computeShader.SetTexture(kernelIndex, GetCsName(3), m_RWlineInnerTex[0]);
_computeShader.Dispatch(kernelIndex, m_texheight, 1, 1);
kernelIndex = FindKernel("maxminsecondCS");
_computeShader.SetBuffer(kernelIndex, GetCsName(0), _gsConstantParam);
_computeShader.SetTexture(kernelIndex, GetCsName(1), m_RWlineInnerTex[0]);
_computeShader.SetTexture(kernelIndex, GetCsName(3), OutOnePixReal_MAX);
_computeShader.SetTexture(kernelIndex, GetCsName(4), OutOnePixImage_MIN);
_computeShader.Dispatch(kernelIndex, 1, 1, 1);
}
private void ExecuteDivideMaxAmpCommand(Texture OutOnePixReal_MAX, Texture OutOnePixImage_MIN, Texture InReal, Texture InImage, Texture OutReal, Texture OutImage)
{
//最大振幅による除算
var kernelIndex = FindKernel("divByMaxAMPCS");
_computeShader.SetBuffer(kernelIndex, GetCsName(0), _gsConstantParam);
_computeShader.SetTexture(kernelIndex, GetCsName(1), OutOnePixReal_MAX);
_computeShader.SetTexture(kernelIndex, GetCsName(2), OutOnePixImage_MIN);
_computeShader.SetTexture(kernelIndex, GetCsName(3), InReal);
_computeShader.SetTexture(kernelIndex, GetCsName(4), InImage);
_computeShader.SetTexture(kernelIndex, GetCsName(5), OutReal);
_computeShader.SetTexture(kernelIndex, GetCsName(6), OutImage);
_computeShader.Dispatch(kernelIndex, 1, m_texheight, 1);
}
private void ExecuteRaiseRICommand(Texture InReal, Texture InImage, Texture OutReal, Texture OutImage)
{
var kernelIndex = FindKernel("raiseRICS");
_computeShader.SetBuffer(kernelIndex, GetCsName(0), _gsConstantParam);
_computeShader.SetTexture(kernelIndex, GetCsName(1), InReal);
_computeShader.SetTexture(kernelIndex, GetCsName(2), InImage);
_computeShader.SetTexture(kernelIndex, GetCsName(3), OutReal);
_computeShader.SetTexture(kernelIndex, GetCsName(4), OutImage);
_computeShader.Dispatch(kernelIndex, 1, m_texheight, 1);
}
private void ExecuteSpectrumScalingCommand(Texture InReal, Texture InImage, Texture OutReal, Texture OutImage)
{
var kernelIndex = FindKernel("spectrumScalingCS");
_computeShader.SetBuffer(kernelIndex, GetCsName(0), _gsConstantParam);
_computeShader.SetTexture(kernelIndex, GetCsName(1), InReal);
_computeShader.SetTexture(kernelIndex, GetCsName(2), InImage);
_computeShader.SetTexture(kernelIndex, GetCsName(3), OutReal);
_computeShader.SetTexture(kernelIndex, GetCsName(4), OutImage);
_computeShader.Dispatch(kernelIndex, 1, m_texheight, 1);
}
private void ExecuteConvolutionCommand(Texture InReal0, Texture InImage0, Texture InReal1, Texture InImage1, Texture OutReal, Texture OutImage)
{
ExecuteConvolutionCommand(InReal0, InImage0, InReal0, InImage0, InReal1, InImage1, InReal1, InImage1, OutReal, OutImage);
}
private void ExecuteConvolutionCommand(Texture InReal0, Texture InImage0, Texture TmpFftReal0, Texture TmpFftImage0, Texture InReal1, Texture InImage1, Texture TmpFftReal1, Texture TmpFftImage1, Texture OutReal, Texture OutImage)
{
//一方のFFT
ExecuteFFTCommand(InReal0, InImage0, TmpFftReal0, TmpFftImage0);
//もう一方のFFT
ExecuteFFTCommand(InReal1, InImage1, TmpFftReal1, TmpFftImage1);
//乗算
ExecuteMultiplyCommand(TmpFftReal0, TmpFftImage0, TmpFftReal1, TmpFftImage1, OutReal, OutImage);
//逆FFT
ExecuteIFFTCommand(OutReal, OutImage);
}
private void ExecuteMultiplyCommand(Texture InReal0, Texture InImage0, Texture InReal1, Texture InImage1, Texture OutReal, Texture OutImage)
{
var kernelIndex = FindKernel("mulCS");
_computeShader.SetBuffer(kernelIndex, GetCsName(0), _gsConstantParam);
_computeShader.SetTexture(kernelIndex, GetCsName(1), InReal0);
_computeShader.SetTexture(kernelIndex, GetCsName(2), InImage0);
_computeShader.SetTexture(kernelIndex, GetCsName(3), InReal1);
_computeShader.SetTexture(kernelIndex, GetCsName(4), InImage1);
_computeShader.SetTexture(kernelIndex, GetCsName(5), OutReal);
_computeShader.SetTexture(kernelIndex, GetCsName(6), OutImage);
_computeShader.Dispatch(kernelIndex, 1, m_texheight, 1);
}
private void ExecuteAddCommand(Texture Tex1, Texture Tex2, Texture Out)
{
var kernelIndex = FindKernel("AddCS");
_computeShader.SetBuffer(kernelIndex, GetCsName(0), _gsConstantParam);
_computeShader.SetTexture(kernelIndex, GetCsName(1), Tex1);
_computeShader.SetTexture(kernelIndex, GetCsName(2), Tex2);
_computeShader.SetTexture(kernelIndex, GetCsName(3), Out);
_computeShader.Dispatch(kernelIndex, 1, m_texheight, 1);
}
}
GenerateStarBurst2.compute
static const int WIDTH = 512;
static const int HEIGHT = 512;
static const float PI = 3.14159;
static const float RAD = PI / 180.0;
static const int LENGTH = HEIGHT;
static const int BUTTERFLY_COUNT = 9;
//--------------------------------
float2 complex_conjugate(float2 cmp)
{
return float2(cmp.x, -cmp.y);
}
float complex_sqr(float2 cmp)
{
return cmp.x * cmp.x + cmp.y * cmp.y;
}
float complex_norm(float2 cmp)
{
return sqrt(complex_sqr(cmp));
}
float2 complex_add(float2 cmp1, float2 cmp2)
{
return float2(cmp1.x + cmp2.x, cmp1.y + cmp2.y);
}
float2 complex_sub(float2 cmp1, float2 cmp2)
{
return float2(cmp1.x - cmp2.x, cmp1.y - cmp2.y);
}
float2 complex_mul(float2 cmp1, float2 cmp2)
{
return float2(cmp1.x * cmp2.x - cmp1.y * cmp2.y, cmp1.y * cmp2.x + cmp1.x * cmp2.y);
}
float2 complex_div(float2 cmp1, float2 cmp2)
{
float2 cmp = complex_mul(cmp1, complex_conjugate(cmp2));
float sqr = complex_sqr(cmp2);
return float2(cmp.x / sqr, cmp.y / sqr);
}
float2 complex_polar(float amp, float phase)
{
return float2(amp * cos(phase), amp * sin(phase));
}
//------------------------
struct ComputeParameters
{
float lambdaR;
float lambdaG;
float lambdaB;
float glareintensity;
float threshold;
};
StructuredBuffer<ComputeParameters> computeConstants;
Texture2D<float4> sourceImageR : register(t0);
Texture2D<float4> sourceImageI : register(t1);
RWTexture2D<float4> destinationImageR : register(u0);
RWTexture2D<float4> destinationImageI : register(u1);
RWTexture2D<float4> destinationImageR1 : register(u2);
RWTexture2D<float4> destinationImageI1 : register(u3);
//------------------------
//copyCSに対応
#pragma kernel mainCopy
[numthreads(WIDTH, 1, 1)]
void mainCopy(uint3 dispatchID : SV_DispatchThreadID)
{
float2 index = dispatchID.xy;
destinationImageR[index] = sourceImageR[index];
}
//BTCSに対応
#pragma kernel mainBinaryThreshold
[numthreads(WIDTH, 1, 1)]
void mainBinaryThreshold(uint3 dispatchID : SV_DispatchThreadID)
{
float2 index = dispatchID.xy;
float3 input = sourceImageR[index].rgb;
float3 col = float3(0.0, 0.0, 0.0);
float r = 0;
float g = 0;
float b = 0;
if ((input.r + input.g + input.b) / 3.0 > computeConstants[0].threshold)
{
col = float3(1.0, 1.0, 1.0);
}
destinationImageR[index] = float4(col, 1.0f);
}
//clearCSに対応
#pragma kernel mainClear
[numthreads(WIDTH, 1, 1)]
void mainClear(uint3 dispatchID : SV_DispatchThreadID)
{
float2 index = dispatchID.xy;
destinationImageR[index] = float4(0.0, 0.0, 0.0, 1.0);
}
//fftCS_シリーズで使用される
void ComputeSrcID(uint passIndex, uint x, out uint2 indices)
{
uint regionWidth = 2 << passIndex;
indices.x = (x & ~(regionWidth - 1)) + (x & (regionWidth / 2 - 1));
indices.y = indices.x + regionWidth / 2;
if (passIndex == 0)
{
indices = reversebits(indices) >> (32 - BUTTERFLY_COUNT) & (LENGTH - 1);
}
}
void ComputeTwiddleFactor(uint passIndex, uint x, out float2 weights)
{
uint regionWidth = 2 << passIndex;
sincos(2.0 * PI * float(x & (regionWidth - 1)) / float(regionWidth), weights.y, weights.x);
weights.y *= -1;
}
static const int REAL = 0;
static const int IMAGE = 1;
#ifdef DOUBLE//メモリに余裕があるとき
groupshared float3 ButterflyArray[2][2][LENGTH];
#define SharedArray(tmpID, x, realImage) (ButterflyArray[(tmpID)][(realImage)][(x)])
#else
groupshared float3 ButterflyArray[2][LENGTH];
#define SharedArray(tmpID, x, realImage) (ButterflyArray[(realImage)][(x)])
#endif
//fftCS_シリーズで使用される
void ButterflyWeightPass(uint passIndex, uint x, uint tmp, out float3 resultR, out float3 resultI)
{
uint2 Indices;
float2 Weights;
ComputeSrcID(passIndex, x, Indices);
float3 inputR1 = SharedArray(tmp, Indices.x, REAL);
float3 inputI1 = SharedArray(tmp, Indices.x, IMAGE);
float3 inputR2 = SharedArray(tmp, Indices.y, REAL);
float3 inputI2 = SharedArray(tmp, Indices.y, IMAGE);
ComputeTwiddleFactor(passIndex, x, Weights);
#ifndef DOUBLE
GroupMemoryBarrierWithGroupSync();//ダブルバッファでない場合は格納の完了を保証する必要がる
#endif
#if INVERSE
resultR = (inputR1 + Weights.x * inputR2 + Weights.y * inputI2) * 0.5;
resultI = (inputI1 - Weights.y * inputR2 + Weights.x * inputI2) * 0.5;
#else
resultR = inputR1 + Weights.x * inputR2 - Weights.y * inputI2;
resultI = inputI1 + Weights.y * inputR2 + Weights.x * inputI2;
#endif
}
void initializeFFT_SharedArray(uint bufferID, inout uint2 texPos)
{
texPos = (texPos + LENGTH / 2) % LENGTH;
SharedArray(0, bufferID, REAL) = sourceImageR[texPos].xyz;
#if ROW && !INVERSE
SharedArray(0, bufferID, IMAGE) = (0.0).xxx;
#else
SharedArray(0, bufferID, IMAGE) = sourceImageI[texPos].xyz;
#endif
}
void ButterflyPass(in uint bufferID, out float3 real, out float3 image)
{
for (uint butterFlyID = 0; butterFlyID < (uint)(BUTTERFLY_COUNT - 1); butterFlyID++)
{
GroupMemoryBarrierWithGroupSync();
ButterflyWeightPass(butterFlyID, bufferID, butterFlyID % 2, SharedArray((butterFlyID + 1) % 2, bufferID, REAL), SharedArray((butterFlyID + 1) % 2, bufferID, IMAGE));
}
GroupMemoryBarrierWithGroupSync();
ButterflyWeightPass(BUTTERFLY_COUNT - 1, bufferID, (BUTTERFLY_COUNT - 1) % 2,
real, image);
}
//fftCS_シリーズに対応
void mainFFT(uint3 dispatchID)
{
const uint bufferID = dispatchID.x;
#if ROW
uint2 texPos = dispatchID.xy;
#else
uint2 texPos = dispatchID.yx;
#endif
initializeFFT_SharedArray(bufferID, texPos);
float3 r_result = 0, i_result = 0;
ButterflyPass(bufferID, r_result, i_result);
destinationImageR[texPos] = float4(r_result, 1);
destinationImageI[texPos] = float4(i_result, 1);
}
#pragma kernel mainFFT_ROW ROW
[numthreads(LENGTH, 1, 1)]
void mainFFT_ROW(uint3 dispatchID : SV_DispatchThreadID)
{
mainFFT(dispatchID);
}
#pragma kernel mainFFT_COL
[numthreads(LENGTH, 1, 1)]
void mainFFT_COL(uint3 dispatchID : SV_DispatchThreadID)
{
mainFFT(dispatchID);
}
#pragma kernel mainFFT_ROW_INV ROW INVERSE
[numthreads(LENGTH, 1, 1)]
void mainFFT_ROW_INV(uint3 dispatchID : SV_DispatchThreadID)
{
mainFFT(dispatchID);
}
#pragma kernel mainFFT_COL_INV INVERSE
[numthreads(LENGTH, 1, 1)]
void mainFFT_COL_INV(uint3 dispatchID : SV_DispatchThreadID)
{
mainFFT(dispatchID);
}
//ampCSに対応
#pragma kernel mainAMPLITUDE
[numthreads(WIDTH, 1, 1)]
void mainAMPLITUDE(uint3 dispatchID : SV_DispatchThreadID)
{
float2 index = dispatchID.xy;
float3 inputR = sourceImageR[index].rgb;
float3 inputI = sourceImageI[index].rgb;
float r = inputR.r * inputR.r + inputI.r * inputI.r;
float g = inputR.g * inputR.g + inputI.g * inputI.g;
float b = inputR.b * inputR.b + inputI.b * inputI.b;
float3 col = float3(sqrt(r), sqrt(g), sqrt(b));
destinationImageR[index] = float4(col, 1.0f);
destinationImageI[index] = float4(col, 1.0f);
}
//maxminfirstCSに対応
#pragma kernel mainMAXMINfirst
[numthreads(1, 1, 1)]
void mainMAXMINfirst(uint3 dispatchID : SV_DispatchThreadID)
{
uint2 index = dispatchID.xy;
float3 color_max = sourceImageR[float2(0, 0)].xyz;
float3 color_min = color_max;
int i, j;
for (i = 0; i < HEIGHT; i++)
{
uint2 indexx = uint2(index.x, i);
float3 color = sourceImageR[indexx].xyz;
color_max = max(color_max, color);
color_min = min(color_min, color);
}
destinationImageR[float2(index.x, 0)] = float4(color_max, 1.0f);
destinationImageR[float2(index.x, 1)] = float4(color_min, 1.0f);
}
//maxminsecondCSに対応
#pragma kernel mainMAXMINsecond
[numthreads(1, 1, 1)]
void mainMAXMINsecond(uint3 dispatchID : SV_DispatchThreadID)
{
uint2 index = dispatchID.xy;
float3 color_max = sourceImageR[float2(0, 0)].xyz;
float3 color_min = color_max;
int i, j;
for (i = 0; i < HEIGHT; i++)
{
uint2 index1 = uint2(i, 0);
uint2 index2 = uint2(i, 1);
float3 colormax = sourceImageR[index1].xyz;
float3 colormin = sourceImageR[index2].xyz;
color_max = max(color_max, colormax);
color_min = min(color_min, colormin);
}
destinationImageR[float2(0, 0)] = float4(color_max, 1.0f);
destinationImageI[float2(0, 0)] = float4(color_min, 1.0f);
}
//divByMaxAMPCSに対応
#pragma kernel mainDivByMaxAMP
[numthreads(WIDTH, 1, 1)]
void mainDivByMaxAMP(uint3 dispatchID : SV_DispatchThreadID)
{
float2 index = dispatchID.xy;
float2 zero = float2(0.0, 0.0);
//sourceImageRに最大値格納したテクスチャをセットしておく
float3 color_max = sourceImageR[zero].rgb;
//destinatinImageR/Iに正規化したいテクスチャをセットしておく
float3 colorR = destinationImageR[index].rgb;
float3 colorI = destinationImageI[index].rgb;
colorR = colorR / color_max;
colorI = colorI / color_max;
float col_max_per = color_max.r;
col_max_per = max(col_max_per, color_max.g);
col_max_per = max(col_max_per, color_max.b);
float3 ratio = color_max / col_max_per;
colorR = colorR * ratio;
colorI = colorI * ratio;
destinationImageR1[index] = float4(colorR, 1.0f);
destinationImageI1[index] = float4(colorI, 1.0f);
}
//raiseRICSに対応
#pragma kernel mainRaiseBottomRealImage
[numthreads(WIDTH, 1, 1)]
void mainRaiseBottomRealImage(uint3 dispatchID : SV_DispatchThreadID)
{
float2 index = dispatchID.xy;
float3 inputR = sourceImageR[index].rgb;
float3 inputI = sourceImageI[index].rgb;
float3 amplitude = float3(sqrt(inputR.r * inputR.r + inputI.r * inputI.r)
, sqrt(inputR.g * inputR.g + inputI.g * inputI.g)
, sqrt(inputR.b * inputR.b + inputI.b * inputI.b));
if ((amplitude.r + amplitude.g + amplitude.b) / 3.0f < 0.9)
{
/*inputR = inputR * 10.0f;
inputI = inputI * 10.0f;*/
inputR = inputR * computeConstants[0].glareintensity;
inputI = inputI * computeConstants[0].glareintensity;
}
if ((inputR.r + inputR.g + inputR.b) / 3.0f >= 1.0)
{
inputR = float3(1.0, 1.0, 1.0);
}
if ((inputI.r + inputI.g + inputI.b) / 3.0f >= 1.0)
{
inputI = float3(1.0, 1.0, 1.0);
}
destinationImageR[index] = float4(inputR, 1.0);
destinationImageI[index] = float4(inputI, 1.0);
}
//spectrumScalingCSに対応
#pragma kernel mainSpectrumScaling
[numthreads(WIDTH, 1, 1)]
void mainSpectrumScaling(uint3 dispatchID : SV_DispatchThreadID)
{
float2 indexR = dispatchID.xy;
float ratioRG = computeConstants[0].lambdaG / computeConstants[0].lambdaR;
float ratioRB = computeConstants[0].lambdaB / computeConstants[0].lambdaR;
float2 uvR = indexR - float2(0.5 * WIDTH, 0.5 * HEIGHT);
float2 uvG = uvR * ratioRG;
float2 uvB = uvR * ratioRB;
float2 indexG = uvG + float2(0.5 * WIDTH, 0.5 * HEIGHT);
float2 indexB = uvB + float2(0.5 * WIDTH, 0.5 * HEIGHT);
float r = sourceImageR[indexR].r;
float g = sourceImageR[indexG].g;
float b = sourceImageR[indexB].b;
destinationImageR[indexR] = float4(r, g, b, 1.0);
r = sourceImageI[indexR].r;
g = sourceImageI[indexG].g;
b = sourceImageI[indexB].b;
destinationImageI[indexR] = float4(r, g, b, 1.0);
}
//mulCSに対応
#pragma kernel mainMULTIPLY
[numthreads(WIDTH, 1, 1)]
void mainMULTIPLY(uint3 dispatchID : SV_DispatchThreadID)
{
float2 index = dispatchID.xy;
float3 color_real1 = sourceImageR[index].rgb;//元1 実部
float3 color_image1 = sourceImageI[index].rgb;//元1 虚部
float3 color_real2 = destinationImageR[index].rgb;//元2 実部
float3 color_image2 = destinationImageI[index].rgb;//元2 虚部
//各色からの複素数生成
float2 compR1 = float2(color_real1.r, color_image1.r);
float2 compG1 = float2(color_real1.g, color_image1.g);
float2 compB1 = float2(color_real1.b, color_image1.b);
float2 compR2 = float2(color_real2.r, color_image2.r);
float2 compG2 = float2(color_real2.g, color_image2.g);
float2 compB2 = float2(color_real2.b, color_image2.b);
//各色毎に乗算
float2 mulcompR = complex_mul(compR1, compR2);
float2 mulcompG = complex_mul(compG1, compG2);
float2 mulcompB = complex_mul(compB1, compB2);
//代入用の複素数作成
float3 colorREAL = float3(mulcompR.x, mulcompG.x, mulcompB.x);
float3 colorIMAGE = float3(mulcompR.y, mulcompG.y, mulcompB.y);
destinationImageR1[index] = float4(colorREAL, 1.0f);
destinationImageI1[index] = float4(colorIMAGE, 1.0f);
}
//AddCSに対応
#pragma kernel mainAdd
[numthreads(WIDTH, 1, 1)]
void mainAdd(uint3 dispatchID : SV_DispatchThreadID)
{
float2 index = dispatchID.xy;
float3 input1 = sourceImageR[index].rgb;
float3 input2 = sourceImageI[index].rgb;
float3 col = input1 + input2;
destinationImageR[index] = float4(col, 1.0f);
}
感想
真ん中だけ妙にグレアってるのは何ででしょうか。
こっちの差分コード実装し忘れました
ポストエフェクトクエスト - 波長方向の積分マジ大事という話 - - Qiita