GLFW-RS only shows black square instead of rotating cubes - opengl

I am trying to reformat C++ code I created from learnopengl.com with Rust bindings and it isn't quite working as expected. I am only getting a black square when I should be getting 10 rotating textured cubes.
Here's an image of my output window whenever I run the application.
Could somebody help me figure out what's going wrong? I can't understand it and it's driving me up the walls.
The code is quite lengthy so here is link to the repository if that makes it easier to read. Github GLFW Repo
main.rs
extern crate gl;
extern crate glfw;
extern crate glm;
extern crate image;
mod shader;
// use glfw::ffi;
use gl::types::*;
use glfw::{Action, Context, Key};
use glm::*;
use std::ffi::c_void;
use std::mem::size_of;
use shader::shader::Shader;
fn main() {
let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
glfw.window_hint(glfw::WindowHint::ContextVersion(4, 3));
glfw.window_hint(glfw::WindowHint::OpenGlForwardCompat(true));
glfw.window_hint(glfw::WindowHint::OpenGlProfile(
glfw::OpenGlProfileHint::Core,
));
let (mut window, events) = glfw
.create_window(800, 600, "Hello this is window", glfw::WindowMode::Windowed)
.expect("Failed to create GLFW window.");
window.set_title("OpenGL Custom App");
window.set_key_polling(true);
window.make_current();
window.set_framebuffer_size_polling(true);
load_gl_functions(&mut window);
// ffi::glfwSetWindowSizeCallback(*window, cbfun: Option<GLFWwindowsizefun>);
// First three verticies, last two texture
let _vertices: [f32; 180] = [
-0.5, -0.5, -0.5, 0.0, 0.0, 0.5, -0.5, -0.5, 1.0, 0.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, 0.5,
-0.5, 1.0, 1.0, -0.5, 0.5, -0.5, 0.0, 1.0, -0.5, -0.5, -0.5, 0.0, 0.0, -0.5, -0.5, 0.5,
0.0, 0.0, 0.5, -0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 1.0, -0.5,
0.5, 0.5, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, 0.5, 0.5, 1.0, 0.0, -0.5, 0.5, -0.5,
1.0, 1.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0,
0.0, -0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5,
-0.5, -0.5, 0.0, 1.0, 0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, 0.5, 0.0, 0.0, 0.5, 0.5, 0.5,
1.0, 0.0, -0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, -0.5, 1.0, 1.0, 0.5, -0.5, 0.5, 1.0, 0.0,
0.5, -0.5, 0.5, 1.0, 0.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, 0.5,
-0.5, 0.0, 1.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0,
-0.5, 0.5, 0.5, 0.0, 0.0, -0.5, 0.5, -0.5, 0.0, 1.0,
];
let cube_positions: [Vec3; 10] = [
glm::vec3(0.0, 0.0, 0.0),
glm::vec3(2.0, 5.0, -15.0),
glm::vec3(-1.5, -2.2, -2.5),
glm::vec3(-3.8, -2.0, -12.3),
glm::vec3(2.4, -0.4, -3.5),
glm::vec3(-1.7, 3.0, -7.5),
glm::vec3(1.3, -2.0, -2.5),
glm::vec3(1.5, 2.0, -2.5),
glm::vec3(1.5, 0.2, -1.5),
glm::vec3(-1.3, 1.0, -1.5),
];
let mut vbo: GLuint = 0;
let mut vao: GLuint = 0;
let mut texture1: u32 = 0;
let mut texture2: u32 = 0;
let shader = Shader::create_shader(
"src/shaders/vertShader.vert",
"src/shaders/fragShader.frag",
&mut window,
)
.expect("There was an error creating the program.");
unsafe {
gl::Enable(gl::DEPTH_TEST);
// Vertices pointer
gl::GenBuffers(1, &mut vbo);
gl::GenVertexArrays(1, &mut vao);
gl::BindVertexArray(vao);
gl::BindBuffer(gl::ARRAY_BUFFER, vbo);
gl::BufferData(
gl::ARRAY_BUFFER,
size_of::<[f32; 180]>() as isize,
_vertices.as_ptr() as *const c_void,
gl::STATIC_DRAW,
);
// Vertext pointer
gl::VertexAttribPointer(
0,
3,
gl::FLOAT,
gl::FALSE,
5 * size_of::<f32>() as i32,
0 as *const c_void,
);
gl::EnableVertexAttribArray(0);
// Texture pointer
gl::VertexAttribPointer(
1,
2,
gl::FLOAT,
gl::FALSE,
5 * size_of::<f32>() as i32,
(3 * size_of::<f32>()) as *const c_void,
);
gl::EnableVertexAttribArray(1);
// Bind textures
gl::GenTextures(1, &mut texture1 as *mut u32);
gl::ActiveTexture(gl::TEXTURE0);
gl::BindTexture(gl::TEXTURE_2D, texture1);
gl::TexParameteri(
gl::TEXTURE_2D,
gl::TEXTURE_WRAP_S,
gl::MIRRORED_REPEAT as i32,
);
gl::TexParameteri(
gl::TEXTURE_2D,
gl::TEXTURE_WRAP_R,
gl::MIRRORED_REPEAT as i32,
);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
gl::TexParameteri(
gl::TEXTURE_2D,
gl::TEXTURE_MIN_FILTER,
gl::LINEAR_MIPMAP_LINEAR as i32,
);
let tex1 = image::open("E:\\Repos\\Rust\\RSOpenGL\\src\\textures\\wall.jpg")
.expect("Failed to load image.")
.into_rgb();
gl::TexImage2D(
gl::TEXTURE_2D,
0,
gl::RGB as i32,
tex1.width() as i32,
tex1.height() as i32,
0,
gl::RGB,
gl::UNSIGNED_BYTE,
tex1.as_ptr() as *const c_void,
);
gl::GenerateMipmap(gl::TEXTURE_2D);
// Texture 2
gl::GenTextures(1, &mut texture2 as *mut u32);
gl::ActiveTexture(gl::TEXTURE1);
gl::BindTexture(gl::TEXTURE_2D, texture2);
gl::TexParameteri(
gl::TEXTURE_2D,
gl::TEXTURE_WRAP_S,
gl::MIRRORED_REPEAT as i32,
);
gl::TexParameteri(
gl::TEXTURE_2D,
gl::TEXTURE_WRAP_R,
gl::MIRRORED_REPEAT as i32,
);
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
gl::TexParameteri(
gl::TEXTURE_2D,
gl::TEXTURE_MIN_FILTER,
gl::LINEAR_MIPMAP_LINEAR as i32,
);
let tex2 = image::open("E:\\Repos\\Rust\\RSOpenGL\\src\\textures\\awesomeface.png")
.expect("Failed to load image.")
.into_rgb();
gl::TexImage2D(
gl::TEXTURE_2D,
0,
gl::RGB as i32,
tex2.width() as i32,
tex2.height() as i32,
0,
gl::RGB,
gl::UNSIGNED_BYTE,
tex2.as_ptr() as *const c_void,
);
gl::GenerateMipmap(gl::TEXTURE_2D);
gl::ClearColor(0.2, 0.3, 0.3, 1.0);
shader.use_shader();
shader.set_int32("texture1", 0);
shader.set_int32("texture2", 1);
}
let mut last_frame = glfw.get_time();
while window.should_close() == false {
let curr_frame: f64 = glfw.get_time();
let delta_time: f64 = curr_frame - last_frame;
println!("{}", delta_time);
last_frame = curr_frame;
// process input
unsafe {
gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
gl::ActiveTexture(gl::TEXTURE0);
gl::BindTexture(gl::TEXTURE_2D, texture1);
gl::ActiveTexture(gl::TEXTURE1);
gl::BindTexture(gl::TEXTURE_2D, texture2);
let projection: glm::Mat4 = glm::ext::perspective::<f32>(
glm::radians(90.0),
800 as f32 / 600 as f32,
0.1,
100.0,
);
shader.set_mat4("projection", projection);
let view: glm::Mat4 = glm::ext::look_at::<f32>(
glm::vec3(0.0, 0.0, 3.0),
glm::vec3(0.0, 0.0, 3.0) + glm::vec3(0.0, 0.0, -1.0),
glm::vec3(0.0, 1.0, 0.0),
);
shader.set_mat4("view", view);
// println!("{:?}\n{:?}", view, projection);
shader.use_shader();
gl::BindVertexArray(vao);
let mut i: f32 = 0.0;
for cube in cube_positions.iter() {
let mut model: glm::Mat4 = glm::mat4(
1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
);
model = glm::ext::translate(&model, *cube);
let angle: f32 = 20.0 + i;
i += 1.0;
model = glm::ext::rotate(
&model,
glm::radians(angle) * glfw.get_time() as f32,
glm::vec3(1.0, 0.3, 0.5),
);
shader.set_mat4("model", model);
// println!("{:?}", model);
gl::DrawArrays(gl::TRIANGLES, 0, 36);
}
}
glfw.poll_events();
for (_, event) in glfw::flush_messages(&events) {
handle_window_event(&mut window, event);
}
window.swap_buffers();
}
unsafe {
gl::DeleteVertexArrays(1, &vao);
gl::DeleteBuffers(1, &vbo);
// glfw::ffi::glfwTerminate(); apparently not necessary
}
}
fn handle_window_event(window: &mut glfw::Window, event: glfw::WindowEvent) {
match event {
glfw::WindowEvent::Key(Key::Escape, _, Action::Press, _) => window.set_should_close(true),
_ => {}
}
}
fn load_gl_functions(window: &mut glfw::Window) {
gl::ClearColor::load_with(|_s| window.get_proc_address("glClearColor"));
gl::Clear::load_with(|_s| window.get_proc_address("glClear"));
gl::GenBuffers::load_with(|_s| window.get_proc_address("glGenBuffers"));
gl::GenVertexArrays::load_with(|_s| window.get_proc_address("glGenVertexArrays"));
gl::BindBuffer::load_with(|_s| window.get_proc_address("glBindBuffer"));
gl::BufferData::load_with(|_s| window.get_proc_address("glBufferData"));
gl::VertexAttribPointer::load_with(|_s| window.get_proc_address("glVertexAttribPointer"));
gl::EnableVertexAttribArray::load_with(|_s| {
window.get_proc_address("glEnableVertexAttribArray")
});
gl::BindVertexArray::load_with(|_s| window.get_proc_address("glBindVertexArray"));
gl::DeleteVertexArrays::load_with(|_s| window.get_proc_address("glDeleteVertexArrays"));
gl::DeleteBuffers::load_with(|_s| window.get_proc_address("glDeleteBuffers"));
gl::GenTextures::load_with(|_s| window.get_proc_address("glGenTextures"));
gl::TexParameteri::load_with(|_s| window.get_proc_address("glTexParameteri"));
gl::TexImage2D::load_with(|_s| window.get_proc_address("glTexImage2D"));
gl::BindTexture::load_with(|_s| window.get_proc_address("glBindTexture"));
gl::ActiveTexture::load_with(|_s| window.get_proc_address("glActiveTexture"));
gl::GenerateMipmap::load_with(|_s| window.get_proc_address("glGenerateMipmap"));
gl::Enable::load_with(|_s| window.get_proc_address("glEnable"));
gl::DrawArrays::load_with(|_s| window.get_proc_address("glDrawArrays"));
}
shader.rs
extern crate gl;
extern crate glfw;
extern crate glm;
pub mod shader {
pub struct Shader {
pub id: u32,
}
impl Shader {
pub fn create_shader(vert_file_path: &str, frag_file_path: &str, window: &mut glfw::Window) -> std::result::Result<Shader, std::io::Error> {
if gl::CreateProgram::is_loaded() == false {
let result = Shader::load_gl_procs(window);
if result == false {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"ERROR::SHADER::CANNOT_LOAD_PROGRAMS",
));
}
}
let vert_text = std::fs::read_to_string(vert_file_path).expect("Unable to read vert file");
let vert_text_ptr = vert_text.as_ptr() as *const *const gl::types::GLchar;
let frag_text = std::fs::read_to_string(frag_file_path).expect("Unable to read frag file");
let frag_text_ptr = frag_text.as_ptr() as *const *const gl::types::GLchar;
//println!("{:?} \n{:?}", vert_text_ptr, frag_text_ptr);
let id: gl::types::GLuint;
unsafe {
let vert_shader = gl::CreateShader(gl::VERTEX_SHADER);
gl::ShaderSource(vert_shader, 1, vert_text_ptr, &0);
gl::CompileShader(vert_shader);
/* STATUS_ACCESS_VIOLATION error
let mut success: i32;
let mut info_log: [gl::types::GLchar; 512] = [0; 512];
gl::GetShaderiv(vert_shader, gl::COMPILE_STATUS, success as *mut i32);
println!("{:?}", success);
if success == 0 {
gl::GetShaderInfoLog(vert_shader, 512, 0 as *mut gl::types::GLsizei, &mut info_log[0]);
for i in info_log.iter() {
print!("{}", i);
}
} */
let frag_shader = gl::CreateShader(gl::FRAGMENT_SHADER);
gl::ShaderSource(frag_shader, 1, frag_text_ptr, &0);
gl::CompileShader(frag_shader);
id = gl::CreateProgram();
gl::AttachShader(id, vert_shader);
gl::AttachShader(id, frag_shader);
gl::LinkProgram(id);
gl::DeleteShader(vert_shader);
gl::DeleteShader(frag_shader);
}
let created_shader: Shader = Shader { id: id };
return Ok(created_shader);
}
pub fn load_gl_procs(window: &mut glfw::Window) -> bool {
gl::CreateShader::load_with(|_s| window.get_proc_address("glCreateShader"));
gl::ShaderSource::load_with(|_s| window.get_proc_address("glShaderSource"));
gl::CompileShader::load_with(|_s| window.get_proc_address("glCompileShader"));
gl::GetShaderiv::load_with(|_s| window.get_proc_address("glGetShaderiv"));
gl::CreateProgram::load_with(|_s| window.get_proc_address("glCreateProgram"));
gl::AttachShader::load_with(|_s| window.get_proc_address("glAttachShader"));
gl::LinkProgram::load_with(|_s| window.get_proc_address("glLinkProgram"));
gl::GetProgramiv::load_with(|_s| window.get_proc_address("glGetProgramiv"));
gl::GetShaderInfoLog::load_with(|_s| window.get_proc_address("glGetShaderInfoLog"));
gl::GetProgramInfoLog::load_with(|_s| window.get_proc_address("glGetProgramInfoLog"));
gl::DeleteShader::load_with(|_s| window.get_proc_address("glDeleteShader"));
gl::UseProgram::load_with(|_s| window.get_proc_address("glUseProgram"));
gl::Uniform1i::load_with(|_s| window.get_proc_address("glUniform1i"));
gl::Uniform1f::load_with(|_s| window.get_proc_address("glUniform1f"));
gl::UniformMatrix4fv::load_with(|_s| window.get_proc_address("glUniformMatrix4fv"));
gl::GetUniformLocation::load_with(|_s| window.get_proc_address("glGetUniformLocation"));
// TODO Check to see if all of these are loaded
return true;
}
pub unsafe fn use_shader(&self) {
gl::UseProgram(self.id as gl::types::GLuint);
}
pub unsafe fn set_bool(&self, name: &str, value: bool) {
gl::Uniform1i(gl::GetUniformLocation(self.id, name.as_ptr() as *const gl::types::GLchar), value as gl::types::GLint);
}
pub unsafe fn set_int32(&self, name: &str, value: i32) {
gl::Uniform1i(gl::GetUniformLocation(self.id, name.as_ptr() as *const gl::types::GLchar), value as gl::types::GLint);
}
pub unsafe fn set_float32(&self, name: &str, value: f32) {
gl::Uniform1f(gl::GetUniformLocation(self.id, name.as_ptr() as *const gl::types::GLchar), value as gl::types::GLfloat);
}
pub unsafe fn set_mat4(&self, name: &str, value: glm::Mat4) {
gl::UniformMatrix4fv(gl::GetUniformLocation(self.id, name.as_ptr() as *const gl::types::GLchar), 1, gl::FALSE, &value[0][0]);
}
}
}
vertShader.vert
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
out vec2 TexCoord;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
TexCoord = aTexCoord;
}
fragShader.frag
#version 330 core
out vec4 FragColor;
in vec2 TexCoord;
uniform sampler2D texture1;
uniform sampler2D texture2;
void main()
{
// FragColor = vec4(ourColor, 1.0f);
FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.5);
}

