Related
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.
I'm new to OpenGl and I was working on texturing. I want to draw one image on three sides and another image on other three sides of cube. I'm unable to do it. I want to know at what point in my code is cube being drawn on window?
This is my code:
#include "06_texturing.hpp"
#include "texture.hpp"
GLuint shaderProgram;
GLuint vbo[2], vao[2];
GLuint tex;
glm::mat4 rotation_matrix;
glm::mat4 projection_matrix;
glm::mat4 c_rotation_matrix;
glm::mat4 lookat_matrix;
glm::mat4 model_matrix;
glm::mat4 view_matrix;
glm::mat4 modelview_matrix;
glm::mat3 normal_matrix;
GLuint uModelViewMatrix;
GLuint viewMatrix;
GLuint normalMatrix;
//-----------------------------------------------------------------
//6 faces, 2 triangles/face, 3 vertices/triangle
const int num_vertices = 36;
glm::vec4 texCoordinates[8];
//Eight vertices in homogenous coordinates
glm::vec4 positions[8] = {
glm::vec4(-0.5, -0.5, 0.5, 1.0),
glm::vec4(-0.5, 0.5, 0.5, 1.0),
glm::vec4(0.5, 0.5, 0.5, 1.0),
glm::vec4(0.5, -0.5, 0.5, 1.0),
glm::vec4(-0.5, -0.5, -0.5, 1.0),
glm::vec4(-0.5, 0.5, -0.5, 1.0),
glm::vec4(0.5, 0.5, -0.5, 1.0),
glm::vec4(0.5, -0.5, -0.5, 1.0)
};
glm::vec4 normals[8] = {
glm::vec4(-0.5, -0.5, 0.5, 1.0),
glm::vec4(-0.5, 0.5, 0.5, 1.0),
glm::vec4(0.5, 0.5, 0.5, 1.0),
glm::vec4(0.5, -0.5, 0.5, 1.0),
glm::vec4(-0.5, -0.5, -0.5, 1.0),
glm::vec4(-0.5, 0.5, -0.5, 1.0),
glm::vec4(0.5, 0.5, -0.5, 1.0),
glm::vec4(0.5, -0.5, -0.5, 1.0)
};
//RGBA colors
glm::vec4 colors[8] = {
glm::vec4(0.0, 0.0, 0.0, 1.0),
glm::vec4(1.0, 0.0, 0.0, 1.0),
glm::vec4(1.0, 1.0, 0.0, 1.0),
glm::vec4(0.0, 1.0, 0.0, 1.0),
glm::vec4(0.0, 0.0, 1.0, 1.0),
glm::vec4(1.0, 0.0, 1.0, 1.0),
glm::vec4(1.0, 1.0, 1.0, 1.0),
glm::vec4(0.0, 1.0, 1.0, 1.0)
};
glm::vec2 t_coords[4] = {
glm::vec2( 0.0, 0.0),
glm::vec2( 0.0, 1.0),
glm::vec2( 1.0, 0.0),
glm::vec2( 1.0, 1.0)
};
glm::vec4 color(0.6, 0.6, 0.6, 1.0);
glm::vec4 black(0.1, 0.1, 0.1, 1.0);
glm::vec4 white(0.2, 0.7, 0.7, 1.0);
glm::vec4 red(1.0, 0.2, 0.2, 1.0);
glm::vec4 yellow(0.8, 0.8, 0.0, 1.0);
glm::vec4 green(0.2, 0.7, 0.2, 1.0);
glm::vec4 blue(0.2, 0.2, 0.7, 1.0);
int tri_idx=0;
glm::vec4 v_positions[num_vertices];
glm::vec4 v_colors[num_vertices];
glm::vec4 v_normals[num_vertices];
glm::vec2 tex_coords[num_vertices];
// quad generates two triangles for each face and assigns colors to the vertices
void quad(int a, int b, int c, int d, glm::vec4 color)
{
v_colors[tri_idx] = color; v_positions[tri_idx] = positions[a];
v_normals[tri_idx] = normals[a];
tex_coords[tri_idx] = t_coords[1];
tri_idx++;
v_colors[tri_idx] = color; v_positions[tri_idx] = positions[b];
v_normals[tri_idx] = normals[b];
tex_coords[tri_idx] = t_coords[0];
tri_idx++;
v_colors[tri_idx] = color; v_positions[tri_idx] = positions[c];
v_normals[tri_idx] = normals[c];
tex_coords[tri_idx] = t_coords[2];
tri_idx++;
v_colors[tri_idx] = color; v_positions[tri_idx] = positions[a];
v_normals[tri_idx] = normals[a];
tex_coords[tri_idx] = t_coords[1];
tri_idx++;
v_colors[tri_idx] = color; v_positions[tri_idx] = positions[c];
v_normals[tri_idx] = normals[c];
tex_coords[tri_idx] = t_coords[2];
tri_idx++;
v_colors[tri_idx] = color; v_positions[tri_idx] = positions[d];
v_normals[tri_idx] = normals[d];
tex_coords[tri_idx] = t_coords[3];
tri_idx++;
}
// generate 12 triangles: 36 vertices and 36 colors
void colorcube(void)
{
quad( 1, 0, 3, 2, red);
quad( 2, 3, 7, 6, green);
quad( 3, 0, 4, 7, white);
quad( 6, 5, 1, 2, yellow);
quad( 4, 5, 6, 7, black);
quad( 5, 4, 0, 1, blue);
}
//-----------------------------------------------------------------
void initBuffersGL(void)
{
// Load shaders and use the resulting shader program
std::string vertex_shader_file("06_vshader.glsl");
std::string fragment_shader_file("06_fshader.glsl");
std::vector<GLuint> shaderList;
shaderList.push_back(csX75::LoadShaderGL(GL_VERTEX_SHADER, vertex_shader_file));
shaderList.push_back(csX75::LoadShaderGL(GL_FRAGMENT_SHADER, fragment_shader_file));
shaderProgram = csX75::CreateProgramGL(shaderList);
glUseProgram( shaderProgram );
// getting the attributes from the shader program
GLuint vPosition = glGetAttribLocation( shaderProgram, "vPosition" );
GLuint vColor = glGetAttribLocation( shaderProgram, "vColor" );
GLuint vNormal = glGetAttribLocation( shaderProgram, "vNormal" );
GLuint texCoord = glGetAttribLocation( shaderProgram, "texCoord" );
uModelViewMatrix = glGetUniformLocation( shaderProgram, "uModelViewMatrix");
normalMatrix = glGetUniformLocation( shaderProgram, "normalMatrix");
viewMatrix = glGetUniformLocation( shaderProgram, "viewMatrix");
// Load Textures
GLuint tex=LoadTexture("images/all1.bmp",256,256);
GLuint tex2=LoadTexture("images/another.bmp",256,256);
glBindTexture(GL_TEXTURE_2D, tex);
//Ask GL for two Vertex Attribute Objects (vao) , one for the sphere and one for the wireframe
glGenVertexArrays (2, vao);
//Ask GL for two Vertex Buffer Object (vbo)
glGenBuffers (2, vbo);
//Set 0 as the current array to be used by binding it
glBindVertexArray (vao[0]);
//Set 0 as the current buffer to be used by binding it
glBindBuffer (GL_ARRAY_BUFFER, vbo[0]);
colorcube();
//Copy the points into the current buffer
glBufferData (GL_ARRAY_BUFFER, sizeof (v_positions) + sizeof(tex_coords) + sizeof(v_normals), NULL, GL_STATIC_DRAW);
glBufferSubData( GL_ARRAY_BUFFER, 0, sizeof(v_positions), v_positions );
glBufferSubData( GL_ARRAY_BUFFER, sizeof(v_positions), sizeof(tex_coords), tex_coords);
glBufferSubData( GL_ARRAY_BUFFER, sizeof(tex_coords)+sizeof(v_positions), sizeof(v_normals), v_normals );
// set up vertex array
//Position
glEnableVertexAttribArray( vPosition );
glVertexAttribPointer( vPosition, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );
//Textures
glEnableVertexAttribArray( texCoord );
glVertexAttribPointer( texCoord, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(sizeof(v_positions)) );
//Normal
glEnableVertexAttribArray( vNormal );
glVertexAttribPointer( vNormal, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(sizeof(v_positions)+sizeof(tex_coords)) );
}
void renderGL(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
rotation_matrix = glm::rotate(glm::mat4(1.0f), glm::radians(xrot), glm::vec3(1.0f,0.0f,0.0f));
rotation_matrix = glm::rotate(rotation_matrix, glm::radians(yrot), glm::vec3(0.0f,1.0f,0.0f));
rotation_matrix = glm::rotate(rotation_matrix, glm::radians(zrot), glm::vec3(0.0f,0.0f,1.0f));
model_matrix = rotation_matrix;
//Creating the lookat and the up vectors for the camera
c_rotation_matrix = glm::rotate(glm::mat4(1.0f), glm::radians(c_xrot), glm::vec3(1.0f,0.0f,0.0f));
c_rotation_matrix = glm::rotate(c_rotation_matrix, glm::radians(c_yrot), glm::vec3(0.0f,1.0f,0.0f));
c_rotation_matrix = glm::rotate(c_rotation_matrix, glm::radians(c_zrot), glm::vec3(0.0f,0.0f,1.0f));
glm::vec4 c_pos = glm::vec4(c_xpos,c_ypos,c_zpos, 1.0)*c_rotation_matrix;
glm::vec4 c_up = glm::vec4(c_up_x,c_up_y,c_up_z, 1.0)*c_rotation_matrix;
//Creating the lookat matrix
lookat_matrix = glm::lookAt(glm::vec3(c_pos),glm::vec3(0.0),glm::vec3(c_up));
//creating the projection matrix
projection_matrix = glm::frustum(-1.0, 1.0, -1.0, 1.0, 1.0, 5.0);
view_matrix = projection_matrix*lookat_matrix;
glUniformMatrix4fv(viewMatrix, 1, GL_FALSE, glm::value_ptr(view_matrix));
// Draw the sphere
modelview_matrix = view_matrix*model_matrix;
glUniformMatrix4fv(uModelViewMatrix, 1, GL_FALSE, glm::value_ptr(modelview_matrix));
normal_matrix = glm::transpose (glm::inverse(glm::mat3(modelview_matrix)));
glUniformMatrix3fv(normalMatrix, 1, GL_FALSE, glm::value_ptr(normal_matrix));
// glBindTexture(GL_TEXTURE_2D, tex);
glBindVertexArray (vao[0]);
glDrawArrays(GL_TRIANGLES, 0, num_vertices);
}
int main(int argc, char** argv)
{
//! The pointer to the GLFW window
GLFWwindow* window;
//! Setting up the GLFW Error callback
glfwSetErrorCallback(csX75::error_callback);
//! Initialize GLFW
if (!glfwInit())
return -1;
//We want OpenGL 4.0
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//This is for MacOSX - can be omitted otherwise
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
//We don't want the old OpenGL
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//! Create a windowed mode window and its OpenGL context
window = glfwCreateWindow(512, 512, "CS475/CS675 Tutorial 6: Texturing a cube", NULL, NULL);
if (!window)
{
glfwTerminate();
return -1;
}
//! Make the window's context current
glfwMakeContextCurrent(window);
//Initialize GLEW
//Turn this on to get Shader based OpenGL
glewExperimental = GL_TRUE;
GLenum err = glewInit();
if (GLEW_OK != err)
{
//Problem: glewInit failed, something is seriously wrong.
std::cerr<<"GLEW Init Failed : %s"<<std::endl;
}
//Keyboard Callback
glfwSetKeyCallback(window, csX75::key_callback);
//Framebuffer resize callback
glfwSetFramebufferSizeCallback(window, csX75::framebuffer_size_callback);
// Ensure we can capture the escape key being pressed below
glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
//Initialize GL state
csX75::initGL();
initBuffersGL();
// Loop until the user closes the window
while (glfwWindowShouldClose(window) == 0)
{
// Render here
renderGL();
// Swap front and back buffers
glfwSwapBuffers(window);
// Poll for and process events
glfwPollEvents();
}
glfwTerminate();
return 0;
}
I want to change in it to render different images on different sides.
If you want to use 2 completely different textures, in the fragment shader, then you have to bind the textures to 2 different texture units. The texture unit can be set by glActiveTexture:
GLuint tex=LoadTexture("images/all1.bmp",256,256);
GLuint tex2=LoadTexture("images/another.bmp",256,256);
int unit_tex0 = 0; // texture unit 0 and binding point 0
int unit_tex1 = 1; // texture unit 1 and binding point 1
glActiveTexture( GL_TEXTURE0 + unit_tex0 );
glBindTexture(GL_TEXTURE_2D, tex);
glActiveTexture( GL_TEXTURE0 + unit_tex1 );
glBindTexture(GL_TEXTURE_2D, tex2);
Further you have to declare 2 texture samplers in the fragment shader. The texture samplers have to be associated to the texture units. Since GLSL version 4.2 this can be done in the fragment shader by specifying binding points:
#version 420
layout (binding = 0) uniform sampler2D u_texture0;
layout (binding = 1) uniform sampler2D u_texture1;
Alternatively you can assign the texture unit index, to the texture sampler uniform by glUniform1i:
uniform sampler2D u_texture0;
uniform sampler2D u_texture1;
GLuint location_tex0 = glGetUniformLocation( shaderProgram, "u_texture0" );
GLuint location_tex1 = glGetUniformLocation( shaderProgram, "u_texture1" );
glUniform1i( location_tex0, unit_tex0 );
glUniform1i( location_tex1, unit_tex1 );
To distinguish between the sides of the cubes, you can use the built in vertex shader variable gl_VertexID.
Create a flat out variable of type int in the vertex shader and pass the vertex id to the fragment shader:
flat out int vertex_id;
void main()
{
.....
vertex_id = gl_VertexID;
.....
}
In the fragment shader yo can decide which texture you want to use by the id corresponding to the vertex coordinate. Note, you have 36 coordinates, the first 3 sides have the ids form 0 to 17 and the other the ids form 18 to 35:
flat in int vertex_id;
void main()
{
.....
if ( vertex_id < 18 )
fragColor = texture(u_texture0, vertUV);
else
fragColor = texture(u_texture1, vertUV);
.....
}
My shader is throwing an error when compiling, but it isn't returning an error log. Any idea why?
#version 430
layout(location = 0) in vec3 vertex_position;
layout(location = 1) in vec2 vertex_texcoord;
layout(location = 2) in vec3 vertex_pos;
layout(location = 3) in vec3 vertex_rot;
layout(location = 4) in vec3 vertex_scl;
layout(location = 5) in int vertex_texnum;
varying vec2 texcoord;
varying float tex_num;
uniform float aspect_ratio;
mat4 view_frustum(
float angle_of_view,
float aspect_ratio,
float z_near,
float z_far
) {
return mat4(
vec4(1.0/tan(angle_of_view), 0.0, 0.0, 0.0),
vec4(0.0, aspect_ratio/tan(angle_of_view), 0.0, 0.0),
vec4(0.0, 0.0, (z_far+z_near)/(z_far-z_near), -2.0*z_far*z_near/(z_far-z_near)),
vec4(0.0, 0.0, 1.0, 0.0)
);
}
mat4 scale(float x, float y, float z)
{
return mat4(
vec4(x, 0.0, 0.0, 0.0),
vec4(0.0, y, 0.0, 0.0),
vec4(0.0, 0.0, z, 0.0),
vec4(0.0, 0.0, 0.0, 1.0)
);
}
mat4 translate(float x, float y, float z)
{
return mat4(
vec4(1.0, 0.0, 0.0, 0.0),
vec4(0.0, 1.0, 0.0, 0.0),
vec4(0.0, 0.0, 1.0, 0.0),
vec4(x, y, z, 1.0)
);
}
mat4 rotate_x(float theta)
{
return mat4(
vec4(1.0, 0.0, 0.0, 0.0),
vec4(0.0, cos(theta), sin(theta), 0.0),
vec4(0.0, -sin(theta), cos(theta), 0.0),
vec4(0.0, 0.0, 0.0, 1.0)
);
}
mat4 rotate_y(float theta)
{
return mat4(
vec4( cos(theta), 0.0, sin(theta), 0.0),
vec4( 0.0, 1.0, 0.0, 0.0),
vec4(-sin(theta), 0.0, cos(theta), 0.0),
vec4( 0.0, 0.0, 0.0, 1.0)
);
}
mat4 rotate_z(float theta)
{
return mat4(
vec4(cos(theta), -sin(theta), 0.0, 0.0),
vec4(sin(theta), cos(theta), 0.0, 0.0),
vec4( 0.0, 0.0, 1.0, 0.0),
vec4( 0.0, 0.0, 0.0, 1.0)
);
}
mat4 frustum = view_frustum(radians(45.0), 1, 0.5, 50.0);
mat4 translation = translate(vertex_pos.x, vertex_pos.y, vertex_pos.z);
mat4 rotation = rotate_x(vertex_rot.x) * rotate_y(vertex_rot.y) * rotate_z(vertex_rot.z);
mat4 scale = scale(vertex_scl.x, vertex_scl.y, vertex_scl.z);
void main () {
gl_Position = frustum * (rotation * (scale * (translation * vec4(vertex_position, 1.0))));
texcoord = vertex_texcoord;
tex_num = vertex_texnum;
}
EDIT: My apologies for the uninformative question, I was sleep deprived.
My shader loading code is here:
void initShaders(GLuint& shader_program) {
std::ifstream file;
std::string line;
file.open("data/shaders/VertexShader.glsl", std::ios::in);
if(!file.is_open()) Error("Failed to open Vertex Shader file.");
std::string vs_str = "";
int i = 0;
while(getline(file,line)) {
vs_str += line + "\n";
++i;
}
std::string fs_str = "";
file.close();
file.open("data/shaders/FragmentShader.glsl", std::ios::in);
if(!file.is_open()) Error("Failed to open Fragment Shader file.");
i = 0;
while(getline(file,line)) {
fs_str += line + "\n";
++i;
}
const char* vertex_shader = &vs_str[0];
const char* fragment_shader = &fs_str[0];
file.close();
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, &vertex_shader, NULL);
glCompileShader(vs);
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, &fragment_shader, NULL);
glCompileShader(fs);
GLint isCompiled = 0;
glGetShaderiv(vs, GL_COMPILE_STATUS, &isCompiled);
if(isCompiled == GL_FALSE)
{
GLint maxLength = 0;
glGetShaderiv(vs, GL_INFO_LOG_LENGTH, &maxLength);
// The maxLength includes the NULL character
std::vector<GLchar> errorLog(maxLength);
glGetShaderInfoLog(vs, maxLength, &maxLength, &errorLog[0]);
std::cout << "VERTEX SHADER ERROR:\n";
for (std::vector<GLchar>::iterator i = errorLog.begin(); i != errorLog.end(); ++i)
{
std::cout << *i;
}
std::cout << "\n";
glDeleteShader(vs); // Don't leak the shader.
quit(EXIT_FAILURE);
}
isCompiled = 0;
glGetShaderiv(fs, GL_COMPILE_STATUS, &isCompiled);
if(isCompiled == GL_FALSE)
{
GLint maxLength = 0;
glGetShaderiv(fs, GL_INFO_LOG_LENGTH, &maxLength);
// The maxLength includes the NULL character
std::vector<GLchar> errorLog(maxLength);
glGetShaderInfoLog(fs, maxLength, &maxLength, &errorLog[0]);
std::cout << "FRAGMENT SHADER ERROR:\n";
for (std::vector<GLchar>::iterator i = errorLog.begin(); i != errorLog.end(); ++i)
{
std::cout << *i;
}
std::cout << "\n";
glDeleteShader(fs); // Don't leak the shader.
quit(EXIT_FAILURE);
}
shader_program = glCreateProgram();
glAttachShader(shader_program, fs);
glAttachShader(shader_program, vs);
glLinkProgram(shader_program);
glUseProgram(shader_program);
}
For me, the shader has a lot of text in the compile log (tested on OpenGL 4.3 Core):
0(10) : warning C7555: 'varying' is deprecated, use 'in/out' instead
0(11) : warning C7555: 'varying' is deprecated, use 'in/out' instead
0(82) : error C1002: the name "scale" is already defined at 0(29)
0(29) : error C1054: initialization of non-variable "scale"
0(85) : error C1020: invalid operands to "*"
The first two errors are because the keyword varying is not used in 430 anymore. You'll have to replace it with out in the vertex shader and with in in the fragment shader.
The 3rd error is because you are naming a variable with the same name as a function (both are named scale), while the 4 and 5 error are follow-up errors due to 3.
Since it seems that your shader loader does not work well, I would suggest you to ask a new question containing the shader loader code.
Im sure there is a better way for me to calculate my transformation matrix from a quaternion but this is what i have for now. I need to pass the 16 values into the shader but am having an issue. I keep getting type incompatibility errors. Also once i get it in there do i have to do anything to it to multiply it to what i already have?
Here is my display and shader
void display()
{ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
if (trackballMove)
{
glRotatef(angle, axis[0], axis[1], axis[2]);
}
//colorcube();
glUniform3fv( theta, 1, Theta );
float a = angle;
float b = axis[0];
float c = axis[1];
float d = axis[2];
float xform[16] = {(a*a+b*b-c*c-d*d),(2*b*c-2*a*d),(2*b*d+2*a*c),0.0,
(2*b*c+2*a*d),(a*a-b*b+c*c-d*d),(2*c*d-2*a*b),0.0,
(2*b*d-2*a*c),(2*c*d+2*a*b),(a*a-b*b-c*c+d*d),0.0,
0.0,0.0,0.0,1.0};
GLuint Transform = glGetUniformLocation( program, "vTransform");
glUniformMatrix4fv(Transform, xform);
glDrawArrays( GL_TRIANGLES, 0, NumVertices );
glutSwapBuffers();
}
here is the shader
#version 150
in vec4 vPosition;
in vec4 vColor;
out vec4 color;
uniform vec3 theta;
uniform vec4 vTransform;
void main()
{
// Compute the sines and cosines of theta for each of
// the three axes in one computation.
vec3 angles = radians( theta );
vec3 c = cos( angles );
vec3 s = sin( angles );
// Remember: these matrices are column-major
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 = vTransform *(rx * ry * rz * vPosition);
}
Your glUniformMatrix4fv does not have sufficient number of arguments. The function signature is
glUniformMatrix4fv(GLint location, GLsizei count,
GLboolean transpose, const GLfloat *value);
See the complete API docs here:
http://www.opengl.org/sdk/docs/man4/xhtml/glUniform.xml
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.