欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

仿制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();
    }

 仿制shazzam的简单功能,将hlsl转换为WPF中的ShaderEffect

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;
        }
    }

 仿制shazzam的简单功能,将hlsl转换为WPF中的ShaderEffect

生成的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;
    }

仿制shazzam的简单功能,将hlsl转换为WPF中的ShaderEffect

仿制shazzam的简单功能,将hlsl转换为WPF中的ShaderEffect

7.源码: https://github.com/lenkasetgithub/song_wpf_pixelshader (exe图标来自easyicon)