There were actually two main problems with my program, both regarding CStrings.
Problem 1: OpenGL couldn't create the program because the text I sent it did not have a null termination character, so it had to be recasted as a CString with one.
let v_shader_code = CString::new(vert_text.as_bytes()).unwrap();
let f_shader_code = CString::new(frag_text.as_bytes()).unwrap();
Problem 2: The "set_*()" functions wouldn't work because the name was being passed as an &str without a null termination character. This also has to be cast into a c string.
pub unsafe fn set_bool(&self, name: &str, value: bool) {
gl::Uniform1i(gl::GetUniformLocation(self.id, CString::new(name).expect("Error converting &str to CString").as_c_str().as_ptr()), value as i32);
}

Related

Calculate Normals in Shader

I am new to OpenGL and Shaders.
I want to write a toon shader.
I have this OpenGL code:
void display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
float black[] = { 0.0, 0.0, 0.0, 1.0 };
float red[] = { 1.0, 0.0, 0.0, 1.0 };
float green[] = { 0.0, 1.0, 0.0, 1.0 };
float blue[] = { 0.0, 0.0, 1.0, 1.0 };
float white[] = { 1.0, 1.0, 1.0, 1.0 };
float lowAmbient[] = { 0.2, 0.2, 0.2, 1.0 };
float fullAmbient[] = { 1.0, 1.0, 1.0, 1.0 };
glMaterialfv(GL_FRONT, GL_AMBIENT, blue);
glMaterialfv(GL_FRONT, GL_DIFFUSE, blue);
glMaterialfv(GL_FRONT, GL_SPECULAR, white);
glMaterialf(GL_FRONT, GL_SHININESS, 128.0);
glLightfv(GL_LIGHT0, GL_AMBIENT, lowAmbient);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0, 0.0, -9);
glRotatef(45, 1, 0, 0);
glRotatef(45, 0, 0, 1);
glBegin(GL_QUADS);
//front
glNormal3f(0.0, 0.0, -1.0);
glVertex3f(-1.0, 1.0, 1.0);
glVertex3f(-1.0, -1.0, 1.0);
glVertex3f(1.0, -1.0, 1.0);
glVertex3f(1.0, 1.0, 1.0);
//back
glNormal3f(0.0, 0.0, 1.0);
glVertex3f(1.0, 1.0, -1.0);
glVertex3f(1.0, -1.0, -1.0);
glVertex3f(-1.0, -1.0, -1.0);
glVertex3f(-1.0, 1.0, -1.0);
//right
glNormal3f(1.0, 0.0, 0.0);
glVertex3f(1.0, 1.0, 1.0);
glVertex3f(1.0, -1.0, 1.0);
glVertex3f(1.0, -1.0, -1.0);
glVertex3f(1.0, 1.0, -1.0);
//left
glNormal3f(-1.0, 0.0, 0.0);
glVertex3f(-1.0, 1.0, -1.0);
glVertex3f(-1.0, -1.0, -1.0);
glVertex3f(-1.0, -1.0, 1.0);
glVertex3f(-1.0, 1.0, 1.0);
//top
glNormal3f(0.0, 1.0, 0.0);
glVertex3f(-1.0, 1.0, -1.0);
glVertex3f(-1.0, 1.0, 1.0);
glVertex3f(1.0, 1.0, 1.0);
glVertex3f(1.0, 1.0, -1.0);
//bottom
glNormal3f(0.0, -1.0, 0.0);
glVertex3f(-1.0, -1.0, -1.0);
glVertex3f(-1.0, -1.0, 1.0);
glVertex3f(1.0, -1.0, 1.0);
glVertex3f(1.0, -1.0, -1.0);
glEnd();
//Swap back and front buffer
glutSwapBuffers();
}
void init()
{
glClearColor(0.0, 0.0, 0.0, 1.0);
glEnable(GL_DEPTH_TEST);
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
float ambientLight[] = { 0.2,0.2,0.2,1.0 };
float diffuseLight[] = { 0.8,0.8,0.8,1.0 };
float specularLight[] = { 1.0,1.0,1.0,1.0 };
float lightPosition[] = { 0.5,0.5,0.0,1.0 };
glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight);
glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
}
My Shader Codes:
#shader vertex
#version 330 core
void main()
{
}
#shader fragment
#version 330 core
vec3 LightPosition = gl_LightSource[0].position;//I don't know this is true or not
vec3 Normal;//I don't know how to calculate
void main()
{
vec4 color1 = gl_FrontMaterial.diffuse + gl_FrontMaterial.specular +
gl_FrontMaterial.ambient;
vec4 color2;
float intensity = dot(LightPosition, Normal);
if (intensity > 0.95) color2 = vec4(1.0, 1.0, 1.0, 1.0);
else if (intensity > 0.75) color2 = vec4(0.8, 0.8, 0.8, 1.0);
else if (intensity > 0.50) color2 = vec4(0.6, 0.6, 0.6, 1.0);
else if (intensity > 0.25) color2 = vec4(0.4, 0.4, 0.4, 1.0);
else color2 = vec4(0.2, 0.2, 0.2, 1.0);
gl_FragColor = color1 * color2;
}
To calculate light intensity and apply colors to my cube object I should know normals.
How can I calculate, or if there is a way, reach them?
(I have no problem with the shader compilation, or other OpenGL stuff. If I close my shader compilation lines I can see a green cube.)

