sharpGL(openGl的.net版)之vao对应多vbo

前言:VBO(vertex Buffer Object):顶点缓冲对象。是在显卡存储空间中开辟的一块区域,在显卡存储空间中开辟一块区域,用于存放顶点的各类属性信息。如顶点坐标、纹理坐标、顶点颜色等数据。 在渲染时直接从显VBO去取数据而不必与CPU进行数据交换。而用VAO来告诉计算机这些数据分别有什么属性、起什么作用。

一、编写MainWindow.xaml

<Grid>
    <wpf:OpenGLControl x:Name="openGLControl1"
                       RenderContextType="FBO" 
                       Loaded="OpenGLControl1_Loaded"
    ></wpf:OpenGLControl>
</Grid>

二、编写MainWindow.xaml.cs后台代码

using SharpGL;
using System;
using System.Runtime.InteropServices;
using System.Windows;


namespace wpfSharpGlVao
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        OpenGL gl;
        uint[] vaos = new uint[1];
        uint[] vbos = new uint[2];
        private uint sProgram = 0;
        IntPtr pArrayIn1;
        IntPtr pArrayInColor1;

        //GLSL语言:顶点着色器的shader(这里填写5,6是为了演示,可取0,1,2,3...)
        string vertexShaderSource = @"
            #version 330 core
            layout(location = 5) in vec3 positionZdy;
            layout(location = 6) in vec4 colorZdy;
            out vec4 coInterim;
            void main()
            {
                gl_Position = vec4(positionZdy, 1.0);
                coInterim = colorZdy;
            }";

        //GLSL语言:片元着色器的shader
        string fragmentShaderSource = @"
        #version 330 core
        in vec4 coInterim;
        out vec4 FragColor;
        void main()
        {
            FragColor = coInterim;
        }";

        float[] vertices = new float[] //顶点位置数组
           {
              0.5f, 0.5f, 0.0f,
              0.5f, -0.5f, 0.0f,
              -0.5f, 0.5f, 0.0f,
           };

        float[] colors = //颜色数组(rgba参数-渐变)
       {
           0f, 1f, 0f, 1f,//白色
           0f, 1f, 0f, 1f,//白色
           0.94f,  0.69f, 0.45f,1f,//橙色
        };

        public MainWindow()
        {
            InitializeComponent();
        }

        //初始化
        private void OpenGLControl1_Loaded(object sender, RoutedEventArgs e)
        {
            initFun();
        }

        //初始化内部方法
        private void initFun()
        {
            gl = openGLControl1.OpenGL;
            openGLControl1.OpenGLDraw += OpenGLControl1_OpenGLDraw_1;
            openGLControl1.FrameRate = 40; //帧率为40

            gl.ClearColor(0, 0, 0, 0);
            gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
            gl.Color(1, 0, 0, 1.0f);

            // 创建并编译顶点着色器
            uint vertexShader = gl.CreateShader(OpenGL.GL_VERTEX_SHADER);
            gl.ShaderSource(vertexShader, vertexShaderSource);
            gl.CompileShader(vertexShader);
            var check = GetCompileStatus(vertexShader);

            // 创建并编译片段着色器
            uint fragmentShader = gl.CreateShader(OpenGL.GL_FRAGMENT_SHADER);
            gl.ShaderSource(fragmentShader, fragmentShaderSource);
            gl.CompileShader(fragmentShader);
            var check2 = GetCompileStatus(fragmentShader);

            // 创建着色器程序并链接
            sProgram = gl.CreateProgram();
            gl.AttachShader(sProgram, vertexShader);
            gl.AttachShader(sProgram, fragmentShader);
            gl.LinkProgram(sProgram);
            gl.ValidateProgram(sProgram);
            gl.DeleteShader(vertexShader);
            gl.DeleteShader(fragmentShader);

            //获取自定义输入属性的位置(对应GLSL语言语言申明的location索引)信息
            //注意,若GLSL语言没有写对(或GLSL内部申明了location却没有使用),这里不报错,只是获取到的是-1
            var positionLocation = gl.GetAttribLocation(sProgram, "positionZdy");
            var colorLocation = gl.GetAttribLocation(sProgram, "colorZdy");

            // 创建VAO并绑定
            gl.GenVertexArrays(1, vaos);//创建1个VAO数组,但填入一个VAO,模拟一个VAO对应多个VBO
            gl.BindVertexArray(vaos[0]); //绑定VAO到上下文

            // 创建2个VBO用于存储顶点数据和颜色数据
            gl.GenBuffers(2, vbos);//创建2个VBO

            //绑定第一个VBO存顶点
            gl.BindBuffer(OpenGL.GL_ARRAY_BUFFER, vbos[0]);
            gl.BufferData(OpenGL.GL_ARRAY_BUFFER, vertices, OpenGL.GL_DYNAMIC_DRAW);
            pArrayIn1 = Marshal.AllocHGlobal(vertices.Length * sizeof(float)); //申请数组的指针
            gl.VertexAttribPointer((uint)positionLocation, 3, OpenGL.GL_FLOAT, false, 0, IntPtr.Zero);  //申明位置解释规则。更多关于该函数的解释,见文末附录
            //注意,这里第一个参数并不是像很多教程那样直接填0或1,很有歧义!实时上他是GLSL语言的location信息,只不过网上有些教程刚好使用了GLSL默认位置的0和1而已。
            //作为示范,我这里故意在GLSL语言中显示声明location=5,而这里为了灵活,直接用位置变量
            gl.EnableVertexAttribArray((uint)positionLocation);

            //绑定第二个VBO存颜色
            gl.BindBuffer(OpenGL.GL_ARRAY_BUFFER, vbos[1]);
            gl.BufferData(OpenGL.GL_ARRAY_BUFFER, colors, OpenGL.GL_DYNAMIC_DRAW); //GL_DYNAMIC_DRAW  GL_STATIC_DRAW
            pArrayInColor1 = Marshal.AllocHGlobal(colors.Length * sizeof(float)); //申请数组的指针。
            gl.VertexAttribPointer((uint)colorLocation, 4, OpenGL.GL_FLOAT, false, 0, IntPtr.Zero);  //申明颜色解释规则。
            gl.EnableVertexAttribArray((uint)colorLocation);
            gl.BindVertexArray(0);
        }


   
        public bool GetCompileStatus(uint shar)
        {
           int[] parameters = new int[] { 0 };
           gl.GetShader(shar, OpenGL.GL_COMPILE_STATUS, parameters);
           return parameters[0] == OpenGL.GL_TRUE;
        }

        //实时渲染
        private void OpenGLControl1_OpenGLDraw_1(object sender, SharpGL.WPF.OpenGLRoutedEventArgs args)
        {
            Dispatcher.Invoke(new Action(() =>
            {
                gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);//DEPTH_BUFFER_BIT在变化窗口大小时需要写,否则变化后不出图
                gl.LoadIdentity();
                gl.Viewport(0, 0, 200, 200); //设定视口宽高便于观察,因为默认管线编程时,[-1,1]是铺满控件的的,因此自己去计算百分比就好了。
                gl.Translate(0f, 0f, -6f);

                gl.UseProgram(sProgram); //激活着色器
                gl.BindVertexArray(vaos[0]);//开启VAO

                //重新设置顶点位置数据,动态改变位置坐标
                //Random random = new Random();
                //float randomNumber = (float)random.NextDouble();
                //vertices[0] = randomNumber;
                //gl.BindBuffer(OpenGL.GL_ARRAY_BUFFER, vbos[0]);
                //gl.BufferData(OpenGL.GL_ARRAY_BUFFER, vertices, OpenGL.GL_DYNAMIC_DRAW);

                gl.DrawArrays(OpenGL.GL_TRIANGLES, 0, 3);//绘制三角形

                gl.BindVertexArray(0); //关闭VAO
                gl.UseProgram(0);
                gl.Flush();
            }));
        }


    }
}

