plsqlfor循环输出三角形_glium指南-03-移动我们的三角形

6d3df1ca1ce63c54b075fd39099395bf.png

原地址: https://github.com/glium/glium/blob/master/book/tuto-03-animated-triangle.md

移动我们的三角形

上一节我们画了个三角形, 这一节我们打算试着让它动起来. 记住OpenGL不像绘画软件. 如果我们想改变屏幕上显示的内容, 我们必须再绘制一次, 然后用新绘制的内容替换掉已经存在的内容. 还好我们已经写好了一个 loop 循环, 它正不断地在窗口中重复绘制, 因此我们所写的代码几乎会立即反映到窗口上.

比较naive的方法

第一个方法是创建一个名为 t 的变量并用它来表示移动中的每一步. 我们将在每次循环时更新 t 的值, 然后把它加到三角形的坐标值上:

let mut t: f32 = -0.5;
let mut closed = false;
while !closed {
    // 更新 `t`
    t += 0.0002;
    if t > 0.5 {
        t = -0.5;
    }

    // 创建形状然后把 `t` 加到每个顶点的x坐标上
    let vertex1 = Vertex { position: [-0.5 + t, -0.5] };
    let vertex2 = Vertex { position: [ 0.0 + t,  0.5] };
    let vertex3 = Vertex { position: [ 0.5 + t, -0.25] };
    let shape = vec![vertex1, vertex2, vertex3];
    let vertex_buffer = glium::VertexBuffer::new(&display, &shape).unwrap();

    // 绘制
    let mut target = display.draw();
    target.clear_color(0.0, 0.0, 1.0, 1.0);
    target.draw(&vertex_buffer, &indices, &program, &glium::uniforms::EmptyUniforms,
                &Default::default()).unwrap();
    target.finish().unwrap();

    events_loop.poll_events(|event| {
        match event {
            glutin::Event::WindowEvent { event, .. } => match event {
                glutin::WindowEvent::CloseRequested => closed = true,
                _ => ()
            },
            _ => (),
        }
    });
}

如果你运行这段代码, 你就能看见我们的三角形从左边移动到右边, 然后又跳回左边.

在上个世纪90年代, 很多游戏开发者都是这样做的. 在你的图形比较简单时(例如一个三角形), 这个方法确实好用. 但是一旦你的图形变得复杂, 例如某些有几千个多边形组成的3D模型, 这个方法的效率会变得特别低. 原因如下:

  • 每次绘制时, CPU都会花大量时间来计算坐标(每个模型的每个顶点都进行一次计算的话, 每次绘制你将进行成百上千次计算).
  • 将我们的形状的顶点数据从内存上传到显卡内存也要花费时间. GPU会等待所有数据上传完毕再开始绘制工作, 而等待的这段时间都被浪费掉了.

Uniform变量

还记得顶点着色器吗? 顶点着色器输入每个顶点的属性, 输出顶点在窗口中的位置. 之前, 我们在程序中让三角形顶点坐标值增加然后将计算的结果上传到GPU中, 现在我们把这件事交给GPU来做.

现在把程序改回上一节结束时的样子, 不过依然保留 t :

let vertex1 = Vertex { position: [-0.5, -0.5] };
let vertex2 = Vertex { position: [ 0.0,  0.5] };
let vertex3 = Vertex { position: [ 0.5, -0.25] };
let shape = vec![vertex1, vertex2, vertex3];

let vertex_buffer = glium::VertexBuffer::new(&display, &shape).unwrap();

let mut t: f32 = -0.5;
let mut closed = false;
while !closed {
    // 更新 `t`
    t += 0.0002;
    if t > 0.5 {
        t = -0.5;
    }

    // 绘制
    let mut target = display.draw();
    target.clear_color(0.0, 0.0, 1.0, 1.0);
    target.draw(&vertex_buffer, &indices, &program, &glium::uniforms::EmptyUniforms,
                &Default::default()).unwrap();
    target.finish().unwrap();

    events_loop.poll_events(|event| {
        match event {
            glutin::Event::WindowEvent { event, .. } => match event {
                glutin::WindowEvent::CloseRequested => closed = true,
                _ => ()
            },
            _ => (),
        }
    });
}

然后, 我们稍微修改一下顶点着色器的代码:

let vertex_shader_src = r#"
    #version 140

    in vec2 position;

    uniform float t;

    void main() {
        vec2 pos = position;
        pos.x += t;
        gl_Position = vec4(pos, 0.0, 1.0);
    }
"#;

你也许注意到了, 这不就是我们之前在rust代码里写的操作吗, 只不过这次这段代码是在GPU上执行的. 我们在着色器的代码中添加了一个用 uniform 关键字声明的变量. uniform变量是一个全局变量, 它的值是在绘制时, 由 draw 函数传递给GPU. 现在, 我们可以用 uniform! 宏来实现:

target.draw(&vertex_buffer, &indices, &program, &uniform! { t: t },
            &Default::default()).unwrap();

使用uniform变量解决了之前第一种方法的两个问题. CPU不必进行任何计算, 而且不必上传整个形状的顶点的数据给GPU, 只需上传 t 的值(一个单精度浮点数)就行了.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值