How do you do 3D rendering in Gtk+ using OpenGL?

I've been searching around the internet a lot for a way to use 3D graphics inside my Gtk application and I have found Gtk Glarea, but I can't find any tutorials. Does anyone have any recommendations?
Here is a demonstration of how to draw a spinning cube in Gtk 3 with OpenGL3.3 in C++:
/* OpenGL Area
*
* GtkGLArea is a widget that allows custom drawing using OpenGL calls.
*/
// compiling with: g++ gl_draw_area.cpp `pkg-config --cflags gtk+-3.0`
// \ `pkg-config --libs gtk+-3.0` -lepoxy
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <gtk/gtk.h>
#include <epoxy/gl.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <iostream>
unsigned int WIDTH = 800;
unsigned int HEIGHT = 600;
using glm::mat4;
using glm::vec3;
using glm::lookAt;
using glm::perspective;
using glm::rotate;
const GLchar *VERTEX_SOURCE =
"#version 330\n"
"in vec3 position;\n"
"in vec3 normal;\n"
"out vec3 transformedNormal;\n"
"out vec3 originalNormal;\n"
"uniform mat4 projection;\n"
"uniform mat4 view;\n"
"uniform mat4 model;\n"
"void main(){\n"
" gl_Position = projection * view * model * vec4(position, 1.0);\n"
" mat3 normalMatrix = transpose(inverse(mat3(view * model)));\n"
" transformedNormal = normalMatrix * normal;\n"
" originalNormal = abs(normal);\n"
"}\n";
const GLchar *FRAGMENT_SOURCE =
"#version 330\n"
"in vec3 transformedNormal;\n"
"in vec3 originalNormal;\n"
"out vec4 outputColor;\n"
"void main() {\n"
"vec3 color = originalNormal;\n"
"float lighting = abs(dot(transformedNormal, vec3(0,0,-1)));\n"
"outputColor = vec4(color * lighting, 1.0f);\n" //constant white
"}";
/* the GtkGLArea widget */
static GtkWidget *gl_area = NULL;
/* The object we are drawing */
static const GLfloat vertex_data[] = {
1.0, -1.0, -1.0, 0.0, -1.0, 0.0,
1.0, -1.0, 1.0, 0.0, -1.0, 0.0,
-1.0, -1.0, 1.0, 0.0, -1.0, 0.0,
1.0, -1.0, -1.0, 0.0, -1.0, 0.0,
-1.0, -1.0, 1.0, 0.0, -1.0, 0.0,
-1.0, -1.0,-1.0, 0.0, -1.0, 0.0,
-1.0, 1.0, 1.0, 0.0, 1.0, 0.0,
1.0, 1.0, 1.0, 0.0, 1.0, 0.0,
1.0, 1.0, -1.0, 0.0, 1.0, 0.0,
-1.0, 1.0, 1.0, 0.0, 1.0, 0.0,
1.0, 1.0, -1.0, 0.0, 1.0, 0.0,
-1.0, 1.0,-1.0, 0.0, 1.0, 0.0,
-1.0, -1.0,-1.0, -1.0, 0.0, 0.0,
-1.0, -1.0, 1.0, -1.0, 0.0, 0.0,
-1.0, 1.0, -1.0, -1.0, 0.0, 0.0,
-1.0, -1.0, 1.0, -1.0, 0.0, 0.0,
-1.0, 1.0, 1.0, -1.0, 0.0, 0.0,
-1.0, 1.0, -1.0, -1.0, 0.0, 0.0,
-1.0, -1.0,1.0, 0.0, 0.0, 1.0,
1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
-1.0, 1.0, 1.0, 0.0, 0.0, 1.0,
1.0, -1.0, 1.0, 0.0, 0.0, 1.0,
1.0, 1.0, 1.0, 0.0, 0.0, 1.0,
-1.0, 1.0, 1.0, 0.0, 0.0, 1.0,
1.0, 1.0, -1.0, 0.0, 0.0, -1.0,
1.0,-1.0, -1.0, 0.0, 0.0, -1.0,
-1.0, -1.0, -1.0, 0.0, 0.0, -1.0,
1.0, 1.0, -1.0, 0.0, 0.0, -1.0,
-1.0, -1.0, -1.0, 0.0, 0.0, -1.0,
-1.0, 1.0, -1.0, 0.0, 0.0, -1.0,
1.0, 1.0, 1.0,1.0, 0.0, 0.0,
1.0, -1.0, 1.0,1.0, 0.0, 0.0,
1.0, -1.0,-1.0,1.0, 0.0, 0.0,
1.0, 1.0, 1.0,1.0, 0.0, 0.0,
1.0, -1.0, -1.0,1.0, 0.0, 0.0,
1.0, 1.0, -1.0,1.0, 0.0, 0.0
};
long current_frame = 0.0;
long delta_time = 0.0;
GDateTime *last_frame;
int dt = 0;
static GLuint position_buffer;
static GLuint program;
static GLuint vao;
mat4 model = mat4(1.0);
/* Create and compile a shader */
static GLuint
create_shader (int type)
{
GLuint shader;
int status;
shader = glCreateShader (type);
if (type== GL_FRAGMENT_SHADER){
glShaderSource (shader, 1, &FRAGMENT_SOURCE, NULL);
}
if (type== GL_VERTEX_SHADER){
glShaderSource (shader, 1, &VERTEX_SOURCE, NULL);
}
glCompileShader (shader);
glGetShaderiv (shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
int log_len;
char *buffer;
glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &log_len);
buffer = (char*)g_malloc (log_len + 1);
glGetShaderInfoLog (shader, log_len, NULL, buffer);
g_warning ("Compile failure in %s shader:\n%s",
type == GL_VERTEX_SHADER ? "vertex" : "fragment",
buffer);
g_free (buffer);
glDeleteShader (shader);
return 0;
}
return shader;
}
/* We need to set up our state when we realize the GtkGLArea widget */
static void
realize (GtkWidget *widget)
{
GdkGLContext *context;
gtk_gl_area_make_current (GTK_GL_AREA (widget));
if (gtk_gl_area_get_error (GTK_GL_AREA (widget)) != NULL)
return;
context = gtk_gl_area_get_context (GTK_GL_AREA (widget));
/* We only use one VAO, so we always keep it bound */
glGenVertexArrays (1, &vao);
glBindVertexArray (vao);
/* This is the buffer that holds the vertices */
glGenBuffers (1, &position_buffer);
glBindBuffer (GL_ARRAY_BUFFER, position_buffer);
glBufferData(GL_ARRAY_BUFFER,sizeof(vertex_data),vertex_data,GL_STATIC_DRAW);
glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray (0);
glVertexAttribPointer (1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray (1);
glBindBuffer (GL_ARRAY_BUFFER, 0);
GLuint vertex, fragment;
int status;
vertex = create_shader (GL_VERTEX_SHADER);
if (vertex == 0)
{
return;
}
fragment = create_shader (GL_FRAGMENT_SHADER);
if (fragment == 0)
{
glDeleteShader (vertex);
return;
}
program = glCreateProgram ();
glAttachShader (program, vertex);
glAttachShader (program, fragment);
glLinkProgram (program);
glGetProgramiv (program, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
int log_len;
char *buffer;
glGetProgramiv (program, GL_INFO_LOG_LENGTH, &log_len);
buffer = (char*)g_malloc (log_len + 1);
glGetProgramInfoLog (program, log_len, NULL, buffer);
g_warning ("Linking failure:\n%s", buffer);
g_free (buffer);
glDeleteProgram (program);
program = 0;
glDeleteShader (vertex);
glDeleteShader (fragment);
return;
}
glDetachShader (program, vertex);
glDetachShader (program, fragment);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
glEnable(GL_DEPTH_TEST);
}
/* We should tear down the state when unrealizing */
static void
unrealize (GtkWidget *widget)
{
gtk_gl_area_make_current (GTK_GL_AREA (widget));
if (gtk_gl_area_get_error (GTK_GL_AREA (widget)) != NULL)
return;
glDeleteBuffers (1, &position_buffer);
glDeleteProgram (program);
}
static void
draw_box (long delta_time)
{
/* Use our shaders */
glUseProgram (program);
model = rotate(model, (float)delta_time/1000, vec3(1,1,0));
glUniformMatrix4fv(glGetUniformLocation(program, "model"), 1, GL_FALSE, &model[0][0]);
vec3 position = vec3(0,0,5);
vec3 front = vec3(0,0,-1);
vec3 up = vec3(0,1,0);
mat4 view = lookAt(position, position + front, up);
glUniformMatrix4fv(glGetUniformLocation(program, "view"), 1, GL_FALSE, &view[0][0]);
mat4 projection = perspective(45.0, double(WIDTH)/double(HEIGHT), 0.1, 100.0);
glUniformMatrix4fv(glGetUniformLocation(program, "projection"), 1, GL_FALSE, &projection[0][0]);
glBindVertexArray(vao);
/* Use the vertices in our buffer */
/* Draw the three vertices as a triangle */
glDrawArrays (GL_TRIANGLES, 0, 36);
/* We finished using the buffers and program */
glBindVertexArray(0);
glDisableVertexAttribArray (0);
glBindBuffer (GL_ARRAY_BUFFER, 0);
glUseProgram (0);
}
static gboolean
render (GtkGLArea *area,
GdkGLContext *context)
{
GDateTime *date_time;
date_time = g_date_time_new_now_local();
current_frame = g_date_time_get_microsecond(date_time);
delta_time = g_date_time_difference(date_time, last_frame) / 1000;
last_frame = date_time;
if (gtk_gl_area_get_error (area) != NULL)
return FALSE;
/* Clear the viewport */
glClearColor (0.0, 0.0, 0.0, 1.0);
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* Draw our object */
draw_box (delta_time);
/* Flush the contents of the pipeline */
glFlush ();
gtk_gl_area_queue_render (area);
return TRUE;
}
static void
on_axis_value_change (void)
{
gtk_widget_queue_draw (gl_area);
}
int main(int argc, char **argv)
{
GtkWidget *window, *box;
/* initialize gtk */
gtk_init(&argc, &argv);
/* Create new top level window. */
window = gtk_window_new( GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW(window),WIDTH,HEIGHT);
gtk_window_set_title(GTK_WINDOW(window), "GL Area");
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE);
g_object_set (box, "margin", 12, NULL);
gtk_box_set_spacing (GTK_BOX (box), 6);
gtk_container_add (GTK_CONTAINER (window), box);
gl_area = gtk_gl_area_new ();
gtk_box_pack_start (GTK_BOX(box), gl_area,1,1, 0);
/* We need to initialize and free GL resources, so we use
* the realize and unrealize signals on the widget
*/
g_signal_connect (gl_area, "realize", G_CALLBACK (realize), NULL);
g_signal_connect (gl_area, "unrealize", G_CALLBACK (unrealize), NULL);
/* The main "draw" call for GtkGLArea */
g_signal_connect (gl_area, "render", G_CALLBACK (render), NULL);
/* Quit form main if got delete event */
g_signal_connect(G_OBJECT(window), "delete-event",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(GTK_WIDGET(window));
gtk_main();
return 0;
}
Result:
And here is a "hello triangle" example in pygtk if you want a minimal python example.

Can't assign C++ array

Context: Need to set verts in TestTextures3SpriteObj s1 to the verts1 array. Gives me an error "expression must be modifiable lvalue". After its copied the vertices will be sent to the GPU as buffer data with OpenGL and GLUT.
Only relevant excerpts of code included
#pragma once
class TestTextures3SpriteObj
{
public:
int spriteid;
int vao;
int texid;
float verts[];
};
const float verts1[] = { 0.5 ,0.5, 0.0, 0.9, 0.5, 0.3, 0.0, 1.0, 0.0,
0.5, -0.5, 0.0, 0.3, 0.3, 0.9, 1.0, 1.0, 1.0,
-0.5, -0.5, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0,
-0.5, 0.5, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0 };
TestTextures3SpriteObj s1;
s1.verts = verts1;
Actually you are not accessing the variable ...
if you want access individual element, use index
s1.verts1[0]
If you want to copy use std::copy
std::copy(verts1, verts1 + 36, s1.verts);
#include <iostream>
using namespace std;
class TestTextures3SpriteObj
{
public:
int spriteid;
int vao;
int texid;
float verts[36]; //assign the size to the array
};
const float verts1[] = { 0.5 ,0.5, 0.0, 0.9, 0.5, 0.3, 0.0, 1.0, 0.0,
0.5, -0.5, 0.0, 0.3, 0.3, 0.9, 1.0, 1.0, 1.0,
-0.5, -0.5, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0,
-0.5, 0.5, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0 };
int main()
{
TestTextures3SpriteObj s1;
int len=sizeof(verts1)/sizeof(verts1[0]);
//copies the entire array to the object with member verts
std::copy(verts1, verts1 + 36, s1.verts);
//printing the values in the s1 object
for(int i=0;i<len;i++)
{
cout<<s1.verts[i]<<" ";
}
}
Assign a size to the array in the class and then later perform the std::copy to copy the values in the verts array.

Visual Studios 2012 RGB cube displaying black cube

I'm just starting out using visual studios and C++ and I've been following along the examples in Ed Angel's Interactive Computer Graphics book. I've seem to come into a snag with the rotating RGB cube example. I followed along exactly and I have got it to run and display a cube but the cube is all black instead of colored. To my understanding everything is correct and I have haven't been able to find any suggestions or hints as to what went wrong from google searches. I was wondering if anyone here with much more experience than I have could help me figure out what happened or point me in the direction.
// Display a rotating color cube
#include "Angel.h"
typedef Angel::vec4 color4;
typedef Angel::vec4 point4;
const int NumVertices = 36;
point4 points[NumVertices];
color4 colors[NumVertices];
//Vertices of a unit cube centered at origin, sides aligned with axes
point4 vertices[8] = {
point4( -0.5, -0.5, 0.5, 1.0),
point4( -0.5, 0.5, 0.5, 1.0),
point4( 0.5, 0.5, 0.5, 1.0),
point4( 0.5, -0.5, 0.5, 1.0),
point4( -0.5, -0.5, -0.5, 1.0),
point4( -0.5, 0.5, -0.5, 1.0),
point4( 0.5, 0.5, -0.5, 1.0),
point4( 0.5, -0.5, -0.5, 1.0)
};
//RGBA colors
color4 vertex_colors[8] = {
color4( 0.0, 0.0, 0.0, 1.0), //black
color4( 1.0, 0.0, 0.0, 1.0), //red
color4( 1.0, 1.0, 0.0, 1.0), //yellow
color4( 0.0, 1.0, 0.0, 1.0), //blue
color4( 0.0, 0.0, 1.0, 1.0), //green
color4( 1.0, 0.0, 1.0, 1.0), //magenta
color4( 1.0, 1.0, 1.0, 1.0), //white
color4( 0.0, 1.0, 1.0, 1.0) //cyan
};
//Array of rotation angles (in degrees) for each coordinate axis
enum { Xaxis = 0, Yaxis = 1, Zaxis = 2, NumAxes = 3 };
int Axis = Xaxis;
GLfloat Theta[NumAxes] = { 0.0, 0.0, 0.0};
GLuint theta; //The location of the "theta" shader uniform variable
//----------------------------------------------------------------------------
//quad generates two triangles for each face and assigns colors to the vertices
int Index = 0;
void
quad( int a, int b, int c, int d)
{
colors[Index] = vertex_colors[a]; points[Index] = vertices[a]; Index++;
colors[Index] = vertex_colors[b]; points[Index] = vertices[b]; Index++;
colors[Index] = vertex_colors[c]; points[Index] = vertices[c]; Index++;
colors[Index] = vertex_colors[a]; points[Index] = vertices[a]; Index++;
colors[Index] = vertex_colors[c]; points[Index] = vertices[c]; Index++;
colors[Index] = vertex_colors[d]; points[Index] = vertices[d]; Index++;
}
//----------------------------------------------------------------------------
//generate 12 triangles: 36 vertices and 36 colors
void
colorcube( void )
{
quad( 1, 0, 3, 2);
quad( 2, 3, 7, 6);
quad( 3, 0, 4, 7);
quad( 6, 5, 1, 2);
quad( 4, 5, 6, 7);
quad( 5, 4, 0, 1);
}
//----------------------------------------------------------------------------
//OpenGL initialization
void
init( void )
{
colorcube();
//Create a vertex array object
GLuint vao;
glGenVertexArrays( 1, &vao );
glBindVertexArray( vao );
//Create and initialize a buffer object
GLuint buffer;
glGenBuffers( 1, &buffer );
glBindBuffer( GL_ARRAY_BUFFER, buffer );
glBufferData( GL_ARRAY_BUFFER, sizeof(points) + sizeof(colors), NULL, GL_STATIC_DRAW );
glBufferSubData( GL_ARRAY_BUFFER, 0, sizeof(points), points );
glBufferSubData( GL_ARRAY_BUFFER, sizeof(points), sizeof(colors), colors );
//Load shaders and use the resulting shader program
GLuint program = InitShader( "vshader36.glsl", "fshader36.glsl" );
std::cout << "Program ID:" <<program;
glUseProgram ( program );
//set up vertex arrays
GLuint vPosition = glGetAttribLocation( program, "vPosition" );
glEnableVertexAttribArray( vPosition );
glVertexAttribPointer( vPosition, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(sizeof(points)) );
theta = glGetUniformLocation( program, "theta" );
glEnable( GL_DEPTH_TEST );
glClearColor( 1.0, 1.0, 1.0, 1.0 );
}
//----------------------------------------------------------------------------
void
display( void )
{
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glUniform3fv( theta, 1, Theta );
glDrawArrays( GL_TRIANGLES, 0, NumVertices );
glutSwapBuffers();
}
//----------------------------------------------------------------------------
void
keyboard( unsigned char key, int x, int y )
{
switch ( key ) {
case 033:
case 'q': case 'Q':
exit( EXIT_SUCCESS );
break;
}
}
//----------------------------------------------------------------------------
void
mouse( int button, int state, int x, int y )
{
if ( state == GLUT_DOWN ) {
switch( button ) {
case GLUT_LEFT_BUTTON: Axis = Xaxis; break;
case GLUT_MIDDLE_BUTTON: Axis = Yaxis; break;
case GLUT_RIGHT_BUTTON: Axis = Zaxis; break;
}
}
}
//----------------------------------------------------------------------------
void
idle( void )
{
Theta[Axis] += 0.01;
if ( Theta[Axis] > 360.0 ) {
Theta[Axis] -= 360.0;
}
glutPostRedisplay();
}
//----------------------------------------------------------------------------
int
main( int argc, char **argv )
{
glutInit(&argc, argv);
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );
glutInitWindowSize( 512, 512 );
glutCreateWindow( "Color Cube" );
glewInit();
init();
glutDisplayFunc( display );
glutKeyboardFunc( keyboard );
glutMouseFunc( mouse );
glutIdleFunc( idle );
glutMainLoop();
return 0;
}
fshader36.glsl
#version 150
in vec4 color;
out vec4 fColor;
void main()
{
fColor = color;
}
vshader36.glsl
#version 150
in vec4 vPosition;
in vec4 vColor;
out vec4 color;
uniform vec3 theta;
void main()
{
vec3 angles = radians( theta );
vec3 c = cos( angles );
vec3 s = sin( angles );
mat4 rx = mat4( 1.0, 0.0, 0.0, 0.0,
0.0, c.x, -s.x, 0.0,
0.0, s.x, c.x, 0.0,
0.0, 0.0, 0.0, 1.0 );
mat4 ry = mat4( c.y, 0.0, s.y, 0.0,
0.0, 1.0, 0.0, 0.0,
-s.y, 0.0, c.y, 0.0,
0.0, 0.0, 0.0, 1.0 );
mat4 rz = mat4( c.z, -s.z, 0.0, 0.0,
s.z, c.z, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0 );
color = vColor;
gl_Position = rz * ry * rx * vPosition;
}
You have to actually set vColor for it to have a valid value. Do the same thing that you did to set vPosition:
GLuint vColor= glGetAttribLocation( program, "vColor" );
glEnableVertexAttribArray( vColor);
glVertexAttribPointer( vColor, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(sizeof(colors)) );
You don't actually pass any colour information to your shader.