三、效果


附:对函数 VertexAttribPointer 的解释:

public void VertexAttribPointer(uint index, int size, uint type, bool normalized, int stride, IntPtr pointer);

解释:从一维数组的实际指针位置(pointer + stride * n)开始,每 stride空间长度,取前 size 个float作为要用的数据。(n是取值次数,比如三角形,n就是1~3)

案例一(假设只有一个vbo):

... 
float[] vertices = new float[] //顶点位置和颜色数组分开
{
              0.5f, 0.5f, 0.0f, //位置数据
              0.5f, -0.5f, 0.0f,
              -0.5f, 0.5f, 0.0f,

               0f, 1f, 0f, 1f, //rgba颜色数据
               0f, 1f, 0f, 1f,
               0.94f,  0.69f, 0.45f,1f
};
...
gl.VertexAttribPointer((uint)positionLocation, 3, OpenGL.GL_FLOAT, false, 3 * sizeof(float), IntPtr.Zero);  //申明位置解释规则
gl.EnableVertexAttribArray((uint)positionLocation);
gl.VertexAttribPointer((uint)colorLocation, 4, OpenGL.GL_FLOAT, false, 4 * sizeof(float), (IntPtr)(9 * sizeof(float)));  //申明颜色解释规则,9是从第9个指针开始扫描
            gl.EnableVertexAttribArray((uint)colorLocation);
