How to change the color of one vertex, but not all?

I am new to OpenGL and GLSL, and learn it through http://open.gl/ .

I managed to draw a triangle and change the color of all the vertices using:

glUniform3f(uniColor, red, 0.0, 0.0) 

If the value "red" is constantly changing, but this updates the color value of all the vertices in the triangle, whereas I want to only change one or two of the vertices.

Looking back at the code, I donโ€™t see where I can implement any logic to focus on one vertex, and not on all (the code is almost entirely based on http://open.gl/content/code/c2_triangle_uniform.txt )

In this code: http://open.gl/content/code/c2_color_triangle.txt , each vertex gets its own color, but it seems to be hard-coded, I canโ€™t dynamically change colors as the program moves.

I suppose

 uniColor = glGetUniformLocation(shader.handle, "triangleColor") 

gives me the location of a variable that I can change, and this variable is used to update the color of all vertices, and maybe what I need to do is create 3 variables, one for each vertex, and then access them, but how to do it?

If I look at 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 say void main () to distinguish which vertex gets which variable?

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() 
+6
source share
2 answers

The uniformity setting for each vertex is not scalable. It would be best to create another Buffer Buffer object to hold the values. This can be done in the same way as you create for line items, except that it will contain 3 GLfloats. You can re-buffer the data as it changes.

My python is trash, so I used yours as a syntax guide, but it should be something like:

 ## 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 may be reloaded:

 ## 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 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 transfer this color value of the vertices from the vertex shader to the fragment shader, which will interpolate its value accordingly during the rasterization.

 # 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); } """ 
+5
source

The approach suggested in @ Pondwater's answer is well founded and reasonable. Since alternatives are always valuable, this is a slightly different approach.

The idea here is that you have two shapes with two different colors. To indicate which vertex one of the two colors uses, you enter an additional vertex attribute. This additional attribute is a single float, where a value of 0.0 means you want to use the first color, and 1.0 for which you want to use the second color.

For your example of a triangle, let's say you want to color the 1st and 3rd vertices blue, and the second red. Extend the vertex data as follows:

 vertices = [0.0, 0.5, 0.0, 0.5, -0.5, 1.0, -0.5, -0.5, 0.0] 

Then you set the second vertex attribute (named colorWeight in the code below), following the same pattern that you used for position . You will have a second set of glEnableVertexAttribArray() , glVertexAttribPointer() , etc. For this new attribute.

In your vertex shader, you add a new colorWeight attribute and pass it to the fragment shader:

 in vec2 position; in float colorWeight; out float fragColorWeight; void main() { gl_Position = vec4(position, 0.0, 1.0); fragColorWeight = colorWeight; } 

Then in the fragment shader you now have two identical colors and mix them based on the relative weight:

 uniform vec3 triangleColor; uniform vec3 secondaryColor; in float fragColorWeight; out vec4 outColor; void main() { vec3 mixedColor = mix(triangleColor, secondaryColor, fragColorWeight); outColor = vec4(mixedColor, 1.0); } 

Now you can get the location of the single secondaryColor variable and set it independently of triangleColor to change the color of only the second vertex of the triangle.

+1
source

Source: https://habr.com/ru/post/972340/


All Articles