本文转自(
http://www.cnblogs.com/aanbpsd/p/3920067.html
)
原作者
故意写错
了一点东西,这就让那些一点脑筋也不想动的小伙伴得不到想要的结果。我在这里把那些地方纠正过来了。
使用Visual Studio SDK制作GLSL词法着色插件
我们在Visual Studio上开发OpenGL ES项目时,避免不了写Shader。这时在vs里直接编辑shader就会显得很方便。但是vs默认是不支持GLSL的语法着色的,我们只好自己动手创造。最简单的实现自定义语法着色的方法就是
创建一个VSIX插件包
,我们只需要安装Visual Studio SDK,
使用内置的模版
就可以构建一个
插件项目
。
1. 安装Visual Studio SDK
在
http://www.microsoft.com/en-us/download/details.aspx?id=40758
下载最新的Visual Studio 2013 SDK。
双击安装,一路next即可。
安装完毕后我们可以在新建项目->模版->C#中看到“扩展性”这一条目,这些就是开发插件用的模版了。
2. 创建插件项目
新建项目,在扩展性标签中,选择
Editor Classifier
模版,命名为ShaderEditor,点击确定。
Visual Studio为我们生成了如下几个文件。
ShaderEditorFormat.cs文件的默认代码如下:
1 [Export(typeof(EditorFormatDefinition))]
2 [ClassificationType(ClassificationTypeNames = "ShaderEditor")]
3 [Name("ShaderEditor")]
4 [UserVisible(true)] //this should be visible to the end user
5 [Order(Before = Priority.Default)] //set the priority to be after the default classifiers
6 internal sealed class ShaderEditorFormat : ClassificationFormatDefinition {
7 /// <summary>
8 /// Defines the visual format for the "ShaderEditor" classification type
9 /// </summary>
10 public ShaderEditorFormat() {
11 this.DisplayName = "ShaderEditor"; //human readable version of the name
12 this.BackgroundColor = Colors.BlueViolet;
13 this.TextDecorations = System.Windows.TextDecorations.Underline;
14 }
15 }
这段代码定义了一个名为"ShaderEditor"的着色类型,编译工程并运行,我们可以在Visual Studio实验实例的工具->选项->字体和颜色中找到一个名为"ShaderEditor"的条目。同时我们会发现所有文本文件的颜色都变成了Colors.BlueViolet并带上了下划线。修改this.DisplayName = "ShaderEditor"的内容,可以改变在字体和颜色中显示的名字。下面的格式设置可以任意修改成喜欢的样式,但要注意在这里的格式只是插件首次安装时的默认设置,这些条目和其它着色选项一样,都可以被用户任意更改。
3. 创建GLSL的着色类型
我们已经了解了如何将着色类型添加到Visual Studio,现在修改
ShaderEditorFormat
.cs,添加我们的着色类型。
1 #region Format definition
2 [Export(typeof(EditorFormatDefinition))]
3 [ClassificationType(ClassificationTypeNames = "GLSLText")]
4 [Name("GLSLText")]
5 [UserVisible(true)]
6 [Order(Before = Priority.Default)]
7 internal sealed class GLSLTextFormatDefinition : ClassificationFormatDefinition
9 public GLSLTextFormatDefinition()
10 {
11 this.DisplayName = "GLSL文本";
12 this.ForegroundColor = Colors.Brown;
13 }
14 }
16 [Export(typeof(EditorFormatDefinition))]
17 [ClassificationType(ClassificationTypeNames = "GLSLIdentifier")]
18 [Name("GLSLIdentifier")]
19 [UserVisible(true)]
20 [Order(Before = Priority.Default)]
21 internal sealed class GLSLIdentifierFormatDefinition : ClassificationFormatDefinition
22 {
23 public GLSLIdentifierFormatDefinition()
24 {
25 this.DisplayName = "GLSL标识符";
26 this.ForegroundColor = Colors.Brown;
27 }
28 }
30 [Export(typeof(EditorFormatDefinition))]
31 [ClassificationType(ClassificationTypeNames = "GLSLComment")]
32 [Name("GLSLComment")]
33 [UserVisible(true)]
34 [Order(Before = Priority.Default)]
35 internal sealed class GLSLCommentFormatDefinition : ClassificationFormatDefinition
36 {
37 public GLSLCommentFormatDefinition()
38 {
39 this.DisplayName = "GLSL注释";
40 this.ForegroundColor = Colors.DarkGray;
41 }
42 }
44 [Export(typeof(EditorFormatDefinition))]
45 [ClassificationType(ClassificationTypeNames = "GLSLKeyword")]
46 [Name("GLSLKeyword")]
47 [UserVisible(true)]
48 [Order(Before = Priority.Default)]
49 internal sealed class GLSLKeywordFormatDefinition : ClassificationFormatDefinition
50 {
51 public GLSLKeywordFormatDefinition()
52 {
53 this.DisplayName = "GLSL关键字";
54 this.ForegroundColor = Colors.Blue;
55 }
56 }
58 [Export(typeof(EditorFormatDefinition))]
59 [ClassificationType(ClassificationTypeNames = "GLSLClass")]
60 [Name("GLSLClass")]
61 [UserVisible(true)]
62 [Order(Before = Priority.Default)]
63 internal sealed class GLSLClassFormatDefinition : ClassificationFormatDefinition
64 {
65 public GLSLClassFormatDefinition()
66 {
67 this.DisplayName = "GLSL类型";
68 this.ForegroundColor = Colors.Green;
69 }
70 }
72 [Export(typeof(EditorFormatDefinition))]
73 [ClassificationType(ClassificationTypeNames = "GLSLQualifier")]
74 [Name("GLSLQualifier")]
75 [UserVisible(true)]
76 [Order(Before = Priority.Default)]
77 internal sealed class GLSLQualifierFormatDefinition : ClassificationFormatDefinition
78 {
79 public GLSLQualifierFormatDefinition()
80 {
81 this.DisplayName = "GLSL限定符";
82 this.ForegroundColor = Colors.Pink;
83 }
84 }
86 [Export(typeof(EditorFormatDefinition))]
87 [ClassificationType(ClassificationTypeNames = "GLSLVariable")]
88 [Name("GLSLVariable")]
89 [UserVisible(true)]
90 [Order(Before = Priority.Default)]
91 internal sealed class GLSLVariableFormatDefinition : ClassificationFormatDefinition
92 {
93 public GLSLVariableFormatDefinition()
94 {
95 this.DisplayName = "GLSL系统变量";
96 this.ForegroundColor = Colors.DarkOrange;
97 }
98 }
100 [Export(typeof(EditorFormatDefinition))]
101 [ClassificationType(ClassificationTypeNames = "GLSLFunction")]
102 [Name("GLSLFunction")]
103 [UserVisible(true)]
104 [Order(Before = Priority.Default)]
105 internal sealed class GLSLFunctionFormatDefinition : ClassificationFormatDefinition
106 {
107 public GLSLFunctionFormatDefinition()
108 {
109 this.DisplayName = "GLSL系统函数";
110 this.ForegroundColor = Colors.DarkTurquoise;
111 }
112 }
113 #endregion //Format definition
4. 导出着色类型
Editor Classifier使用了MEF框架,关于MEF的具体细节,请参考MSDN的相关文档。
我们需要注意的是,在MEF中,光定义了着色类型还不够,我们需要导出一个ClassificationTypeDefinition,才能在系统中生效。
打开
ShaderEditorType
.cs,我们看到系统生成的代码如下:
1 internal static class ShaderEditorClassificationDefinition {
2 [Export(typeof(ClassificationTypeDefinition))]
3 [Name("ShaderEditor")]
4 internal static ClassificationTypeDefinition ShaderEditorType = null;
5 }
这里的Name与之前默认生成的ShaderEditor相同,同理,我们将这里的代码修改成方才定义的类型
1 internal static class ShaderEditorClassificationDefinition
3 [Export(typeof(ClassificationTypeDefinition))]
4 [Name("GLSLText")]
5 internal static ClassificationTypeDefinition GLSLTextType = null;
7 [Export(typeof(ClassificationTypeDefinition))]
8 [Name("GLSLIdentifier")]
9 internal static ClassificationTypeDefinition GLSLIdentifierType = null;
11 [Export(typeof(ClassificationTypeDefinition))]
12 [Name("GLSLComment")]
13 internal static ClassificationTypeDefinition GLSLCommentType = null;
15 [Export(typeof(ClassificationTypeDefinition))]
16 [Name("GLSLKeyword")]
17 internal static ClassificationTypeDefinition GLSLKeywordType = null;
19 [Export(typeof(ClassificationTypeDefinition))]
20 [Name("GLSLClass")]
21 internal static ClassificationTypeDefinition GLSLClassType = null;
23 [Export(typeof(ClassificationTypeDefinition))]
24 [Name("GLSLQualifier")]
25 internal static ClassificationTypeDefinition GLSLQualifierType = null;
27 [Export(typeof(ClassificationTypeDefinition))]
28 [Name("GLSLVariable")]
29 internal static ClassificationTypeDefinition GLSLVariableType = null;
31 [Export(typeof(ClassificationTypeDefinition))]
32 [Name("GLSLFunction")]
33 internal static ClassificationTypeDefinition GLSLFunctionType = null;
5. 关联文件类型
打开ShaderEditor.cs
1 [Export(typeof(IClassifierProvider))]
2 [ContentType("text")]
3 internal class ShaderEditorProvider : IClassifierProvider {
4 [Import]
5 internal IClassificationTypeRegistryService ClassificationRegistry = null; // Set via MEF
7 public IClassifier GetClassifier(ITextBuffer buffer) {
8 return buffer.Properties.GetOrCreateSingletonProperty<ShaderEditor>(delegate { return new ShaderEditor(ClassificationRegistry); });
9 }
10 }
代码ContentType("text")创建了一个Provider并将它们对所有text类型的文件生效。
GLSL主要的文件扩展名为.vsh和.fsh,为了只对这两个扩展名生效,我们需要自定义一个ContentType,并创建两个扩展名关联。将上述代码修改为:
1 [Export(typeof(ITaggerProvider))]
2 [ContentType("glsl")]
3 [TagType(typeof(ClassificationTag))]
4 internal sealed class GLSLClassifierProvider : ITaggerProvider {
6 [Export]
7 [Name("glsl")]
8 [BaseDefinition("code")]
9 internal static ContentTypeDefinition GLSLContentType = null;
11 [Export]
12 [FileExtension(".vsh")]
13 [ContentType("glsl")]
14 internal static FileExtensionToContentTypeDefinition GLSLVshType = null;
16 [Export]
17 [FileExtension(".fsh")]
18 [ContentType("glsl")]
19 internal static FileExtensionToContentTypeDefinition GLSLFshType = null;
21 [Import]
22 internal IClassificationTypeRegistryService classificationTypeRegistry = null;
24 [Import]
25 internal IBufferTagAggregatorFactoryService aggregatorFactory = null;
27 public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag {
28 return new GLSLClassifier(buffer, classificationTypeRegistry) as ITagger<T>;
29 }
(这段代码有问题,请用我改过的)
1 [Export(typeof(ITaggerProvider))]
2 [ContentType("glsl")]
3 [TagType(typeof(ClassificationTag))]
4 internal sealed class GLSLClassifierProvider : ITaggerProvider
7 [Export]
8 [Name("glsl")]
9 [BaseDefinition("code")]
10 internal static ContentTypeDefinition GLSLContentType = null;
12 [Export]
13 [FileExtension(".vert")]
14 [ContentType("glsl")]
15 internal static FileExtensionToContentTypeDefinition GLSLVshType = null;
17 [Export]
18 [FileExtension(".frag")]
19 [ContentType("glsl")]
20 internal static FileExtensionToContentTypeDefinition GLSLFshType = null;
22 [Export]
23 [FileExtension(".geom")]
24 [ContentType("glsl")]
25 internal static FileExtensionToContentTypeDefinition GLSLGshType = null;
27 [Import]
28 internal IClassificationTypeRegistryService classificationTypeRegistry = null;
30 [Import]
31 internal IBufferTagAggregatorFactoryService aggregatorFactory = null;
33 public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
34 {
35 return new GLSLClassifier(buffer, classificationTypeRegistry) as ITagger<T>;
36 }
这样我们就创建了只针对vert、frag和geom文件生效的Editor。也就是顶点Shader、片段Shader和几何Shader。
6. 使用gplex进行词法分析
我们需要使用词法分析扫描器来实现具体的着色功能,gplex可以为我们生成C#语言的扫描器,下载地址:
http://gplex.codeplex.com/
解压后在binaries文件夹下找到gplex.exe,把它拷贝到项目的根目录下。
在项目根目录下新建一个GLSL文件夹,新建GLSLLexer.lex文件。并把它们添加到proj中。
在proj上右键->属性,在生成事件选项卡中,在预先生成事件命令行中输入
cd $(ProjectDir)GLSL\
$(ProjectDir)\gplex GLSLLexer
打开GLSLLexer.lex,写入以下代码:
1 %option unicode, codepage:raw
4 // User code is all now in ScanHelper.cs
7 %namespace Shane
8 %option verbose, summary, noparser, nofiles, unicode
10 %{
11 public int nextToken() { return yylex(); }
12 public int getPos() { return yypos; }
13 public int getLength() { return yyleng; }
14 %}
16 //=============================================================
17 //=============================================================
19 number ([0-9])+
20 chars [A-Za-z]
21 cstring [A-Za-z_]
22 blank " "
23 delim [ \t\n]
24 word {chars}+
25 singleLineComment "//"[^\n]*
26 multiLineComment "/*"[^*]*\*(\*|([^*/]([^*])*\*))*\/
28 comment {multiLineComment}|{singleLineComment}
29 class bool|int|float|bvec|ivec|vec|vec2|vec3|vec4|mat2|mat3|mat4|sampler1D|sampler2D|sampler3D|samplerCube|sampler1DShadow|sampler2DShadow
30 keyword return|if|else|while|do|for|foreach|break|continue|switch|case|default|goto|class|struct|enum|extern|interface|namespace|public|static|this|volatile|using|in|out|true|false
31 qualifier const|attribute|uniform|varying
32 systemVariable gl_BackColor|gl_BackLightModelProduct|gl_BackLightProduct|gl_BackMaterial|gl_BackSecondaryColor|gl_ClipPlane|gl_ClipVertex|gl_Color|gl_DepthRange|gl_DepthRangeParameters|gl_EyePlaneQ|gl_EyePlaneR|gl_EyePlaneS|gl_EyePlaneT|gl_Fog|gl_FogCoord|gl_FogFragCoord|gl_FogParameters|gl_FragColor|gl_FragCoord|gl_FragData|gl_FragDepth|gl_FrontColor|gl_FrontFacing|gl_FrontLightModelProduct|gl_FrontLightProduct|gl_FrontMaterial|gl_FrontSecondaryColor|gl_LightModel|gl_LightModelParameters|gl_LightModelProducts|gl_LightProducts|gl_LightSource|gl_LightSourceParameters|gl_MaterialParameters|gl_MaxClipPlanes|gl_MaxCombinedTextureImageUnits|gl_MaxDrawBuffers|gl_MaxFragmentUniformComponents|gl_MaxLights|gl_MaxTextureCoords|gl_MaxTextureImageUnits|gl_MaxTextureUnits|gl_MaxVaryingFloats|gl_MaxVertexAttribs|gl_MaxVertexTextureImageUnits|gl_MaxVertexUniformComponents|gl_ModelViewMatrix|gl_ModelViewMatrixInverse|gl_ModelViewMatrixInverseTranspose|gl_ModelViewMatrixTranspose|gl_ModelViewProjectionMatrix|gl_ModelViewProjectionMatrixInverse|gl_ModelViewProjectionMatrixInverseTranspose|gl_ModelViewProjectionMatrixTranspose|gl_MultiTexCoord0|gl_MultiTexCoord1|gl_MultiTexCoord10|gl_MultiTexCoord11|gl_MultiTexCoord2|gl_MultiTexCoord3|gl_MultiTexCoord4|gl_MultiTexCoord5|gl_MultiTexCoord6|gl_MultiTexCoord7|gl_MultiTexCoord8|gl_MultiTexCoord9|gl_Normal|gl_NormalMatrix|gl_NormalScale|gl_ObjectPlaneQ|gl_ObjectPlaneR|gl_ObjectPlaneS|gl_ObjectPlaneT|gl_Point|gl_PointParameters|gl_PointSize|gl_Position|gl_ProjectionMatrix|gl_ProjectionMatrixInverse|gl_ProjectionMatrixInverseTranspose|gl_ProjectionMatrixTranspose|gl_SecondaryColor|gl_TexCoord|gl_TextureEnvColor|gl_TextureMatrix|gl_TextureMatrixInverse|gl_TextureMatrixInverseTranspose|gl_TextureMatrixTranspose|gl_Vertex
33 systemFunction radians|degress|sin|cos|tan|asin|acos|atan|pow|exp|log|exp2|log2|sqrt|inversesqrt|abs|sign|floor|ceil|fract|mod|min|max|clamp|mix|step|smoothstep|length|distance|dot|cross|normalize|faceforward|reflect|matrixCompMult|lessThan|lessThanEqual|greaterThan|greaterThanEqual|equal|notEqual|any|all|not|texture2D|texture2DProj|texture2DLod|texture2DProjLod|textureCube|textureCubeLod
34 identifier {cstring}+{number}*[{cstring}@]*{number}*
36 %%
38 {keyword} return (int)GLSLTokenType.Keyword;
39 {class} return (int)GLSLTokenType.Class;
40 {qualifier} return (int)GLSLTokenType.Qualifier;
41 {systemVariable} return (int)GLSLTokenType.SystemVariable;
42 {systemFunction} return (int)GLSLTokenType.SystemFunction;
43 {identifier} return (int)GLSLTokenType.Identifier;
44 {comment} return (int)GLSLTokenType.Comment;
46 %%
保存并关闭,这时生成一下项目,我们会看到在GLSL目录下生成了GLSLLexer.cs文件,同样把这个文件添加到proj中。
如果你很懒,可以直接用这个(点此下载),这是我已经生成了的GLSLLexer.cs。(源码太大,多次上传失败)
7. 处理扫描结果
接下来我们要在ShaderEditor.cs中处理我们的扫描结果,并最终对匹配的代码行进行着色。
首先删除默认创建的ShaderEditor类。
添加一个GLSLToken枚举,这个枚举就是GLSLLexer.cs返回的枚举类型,它用来通知我们当前的语句块是哪个类型。
代码如下:
1 public enum GLSLTokenType
3 Text = 1,
4 Keyword,
5 Comment,
6 Identifier,
7 Class,
8 Qualifier,
9 SystemVariable,
10 SystemFunction
创建我们自己的ShaderEditor类,代码如下:
1 #region Provider definition
2 [Export(typeof(ITaggerProvider))]
3 [ContentType("glsl")]
4 [TagType(typeof(ClassificationTag))]
5 internal sealed class GLSLClassifierProvider : ITaggerProvider
8 [Export]
9 [Name("glsl")]
10 [BaseDefinition("code")]
11 internal static ContentTypeDefinition GLSLContentType = null;
13 [Export]
14 [FileExtension(".vert")]
15 [ContentType("glsl")]
16 internal static FileExtensionToContentTypeDefinition GLSLVshType = null;
18 [Export]
19 [FileExtension(".frag")]
20 [ContentType("glsl")]
21 internal static FileExtensionToContentTypeDefinition GLSLFshType = null;
23 [Export]
24 [FileExtension(".geom")]
25 [ContentType("glsl")]
26 internal static FileExtensionToContentTypeDefinition GLSLGshType = null;
28 [Import]
29 internal IClassificationTypeRegistryService classificationTypeRegistry = null;
31 [Import]
32 internal IBufferTagAggregatorFactoryService aggregatorFactory = null;
34 public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
35 {
36 return new GLSLClassifier(buffer, classificationTypeRegistry) as ITagger<T>;
37 }
38 }
39 #endregion //provider def
41 #region Classifier
42 internal sealed class GLSLClassifier : ITagger<ClassificationTag>
43 {
44 internal GLSLClassifier(ITextBuffer buffer, IClassificationTypeRegistryService typeService)
45 {
46 textBuffer = buffer;
47 typeDic = new Dictionary<GLSLTokenType, IClassificationType>();
48 typeDic[GLSLTokenType.Text] = typeService.GetClassificationType("GLSLText");
49 typeDic[GLSLTokenType.Identifier] = typeService.GetClassificationType("GLSLIdentifier");
50 typeDic[GLSLTokenType.Keyword] = typeService.GetClassificationType("GLSLKeyword");
51 typeDic[GLSLTokenType.Class] = typeService.GetClassificationType("GLSLClass");
52 typeDic[GLSLTokenType.Comment] = typeService.GetClassificationType("GLSLComment");
53 typeDic[GLSLTokenType.Qualifier] = typeService.GetClassificationType("GLSLQualifier");
54 typeDic[GLSLTokenType.SystemVariable] = typeService.GetClassificationType("GLSLVariable");
55 typeDic[GLSLTokenType.SystemFunction] = typeService.GetClassificationType("GLSLFunction");
56 }
58 public event EventHandler<SnapshotSpanEventArgs> TagsChanged
59 {
60 add { }
61 remove { }
62 }
64 public IEnumerable<ITagSpan<ClassificationTag>> GetTags(NormalizedSnapshotSpanCollection spans)
65 {
66 IClassificationType classification = typeDic[GLSLTokenType.Text];
67 string text = spans[0].Snapshot.GetText();
68 yield return new TagSpan<ClassificationTag>(
69 new SnapshotSpan(spans[0].Snapshot, new Span(0, text.Length)),
70 new ClassificationTag(classification));
71 scanner.SetSource(text, 0);
72 int tok;
73 do
74 {
75 tok = scanner.nextToken();
76 int pos = scanner.getPos();
77 int len = scanner.getLength();
78 int total = text.Length;
79 if (pos < 0 || len < 0 || pos > total)
80 {
81 continue;
82 }
83 if (pos + len > total)
84 {
85 len = total - pos;
86 }
87 if (typeDic.TryGetValue((GLSLTokenType)tok, out classification))
88 {
89 yield return new TagSpan<ClassificationTag>(
90 new SnapshotSpan(spans[0].Snapshot, new Span(pos, len)),
91 new ClassificationTag(classification));
92 }
93 } while (tok > (int)Tokens.EOF);
94 }
96 ITextBuffer textBuffer;
97 IDictionary<GLSLTokenType, IClassificationType> typeDic;
98 Scanner scanner = new Scanner();
99 }
100 #endregion //Classifier
GLSLClassifierProvider
TagsChanged事件保证在代码发生改变时实时刷新编辑器。
构造方法中,通过typeService.GetClassificationType("GLSLIdentifier")取得我们定义的实例,并把它们和枚举关联起来,
GetClassificationType的参数传入着色类型的Name。
GetTags方法是由系统调用的迭代方法,yield return new TagSpan<ClassificationTag>()返回我们的着色对象,即可实现着色效果。
编译并运行,可以看到vert、fraghe geom已经有了语法着色了。
如果你实在做不出来,不如去我的Github下载源码,直接编译即可。
2016-05-14
根据GLSL4.3更新了GLSL的关键字、内置类型、内置变量、内置函数,添加了对“数值”的高亮显示。
重命名为GLGLHighlight。
为了方便换色,我把常用颜色保存到这里。
如果您愿意花几块钱请我喝杯茶的话,可以用手机扫描下方的二维码,通过微信捐赠。我会努力写出更好的文章。
(微信捐赠不显示捐赠者的个人信息,如需要,请注明您的联系方式(微信留言只显示10个汉字))
Thank you for your kindly donation!
微信捐赠二维码:
Donate by microMsg: