本文轉自(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 8 { 9 public GLSLTextFormatDefinition() 10 { 11 this.DisplayName = "GLSL文本"; 12 this.ForegroundColor = Colors.Brown; 13 } 14 } 15 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 } 29 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 } 43 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 } 57 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 } 71 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 } 85 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 } 99 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 2 { 3 [Export(typeof(ClassificationTypeDefinition))] 4 [Name("GLSLText")] 5 internal static ClassificationTypeDefinition GLSLTextType = null; 6 7 [Export(typeof(ClassificationTypeDefinition))] 8 [Name("GLSLIdentifier")] 9 internal static ClassificationTypeDefinition GLSLIdentifierType = null; 10 11 [Export(typeof(ClassificationTypeDefinition))] 12 [Name("GLSLComment")] 13 internal static ClassificationTypeDefinition GLSLCommentType = null; 14 15 [Export(typeof(ClassificationTypeDefinition))] 16 [Name("GLSLKeyword")] 17 internal static ClassificationTypeDefinition GLSLKeywordType = null; 18 19 [Export(typeof(ClassificationTypeDefinition))] 20 [Name("GLSLClass")] 21 internal static ClassificationTypeDefinition GLSLClassType = null; 22 23 [Export(typeof(ClassificationTypeDefinition))] 24 [Name("GLSLQualifier")] 25 internal static ClassificationTypeDefinition GLSLQualifierType = null; 26 27 [Export(typeof(ClassificationTypeDefinition))] 28 [Name("GLSLVariable")] 29 internal static ClassificationTypeDefinition GLSLVariableType = null; 30 31 [Export(typeof(ClassificationTypeDefinition))] 32 [Name("GLSLFunction")] 33 internal static ClassificationTypeDefinition GLSLFunctionType = null; 34 }
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 6 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 { 5 6 [Export] 7 [Name("glsl")] 8 [BaseDefinition("code")] 9 internal static ContentTypeDefinition GLSLContentType = null; 10 11 [Export] 12 [FileExtension(".vsh")] 13 [ContentType("glsl")] 14 internal static FileExtensionToContentTypeDefinition GLSLVshType = null; 15 16 [Export] 17 [FileExtension(".fsh")] 18 [ContentType("glsl")] 19 internal static FileExtensionToContentTypeDefinition GLSLFshType = null; 20 21 [Import] 22 internal IClassificationTypeRegistryService classificationTypeRegistry = null; 23 24 [Import] 25 internal IBufferTagAggregatorFactoryService aggregatorFactory = null; 26 27 public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag { 28 return new GLSLClassifier(buffer, classificationTypeRegistry) as ITagger<T>; 29 } 30 }
(這段代碼有問題,請用我改過的)
1 [Export(typeof(ITaggerProvider))] 2 [ContentType("glsl")] 3 [TagType(typeof(ClassificationTag))] 4 internal sealed class GLSLClassifierProvider : ITaggerProvider 5 { 6 7 [Export] 8 [Name("glsl")] 9 [BaseDefinition("code")] 10 internal static ContentTypeDefinition GLSLContentType = null; 11 12 [Export] 13 [FileExtension(".vert")] 14 [ContentType("glsl")] 15 internal static FileExtensionToContentTypeDefinition GLSLVshType = null; 16 17 [Export] 18 [FileExtension(".frag")] 19 [ContentType("glsl")] 20 internal static FileExtensionToContentTypeDefinition GLSLFshType = null; 21 22 [Export] 23 [FileExtension(".geom")] 24 [ContentType("glsl")] 25 internal static FileExtensionToContentTypeDefinition GLSLGshType = null; 26 27 [Import] 28 internal IClassificationTypeRegistryService classificationTypeRegistry = null; 29 30 [Import] 31 internal IBufferTagAggregatorFactoryService aggregatorFactory = null; 32 33 public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag 34 { 35 return new GLSLClassifier(buffer, classificationTypeRegistry) as ITagger<T>; 36 } 37 }
這樣我們就創建了只針對vert、frag和geom文件生效的Editor。也就是頂點Shader、片段Shader和幾何Shader。
6. 使用gplex進行詞法分析
我們需要使用詞法分析掃描器來實現具體的著色功能,gplex可以為我們生成C#語言的掃描器,下載地址:
解壓后在binaries文件夾下找到gplex.exe,把它拷貝到項目的根目錄下。
在項目根目錄下新建一個GLSL文件夾,新建GLSLLexer.lex文件。并把它們添加到proj中。
在proj上右鍵->屬性,在生成事件選項卡中,在預先生成事件命令行中輸入
cd $(ProjectDir)GLSL\
$(ProjectDir)\gplex GLSLLexer
打開GLSLLexer.lex,寫入以下代碼:
1 %option unicode, codepage:raw 2 3 %{ 4 // User code is all now in ScanHelper.cs 5 %} 6 7 %namespace Shane 8 %option verbose, summary, noparser, nofiles, unicode 9 10 %{ 11 public int nextToken() { return yylex(); } 12 public int getPos() { return yypos; } 13 public int getLength() { return yyleng; } 14 %} 15 16 //============================================================= 17 //============================================================= 18 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 "/*"[^*]*\*(\*|([^*/]([^*])*\*))*\/ 27 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}* 35 36 %% 37 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; 45 46 %%
保存并關閉,這時生成一下項目,我們會看到在GLSL目錄下生成了GLSLLexer.cs文件,同樣把這個文件添加到proj中。
如果你很懶,可以直接用這個(點此下載),這是我已經生成了的GLSLLexer.cs。(源碼太大,多次上傳失敗)
7. 處理掃描結果
接下來我們要在ShaderEditor.cs中處理我們的掃描結果,并最終對匹配的代碼行進行著色。
首先刪除默認創建的ShaderEditor類。
添加一個GLSLToken枚舉,這個枚舉就是GLSLLexer.cs返回的枚舉類型,它用來通知我們當前的語句塊是哪個類型。
代碼如下:
1 public enum GLSLTokenType 2 { 3 Text = 1, 4 Keyword, 5 Comment, 6 Identifier, 7 Class, 8 Qualifier, 9 SystemVariable, 10 SystemFunction 11 }
創建我們自己的ShaderEditor類,代碼如下:

1 #region Provider definition 2 [Export(typeof(ITaggerProvider))] 3 [ContentType("glsl")] 4 [TagType(typeof(ClassificationTag))] 5 internal sealed class GLSLClassifierProvider : ITaggerProvider 6 { 7 8 [Export] 9 [Name("glsl")] 10 [BaseDefinition("code")] 11 internal static ContentTypeDefinition GLSLContentType = null; 12 13 [Export] 14 [FileExtension(".vert")] 15 [ContentType("glsl")] 16 internal static FileExtensionToContentTypeDefinition GLSLVshType = null; 17 18 [Export] 19 [FileExtension(".frag")] 20 [ContentType("glsl")] 21 internal static FileExtensionToContentTypeDefinition GLSLFshType = null; 22 23 [Export] 24 [FileExtension(".geom")] 25 [ContentType("glsl")] 26 internal static FileExtensionToContentTypeDefinition GLSLGshType = null; 27 28 [Import] 29 internal IClassificationTypeRegistryService classificationTypeRegistry = null; 30 31 [Import] 32 internal IBufferTagAggregatorFactoryService aggregatorFactory = null; 33 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 40 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 } 57 58 public event EventHandler<SnapshotSpanEventArgs> TagsChanged 59 { 60 add { } 61 remove { } 62 } 63 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 } 95 96 ITextBuffer textBuffer; 97 IDictionary<GLSLTokenType, IClassificationType> typeDic; 98 Scanner scanner = new Scanner(); 99 } 100 #endregion //Classifier
TagsChanged事件保證在代碼發生改變時實時刷新編輯器。
構造方法中,通過typeService.GetClassificationType("GLSLIdentifier")取得我們定義的實例,并把它們和枚舉關聯起來,
GetClassificationType的參數傳入著色類型的Name。
GetTags方法是由系統調用的迭代方法,yield return new TagSpan<ClassificationTag>()返回我們的著色對象,即可實現著色效果。
編譯并運行,可以看到vert、fraghe geom已經有了語法著色了。
如果你實在做不出來,不如去我的Github下載源碼,直接編譯即可。
2016-05-14
根據GLSL4.3更新了GLSL的關鍵字、內置類型、內置變量、內置函數,添加了對“數值”的高亮顯示。
重命名為GLGLHighlight。
為了方便換色,我把常用顏色保存到這里。
文章列表