Is there a way to apply a sinewave distortion effect purely in shaders?

Sinewave distortion of a 2D image is a classic visual effect: taking a 2D image and warping it along either the X or the Y axis by shifting pixels according to a sine wave. It ends up looking something like this:
I've seen a few examples of code for it, and the standard way to do this with OpenGL seems to be, for a an image of dimensions (x, y):
for each column from 0 to X
draw a single quad one pixel wide and y pixels high, offset by a sine wave value
Of course, this involves a lot of work on the client-side. Is there any way to draw a single quad and offload the distortion work to the GPU with shaders? Only vertex and fragment shaders; I'm using OpenGL 2, so there are no geometry shaders available.
I know I could use a fragment shader to sample texture coordinates that are offset by a sine wave, but getting them to place at locations outside the original box defined by the quad would be tricky, and I'd prefer not to have the output be clipped like in the sample picture. Is there any way around this problem?
Yes, this can be done using shaders. Using a vertex shader you can apply a sine distortion on a grid. A fragment shader can modulate the texture coordinate, but not the target pixel location; fragment shaders are gatherers and can not do data scattering.
Update
Working example for texture coordinate modulation:
#include <stdlib.h>
#include <stdio.h>
#include <GL/glew.h>
#include <GL/glfw.h>
static void pushModelview()
{
GLenum prev_matrix_mode;
glGetIntegerv(GL_MATRIX_MODE, &prev_matrix_mode);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glMatrixMode(prev_matrix_mode);
}
static void popModelview()
{
GLenum prev_matrix_mode;
glGetIntegerv(GL_MATRIX_MODE, &prev_matrix_mode);
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(prev_matrix_mode);
}
static const GLchar *vertex_shader_source =
"#version 130\n"
"void main()"
"{"
" gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;"
" gl_TexCoord[0] = gl_MultiTexCoord0;"
" gl_FrontColor = gl_Color;"
" gl_BackColor = gl_Color;"
"}\0";
GLuint shaderVertex = 0;
static const GLchar *fragment_shader_source =
"#version 130\n"
"uniform sampler2D texCMYK;\n"
"uniform sampler2D texRGB;\n"
"uniform float T;\n"
"const float pi = 3.14159265;\n"
"void main()\n"
"{\n"
" float ts = gl_TexCoord[0].s;\n"
" vec2 mod_texcoord = gl_TexCoord[0].st + vec2(0, 0.5*sin(T + 1.5*ts*pi));\n"
" gl_FragColor = -texture2D(texCMYK, mod_texcoord) + texture2D(texRGB, gl_TexCoord[0].st);\n"
"}\n\0";
GLuint shaderFragment = 0;
GLuint shaderProgram = 0;
#define TEX_CMYK_WIDTH 2
#define TEX_CMYK_HEIGHT 2
GLubyte textureDataCMYK[TEX_CMYK_WIDTH * TEX_CMYK_HEIGHT][3] = {
{0x00, 0xff, 0xff}, {0xff, 0x00, 0xff},
{0xff, 0xff, 0x00}, {0x00, 0x00, 0x00}
};
GLuint texCMYK = 0;
#define TEX_RGB_WIDTH 2
#define TEX_RGB_HEIGHT 2
GLubyte textureDataRGB[TEX_RGB_WIDTH * TEX_RGB_HEIGHT][3] = {
{0x00, 0x00, 0xff}, {0xff, 0xff, 0xff},
{0xff, 0x00, 0x00}, {0x00, 0xff, 0x00}
};
GLuint texRGB = 0;
GLfloat cube_vertices[][8] = {
/* X Y Z Nx Ny Nz S T */
{-1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0}, // 0
{ 1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0}, // 1
{ 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0}, // 2
{-1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0}, // 3
{ 1.0, -1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0},
{-1.0, -1.0, -1.0, 0.0, 0.0, -1.0, 1.0, 0.0},
{-1.0, 1.0, -1.0, 0.0, 0.0, -1.0, 1.0, 1.0},
{ 1.0, 1.0, -1.0, 0.0, 0.0, -1.0, 0.0, 1.0},
{-1.0, -1.0, 1.0, -1.0, 0.0, 0.0, 0.0, 0.0},
{-1.0, -1.0, -1.0, -1.0, 0.0, 0.0, 1.0, 0.0},
{-1.0, 1.0, -1.0, -1.0, 0.0, 0.0, 1.0, 1.0},
{-1.0, 1.0, 1.0, -1.0, 0.0, 0.0, 0.0, 1.0},
{ 1.0, -1.0, -1.0, 1.0, 0.0, 0.0, 0.0, 0.0},
{ 1.0, -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0},
{ 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0},
{ 1.0, 1.0, -1.0, 1.0, 0.0, 0.0, 0.0, 1.0},
{ 1.0, -1.0, -1.0, 0.0, -1.0, 0.0, 0.0, 0.0},
{-1.0, -1.0, -1.0, 0.0, -1.0, 0.0, 1.0, 0.0},
{-1.0, -1.0, 1.0, 0.0, -1.0, 0.0, 1.0, 1.0},
{ 1.0, -1.0, 1.0, 0.0, -1.0, 0.0, 0.0, 1.0},
{-1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0},
{ 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0},
{ 1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 1.0, 1.0},
{-1.0, 1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 1.0},
};
static void draw_cube(void)
{
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(GLfloat) * 8, &cube_vertices[0][0]);
glNormalPointer(GL_FLOAT, sizeof(GLfloat) * 8, &cube_vertices[0][3]);
glTexCoordPointer(2, GL_FLOAT, sizeof(GLfloat) * 8, &cube_vertices[0][6]);
glDrawArrays(GL_QUADS, 0, 24);
}
static void bind_sampler_to_unit_with_texture(GLchar const * const sampler_name, GLuint texture_unit, GLuint texture)
{
glActiveTexture(GL_TEXTURE0 + texture_unit);
glBindTexture(GL_TEXTURE_2D, texture);
GLuint loc_sampler = glGetUniformLocation(shaderProgram, sampler_name);
glUniform1i(loc_sampler, texture_unit);
}
static void display(double T)
{
int window_width, window_height;
glfwGetWindowSize(&window_width, &window_height);
if( !window_width || !window_height )
return;
const float window_aspect = (float)window_width / (float)window_height;
glDisable(GL_SCISSOR_TEST);
glClearColor(0.5, 0.5, 0.7, 1.0);
glClearDepth(1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glViewport(0, 0, window_width, window_height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-window_aspect, window_aspect, -1, 1, 1, 100);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0, 0, -5);
pushModelview();
glRotatef(T * 0.1 * 180, 0., 1., 0.);
glRotatef(T * 0.1 * 60, 1., 0., 0.);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glUseProgram(shaderProgram);
glUniform1f(glGetUniformLocation(shaderProgram, "T"), T);
bind_sampler_to_unit_with_texture("texCMYK", 0, texCMYK);
bind_sampler_to_unit_with_texture("texRGB", 1, texRGB);
draw_cube();
popModelview();
glfwSwapBuffers();
}
static int open_window(void)
{
#if 0
glfwWindowHint(GLFW_OPENGL_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_OPENGL_VERSION_MINOR, 0);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);
#endif
if( glfwOpenWindow(0, 0, /* default size */
8, 8, 8, /* 8 bits per channel */
8, 24, 8, /* 8 alpha, 24 depth, 8 stencil */
GLFW_WINDOW) != GL_TRUE ) {
fputs("Could not open window.\n", stderr);
return 0;
}
if( glewInit() != GLEW_OK ) {
fputs("Could not initialize extensions.\n", stderr);
return 0;
}
return 1;
}
static int check_extensions(void)
{
if( !GLEW_ARB_vertex_shader ||
!GLEW_ARB_fragment_shader ) {
fputs("Required OpenGL functionality not supported by system.\n", stderr);
return 0;
}
return 1;
}
static int check_shader_compilation(GLuint shader)
{
GLint n;
glGetShaderiv(shader, GL_COMPILE_STATUS, &n);
if( n == GL_FALSE ) {
GLchar *info_log;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &n);
info_log = malloc(n);
glGetShaderInfoLog(shader, n, &n, info_log);
fprintf(stderr, "Shader compilation failed: %*s\n", n, info_log);
free(info_log);
return 0;
}
return 1;
}
static int init_resources(void)
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glGenTextures(1, &texCMYK);
glBindTexture(GL_TEXTURE_2D, texCMYK);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, TEX_CMYK_WIDTH, TEX_CMYK_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, textureDataCMYK);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glGenTextures(1, &texRGB);
glBindTexture(GL_TEXTURE_2D, texRGB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, TEX_RGB_WIDTH, TEX_RGB_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, textureDataRGB);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
shaderVertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(shaderVertex, 1, (const GLchar**)&vertex_shader_source, NULL);
glCompileShader(shaderVertex);
if( !check_shader_compilation(shaderVertex) )
return 0;
shaderFragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(shaderFragment, 1, (const GLchar**)&fragment_shader_source, NULL);
glCompileShader(shaderFragment);
if( !check_shader_compilation(shaderFragment) )
return 0;
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, shaderVertex);
glAttachShader(shaderProgram, shaderFragment);
glLinkProgram(shaderProgram);
return 1;
}
static void main_loop(void)
{
glfwSetTime(0);
while( glfwGetWindowParam(GLFW_OPENED) == GL_TRUE ) {
display(glfwGetTime());
}
}
int main(int argc, char *argv[])
{
if( glfwInit() != GL_TRUE ) {
fputs("Could not initialize framework.\n", stderr);
return -1;
}
if( !open_window() )
return -1;
if( !check_extensions() )
return -1;
if( !init_resources() )
return -1;
main_loop();
glfwTerminate();
return 0;
}
The fragment shader part is this:
#version 130
uniform sampler2D texCMYK;
uniform sampler2D texRGB;
uniform float T;
const float pi = 3.14159265;
void main()
{
float ts = gl_TexCoord[0].s;
vec2 mod_texcoord = gl_TexCoord[0].st + vec2(0, 0.5*sin(T + 1.5*ts*pi));
gl_FragColor = -texture2D(texCMYK, mod_texcoord) + texture2D(texRGB, gl_TexCoord[0].st);
};
Update – a shader that "expands":
uniform sampler2D texCMYK;
uniform sampler2D texRGB;
uniform float T;
const float pi = 3.14159265;
void main()
{
float ts = gl_TexCoord[0].s;
vec2 mod_texcoord = gl_TexCoord[0].st*vec2(1., 2.) + vec2(0, -0.5 + 0.5*sin(T + 1.5*ts*pi));
if( mod_texcoord.t < 0. || mod_texcoord.t > 1. ) { discard; }
gl_FragColor = -texture2D(texCMYK, mod_texcoord) + texture2D(texRGB, gl_TexCoord[0].st);
};
For a given input quad render a quad 2 * max_amplitude taller (maybe with a vertex shader?) and in your pixel shader discard pixels that aren't currently being sin()'d onto.
That way you can reach "outside" your original quad.