Benjamin Lindley
Benjamin Lindley

Reputation: 103713

glVertexAttrib4fv won't pass to shader input at location 0

I'm trying to learn OpenGL and Rust at the same time. I'm using the OpenGL Superbible Sixth Edition, and got stuck in chapter 3 which introduces the function glVertexAttrib4fv to offset the position of a triangle. It worked fine when I did it in C++, but when I tried to translate it to Rust, the triangle disappeared. I've tried to reduce the example as much as possible to the following code (cargo dependencies are glutin = "*" and gl = "*"):

main.rs

extern crate glutin;
extern crate gl;

use std::io::Read;

fn main() {
    unsafe {
        let win = glutin::Window::new().unwrap();
        win.make_current().unwrap();
        gl::load_with(|s| win.get_proc_address(s));

        let program = build_shader_program();
        gl::UseProgram(program);

        let mut vao = std::mem::uninitialized();
        gl::GenVertexArrays(1, &mut vao);
        gl::BindVertexArray(vao);

        let red = [1.0, 0.0, 0.0, 1.0];

        let mut running = true;
        while running {
            for event in win.poll_events() {
                if let glutin::Event::Closed = event {
                    running = false;
                }
            }

            win.swap_buffers().unwrap();
            gl::ClearBufferfv(gl::COLOR, 0, &red[0]);

            let attrib = [0.5, 0.0, 0.0, 0.0];
            panic_if_error("before VertexAttrib4fv");
            gl::VertexAttrib4fv(0, &attrib[0]);
            panic_if_error("after VertexAttrib4fv");

            gl::DrawArrays(gl::TRIANGLES, 0, 3);
        }
    }
}

fn panic_if_error(message: &str) {
    unsafe {
        match gl::GetError() {
            gl::NO_ERROR => (),
            _ => panic!("{}", message),
        }
    }
}

fn load_file_as_cstring(path: &str) -> std::ffi::CString {
    let mut contents = Vec::new();
    let mut file = std::fs::File::open(path).unwrap();
    file.read_to_end(&mut contents).unwrap();
    std::ffi::CString::new(contents).unwrap()
}

fn load_and_compile_shader(path: &str, shader_type: u32) -> u32 {
    let contents = load_file_as_cstring(path);
    unsafe {
        let shader_id = gl::CreateShader(shader_type);

        let source_ptr = contents.as_ptr();
        gl::ShaderSource(shader_id, 1, &source_ptr, std::ptr::null());
        gl::CompileShader(shader_id);

        let mut result = std::mem::uninitialized();
        gl::GetShaderiv(shader_id, gl::COMPILE_STATUS, &mut result);
        assert_eq!(result, gl::TRUE as i32);

        shader_id
    }
}

fn build_shader_program() -> u32 {
    let vert = load_and_compile_shader("a.vert", gl::VERTEX_SHADER);
    let frag = load_and_compile_shader("a.frag", gl::FRAGMENT_SHADER);

    unsafe {
        let program_id = gl::CreateProgram();
        gl::AttachShader(program_id, vert);
        gl::AttachShader(program_id, frag);
        gl::LinkProgram(program_id);

        let mut result = std::mem::uninitialized();
        gl::GetProgramiv(program_id, gl::LINK_STATUS, &mut result);
        assert_eq!(result, gl::TRUE as i32);

        program_id
    }
}

a.frag

#version 430 core
out vec4 color;
void main() {
    color = vec4(1.0, 1.0, 1.0, 1.0);
}

a.vert

#version 430 core
layout (location = 0) in vec4 offset;
void main() {
    const vec4 vertices[3] =
        vec4[3](vec4( 0.25, -0.25, 0.5, 1.0),
                vec4(-0.25, -0.25, 0.5, 1.0),
                vec4( 0.25,  0.25, 0.5, 1.0));

    gl_Position = vertices[gl_VertexID];             // LINE 1
    // gl_Position = vertices[gl_VertexID] + offset; // LINE 2
}

This code, as is, produces a white triangle in the middle of a red window.

enter image description here

Now, my expectation is that when I comment out LINE 1 in the vertex shader, and uncomment LINE 2, the triangle should move a quarter of a screen to the right, due to this code in "main.rs":

let attrib = [0.5, 0.0, 0.0, 0.0];
panic_if_error("before VertexAttrib4fv");
gl::VertexAttrib4fv(0, &attrib[0]);
panic_if_error("after VertexAttrib4fv");

But instead, the triangle disappears altogether. The panic_if_error call before and after gl::VertexAttrib4fv ensures that gl::GetError returns gl::NO_ERROR.

Question: Does anybody know why this is happening?

Other things of note. While I was searching for the answer to this, I came upon this question, where the user is having a similar problem (except in C++, where I had no problem). Anyway, one of the comments there incidentally lead me to try changing the location from 0 to 1, as in this:

layout (location = 1) in vec4 offset;

for the vertex shader, and this for the call to gl::VertexAttrib4fv:

gl::VertexAttrib4fv(1, &attrib[0]);

Well, that worked, but I have no idea why, and would still like to know what the problem is with using location 0 there (since that's what the book shows, and it worked fine in C++).

Upvotes: 2

Views: 394

Answers (1)

Reto Koradi
Reto Koradi

Reputation: 54592

You need to make sure that you have a Core Profile context. If you do not specify this, you may be creating a Compatibility Profile context. In the Compatibility Profile, vertex attribute 0 has a special meaning. From the OpenGL 3.2 Compatibility Profile spec:

Setting generic vertex attribute zero specifies a vertex; the four vertex coordinates are taken from the values of attribute zero. A Vertex2, Vertex3, or Vertex4 command is completely equivalent to the corresponding VertexAttrib* command with an index of zero. Setting any other generic vertex attribute updates the current values of the attribute. There are no current values for vertex attribute zero.

In other words, vertex attribute 0 is an alias for the fixed function vertex position in the compatibility profile.

The above does not apply in the Core Profile. Vertex attribute 0 has not special meaning, and can be used like any other vertex attribute.

Based on what you already found, you need to use the with_gl_profile method with argument Core to specify that you want to use the core profile when creating the window.

Upvotes: 2

Related Questions