I'm new to OpenGL and GLSL, and am learning it through http://open.gl/ .
I've managed to draw a triangle and change the color of all the vertexes using:
glUniform3f(uniColor, red, 0.0, 0.0)
Where the value of "red" is constantly changing, but this updates the color value of all the vertexes in the triangle, while I only want to change one or two of the vertexes.
Looking over the code I don't see where I can implement any logic to focus on one vertex instead of them all (the code is almost completely based on http://open.gl/content/code/c2_triangle_uniform.txt)
In this code however: http://open.gl/content/code/c2_color_triangle.txt , each vertex gets it's own color, but it seems to be hard coded, I can't dynamically change the colors as the program progresses.
I'm guessing
uniColor = glGetUniformLocation(shader.handle, "triangleColor")
gives me the location of a variable I can change and this variable is used to update the color of all the vertexes, and that perhaps what I need to do is create 3 variables, one for each vertex and then access those, but how do I do that?
If I look at the GLSL I have a "uniform vec3 triangleColor;" which is then used in
void main()
{
outColor = vec4(triangleColor, 1.0);
}
but even if I create 3 such triangleColor variables how would I tell void main() to distinguish which vertex gets what variable?
The code:
import pyglet
from pyglet.gl import *
from shader import Shader
from ctypes import pointer, sizeof
import math
import time
window = pyglet.window.Window(800, 600, "OpenGL")
window.set_location(100, 100)
# Vertex Input
## Vertex Array Objects
vao = GLuint()
glGenVertexArrays(1, pointer(vao))
glBindVertexArray(vao)
## Vertex Buffer Object
vbo = GLuint()
glGenBuffers(1, pointer(vbo)) # Generate 1 buffer
vertices = [0.0, 0.5,
0.5, -0.5,
-0.5, -0.5]
## Convert the verteces array to a GLfloat array, usable by glBufferData
vertices_gl = (GLfloat * len(vertices))(*vertices)
## Upload data to GPU
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_gl), vertices_gl, GL_STATIC_DRAW)
# Shaders (Vertex and Fragment shaders)
vertex = """
#version 150
in vec2 position;
void main()
{
gl_Position = vec4(position, 0.0, 1.0);
}
"""
fragment = """
#version 150
uniform vec3 triangleColor;
out vec4 outColor;
void main()
{
outColor = vec4(triangleColor, 1.0);
}
"""
## Compiling shaders and combining them into a program
shader = Shader(vertex, fragment)
shader.bind() #glUseProgram
# Making the link between vertex data and attributes
## shader.handle holds the value of glCreateProgram()
posAttrib = glGetAttribLocation(shader.handle, "position")
glEnableVertexAttribArray(posAttrib)
glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0)
uniColor = glGetUniformLocation(shader.handle, "triangleColor")
# Set clear color
glClearColor(0.0, 0.0, 0.0, 1.0)
@window.event
def on_draw():
# Set the color of the triangle
red = (math.sin(time.clock() * 4.0) + 1.0) / 2.0
glUniform3f(uniColor, red, 0.0, 0.0)
# Clear the screen to black
glClear(GL_COLOR_BUFFER_BIT)
# Draw a triangle from the 3 vertices
glDrawArrays(GL_TRIANGLES, 0, 3)
@window.event
def on_key_press(symbol, modifiers):
pass
@window.event
def on_key_release(symbol, modifiers):
pass
def update(dt):
pass
pyglet.clock.schedule(update)
pyglet.app.run()
Setting a uniform for each vertex isn't really scalable. A better way would be to create another Vertex Buffer Object to store the values. This can be done similar to the one you create for positions, except this one will contain 3 GLfloats. You can re-buffer the data as it changes.
My python is rubbish, so i've used yours as a syntax guide, but it should be something along the lines of:
## Vertex Buffer Object
vbocolors = GLuint()
glGenBuffers(1, pointer(vbocolors ))
colors = [1.0, 0.0, 0.0, # red vertex
0.0, 1.0, 0.0, # green vertex
0.0, 0.0, 1.0] # blue vertex
## Convert the verteces array to a GLfloat array, usable by glBufferData
colors_gl = (GLfloat * len(colors))(*colors)
## Upload data to GPU
glBindBuffer(GL_ARRAY_BUFFER, vbocolors )
glBufferData(GL_ARRAY_BUFFER, sizeof(colors_gl), colors_gl, GL_STATIC_DRAW)
Later new colors can be re-buffered:
## Upload new data to GPU
glBindBuffer(GL_ARRAY_BUFFER, vbocolors )
glBufferData(GL_ARRAY_BUFFER, sizeof(new_colors_gl), new_colors_gl, GL_STATIC_DRAW)
Setting the vertex attribute pointers:
colorAttrib = glGetAttribLocation(shader.handle, "color")
glEnableVertexAttribArray(colorAttrib )
glVertexAttribPointer(colorAttrib, 3, GL_FLOAT, GL_FALSE, 0, 0)
Then in your shader, you want to pass this vertex color value from the vertex shader to the fragment shader, which will interpolate its value accordingly during the rasterization process.
# Shaders (Vertex and Fragment shaders)
vertex = """
#version 150
in vec2 position;
in vec3 color;
out vec3 interpColor;
void main()
{
gl_Position = vec4(position, 0.0, 1.0);
interpColor= color;
}
"""
fragment = """
#version 150
in vec3 interpColor;
out vec4 outColor;
void main()
{
outColor = vec4(interpColor, 1.0);
}
"""