zoukankan      html  css  js  c++  java
  • 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渲染的整个编码过程。今后我们的工作都是基于这个基本流程进行的,只不过在各个方面进行强化,增加新的功能。

  • 相关阅读:
    左偏树
    论在Windows下远程连接Ubuntu
    ZOJ 3711 Give Me Your Hand
    SGU 495. Kids and Prizes
    POJ 2151 Check the difficulty of problems
    CodeForces 148D. Bag of mice
    HDU 3631 Shortest Path
    HDU 1869 六度分离
    HDU 2544 最短路
    HDU 3584 Cube
  • 原文地址:https://www.cnblogs.com/bitzhuwei/p/CSharpGL-1-start-from-a-simple-demo-with-legacy-modern-opengl.html
Copyright © 2011-2022 走看看