CSharpGL(1)從最簡單的例子開始使用CSharpGL


CSharpGL(1)從最簡單的例子開始使用CSharpGL

 

2016-08-13

由於CSharpGL一直在更新,現在這個教程已經不適用最新的代碼了。CSharpGL源碼中包含20多個獨立的Demo,更適合入門參考。

為了盡可能提升渲染效率,CSharpGL是面向Shader的,因此稍有難度。

主要內容

在VS2013中使用設計好的控件GLCanvas。

借助GLCanvas,用legacy OpenGL繪制一個四面體。

借助GLCanvas,用modern OpenGL繪制一個四面體。

+BIT祝威+悄悄在此留下版了個權的信說:

下載

您可以在(https://github.com/bitzhuwei/CSharpGL)找到最新的源碼。歡迎感興趣的同學fork之。

如果您不會用GitHub,可以點此(https://github.com/bitzhuwei/CSharpGL/archive/master.zip)下載zip包。

使用GLCanvas

打開CSharpGL

萬事開頭難,你在下載打開CSharpGL后,應該能看到下圖所示的解決方案。打開CSharpGL.Winforms.Demo項目下的FormPyramidVAOElement,會看到一個窗口里的四面體在慢慢旋轉。這就是用OpenGL繪制的。

 

新建Winform項目

為了演示全部過程,我們新建一個項目"HelloCSharpGL"。

剛剛新建的項目如下圖所示。

添加引用

我們需要添加對CSharpGL各個類庫的引用,如下圖所示。

如下圖所示,添加這么幾個類庫:

Utilities:含有一些輔助類型。

CSharpGL:封裝了OpenGL指令。

CSharpGL.Maths:封裝了對矩陣和向量的操作。

CSharpGL.Objects:含有Camera、RenderContext、Shader、SceneElement、Picking、UI等類型。

CSharpGL.Winforms:含有GLCanvas控件。

這幾個庫都是必須的。

使用GLCanvas控件

此時,打開"工具箱",就會看到GLCanvas控件。

把GLCanvas控件拖拽到Form1窗體上,並設置其Anchor屬性。

下面,我們先編譯一下。

編譯成功之后,關閉Form1,然后再次打開Form1,你會看到本篇最開頭所示的GLCanvas控件中出現一個旋轉的四面體。

注意,這只是在設計階段的效果,在運行時並不會顯示任何內容。不信的話,現在我們把HelloCSharpGL項目設為啟動項。

然后,點擊"啟動",我們來看看啟動后的程序是什么效果。

你會看到一個漆黑的窗口。此時GLCanvas並沒有繪制任何內容。

這樣,GLCanvas就成功添加到窗口中了。

下面我們分別說明如何用legacy OpenGL和modern OpenGL繪圖。

 

+BIT祝威+悄悄在此留下版了個權的信說:

用legacy OpenGL繪制一個四面體

添加代碼:繪制四面體

繼續上文所述,雙擊Form1中的GLCanvas控件,進入"OpenGLDraw"事件代碼。編寫下面所述的代碼。

 1         private void glCanvas1_OpenGLDraw(object sender, PaintEventArgs e)  2  {  3             // Clear the color and depth buffer.
 4             GL.Clear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);  5 
 6  DrawPyramid();  7  }  8 
 9         public static void DrawPyramid() 10  { 11             // Load the identity matrix.
12  GL.LoadIdentity(); 13 
14             // Rotate around the Y axis.
15             GL.Rotate(rotation, 0.0f, 1.0f, 0.0f); 16 
17             // Draw a coloured pyramid.
18  GL.Begin(GL.GL_TRIANGLES); 19             GL.Color(1.0f, 0.0f, 0.0f); 20             GL.Vertex(0.0f, 1.0f, 0.0f); 21             GL.Color(0.0f, 1.0f, 0.0f); 22             GL.Vertex(-1.0f, -1.0f, 1.0f); 23             GL.Color(0.0f, 0.0f, 1.0f); 24             GL.Vertex(1.0f, -1.0f, 1.0f); 25             GL.Color(1.0f, 0.0f, 0.0f); 26             GL.Vertex(0.0f, 1.0f, 0.0f); 27             GL.Color(0.0f, 0.0f, 1.0f); 28             GL.Vertex(1.0f, -1.0f, 1.0f); 29             GL.Color(0.0f, 1.0f, 0.0f); 30             GL.Vertex(1.0f, -1.0f, -1.0f); 31             GL.Color(1.0f, 0.0f, 0.0f); 32             GL.Vertex(0.0f, 1.0f, 0.0f); 33             GL.Color(0.0f, 1.0f, 0.0f); 34             GL.Vertex(1.0f, -1.0f, -1.0f); 35             GL.Color(0.0f, 0.0f, 1.0f); 36             GL.Vertex(-1.0f, -1.0f, -1.0f); 37             GL.Color(1.0f, 0.0f, 0.0f); 38             GL.Vertex(0.0f, 1.0f, 0.0f); 39             GL.Color(0.0f, 0.0f, 1.0f); 40             GL.Vertex(-1.0f, -1.0f, -1.0f); 41             GL.Color(0.0f, 1.0f, 0.0f); 42             GL.Vertex(-1.0f, -1.0f, 1.0f); 43  GL.End(); 44 
45             rotation += 3.0f; 46  } 47 
48         private double rotation;

 

查看效果

我們已經添加了用legacy OpenGL繪制四面體的代碼,現在就編譯運行起來看看效果。

可以看到,確實繪制出了一個四面體。不過它距離窗口太近了,以至於一部分內容被削去了。下面我們把它放到合適的位置去。更准確地說,是把Camera移動到合適的位置去。

添加代碼:設定投影矩陣和視圖矩陣

為GLCanvas控件的Resize事件添加代碼。

在GLCanvas調整大小時,就會自動調用Resize事件,所以在這里調整投影矩陣和視圖矩陣是最合適的。

視圖矩陣指定了Camera的位置、朝向和上方。投影矩陣指定了Camera的透視模式和拍攝范圍。

 1         private void glCanvas1_Resize(object sender, EventArgs e)  2  {  3  ResizeGL(glCanvas1.Width, glCanvas1.Height);  4  }  5         void ResizeGL(double width, double height)  6  {  7             // Set the projection matrix.
 8  GL.MatrixMode(GL.GL_PROJECTION);  9 
10             // Load the identity.
11  GL.LoadIdentity(); 12 
13             // Create a perspective transformation.
14             GL.gluPerspective(60.0f, width / height, 0.01, 100.0); 15 
16             // Use the 'look at' helper function to position and aim the camera.
17             GL.gluLookAt(-5, 5, -5, 0, 0, 0, 0, 1, 0); 18 
19             // Set the modelview matrix.
20  GL.MatrixMode(GL.GL_MODELVIEW); 21         }

 

查看效果

現在再次編譯運行,可以看到效果如下。

 

Legacy OpenGL繪制四面體到此就成功完成了。可以看到這是十分簡單的,拖拽一個GLCanvas控件,在"OpenGLDraw"事件里繪制模型,在"Resize"事件里調整Camera。就這么點事。

Legacy OpenGL的缺點是,當模型的頂點很多時,需要頻繁調用glVertex(還有glColor、glTexCoord等),這樣的執行效率是很低的。下面要講的modern OpenGL就可以大幅提升渲染效率。

+BIT祝威+悄悄在此留下版了個權的信說:

用modern OpenGL繪制一個四面體

用modern OpenGL需要准備的東西比較多,我們一個一個來。

准備一個窗體

我們新建一個窗體來演示modern OpenGL的寫法。

新建的窗體名就叫做"FormModernOpenGL"。

然后也拖拽一個GLCanvas給FormModernOpenGL。也設置好Anchor屬性。

准備PyramidDemo

我們添加一個PyramidDemo類,用於加載shader、四面體模型和渲染操作。

我們暫時先不實現PyramidDemo,就讓它占個坑位。

 

准備vertex shader

Modern OpenGL需要用GLSL編寫的shader進行渲染。其中必不可少的是vertex shader和fragment shader。現在來准備vertex shader。

shader本質是一個供GPU使用的源代碼,所以用"文本文件"即可。Vertex shader命名為"PyramidDemo.vert"。

PyramidDemo.vert內容如下:

 1 #version 150 core  2 
 3 in vec3 in_Position;  4 in vec3 in_Color;  5 out vec4 pass_Color;  6 
 7 uniform mat4 MVP;  8 
 9 void main(void) 10 { 11     gl_Position = MVP * vec4(in_Position, 1.0);// setup vertex's position
12 
13     pass_Color = vec4(in_Color, 1.0);// pass color to fragment shader
14 }

注意:shader里即使是注釋也不能有中文字符,否則會出現編譯錯誤。也許以后的OpenGL版本會改善這一點。

准備fragment shader

同理准備fragment shader。

Fragment shader內容如下:

1 #version 150 core 2 
3 in vec4 pass_Color; 4 out vec4 out_Color;// any name for 'out_Color' is OK, but make sure it's a 'out vec4'
5 
6 void main(void) 7 { 8     out_Color = pass_Color;// setup color for this fragment
9 }

 

+BIT祝威+悄悄在此留下版了個權的信說:

設置shader文件屬性

為了使用shader文件,我們需要設置一下shader文件的屬性。

設置"復制到輸出目錄"屬性為"如果較新則復制"。

Shader類和ShaderProgram類

有了shader的源代碼,現在我們來加載shader。這就需要添加一個Shader類和一個ShaderProgram類。

Shader類用於加載一個Shader(vertex shader或fragment shader)

 1     /// <summary>
 2     /// This is the base class for all shaders (vertex and fragment). It offers functionality  3     /// which is core to all shaders, such as file loading and binding.  4     /// </summary>
 5     public class Shader  6  {  7         public void Create(uint shaderType, string source)  8  {  9             // Create the OpenGL shader object.
10             ShaderObject = GL.CreateShader(shaderType); 11 
12             // Set the shader source.
13  GL.ShaderSource(ShaderObject, source); 14 
15             // Compile the shader object.
16  GL.CompileShader(ShaderObject); 17 
18             // Now that we've compiled the shader, check it's compilation status. If it's not compiled properly, we're 19             // going to throw an exception.
20             if (GetCompileStatus() == false) 21  { 22                 string log = GetInfoLog(); 23                 throw new ShaderCompilationException(string.Format("Failed to compile shader with ID {0}.", ShaderObject), log); 24  } 25  } 26 
27         public void Delete() 28  { 29  GL.DeleteShader(ShaderObject); 30             ShaderObject = 0; 31  } 32 
33         public bool GetCompileStatus() 34  { 35             int[] parameters = new int[] { 0 }; 36  GL.GetShader(ShaderObject, GL.GL_COMPILE_STATUS, parameters); 37             return parameters[0] == GL.GL_TRUE; 38  } 39 
40         public string GetInfoLog() 41  { 42             // Get the info log length.
43             int[] infoLength = new int[] { 0 }; 44  GL.GetShader(ShaderObject, 45  GL.GL_INFO_LOG_LENGTH, infoLength); 46             int bufSize = infoLength[0]; 47 
48             // Get the compile info.
49             StringBuilder il = new StringBuilder(bufSize); 50  GL.GetShaderInfoLog(ShaderObject, bufSize, IntPtr.Zero, il); 51 
52             string log = il.ToString(); 53             return log; 54  } 55 
56         /// <summary>
57         /// Gets the shader object. 58         /// </summary>
59         public uint ShaderObject { get; protected set; } 60     }
Shader

 

ShaderProgram類用於加載ShaderProgram。

 1     public class ShaderProgram  2  {  3         private readonly Shader vertexShader = new Shader();  4         private readonly Shader fragmentShader = new Shader();  5 
 6         /// <summary>
 7         /// Creates the shader program.  8         /// </summary>
 9         /// <param name="vertexShaderSource">The vertex shader source.</param>
 10         /// <param name="fragmentShaderSource">The fragment shader source.</param>
 11         /// <param name="attributeLocations">The attribute locations. This is an optional array of  12         /// uint attribute locations to their names.</param>
 13         /// <exception cref="ShaderCompilationException"></exception>
 14         public void Create(string vertexShaderSource, string fragmentShaderSource,  15             Dictionary<uint, string> attributeLocations)  16  {  17             // Create the shaders.
 18  vertexShader.Create(GL.GL_VERTEX_SHADER, vertexShaderSource);  19  fragmentShader.Create(GL.GL_FRAGMENT_SHADER, fragmentShaderSource);  20 
 21             // Create the program, attach the shaders.
 22             ShaderProgramObject = GL.CreateProgram();  23  GL.AttachShader(ShaderProgramObject, vertexShader.ShaderObject);  24  GL.AttachShader(ShaderProgramObject, fragmentShader.ShaderObject);  25 
 26             // Before we link, bind any vertex attribute locations.
 27             if (attributeLocations != null)  28  {  29                 foreach (var vertexAttributeLocation in attributeLocations)  30  GL.BindAttribLocation(ShaderProgramObject, vertexAttributeLocation.Key, vertexAttributeLocation.Value);  31  }  32 
 33             // Now we can link the program.
 34  GL.LinkProgram(ShaderProgramObject);  35 
 36             // Now that we've compiled and linked the shader, check it's link status. If it's not linked properly, we're  37             // going to throw an exception.
 38             if (GetLinkStatus() == false)  39  {  40                 throw new ShaderCompilationException(string.Format("Failed to link shader program with ID {0}.", ShaderProgramObject), GetInfoLog());  41  }  42  }  43 
 44         public void Delete()  45  {  46  GL.DetachShader(ShaderProgramObject, vertexShader.ShaderObject);  47  GL.DetachShader(ShaderProgramObject, fragmentShader.ShaderObject);  48  vertexShader.Delete();  49  fragmentShader.Delete();  50  GL.DeleteProgram(ShaderProgramObject);  51             ShaderProgramObject = 0;  52  }  53 
 54         public uint GetAttributeLocation(string attributeName)  55  {  56             // If we don't have the attribute name in the dictionary, get it's  57             // location and add it.
 58             if (attributeNamesToLocations.ContainsKey(attributeName) == false)  59  {  60                 int location = GL.GetAttribLocation(ShaderProgramObject, attributeName);  61                 if (location < 0) { throw new Exception(); }  62 
 63                 attributeNamesToLocations[attributeName] = (uint)location;  64  }  65 
 66             // Return the attribute location.
 67             return attributeNamesToLocations[attributeName];  68  }  69 
 70         public void BindAttributeLocation(uint location, string attribute)  71  {  72  GL.BindAttribLocation(ShaderProgramObject, location, attribute);  73  }  74 
 75         public void Bind()  76  {  77  GL.UseProgram(ShaderProgramObject);  78  }  79 
 80         public void Unbind()  81  {  82             GL.UseProgram(0);  83  }  84 
 85         public bool GetLinkStatus()  86  {  87             int[] parameters = new int[] { 0 };  88  GL.GetProgram(ShaderProgramObject, GL.GL_LINK_STATUS, parameters);  89             return parameters[0] == GL.GL_TRUE;  90  }  91 
 92         public string GetInfoLog()  93  {  94             // Get the info log length.
 95             int[] infoLength = new int[] { 0 };  96  GL.GetProgram(ShaderProgramObject, GL.GL_INFO_LOG_LENGTH, infoLength);  97             int bufSize = infoLength[0];  98 
 99             // Get the compile info.
100             StringBuilder il = new StringBuilder(bufSize); 101  GL.GetProgramInfoLog(ShaderProgramObject, bufSize, IntPtr.Zero, il); 102 
103             string log = il.ToString(); 104             return log; 105  } 106 
107         public void AssertValid() 108  { 109             if (vertexShader.GetCompileStatus() == false) 110  { 111                 string log = vertexShader.GetInfoLog(); 112                 throw new Exception(log); 113  } 114             if (fragmentShader.GetCompileStatus() == false) 115  { 116                 string log = fragmentShader.GetInfoLog(); 117                 throw new Exception(log); 118  } 119             if (GetLinkStatus() == false) 120  { 121                 string log = GetInfoLog(); 122                 throw new Exception(log); 123  } 124  } 125 
126         /// <summary>
127         /// 請注意你的數據類型最終將轉換為int還是float 128         /// </summary>
129         /// <param name="uniformName"></param>
130         /// <param name="v1"></param>
131         public void SetUniform(string uniformName, int v1) 132  { 133  GL.Uniform1(GetUniformLocation(uniformName), v1); 134  } 135 
136         /// <summary>
137         /// 請注意你的數據類型最終將轉換為int還是float 138         /// </summary>
139         /// <param name="uniformName"></param>
140         /// <param name="v1"></param>
141         /// <param name="v2"></param>
142         public void SetUniform(string uniformName, int v1, int v2) 143  { 144  GL.Uniform2(GetUniformLocation(uniformName), v1, v2); 145  } 146 
147         /// <summary>
148         /// 請注意你的數據類型最終將轉換為int還是float 149         /// </summary>
150         /// <param name="uniformName"></param>
151         /// <param name="v1"></param>
152         /// <param name="v2"></param>
153         /// <param name="v3"></param>
154         public void SetUniform(string uniformName, int v1, int v2, int v3) 155  { 156  GL.Uniform3(GetUniformLocation(uniformName), v1, v2, v3); 157  } 158 
159         /// <summary>
160         /// 請注意你的數據類型最終將轉換為int還是float 161         /// </summary>
162         /// <param name="uniformName"></param>
163         /// <param name="v1"></param>
164         /// <param name="v2"></param>
165         /// <param name="v3"></param>
166         /// <param name="v4"></param>
167         public void SetUniform(string uniformName, int v1, int v2, int v3, int v4) 168  { 169  GL.Uniform4(GetUniformLocation(uniformName), v1, v2, v3, v4); 170  } 171 
172         /// <summary>
173         /// 請注意你的數據類型最終將轉換為int還是float 174         /// </summary>
175         /// <param name="uniformName"></param>
176         /// <param name="v1"></param>
177         public void SetUniform(string uniformName, float v1) 178  { 179  GL.Uniform1(GetUniformLocation(uniformName), v1); 180  } 181 
182         /// <summary>
183         /// 請注意你的數據類型最終將轉換為int還是float 184         /// </summary>
185         /// <param name="uniformName"></param>
186         /// <param name="v1"></param>
187         /// <param name="v2"></param>
188         public void SetUniform(string uniformName, float v1, float v2) 189  { 190  GL.Uniform2(GetUniformLocation(uniformName), v1, v2); 191  } 192 
193         /// <summary>
194         /// 請注意你的數據類型最終將轉換為int還是float 195         /// </summary>
196         /// <param name="uniformName"></param>
197         /// <param name="v1"></param>
198         /// <param name="v2"></param>
199         /// <param name="v3"></param>
200         public void SetUniform(string uniformName, float v1, float v2, float v3) 201  { 202  GL.Uniform3(GetUniformLocation(uniformName), v1, v2, v3); 203  } 204 
205         /// <summary>
206         /// 請注意你的數據類型最終將轉換為int還是float 207         /// </summary>
208         /// <param name="uniformName"></param>
209         /// <param name="v1"></param>
210         /// <param name="v2"></param>
211         /// <param name="v3"></param>
212         /// <param name="v4"></param>
213         public void SetUniform(string uniformName, float v1, float v2, float v3, float v4) 214  { 215  GL.Uniform4(GetUniformLocation(uniformName), v1, v2, v3, v4); 216  } 217 
218         /// <summary>
219         /// 請注意你的數據類型最終將轉換為int還是float 220         /// </summary>
221         /// <param name="uniformName"></param>
222         /// <param name="m"></param>
223         public void SetUniformMatrix3(string uniformName, float[] m) 224  { 225             GL.UniformMatrix3(GetUniformLocation(uniformName), 1, false, m); 226  } 227 
228         /// <summary>
229         /// 請注意你的數據類型最終將轉換為int還是float 230         /// </summary>
231         /// <param name="uniformName"></param>
232         /// <param name="m"></param>
233         public void SetUniformMatrix4(string uniformName, float[] m) 234  { 235             GL.UniformMatrix4(GetUniformLocation(uniformName), 1, false, m); 236  } 237 
238         public int GetUniformLocation(string uniformName) 239  { 240             // If we don't have the uniform name in the dictionary, get it's 241             // location and add it.
242             if (uniformNamesToLocations.ContainsKey(uniformName) == false) 243  { 244                 uniformNamesToLocations[uniformName] = GL.GetUniformLocation(ShaderProgramObject, uniformName); 245                 // TODO: if it's not found, we should probably throw an exception.
246  } 247 
248             // Return the uniform location.
249             return uniformNamesToLocations[uniformName]; 250  } 251 
252         /// <summary>
253         /// Gets the shader program object. 254         /// </summary>
255         /// <value>
256         /// The shader program object. 257         /// </value>
258         public uint ShaderProgramObject { get; protected set; } 259 
260 
261         /// <summary>
262         /// A mapping of uniform names to locations. This allows us to very easily specify 263         /// uniform data by name, quickly looking up the location first if needed. 264         /// </summary>
265         private readonly Dictionary<string, int> uniformNamesToLocations = new Dictionary<string, int>(); 266 
267         /// <summary>
268         /// A mapping of attribute names to locations. This allows us to very easily specify 269         /// attribute data by name, quickly looking up the location first if needed. 270         /// </summary>
271         private readonly Dictionary<string, uint> attributeNamesToLocations = new Dictionary<string, uint>(); 272     }
ShaderProgram

 

另外,添加一個輔助類ShaderCompilationException。

 1  [Serializable]  2     public class ShaderCompilationException : Exception  3  {  4         private readonly string compilerOutput;  5 
 6         public ShaderCompilationException(string compilerOutput)  7  {  8             this.compilerOutput = compilerOutput;  9  } 10         public ShaderCompilationException(string message, string compilerOutput) 11             : base(message) 12  { 13             this.compilerOutput = compilerOutput; 14  } 15         public ShaderCompilationException(string message, Exception inner, string compilerOutput) 16             : base(message, inner) 17  { 18             this.compilerOutput = compilerOutput; 19  } 20         protected ShaderCompilationException( 21  System.Runtime.Serialization.SerializationInfo info, 22  System.Runtime.Serialization.StreamingContext context) 23             : base(info, context) { } 24 
25         public string CompilerOutput { get { return compilerOutput; } } 26     }
ShaderCompilationException

 

實現PyramidDemo

加載shader,設置shader program

在PyramidDemo里實現。

 1         private ShaderProgram shaderProgram;  2 
 3         public void Initilize()  4  {  5  InitShaderProgram();  6  }  7 
 8         private void InitShaderProgram()  9  { 10             var vertexShaderSource = File.ReadAllText(@"PyramidDemo.vert"); 11             var fragmentShaderSource = File.ReadAllText(@"PyramidDemo.frag"); 12 
13             this.shaderProgram = new ShaderProgram(); 14 
15             this.shaderProgram.Create(vertexShaderSource, fragmentShaderSource, null); 16             this.shaderProgram.AssertValid(); 17 
18         }

 

+BIT祝威+悄悄在此留下版了個權的信說:

用VAO/VBO設置四面體模型

四面體模型的數據還是legacy OpenGL里的數據,但是不再用glVertex設置,而是用VAO/VBO來指定。

 1         const int vertexCount = 12;  2         private uint[] vertexArrayObject;  3 
 4         public void Initilize()  5  {  6  InitShaderProgram();  7 
 8  InitVAO();  9  } 10 
11         private void InitVAO() 12  { 13             // reserve a vertex array object(VAO) 預約一個VAO
14             this.vertexArrayObject = new uint[1]; 15             GL.GenVertexArrays(1, this.vertexArrayObject); 16 
17             // prepare vertex buffer object(VBO) for vertexes' positions 為頂點位置准備VBO
18             uint[] positionBufferObject = new uint[1]; 19  { 20                 // specify position array
21                 var positionArray = new UnmanagedArray<vec3>(vertexCount); 22                 positionArray[0] = new vec3(0.0f, 1.0f, 0.0f); 23                 positionArray[1] = new vec3(-1.0f, -1.0f, 1.0f); 24                 positionArray[2] = new vec3(1.0f, -1.0f, 1.0f); 25                 positionArray[3] = new vec3(0.0f, 1.0f, 0.0f); 26                 positionArray[4] = new vec3(1.0f, -1.0f, 1.0f); 27                 positionArray[5] = new vec3(1.0f, -1.0f, -1.0f); 28                 positionArray[6] = new vec3(0.0f, 1.0f, 0.0f); 29                 positionArray[7] = new vec3(1.0f, -1.0f, -1.0f); 30                 positionArray[8] = new vec3(-1.0f, -1.0f, -1.0f); 31                 positionArray[9] = new vec3(0.0f, 1.0f, 0.0f); 32                 positionArray[10] = new vec3(-1.0f, -1.0f, -1.0f); 33                 positionArray[11] = new vec3(-1.0f, -1.0f, 1.0f); 34 
35                 // put positions into VBO
36                 GL.GenBuffers(1, positionBufferObject); 37                 GL.BindBuffer(BufferTarget.ArrayBuffer, positionBufferObject[0]); 38  GL.BufferData(BufferTarget.ArrayBuffer, positionArray, BufferUsage.StaticDraw); 39 
40  positionArray.Dispose(); 41  } 42 
43             // prepare vertex buffer object(VBO) for vertexes' colors
44             uint[] colorBufferObject = new uint[1]; 45  { 46                 // specify color array
47                 UnmanagedArray<vec3> colorArray = new UnmanagedArray<vec3>(vertexCount); 48                 colorArray[0] = new vec3(1.0f, 0.0f, 0.0f); 49                 colorArray[1] = new vec3(0.0f, 1.0f, 0.0f); 50                 colorArray[2] = new vec3(0.0f, 0.0f, 1.0f); 51                 colorArray[3] = new vec3(1.0f, 0.0f, 0.0f); 52                 colorArray[4] = new vec3(0.0f, 0.0f, 1.0f); 53                 colorArray[5] = new vec3(0.0f, 1.0f, 0.0f); 54                 colorArray[6] = new vec3(1.0f, 0.0f, 0.0f); 55                 colorArray[7] = new vec3(0.0f, 1.0f, 0.0f); 56                 colorArray[8] = new vec3(0.0f, 0.0f, 1.0f); 57                 colorArray[9] = new vec3(1.0f, 0.0f, 0.0f); 58                 colorArray[10] = new vec3(0.0f, 0.0f, 1.0f); 59                 colorArray[11] = new vec3(0.0f, 1.0f, 0.0f); 60 
61                 // put colors into VBO
62                 GL.GenBuffers(1, colorBufferObject); 63                 GL.BindBuffer(BufferTarget.ArrayBuffer, colorBufferObject[0]); 64  GL.BufferData(BufferTarget.ArrayBuffer, colorArray, BufferUsage.StaticDraw); 65 
66  colorArray.Dispose(); 67  } 68 
69             uint positionLocation = shaderProgram.GetAttributeLocation("in_Position"); 70             uint colorLocation = shaderProgram.GetAttributeLocation("in_Color"); 71 
72  { 73                 // bind the vertex array object(VAO), we are going to specify data for it.
74                 GL.BindVertexArray(vertexArrayObject[0]); 75 
76                 // specify vertexes' positions
77                 GL.BindBuffer(BufferTarget.ArrayBuffer, positionBufferObject[0]); 78                 GL.VertexAttribPointer(positionLocation, 3, GL.GL_FLOAT, false, 0, IntPtr.Zero); 79  GL.EnableVertexAttribArray(positionLocation); 80 
81                 // specify vertexes' colors
82                 GL.BindBuffer(BufferTarget.ArrayBuffer, colorBufferObject[0]); 83                 GL.VertexAttribPointer(colorLocation, 3, GL.GL_FLOAT, false, 0, IntPtr.Zero); 84  GL.EnableVertexAttribArray(colorLocation); 85 
86                 // Unbind the vertex array object(VAO), we've finished specifying data for it.
87                 GL.BindVertexArray(0); 88  } 89         }
InitVAO

 

關於這里的UnmanagedArray<vec3>類型,您可以在這里找到詳細介紹(C#+無unsafe的非托管大數組(large unmanaged array in c# without 'unsafe' keyword))。

用VAO進行渲染

現在shader已加載,VAO/VBO已准備好了模型數據(位置和顏色),就差渲染這一步了。

        public void Render() { mat4 mvp; { // model rotates
                mat4 modelMatrix = glm.rotate(rotation, new vec3(0, 1, 0)); // same as gluLookAt()
                mat4 viewMatrix = glm.lookAt(new vec3(-5, 5, -5), new vec3(0, 0, 0), new vec3(0, 1, 0)); // same as gluPerspective()
                int[] viewport = new int[4]; GL.GetInteger(GetTarget.Viewport, viewport); float width = viewport[2]; float height = viewport[3]; mat4 projectionMatrix = glm.perspective((float)(60.0f * Math.PI / 180.0f), width / height, 0.01f, 100.0f); // get MVP in "uniform mat4 MVP;" in the vertex shader
                mvp = projectionMatrix * viewMatrix * modelMatrix; } // bind the shader program to setup uniforms
            this.shaderProgram.Bind(); // setup MVP
            this.shaderProgram.SetUniformMatrix4("MVP", mvp.to_array()); { // bind vertex array object(VAO)
                GL.BindVertexArray(this.vertexArrayObject[0]); // draw the model: in GL_TRIANGLES mode, there are 'vertexCount' vertexes
                GL.DrawArrays(GL.GL_TRIANGLES, 0, vertexCount); // unbind vertex array object(VAO)
                GL.BindVertexArray(0); } // unbind the shader program
            this.shaderProgram.Unbind(); rotation += 3.0f; } private float rotation;

 

查看效果

Modern OpenGL渲染的效果與legacy OpenGL並沒有差別。

 

+BIT祝威+悄悄在此留下版了個權的信說:

對比

可以看到,用legacy OpenGL的步驟相對modern OpenGL而言是十分簡單的。而想用modern OpenGL渲染時,我們編寫了Shader、ShaderProgram、mat4、vec3、UnmanagedArray<T>等大量的類型,到此才完成了modern OpenGL的一個簡單示例的代碼。這其中任何一個步驟出一點錯誤都可能導致最終無法正常渲染,且調試難度很大。OpenGL難學大概就在這里了。

但是legacy OpenGL在渲染時調用的OpenGL指令比modern OpenGL多得多。另外,modern OpenGL用VAO/VBO會把模型數據上傳到顯卡內存,這也大大加速的渲染過程。再者,modern OpenGL用shader代替了固定管線,shader比固定管線靈活得多,用慣了會覺得既好用又強大。所以我們還是要堅持學用modern OpenGL。

+BIT祝威+悄悄在此留下版了個權的信說:

CSharpGL.vsix

我制作了一個CSharpGL.vsix插件,安裝后可以使用模板項目來體會CSharpGL的用法。

詳情在此(http://www.cnblogs.com/bitzhuwei/p/install-and-use-CSharpGL-vsix.html)。

總結

本篇分別用legacy OpenGL和modern OpenGL實現了一個渲染四面體的例子。例子雖簡單,但是包含了OpenGL渲染的整個編碼過程。今后我們的工作都是基於這個基本流程進行的,只不過在各個方面進行強化,增加新的功能。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM