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

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.

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.)

C++ OpenGL Textured Cube Missing Triangles

On my journey towards being competent at OpenGL, I was trying to make a textured cube when I came upon this problem:
Only part of my cube is being rendered. There should be 12 triangles but only 3 of them are being rendered. I've looked at many tutorials, but I can't seem to find the problem/main difference between our code. Here's mine:
...
int main(int argc, char* argv[]) {
if (!setupGLFW()) return 1;
setupApple();
glfwWindowHint(GLFW_SAMPLES, 4);
GLFWwindow* window = glfwCreateWindow(WIN_WIDTH, WIN_HEIGHT, "Textured Cube", NULL, NULL);
if (!window) {
log_msg(LOG_ERROR, "Could not open a GLFW3 window!\n");
return 1;
}
glfwMakeContextCurrent(window);
if (!setupGLEW()) return 1;
glClearColor(0.5, 0.5, 0.5, 1.0);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
static const int vertices = 12 * 3;
static const GLfloat points[] = {
-1.0, -1.0, -1.0,
-1.0, -1.0, 1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, -1.0,
-1.0, -1.0, -1.0,
-1.0, 1.0, -1.0,
1.0, -1.0, 1.0,
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
1.0, -1.0, -1.0,
-1.0, -1.0, -1.0,
-1.0, -1.0, -1.0,
-1.0, 1.0, 1.0,
-1.0, 1.0, -1.0,
1.0, -1.0, 1.0,
-1.0, -1.0, 1.0,
-1.0, -1.0, -1.0,
-1.0, 1.0, 1.0,
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
1.0, -1.0, -1.0,
1.0, 1.0, -1.0,
1.0, -1.0, -1.0,
1.0, 1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, -1.0,
-1.0, 1.0, -1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, -1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
1.0, -1.0, 1.0
};
static const GLfloat textureCoords[] = {
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1,
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1,
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1,
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1,
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1,
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1,
};
GLuint pointBuffer;
glGenBuffers(1, &pointBuffer);
glBindBuffer(GL_ARRAY_BUFFER, pointBuffer);
glBufferData(GL_ARRAY_BUFFER, vertices * 3, points, GL_STATIC_DRAW);
GLuint textureBuffer;
glGenBuffers(1, &textureBuffer);
glBindBuffer(GL_ARRAY_BUFFER, textureBuffer);
glBufferData(GL_ARRAY_BUFFER, vertices * 2, textureCoords, GL_STATIC_DRAW);
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, pointBuffer);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glBindBuffer(GL_ARRAY_BUFFER, textureBuffer);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
GLuint program = getProgramFromFiles("text_cube.vs.glsl", "text_cube.fs.glsl");
glm::mat4 MVP = calculateMVP();
GLuint texture = loadBMP("uvtemplate.bmp");
if (!program) {
log_msg(LOG_ERROR, "There was a problem opening shader.\n");
// clean up
return 1;
}
if (!texture) {
log_msg(LOG_ERROR, "There was a problem opening shader.\n");
// clean up
return 1;
}
glUseProgram(program);
GLuint MVPID = glGetUniformLocation(program, "MVP");
GLuint textureID = glGetUniformLocation(program, "cube_texture");
glUniformMatrix4fv(MVPID, 1, GL_FALSE, &MVP[0][0]);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(textureID, 0);
while (!glfwWindowShouldClose(window) && glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, vertices);
glfwPollEvents();
glfwSwapBuffers(window);
}
// clean up
return 0;
}
Just so you know, the error is not with setupGLFW(), setupApple(), setupGLEW(), getProgramFromFiles(), calculateMVP(), loadBMP(), or my vertex and fragment shaders, which are in different files. This compiles fine because I include GLFW and GLEW from myglutils.h This is uvtemplate.bmp, its a 512x512 image:
If you guys could help me, it would be greatly appreciated. Thanks!
The problem is the call to glBufferData(). You set the size in floats but you have to specify it in bytes using sizeof(GLfloat). That's why OpenGL isn't receiving all of your data.
void glBufferData
(
GLenum target,
GLsizeiptr size,
const GLvoid* data,
GLenum usage
);
So you need to replace:
glBufferData(GL_ARRAY_BUFFER, vertices * 3, points, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, vertices * 2, textureCoords, GL_STATIC_DRAW);
with:
glBufferData(GL_ARRAY_BUFFER, vertices * 3 * sizeof(GLfloat), points, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, vertices * 2 * sizeof(GLfloat), textureCoords, GL_STATIC_DRAW);

