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

  • 相关阅读:
    Linux 用户管理
    oracle索引(转)
    oracle物理视图(转)
    oracle事物总结(转)
    SQL条件循环语句以及异常知识整理
    ORACLE 中ROWNUM用法总结! (转)
    PL/SQL编程重点语句输出整理
    Oracle函数整理
    Oracle子查询中any、some、all之间的区别
    Oracle DQL查询语言整理
  • 原文地址:https://www.cnblogs.com/bitzhuwei/p/CSharpGL-1-start-from-a-simple-demo-with-legacy-modern-opengl.html
Copyright © 2020-2023  润新知