Refactor the shaders module to present a nicer interface
This commit is contained in:
parent
f7db790061
commit
135db0d834
@ -7,9 +7,8 @@ import OpenGL.GL as gl
|
|||||||
import sys
|
import sys
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
import numpy
|
import numpy
|
||||||
import ctypes
|
|
||||||
|
|
||||||
from kitty.shaders import ShaderProgram, VertexArrayObject
|
from kitty.shaders import ShaderProgram, array
|
||||||
|
|
||||||
vertex_shader = """
|
vertex_shader = """
|
||||||
# version 410
|
# version 410
|
||||||
@ -78,62 +77,69 @@ def _main():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
gl.glClearColor(0.5, 0.5, 0.5, 0)
|
gl.glClearColor(0.5, 0.5, 0.5, 0)
|
||||||
triangle_texture(window)
|
rectangle_texture(window)
|
||||||
finally:
|
finally:
|
||||||
glfw.glfwDestroyWindow(window)
|
glfw.glfwDestroyWindow(window)
|
||||||
|
|
||||||
|
|
||||||
|
def triangle_vertices(width=0.8, height=0.8):
|
||||||
|
return array(
|
||||||
|
0.0, height,
|
||||||
|
-width, -height,
|
||||||
|
width, -height,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def rectangle_vertices(left=-0.8, top=0.8, right=0.8, bottom=-0.8):
|
||||||
|
vertex_data = array(
|
||||||
|
right, top,
|
||||||
|
right, bottom,
|
||||||
|
left, bottom,
|
||||||
|
right, top,
|
||||||
|
left, bottom,
|
||||||
|
left, top
|
||||||
|
)
|
||||||
|
|
||||||
|
texture_coords = array(
|
||||||
|
1, 0, # right top
|
||||||
|
1, 1, # right bottom
|
||||||
|
0, 1, # left bottom
|
||||||
|
1, 0, # right top
|
||||||
|
0, 1, # left bottom
|
||||||
|
0, 0) # left top
|
||||||
|
return vertex_data, texture_coords
|
||||||
|
|
||||||
|
|
||||||
def rectangle(window):
|
def rectangle(window):
|
||||||
program = ShaderProgram(vertex_shader, fragment)
|
program = ShaderProgram(vertex_shader, fragment)
|
||||||
vao = VertexArrayObject()
|
|
||||||
|
|
||||||
with program, vao:
|
with program:
|
||||||
vao.make_rectangle()
|
program.set_attribute_data('vertex', rectangle_vertices()[0])
|
||||||
vertex = program.attribute_location('vertex')
|
|
||||||
gl.glEnableVertexAttribArray(vertex)
|
|
||||||
gl.glVertexAttribPointer(
|
|
||||||
vertex, 2, gl.GL_FLOAT, gl.GL_FALSE, 0, None)
|
|
||||||
|
|
||||||
while not glfw.glfwWindowShouldClose(window):
|
while not glfw.glfwWindowShouldClose(window):
|
||||||
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
|
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
|
||||||
with program, vao:
|
with program:
|
||||||
# Activate the texture
|
gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
|
||||||
# glActiveTexture(GL_TEXTURE0)
|
|
||||||
# glBindTexture(GL_TEXTURE_2D, texture_id)
|
|
||||||
# sampler_loc = program.attribute_location('texture_sampler')
|
|
||||||
# glUniform1i(sampler_loc, 0)
|
|
||||||
|
|
||||||
# Modern GL makes the draw call really simple
|
|
||||||
# All the complexity has been pushed elsewhere
|
|
||||||
gl.glDrawElements(gl.GL_TRIANGLE_STRIP, 4, gl.GL_UNSIGNED_BYTE, None)
|
|
||||||
|
|
||||||
# Now lets show our master piece on the screen
|
|
||||||
glfw.glfwSwapBuffers(window)
|
glfw.glfwSwapBuffers(window)
|
||||||
glfw.glfwWaitEvents()
|
glfw.glfwWaitEvents()
|
||||||
|
|
||||||
|
|
||||||
def triangle(window):
|
def triangle(window):
|
||||||
program = ShaderProgram(vertex_shader, fragment)
|
program = ShaderProgram(vertex_shader, fragment)
|
||||||
vao = VertexArrayObject()
|
with program:
|
||||||
with program, vao:
|
program.set_attribute_data('vertex', triangle_vertices())
|
||||||
vao.make_triangle()
|
|
||||||
vertex = program.attribute_location('vertex')
|
|
||||||
gl.glEnableVertexAttribArray(vertex)
|
|
||||||
gl.glVertexAttribPointer(
|
|
||||||
vertex, 2, gl.GL_FLOAT, gl.GL_FALSE, 0, None)
|
|
||||||
|
|
||||||
while not glfw.glfwWindowShouldClose(window):
|
while not glfw.glfwWindowShouldClose(window):
|
||||||
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
|
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
|
||||||
with program, vao:
|
with program:
|
||||||
gl.glDrawArrays(gl.GL_TRIANGLES, 0, 3)
|
gl.glDrawArrays(gl.GL_TRIANGLES, 0, 3)
|
||||||
|
|
||||||
glfw.glfwSwapBuffers(window)
|
glfw.glfwSwapBuffers(window)
|
||||||
glfw.glfwWaitEvents()
|
glfw.glfwWaitEvents()
|
||||||
|
|
||||||
|
textured_shaders = (
|
||||||
def triangle_texture(window):
|
'''
|
||||||
program = ShaderProgram(
|
|
||||||
'''
|
|
||||||
#version 410
|
#version 410
|
||||||
in vec2 vertex;
|
in vec2 vertex;
|
||||||
in vec2 texture_position;
|
in vec2 texture_position;
|
||||||
@ -145,7 +151,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
''',
|
''',
|
||||||
|
|
||||||
'''
|
'''
|
||||||
#version 410
|
#version 410
|
||||||
uniform sampler2D tex;
|
uniform sampler2D tex;
|
||||||
in vec2 texture_position_out;
|
in vec2 texture_position_out;
|
||||||
@ -153,34 +159,55 @@ out vec4 final_color;
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
final_color = texture(tex, texture_position_out);
|
final_color = texture(tex, texture_position_out);
|
||||||
// final_color = vec4(texture_position_out, 0, 1.0);
|
|
||||||
}
|
}
|
||||||
''')
|
''')
|
||||||
|
|
||||||
vao = VertexArrayObject()
|
|
||||||
|
def texture_data():
|
||||||
img = Image.open('/home/kovid/work/calibre/resources/images/library.png')
|
img = Image.open('/home/kovid/work/calibre/resources/images/library.png')
|
||||||
img_data = numpy.array(list(img.getdata()), numpy.int8)
|
img_data = numpy.array(list(img.getdata()), numpy.int8)
|
||||||
with program, vao:
|
return img_data, img.size[0], img.size[1]
|
||||||
program.set_2d_texture('tex', img_data, img.size[0], img.size[1])
|
|
||||||
vao.make_triangle(add_texture=True)
|
|
||||||
vertex = program.attribute_location('vertex')
|
def triangle_texture(window):
|
||||||
gl.glEnableVertexAttribArray(vertex)
|
program = ShaderProgram(*textured_shaders)
|
||||||
gl.glVertexAttribPointer(
|
img_data, w, h = texture_data()
|
||||||
vertex, 2, gl.GL_FLOAT, gl.GL_FALSE, 0, None)
|
with program:
|
||||||
texture_position = program.attribute_location('texture_position')
|
program.set_2d_texture('tex', img_data, w, h)
|
||||||
gl.glEnableVertexAttribArray(texture_position)
|
program.set_attribute_data('vertex', triangle_vertices())
|
||||||
gl.glVertexAttribPointer(
|
program.set_attribute_data('texture_position', array(
|
||||||
texture_position, 2, gl.GL_FLOAT, gl.GL_TRUE, 0, ctypes.c_void_p(6 * gl.sizeof(gl.GLfloat)))
|
0.5, 1.0,
|
||||||
|
0.0, 0.0,
|
||||||
|
1.0, 0.0
|
||||||
|
))
|
||||||
|
|
||||||
while not glfw.glfwWindowShouldClose(window):
|
while not glfw.glfwWindowShouldClose(window):
|
||||||
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
|
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
|
||||||
with program, vao:
|
with program:
|
||||||
gl.glDrawArrays(gl.GL_TRIANGLES, 0, 3)
|
gl.glDrawArrays(gl.GL_TRIANGLES, 0, 3)
|
||||||
|
|
||||||
glfw.glfwSwapBuffers(window)
|
glfw.glfwSwapBuffers(window)
|
||||||
glfw.glfwWaitEvents()
|
glfw.glfwWaitEvents()
|
||||||
|
|
||||||
|
|
||||||
|
def rectangle_texture(window):
|
||||||
|
program = ShaderProgram(*textured_shaders)
|
||||||
|
img_data, w, h = texture_data()
|
||||||
|
with program:
|
||||||
|
program.set_2d_texture('tex', img_data, w, h)
|
||||||
|
rv, texc = rectangle_vertices()
|
||||||
|
program.set_attribute_data('vertex', rv)
|
||||||
|
program.set_attribute_data('texture_position', texc)
|
||||||
|
|
||||||
|
while not glfw.glfwWindowShouldClose(window):
|
||||||
|
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
|
||||||
|
with program:
|
||||||
|
gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
|
||||||
|
|
||||||
|
glfw.glfwSwapBuffers(window)
|
||||||
|
glfw.glfwWaitEvents()
|
||||||
|
|
||||||
|
|
||||||
def on_error(code, msg):
|
def on_error(code, msg):
|
||||||
if isinstance(msg, bytes):
|
if isinstance(msg, bytes):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -21,6 +21,7 @@ class ShaderProgram:
|
|||||||
"""
|
"""
|
||||||
self.program_id = gl.glCreateProgram()
|
self.program_id = gl.glCreateProgram()
|
||||||
self.texture_id = None
|
self.texture_id = None
|
||||||
|
self.is_active = False
|
||||||
vs_id = self.add_shader(vertex, gl.GL_VERTEX_SHADER)
|
vs_id = self.add_shader(vertex, gl.GL_VERTEX_SHADER)
|
||||||
frag_id = self.add_shader(fragment, gl.GL_FRAGMENT_SHADER)
|
frag_id = self.add_shader(fragment, gl.GL_FRAGMENT_SHADER)
|
||||||
|
|
||||||
@ -36,6 +37,8 @@ class ShaderProgram:
|
|||||||
raise ValueError('Error linking shader program: %s' % info)
|
raise ValueError('Error linking shader program: %s' % info)
|
||||||
gl.glDeleteShader(vs_id)
|
gl.glDeleteShader(vs_id)
|
||||||
gl.glDeleteShader(frag_id)
|
gl.glDeleteShader(frag_id)
|
||||||
|
self.vao_id = gl.glGenVertexArrays(1)
|
||||||
|
self.attribute_buffers = {}
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
def __hash__(self) -> int:
|
||||||
return self.program_id
|
return self.program_id
|
||||||
@ -72,13 +75,17 @@ class ShaderProgram:
|
|||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
gl.glUseProgram(self.program_id)
|
gl.glUseProgram(self.program_id)
|
||||||
|
gl.glBindVertexArray(self.vao_id)
|
||||||
|
self.is_active = True
|
||||||
if self.texture_id is not None:
|
if self.texture_id is not None:
|
||||||
gl.glActiveTexture(gl.GL_TEXTURE0)
|
gl.glActiveTexture(gl.GL_TEXTURE0)
|
||||||
gl.glBindTexture(gl.GL_TEXTURE_2D, self.texture_id)
|
gl.glBindTexture(gl.GL_TEXTURE_2D, self.texture_id)
|
||||||
gl.glUniform1i(self.texture_var, 0) # 0 because using GL_TEXTURE0
|
gl.glUniform1i(self.texture_var, 0) # 0 because using GL_TEXTURE0
|
||||||
|
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
|
gl.glBindVertexArray(0)
|
||||||
gl.glUseProgram(0)
|
gl.glUseProgram(0)
|
||||||
|
self.is_active = False
|
||||||
|
|
||||||
def set_2d_texture(self, var_name, data, width, height, data_type='rgba',
|
def set_2d_texture(self, var_name, data, width, height, data_type='rgba',
|
||||||
min_filter=gl.GL_LINEAR, mag_filter=gl.GL_LINEAR,
|
min_filter=gl.GL_LINEAR, mag_filter=gl.GL_LINEAR,
|
||||||
@ -100,62 +107,25 @@ class ShaderProgram:
|
|||||||
0, external_format, gl.GL_UNSIGNED_BYTE, data)
|
0, external_format, gl.GL_UNSIGNED_BYTE, data)
|
||||||
return texture_id
|
return texture_id
|
||||||
|
|
||||||
|
def set_attribute_data(self, attribute_name, data, items_per_attribute_value=2, volatile=False, normalize=False):
|
||||||
|
if not self.is_active:
|
||||||
|
raise RuntimeError('The program must be active before you can add buffers')
|
||||||
|
if len(data) % items_per_attribute_value != 0:
|
||||||
|
raise ValueError('The length of the data buffer is not a multiple of the items_per_attribute_value')
|
||||||
|
buf_id = self.attribute_buffers[attribute_name] = gl.glGenBuffers(1)
|
||||||
|
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buf_id)
|
||||||
|
gl.glBufferData(gl.GL_ARRAY_BUFFER, ArrayDatatype.arrayByteCount(data), data, gl.GL_STREAM_DRAW if volatile else gl.GL_STATIC_DRAW)
|
||||||
|
loc = self.attribute_location(attribute_name)
|
||||||
|
gl.glEnableVertexAttribArray(loc)
|
||||||
|
typ = {
|
||||||
|
gl.GLfloat: gl.GL_FLOAT,
|
||||||
|
gl.GLubyte: gl.GL_UNSIGNED_BYTE,
|
||||||
|
gl.GLuint: gl.GL_UNSIGNED_INT,
|
||||||
|
}[data._type_]
|
||||||
|
gl.glVertexAttribPointer(
|
||||||
|
loc, items_per_attribute_value, typ, gl.GL_TRUE if normalize else gl.GL_FALSE, 0, None
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def array(*args, dtype=gl.GLfloat):
|
def array(*args, dtype=gl.GLfloat):
|
||||||
return (dtype * len(args))(*args)
|
return (dtype * len(args))(*args)
|
||||||
|
|
||||||
|
|
||||||
def make_buffer(data, target=gl.GL_ARRAY_BUFFER, usage=gl.GL_STREAM_DRAW):
|
|
||||||
buf_id = gl.glGenBuffers(1)
|
|
||||||
gl.glBindBuffer(target, buf_id)
|
|
||||||
gl.glBufferData(target, ArrayDatatype.arrayByteCount(data), data, usage)
|
|
||||||
return buf_id
|
|
||||||
|
|
||||||
|
|
||||||
class VertexArrayObject:
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.vao_id = gl.glGenVertexArrays(1)
|
|
||||||
self.is_active = False
|
|
||||||
self.texture_id = None
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
gl.glBindVertexArray(self.vao_id)
|
|
||||||
self.is_active = True
|
|
||||||
|
|
||||||
def __exit__(self, *a):
|
|
||||||
gl.glBindVertexArray(0)
|
|
||||||
self.is_active = False
|
|
||||||
|
|
||||||
def make_triangle(self, width=0.8, height=0.8, add_texture=False, usage=gl.GL_STATIC_DRAW):
|
|
||||||
if not self.is_active:
|
|
||||||
raise RuntimeError('This VertexArrayObject is not active')
|
|
||||||
if add_texture:
|
|
||||||
vertices = array(
|
|
||||||
0.0, height,
|
|
||||||
-width, -height,
|
|
||||||
width, -height,
|
|
||||||
0.5, 1.0,
|
|
||||||
0.0, 0.0,
|
|
||||||
1.0, 0.0
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
vertices = array(
|
|
||||||
0.0, height,
|
|
||||||
-width, -height,
|
|
||||||
width, -height,
|
|
||||||
)
|
|
||||||
make_buffer(vertices, usage=usage)
|
|
||||||
|
|
||||||
def make_rectangle(self, left=-0.8, top=0.8, right=0.8, bottom=-0.8, usage=gl.GL_STATIC_DRAW):
|
|
||||||
if not self.is_active:
|
|
||||||
raise RuntimeError('This VertexArrayObject is not active')
|
|
||||||
vertices = array(
|
|
||||||
left, bottom,
|
|
||||||
right, bottom,
|
|
||||||
left, top,
|
|
||||||
right, top
|
|
||||||
)
|
|
||||||
make_buffer(vertices, usage=usage)
|
|
||||||
elements = array(0, 1, 2, 3, dtype=gl.GLubyte)
|
|
||||||
make_buffer(elements, gl.GL_ELEMENT_ARRAY_BUFFER, usage=usage)
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user