仿制shazzam的简单功能,将hlsl转换为WPF中的ShaderEffect
程序员文章站
2022-05-16 12:00:26
(此文章只是在对WPF的Effect产生兴趣才稍微研究了一点后面的知识;需要了解更多可参考https://archive.codeplex.com/?p=shazzam的源代码以及WPF基础知识) 1.之前一直使用blend里自带的几个特效,突然有一天比较好奇这些特效是怎么来的。 然后就听说了sha ......
(此文章只是在对wpf的effect产生兴趣才稍微研究了一点后面的知识;需要了解更多可参考https://archive.codeplex.com/?p=shazzam的源代码以及wpf基础知识)
1.之前一直使用blend里自带的几个特效,突然有一天比较好奇这些特效是怎么来的。
然后就听说了shazzam并看到更多的特效
2.在参考网址下载了shazzam的代码来研究研究,只抽取出里面【如何将.fx文件编译为.ps,再产生一个调用.ps文件的.cs文件,然后就可以像正常使用其它自带effect一样使用了】这一过程
3.hlsl语法网上有很多教程啊,目前就直接拿一些写好的来用就行,一个简单的toonshader.fx
/// <description>an effect that applies cartoon-like shading (posterization).</description> sampler2d inputsampler : register(s0); //----------------------------------------------------------------------------------------- // shader constant register mappings (scalars - float, double, point, color, point3d, etc.) //----------------------------------------------------------------------------------------- /// <summary>the number of color levels to use.</summary> /// <minvalue>3</minvalue> /// <maxvalue>15</maxvalue> /// <defaultvalue>5</defaultvalue> float levels : register(c0); float4 main(float2 uv : texcoord) : color { float4 color = tex2d( inputsampler, uv ); color.rgb /= color.a; int levels = floor(levels); color.rgb *= levels; color.rgb = floor(color.rgb); color.rgb /= levels; color.rgb *= color.a; return color; }
4.shadercompiler:利用dxd的d3dxcompileshader将.fx文件转换为.ps文件
public void compile(string codetext, string output, string fxname, shaderprofile shaderprofile = shaderprofile.ps_2_0) { iscompiled = false; string path = output; intptr defines = intptr.zero; intptr includes = intptr.zero; intptr ppconstanttable = intptr.zero; string methodname = "main"; string targetprofile2 = "ps_2_0"; targetprofile2 = ((shaderprofile != shaderprofile.ps_3_0) ? "ps_2_0" : "ps_3_0"); bool usedx10 = false; int hr2 = 0; id3dxbuffer ppshader2; id3dxbuffer pperrormsgs2; if (!usedx10) { hr2 = ((intptr.size != 8) ? dxhelper.d3dxcompileshader(codetext, codetext.length, defines, includes, methodname, targetprofile2, 0, out ppshader2, out pperrormsgs2, out ppconstanttable) : dxhelper.d3dxcompileshader64bit(codetext, codetext.length, defines, includes, methodname, targetprofile2, 0, out ppshader2, out pperrormsgs2, out ppconstanttable)); } else { int phr = 0; hr2 = dxhelper.d3dx10compilefrommemory(codetext, codetext.length, string.empty, intptr.zero, intptr.zero, methodname, targetprofile2, 0, 0, intptr.zero, out ppshader2, out pperrormsgs2, ref phr); } if (hr2 != 0) { intptr errors = pperrormsgs2.getbufferpointer(); pperrormsgs2.getbuffersize(); errortext = marshal.ptrtostringansi(errors); iscompiled = false; } else { errortext = ""; iscompiled = true; string pspath = path + fxname; intptr pcompiledps = ppshader2.getbufferpointer(); int compiledpssize = ppshader2.getbuffersize(); byte[] compiledps = new byte[compiledpssize]; marshal.copy(pcompiledps, compiledps, 0, compiledps.length); using (filestream psfile = file.open(pspath, filemode.create, fileaccess.write)) { psfile.write(compiledps, 0, compiledps.length); } } if (ppshader2 != null) { marshal.releasecomobject(ppshader2); } ppshader2 = null; if (pperrormsgs2 != null) { marshal.releasecomobject(pperrormsgs2); } pperrormsgs2 = null; compilefinished(); }
5.codegenerator:生成引用.ps文件的effect.cs文件
private static string generatecode(codedomprovider provider, codecompileunit compileunit) { // generate source code using the code generator. using (stringwriter writer = new stringwriter()) { string indentstring = indentusingtabs ? "\t" : string.format("{0," + indentspaces.tostring() + "}", " "); codegeneratoroptions options = new codegeneratoroptions { indentstring = indentstring, blanklinesbetweenmembers = true, bracingstyle = "c" }; provider.generatecodefromcompileunit(compileunit, writer, options); string text = writer.tostring(); // fix up code: make static dp fields readonly, and use triple-slash or triple-quote comments for xml doc comments. if (provider.fileextension == "cs") { text = text.replace("public static dependencyproperty", "public static readonly dependencyproperty"); text = regex.replace(text, @"// <(?!/?auto-generated)", @"/// <"); } else if (provider.fileextension == "vb") { text = text.replace("public shared ", "public shared readonly "); text = text.replace("'<", "'''<"); } return text; } }
生成的cs文件内容如下:
//------------------------------------------------------------------------------ // <auto-generated> // 此代码由工具生成。 // 运行时版本:4.0.30319.42000 // // 对此文件的更改可能会导致不正确的行为,并且如果 // 重新生成代码,这些更改将会丢失。 // </auto-generated> //------------------------------------------------------------------------------ using system; using system.componentmodel; using system.windows; using system.windows.media; using system.windows.media.effects; using system.windows.media.media3d; namespace shaderpan { /// <summary>an effect that applies cartoon-like shading (posterization).</summary> public class toonshadereffect : shadereffect { public static readonly dependencyproperty inputproperty = shadereffect.registerpixelshadersamplerproperty("input", typeof(toonshadereffect), 0); public static readonly dependencyproperty levelsproperty = dependencyproperty.register("levels", typeof(double), typeof(toonshadereffect), new uipropertymetadata(((double)(5d)), pixelshaderconstantcallback(0))); public toonshadereffect() { pixelshader pixelshader = new pixelshader(); pixelshader.urisource = new uri("c:\\users\\administrator\\desktop\\wpftpl\\shader\\toonshader.ps", urikind.absolute); this.pixelshader = pixelshader; this.updateshadervalue(inputproperty); this.updateshadervalue(levelsproperty); } public brush input { get { return ((brush)(this.getvalue(inputproperty))); } set { this.setvalue(inputproperty, value); } } /// <summary>the number of color levels to use.</summary> public double levels { get { return ((double)(this.getvalue(levelsproperty))); } set { this.setvalue(levelsproperty, value); } } } }
6.shaderpantest:测试功能--运用c#动态编译生成来使用effect
public static assembly compileinmemory(string code) { var provider = new csharpcodeprovider(new dictionary<string, string>() { { "compilerversion", "v4.0" } }); compilerparameters options = new compilerparameters(); options.referencedassemblies.add("system.dll"); options.referencedassemblies.add("system.core.dll"); options.referencedassemblies.add("windowsbase.dll"); options.referencedassemblies.add("presentationframework.dll"); options.referencedassemblies.add("presentationcore.dll"); options.includedebuginformation = false; options.generateexecutable = false; options.generateinmemory = true; compilerresults results = provider.compileassemblyfromsource(options, code); provider.dispose(); if (results.errors.count == 0) return results.compiledassembly; else return null; }
7.源码: https://github.com/lenkasetgithub/song_wpf_pixelshader (exe图标来自easyicon)