...
//对于一维数组来说,取位置的步骤是:
//   1、从一维数组第 0     个float指针位置开始,每3(对应第5个参数)个float取前3(对应第2个参数)个float当做位置;
//   2、从一维数组第 0+3   个float指针位置开始,每3(对应第5个参数)个float取前3(对应第2个参数)个float当做位置;
//   3、从一维数组第 0+3*2 个float指针位置开始,每3(对应第5个参数)个float取前3(对应第2个参数)个float当做位置;
//对于一维数组来说,取颜色的步骤是:
//   1、从一维数组第 9     个float指针位置开始,每4(对应第5个参数)个float取前4(对应第2个参数)个float当做颜色;
//   2、从一维数组第 9+4   个float指针位置开始,每4(对应第5个参数)个float取前4(对应第2个参数)个float当做颜色;
//   3、从一维数组第 9+4*2 个float指针位置开始,每4(对应第5个参数)个float取前4(对应第2个参数)个float当做颜色
//说明:因为没有混合位置和颜色向量,也就是数据分离的很干净,解释器不需要知道步长,故第5个参数,两个也可以填0*sizeof(float)

案例二(假设只有一个vbo):

...
float[] vertices = new float[] //顶点位置和颜色数组混合
{
   0.5f, 0.5f, 0.0f,   0f, 1f, 0f, 1f, //位置和颜色数据
   0.5f, -0.5f, 0.0f,  0f, 1f, 0f, 1f,
   -0.5f, 0.5f, 0.0f,  0.94f,  0.69f, 0.45f,1f
};
...
gl.VertexAttribPointer((uint)positionLocation, 3, OpenGL.GL_FLOAT, false, 7 * sizeof(float), IntPtr.Zero);  //申明位置解释规则
gl.EnableVertexAttribArray((uint)positionLocation);
 gl.VertexAttribPointer((uint)colorLocation, 4, OpenGL.GL_FLOAT, false, 7 * sizeof(float), (IntPtr)(3 * sizeof(float)));  //申明颜色解释规则
gl.EnableVertexAttribArray((uint)colorLocation);
//对于一维数组来说,取位置的步骤是:
//   1、从一维数组第 0     个float指针位置开始,每7个float取前3个float当做位置;
//   2、从一维数组第 0+7   个float指针位置开始,每7个float取前3个float当做位置;
//   3、从一维数组第 0+7*2 个float指针位置开始,每7个float取前3个float当做位置;
//对于一维数组来说,取颜色的步骤是:
//   1、从一维数组第 3     个float指针位置开始,每7个float取前4个float当做颜色;
//   2、从一维数组第 3+7   个float指针位置开始,每7个float取前4个float当做颜色;
//   3、从一维数组第 3+7*2 个float指针位置开始,每7个float取前4个float当做颜色
//因为进行了位置和颜色向量混合,解释器需要知道步长,故第5个参数,需要指定步长,即这里的 7 * sizeof(float)

通过对 VertexAttribPointer 函数的理解,可以发现,案例一二虽然数组定义不同,但是通过设定不同的解析方式。最终渲染效果是一样的。


好了,本次关于sharpGL的使用着色器(更为现代的“管线编程”),一个VAO绑定多个VBO篇就讲到这里了,更多内容,请期待下次的讲述

欢迎有问题的伙伴及时留意讨论,有不足之处还望指正

祝大家生活工作愉快~

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值