Use two Uniforms in Vertex Shader

was following the OpenGL RedBook and was able to use a Uniform for a frustrum or translate matrix. I have become a little lost trying to use both at the same time.
//Declarations
GLfloat frustum[4][4] = {
{((2.0*frusZNe //Etc...
GLfloat translate[4][4] = {
{1.0, 0.0, //Etc...
And then I use it in Init:
GLuint uboIndex;
GLint uboSize;
GLuint ubo;
GLvoid *buffer;
uboIndex = glGetUniformBlockIndex(program, "frustumUniform");
glGetActiveUniformBlockiv(program, uboIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &uboSize);
buffer = malloc(uboSize);
const char* frustumName[1] = {"frustum"};
GLuint indices[1];
GLint size[1];
GLint offset[1];
GLint type[1];
glGetUniformIndices(program, 1, frustumName, indices);
glGetActiveUniformsiv(program, 1, indices, GL_UNIFORM_OFFSET, offset);
glGetActiveUniformsiv(program, 1, indices, GL_UNIFORM_SIZE, size);
glGetActiveUniformsiv(program, 1, indices, GL_UNIFORM_TYPE, type);
//memcpy(&buffer + 0, &frustum, sizeof(frustum));
glGenBuffers(1, &ubo);
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
glBufferData(GL_UNIFORM_BUFFER, uboSize, &frustum, GL_STATIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, uboIndex, ubo);
Immediately afterwards, I do the same thing for the translate one but change some of the values, such as: glGetUniformBlockIndex(program, "translateUniform");
///Translate Uniform, same 20 statements as above, with alterations
GLuint uboIndex2;
GLint uboSize2;
GLuint ubo2;
GLvoid *buffer2;
uboIndex2 = glGetUniformBlockIndex(program, "translateUniform");
glGetActiveUniformBlockiv(program, uboIndex2, GL_UNIFORM_BLOCK_DATA_SIZE, &uboSize2);
buffer2 = malloc(uboSize2);
string offsetString = iToS(uboIndex2);
debugMsg(offsetString + "Index2, 1:");
const char* translateName[1] = {"translate"};
GLuint indices2[1];
GLint size2[1];
GLint offset2[1];
GLint type2[1];
glGetUniformIndices(program, 1, translateName, indices2);
glGetActiveUniformsiv(program, 1, indices2, GL_UNIFORM_OFFSET, offset2);
glGetActiveUniformsiv(program, 1, indices2, GL_UNIFORM_SIZE, size2);
glGetActiveUniformsiv(program, 1, indices2, GL_UNIFORM_TYPE, type2);
//memcpy(&buffer + 0, &frustum, sizeof(frustum));
glGenBuffers(1, &ubo2);
glBindBuffer(GL_UNIFORM_BUFFER, ubo2);
glBufferData(GL_UNIFORM_BUFFER, uboSize2, &translate, GL_STATIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, uboIndex2, ubo2);
string uboIndex2S = iToS(uboIndex2);
debugMsg("Index 2: " + uboIndex2S);
string uboSizeS, uboSizeS2, uboIndexS, uboIndexS2;
uboSizeS = iToS(uboSize);
uboSizeS2 = iToS(uboSize2);
uboIndexS = iToS(uboIndex);
uboIndexS2 = iToS(uboIndex2);
debugMsg(uboSizeS + " ," + uboSizeS2 + " ," + uboIndexS + " , " + uboIndexS2);
Vertex shader:
#version 430 core
uniform frustumUniform {
mat4 frustum;
};
uniform translateUniform {
mat4 translate;
};
layout(location = 0) in vec4 vPosition;
void
main()
{
vec3 trans = vec3(.2, 0, 0);
mat4 view = 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, -200.0, 1.0);
mat4 rotate = mat4(0.2, 0.0, 0.3, 0.0,
0.0, 1.0, 0.0, 0.0,
-0.3, 0.0, 0.2, 0.0,
0.0, 0.0, 20.0, 1.0);
mat4 translateOld = 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);
gl_Position = frustum * translate * view * vPosition;
}
It compiles, but the second Uniform no longer works. Any ideas?

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.