mirror of
https://github.com/ManimCommunity/manim.git
synced 2026-06-22 10:01:47 +00:00
Something is rendering now but the relative manim units don't work Co-authored-by: Jason Villanueva <a@jsonvillanueva.com>
This commit is contained in:
parent
5d8ea6a4a3
commit
1e57feccdb
14 changed files with 836 additions and 243 deletions
|
|
@ -1,14 +1,32 @@
|
|||
import time
|
||||
|
||||
from pyglet.gl import Config
|
||||
from pyglet.window import Window
|
||||
|
||||
import manim.utils.color.manim_colors as col
|
||||
from manim._config import tempconfig
|
||||
from manim.camera.camera import OpenGLCamera, OpenGLCameraFrame
|
||||
from manim.constants import OUT
|
||||
from manim.mobject.geometry.arc import Circle
|
||||
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject
|
||||
from manim.renderer.opengl_renderer import OpenGLRenderer
|
||||
|
||||
renderer = OpenGLRenderer(1920, 1080)
|
||||
vm = OpenGLVMobject([col.RED, col.GREEN])
|
||||
vm.set_points_as_corners([[0, 0, 0], [1, 0, 0], [1, 1, 0]])
|
||||
# print(vm.color)
|
||||
# print(vm.fill_color)
|
||||
# print(vm.stroke_color)
|
||||
if __name__ == "__main__":
|
||||
with tempconfig({"renderer": "opengl"}):
|
||||
win = Window(width=1920, height=1080)
|
||||
renderer = OpenGLRenderer(1920, 1080)
|
||||
# vm = OpenGLVMobject([col.RED, col.GREEN])
|
||||
vm = Circle(radius=100)
|
||||
# vm.set_points_as_corners([[-1920/2, 0, 0], [1920/2, 0, 0], [0, 1080/2, 0]])
|
||||
# print(vm.color)
|
||||
# print(vm.fill_color)
|
||||
# print(vm.stroke_color)
|
||||
|
||||
camera = OpenGLCameraFrame((1920, 1090))
|
||||
renderer.render_vmobject(vm, camera)
|
||||
camera = OpenGLCameraFrame((1920, 1080), center_point=OUT * 10)
|
||||
renderer.set_camera(camera)
|
||||
|
||||
for _ in range(4):
|
||||
renderer.render_vmobject(vm)
|
||||
win.dispatch_events()
|
||||
win.flip()
|
||||
time.sleep(1)
|
||||
|
|
|
|||
444
manim/mobject/opengl/shader.py
Normal file
444
manim/mobject/opengl/shader.py
Normal file
|
|
@ -0,0 +1,444 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
|
||||
import moderngl
|
||||
import numpy as np
|
||||
|
||||
from .. import config
|
||||
from ..utils import opengl
|
||||
from ..utils.simple_functions import get_parameters
|
||||
|
||||
SHADER_FOLDER = Path(__file__).parent / "shaders"
|
||||
shader_program_cache: dict = {}
|
||||
file_path_to_code_map: dict = {}
|
||||
|
||||
__all__ = [
|
||||
"Object3D",
|
||||
"Mesh",
|
||||
"Shader",
|
||||
"FullScreenQuad",
|
||||
]
|
||||
|
||||
|
||||
def get_shader_code_from_file(file_path: Path) -> str:
|
||||
if file_path in file_path_to_code_map:
|
||||
return file_path_to_code_map[file_path]
|
||||
source = file_path.read_text()
|
||||
include_lines = re.finditer(
|
||||
r"^#include (?P<include_path>.*\.glsl)$",
|
||||
source,
|
||||
flags=re.MULTILINE,
|
||||
)
|
||||
for match in include_lines:
|
||||
include_path = match.group("include_path")
|
||||
included_code = get_shader_code_from_file(
|
||||
file_path.parent / include_path,
|
||||
)
|
||||
source = source.replace(match.group(0), included_code)
|
||||
file_path_to_code_map[file_path] = source
|
||||
return source
|
||||
|
||||
|
||||
def filter_attributes(unfiltered_attributes, attributes):
|
||||
# Construct attributes for only those needed by the shader.
|
||||
filtered_attributes_dtype = []
|
||||
for i, dtype_name in enumerate(unfiltered_attributes.dtype.names):
|
||||
if dtype_name in attributes:
|
||||
filtered_attributes_dtype.append(
|
||||
(
|
||||
dtype_name,
|
||||
unfiltered_attributes.dtype[i].subdtype[0].str,
|
||||
unfiltered_attributes.dtype[i].shape,
|
||||
),
|
||||
)
|
||||
|
||||
filtered_attributes = np.zeros(
|
||||
unfiltered_attributes[unfiltered_attributes.dtype.names[0]].shape[0],
|
||||
dtype=filtered_attributes_dtype,
|
||||
)
|
||||
|
||||
for dtype_name in unfiltered_attributes.dtype.names:
|
||||
if dtype_name in attributes:
|
||||
filtered_attributes[dtype_name] = unfiltered_attributes[dtype_name]
|
||||
|
||||
return filtered_attributes
|
||||
|
||||
|
||||
class Object3D:
|
||||
def __init__(self, *children):
|
||||
self.model_matrix = np.eye(4)
|
||||
self.normal_matrix = np.eye(4)
|
||||
self.children = []
|
||||
self.parent = None
|
||||
self.add(*children)
|
||||
self.init_updaters()
|
||||
|
||||
# TODO: Use path_func.
|
||||
def interpolate(self, start, end, alpha, _):
|
||||
self.model_matrix = (1 - alpha) * start.model_matrix + alpha * end.model_matrix
|
||||
self.normal_matrix = (
|
||||
1 - alpha
|
||||
) * start.normal_matrix + alpha * end.normal_matrix
|
||||
|
||||
def single_copy(self):
|
||||
copy = Object3D()
|
||||
copy.model_matrix = self.model_matrix.copy()
|
||||
copy.normal_matrix = self.normal_matrix.copy()
|
||||
return copy
|
||||
|
||||
def copy(self):
|
||||
node_to_copy = {}
|
||||
|
||||
bfs = [self]
|
||||
while bfs:
|
||||
node = bfs.pop(0)
|
||||
bfs.extend(node.children)
|
||||
|
||||
node_copy = node.single_copy()
|
||||
node_to_copy[node] = node_copy
|
||||
|
||||
# Add the copy to the copy of the parent.
|
||||
if node.parent is not None and node is not self:
|
||||
node_to_copy[node.parent].add(node_copy)
|
||||
return node_to_copy[self]
|
||||
|
||||
def add(self, *children):
|
||||
for child in children:
|
||||
if child.parent is not None:
|
||||
raise Exception(
|
||||
"Attempt to add child that's already added to another Object3D",
|
||||
)
|
||||
self.remove(*children, current_children_only=False)
|
||||
self.children.extend(children)
|
||||
for child in children:
|
||||
child.parent = self
|
||||
|
||||
def remove(self, *children, current_children_only=True):
|
||||
if current_children_only:
|
||||
for child in children:
|
||||
if child.parent != self:
|
||||
raise Exception(
|
||||
"Attempt to remove child that isn't added to this Object3D",
|
||||
)
|
||||
self.children = list(filter(lambda child: child not in children, self.children))
|
||||
for child in children:
|
||||
child.parent = None
|
||||
|
||||
def get_position(self):
|
||||
return self.model_matrix[:, 3][:3]
|
||||
|
||||
def set_position(self, position):
|
||||
self.model_matrix[:, 3][:3] = position
|
||||
return self
|
||||
|
||||
def get_meshes(self):
|
||||
dfs = [self]
|
||||
while dfs:
|
||||
parent = dfs.pop()
|
||||
if isinstance(parent, Mesh):
|
||||
yield parent
|
||||
dfs.extend(parent.children)
|
||||
|
||||
def get_family(self):
|
||||
dfs = [self]
|
||||
while dfs:
|
||||
parent = dfs.pop()
|
||||
yield parent
|
||||
dfs.extend(parent.children)
|
||||
|
||||
def align_data_and_family(self, _):
|
||||
pass
|
||||
|
||||
def hierarchical_model_matrix(self):
|
||||
if self.parent is None:
|
||||
return self.model_matrix
|
||||
|
||||
model_matrices = [self.model_matrix]
|
||||
current_object = self
|
||||
while current_object.parent is not None:
|
||||
model_matrices.append(current_object.parent.model_matrix)
|
||||
current_object = current_object.parent
|
||||
return np.linalg.multi_dot(list(reversed(model_matrices)))
|
||||
|
||||
def hierarchical_normal_matrix(self):
|
||||
if self.parent is None:
|
||||
return self.normal_matrix[:3, :3]
|
||||
|
||||
normal_matrices = [self.normal_matrix]
|
||||
current_object = self
|
||||
while current_object.parent is not None:
|
||||
normal_matrices.append(current_object.parent.model_matrix)
|
||||
current_object = current_object.parent
|
||||
return np.linalg.multi_dot(list(reversed(normal_matrices)))[:3, :3]
|
||||
|
||||
def init_updaters(self):
|
||||
self.time_based_updaters = []
|
||||
self.non_time_updaters = []
|
||||
self.has_updaters = False
|
||||
self.updating_suspended = False
|
||||
|
||||
def update(self, dt=0):
|
||||
if not self.has_updaters or self.updating_suspended:
|
||||
return self
|
||||
for updater in self.time_based_updaters:
|
||||
updater(self, dt)
|
||||
for updater in self.non_time_updaters:
|
||||
updater(self)
|
||||
return self
|
||||
|
||||
def get_time_based_updaters(self):
|
||||
return self.time_based_updaters
|
||||
|
||||
def has_time_based_updater(self):
|
||||
return len(self.time_based_updaters) > 0
|
||||
|
||||
def get_updaters(self):
|
||||
return self.time_based_updaters + self.non_time_updaters
|
||||
|
||||
def add_updater(self, update_function, index=None, call_updater=True):
|
||||
if "dt" in get_parameters(update_function):
|
||||
updater_list = self.time_based_updaters
|
||||
else:
|
||||
updater_list = self.non_time_updaters
|
||||
|
||||
if index is None:
|
||||
updater_list.append(update_function)
|
||||
else:
|
||||
updater_list.insert(index, update_function)
|
||||
|
||||
self.refresh_has_updater_status()
|
||||
if call_updater:
|
||||
self.update()
|
||||
return self
|
||||
|
||||
def remove_updater(self, update_function):
|
||||
for updater_list in [self.time_based_updaters, self.non_time_updaters]:
|
||||
while update_function in updater_list:
|
||||
updater_list.remove(update_function)
|
||||
self.refresh_has_updater_status()
|
||||
return self
|
||||
|
||||
def clear_updaters(self):
|
||||
self.time_based_updaters = []
|
||||
self.non_time_updaters = []
|
||||
self.refresh_has_updater_status()
|
||||
return self
|
||||
|
||||
def match_updaters(self, mobject):
|
||||
self.clear_updaters()
|
||||
for updater in mobject.get_updaters():
|
||||
self.add_updater(updater)
|
||||
return self
|
||||
|
||||
def suspend_updating(self):
|
||||
self.updating_suspended = True
|
||||
return self
|
||||
|
||||
def resume_updating(self, call_updater=True):
|
||||
self.updating_suspended = False
|
||||
if call_updater:
|
||||
self.update(dt=0)
|
||||
return self
|
||||
|
||||
def refresh_has_updater_status(self):
|
||||
self.has_updaters = len(self.get_updaters()) > 0
|
||||
return self
|
||||
|
||||
|
||||
class Mesh(Object3D):
|
||||
def __init__(
|
||||
self,
|
||||
shader=None,
|
||||
attributes=None,
|
||||
geometry=None,
|
||||
material=None,
|
||||
indices=None,
|
||||
use_depth_test=True,
|
||||
primitive=moderngl.TRIANGLES,
|
||||
):
|
||||
super().__init__()
|
||||
if shader is not None and attributes is not None:
|
||||
self.shader = shader
|
||||
self.attributes = attributes
|
||||
self.indices = indices
|
||||
elif geometry is not None and material is not None:
|
||||
self.shader = material
|
||||
self.attributes = geometry.attributes
|
||||
self.indices = geometry.index
|
||||
else:
|
||||
raise Exception(
|
||||
"Mesh requires either attributes and a Shader or a Geometry and a "
|
||||
"Material",
|
||||
)
|
||||
self.use_depth_test = use_depth_test
|
||||
self.primitive = primitive
|
||||
self.skip_render = False
|
||||
self.init_updaters()
|
||||
|
||||
def single_copy(self):
|
||||
copy = Mesh(
|
||||
attributes=self.attributes.copy(),
|
||||
shader=self.shader,
|
||||
indices=self.indices.copy() if self.indices is not None else None,
|
||||
use_depth_test=self.use_depth_test,
|
||||
primitive=self.primitive,
|
||||
)
|
||||
copy.skip_render = self.skip_render
|
||||
copy.model_matrix = self.model_matrix.copy()
|
||||
copy.normal_matrix = self.normal_matrix.copy()
|
||||
# TODO: Copy updaters?
|
||||
return copy
|
||||
|
||||
def set_uniforms(self, renderer):
|
||||
self.shader.set_uniform(
|
||||
"u_model_matrix",
|
||||
opengl.matrix_to_shader_input(self.model_matrix),
|
||||
)
|
||||
self.shader.set_uniform("u_view_matrix", renderer.camera.formatted_view_matrix)
|
||||
self.shader.set_uniform(
|
||||
"u_projection_matrix",
|
||||
renderer.camera.projection_matrix,
|
||||
)
|
||||
|
||||
def render(self):
|
||||
if self.skip_render:
|
||||
return
|
||||
|
||||
if self.use_depth_test:
|
||||
self.shader.context.enable(moderngl.DEPTH_TEST)
|
||||
else:
|
||||
self.shader.context.disable(moderngl.DEPTH_TEST)
|
||||
|
||||
from moderngl import Attribute
|
||||
|
||||
shader_attributes = []
|
||||
for k, v in self.shader.shader_program._members.items():
|
||||
if isinstance(v, Attribute):
|
||||
shader_attributes.append(k)
|
||||
shader_attributes = filter_attributes(self.attributes, shader_attributes)
|
||||
|
||||
vertex_buffer_object = self.shader.context.buffer(shader_attributes.tobytes())
|
||||
if self.indices is None:
|
||||
index_buffer_object = None
|
||||
else:
|
||||
vert_index_data = self.indices.astype("i4").tobytes()
|
||||
if vert_index_data:
|
||||
index_buffer_object = self.shader.context.buffer(vert_index_data)
|
||||
else:
|
||||
index_buffer_object = None
|
||||
vertex_array_object = self.shader.context.simple_vertex_array(
|
||||
self.shader.shader_program,
|
||||
vertex_buffer_object,
|
||||
*shader_attributes.dtype.names,
|
||||
index_buffer=index_buffer_object,
|
||||
)
|
||||
vertex_array_object.render(self.primitive)
|
||||
vertex_buffer_object.release()
|
||||
vertex_array_object.release()
|
||||
if index_buffer_object is not None:
|
||||
index_buffer_object.release()
|
||||
|
||||
|
||||
class Shader:
|
||||
def __init__(
|
||||
self,
|
||||
context,
|
||||
name=None,
|
||||
source=None,
|
||||
):
|
||||
global shader_program_cache
|
||||
self.context = context
|
||||
self.name = name
|
||||
|
||||
# See if the program is cached.
|
||||
if (
|
||||
self.name in shader_program_cache
|
||||
and shader_program_cache[self.name].ctx == self.context
|
||||
):
|
||||
self.shader_program = shader_program_cache[self.name]
|
||||
elif source is not None:
|
||||
# Generate the shader from inline code if it was passed.
|
||||
self.shader_program = context.program(**source)
|
||||
else:
|
||||
# Search for a file containing the shader.
|
||||
source_dict = {}
|
||||
source_dict_key = {
|
||||
"vert": "vertex_shader",
|
||||
"frag": "fragment_shader",
|
||||
"geom": "geometry_shader",
|
||||
}
|
||||
shader_folder = SHADER_FOLDER / name
|
||||
for shader_file in shader_folder.iterdir():
|
||||
shader_file_path = shader_folder / shader_file
|
||||
shader_source = get_shader_code_from_file(shader_file_path)
|
||||
source_dict[source_dict_key[shader_file_path.stem]] = shader_source
|
||||
self.shader_program = context.program(**source_dict)
|
||||
|
||||
# Cache the shader.
|
||||
if name is not None and name not in shader_program_cache:
|
||||
shader_program_cache[self.name] = self.shader_program
|
||||
|
||||
def set_uniform(self, name, value):
|
||||
try:
|
||||
self.shader_program[name] = value
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
class FullScreenQuad(Mesh):
|
||||
def __init__(
|
||||
self,
|
||||
context,
|
||||
fragment_shader_source=None,
|
||||
fragment_shader_name=None,
|
||||
):
|
||||
if fragment_shader_source is None and fragment_shader_name is None:
|
||||
raise Exception("Must either pass shader name or shader source.")
|
||||
|
||||
if fragment_shader_name is not None:
|
||||
# Use the name.
|
||||
shader_file_path = SHADER_FOLDER / f"{fragment_shader_name}.frag"
|
||||
fragment_shader_source = get_shader_code_from_file(shader_file_path)
|
||||
elif fragment_shader_source is not None:
|
||||
fragment_shader_source = textwrap.dedent(fragment_shader_source.lstrip())
|
||||
|
||||
shader = Shader(
|
||||
context,
|
||||
source={
|
||||
"vertex_shader": """
|
||||
#version 330
|
||||
in vec4 in_vert;
|
||||
uniform mat4 u_model_view_matrix;
|
||||
uniform mat4 u_projection_matrix;
|
||||
void main() {{
|
||||
vec4 camera_space_vertex = u_model_view_matrix * in_vert;
|
||||
vec4 clip_space_vertex = u_projection_matrix * camera_space_vertex;
|
||||
gl_Position = clip_space_vertex;
|
||||
}}
|
||||
""",
|
||||
"fragment_shader": fragment_shader_source,
|
||||
},
|
||||
)
|
||||
attributes = np.zeros(6, dtype=[("in_vert", np.float32, (4,))])
|
||||
attributes["in_vert"] = np.array(
|
||||
[
|
||||
[-config["frame_x_radius"], -config["frame_y_radius"], 0, 1],
|
||||
[-config["frame_x_radius"], config["frame_y_radius"], 0, 1],
|
||||
[config["frame_x_radius"], config["frame_y_radius"], 0, 1],
|
||||
[-config["frame_x_radius"], -config["frame_y_radius"], 0, 1],
|
||||
[config["frame_x_radius"], -config["frame_y_radius"], 0, 1],
|
||||
[config["frame_x_radius"], config["frame_y_radius"], 0, 1],
|
||||
],
|
||||
)
|
||||
shader.set_uniform("u_model_view_matrix", opengl.view_matrix())
|
||||
shader.set_uniform(
|
||||
"u_projection_matrix",
|
||||
opengl.orthographic_projection_matrix(),
|
||||
)
|
||||
super().__init__(shader, attributes)
|
||||
|
||||
def render(self):
|
||||
super().render()
|
||||
|
|
@ -5,14 +5,18 @@ from typing import TYPE_CHECKING
|
|||
|
||||
import moderngl as gl
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from typing_extensions import override
|
||||
|
||||
import manim.constants as const
|
||||
import manim.utils.color.manim_colors as color
|
||||
from manim._config import config, logger
|
||||
from manim.camera.camera import OpenGLCameraFrame
|
||||
from manim.mobject.types.vectorized_mobject import VMobject
|
||||
from manim.renderer.opengl_shader_program import load_shader_program_by_folder
|
||||
from manim.renderer.renderer import Renderer, RendererData
|
||||
from manim.renderer.renderer import ImageType, Renderer, RendererData
|
||||
from manim.renderer.shader_wrapper import ShaderWrapper
|
||||
from manim.utils.iterables import listify, resize_array, resize_with_interpolation
|
||||
from manim.utils.space_ops import cross2d, earclip_triangulation, z_to_vector
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -21,15 +25,31 @@ if TYPE_CHECKING:
|
|||
import manim.utils.color.core as c
|
||||
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject
|
||||
|
||||
fill_dtype = [
|
||||
("point", np.float32, (3,)),
|
||||
# ("orientation", np.float32, (3,)),
|
||||
("unit_normal", np.float32, (3,)),
|
||||
("color", np.float32, (4,)),
|
||||
("vert_index", np.float32, (1,)),
|
||||
]
|
||||
stroke_dtype = [
|
||||
("point", np.float32, (3,)),
|
||||
("prev_point", np.float32, (3,)),
|
||||
("next_point", np.float32, (3,)),
|
||||
("stroke_width", np.float32, (1,)),
|
||||
("color", np.float32, (4,)),
|
||||
]
|
||||
|
||||
|
||||
class GLRenderData(RendererData):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.fill_rgbas = np.zeros((1, 4))
|
||||
self.stroke_rgbas = np.zeros((1, 4))
|
||||
self.stroke_widths = np.zeros((1, 1))
|
||||
self.normals = np.zeros((1, 4))
|
||||
self.orientation = None
|
||||
self.mesh = np.zeros((0, 3))
|
||||
self.orientation = np.zeros((1, 1))
|
||||
self.vert_indices = np.zeros((0, 3))
|
||||
self.bounding_box = np.zeros((3, 3))
|
||||
|
||||
def __repr__(self) -> str:
|
||||
|
|
@ -43,7 +63,7 @@ normals:
|
|||
orientation:
|
||||
{self.orientation}
|
||||
mesh:
|
||||
{self.mesh}
|
||||
{self.vert_indices}
|
||||
bounding_box:
|
||||
{self.bounding_box}
|
||||
"""
|
||||
|
|
@ -165,22 +185,27 @@ class OpenGLRenderer(Renderer):
|
|||
self.pixel_width = pixel_width
|
||||
self.pixel_height = pixel_height
|
||||
self.samples = samples
|
||||
self.background_color = (color.BLACK,)
|
||||
self.background_color = background_color.to_rgba()
|
||||
self.background_image = background_image
|
||||
|
||||
self.rgb_max_val: float = np.iinfo(self.pixel_array_dtype).max
|
||||
|
||||
# Initializing Context
|
||||
logger.debug("Initializing OpenGL context and framebuffers")
|
||||
self.ctx = gl.create_context(standalone=True)
|
||||
self.ctx = gl.create_context()
|
||||
self.target_fbo = self.ctx.simple_framebuffer(
|
||||
(self.pixel_width, self.pixel_height), samples=self.samples
|
||||
)
|
||||
self.output_fbo = self.ctx.framebuffer(
|
||||
color_attachments=[
|
||||
self.ctx.renderbuffer((self.pixel_width, self.pixel_height))
|
||||
]
|
||||
)
|
||||
|
||||
fbo = self.ctx.detect_framebuffer()
|
||||
if not fbo:
|
||||
self.output_fbo = self.ctx.framebuffer(
|
||||
color_attachments=[
|
||||
self.ctx.renderbuffer((self.pixel_width, self.pixel_height))
|
||||
]
|
||||
)
|
||||
else:
|
||||
self.output_fbo = fbo
|
||||
|
||||
# Preparing vmobject shader
|
||||
logger.debug("Initializing Shader Programs")
|
||||
|
|
@ -191,8 +216,70 @@ class OpenGLRenderer(Renderer):
|
|||
self.ctx, "quadratic_bezier_stroke"
|
||||
)
|
||||
|
||||
def set_camera(self, camera: OpenGLCameraFrame) -> ImageType:
|
||||
self.vmobject_fill_program["is_fixed_in_frame"] = 0.0
|
||||
self.vmobject_fill_program["frame_shape"] = camera.frame_shape
|
||||
self.vmobject_fill_program["focal_distance"] = float(
|
||||
camera.get_focal_distance()
|
||||
)
|
||||
self.vmobject_fill_program["camera_center"] = tuple(camera.get_center())
|
||||
self.vmobject_fill_program["camera_rotation"] = tuple(
|
||||
np.array(camera.get_inverse_camera_rotation_matrix()).T.flatten()
|
||||
)
|
||||
self.vmobject_fill_program["light_source_position"] = (-10, 10, 10)
|
||||
# TODO: convert to singular 4x4 matrix after getting *something* to render
|
||||
# self.vmobject_fill_program['view'].value = camera.get_view()?
|
||||
self.vmobject_stroke_program["is_fixed_in_frame"].value = 0.0
|
||||
self.vmobject_stroke_program["anti_alias_width"].value = 1.0
|
||||
self.vmobject_stroke_program["frame_shape"].value = np.asarray(
|
||||
camera.frame_shape
|
||||
)
|
||||
# self.vmobject_stroke_program['pixel_shape'].value = camera.frame_shape
|
||||
self.vmobject_stroke_program["focal_distance"].value = np.asarray(
|
||||
camera.get_focal_distance()
|
||||
)
|
||||
self.vmobject_stroke_program["camera_center"].value = np.asarray(
|
||||
camera.get_center()
|
||||
)
|
||||
self.vmobject_stroke_program["camera_rotation"].value = np.array(
|
||||
camera.get_inverse_camera_rotation_matrix()
|
||||
).T.flatten()
|
||||
self.vmobject_stroke_program["light_source_position"].value = np.array(
|
||||
[-10, 10, 10]
|
||||
)
|
||||
|
||||
def get_stroke_shader_data(self, mob: OpenGLVMobject) -> np.ndarray:
|
||||
if not isinstance(mob.renderer_data, GLRenderData):
|
||||
raise TypeError()
|
||||
|
||||
points = mob.points
|
||||
stroke_data = np.zeros(len(points), dtype=stroke_dtype)
|
||||
|
||||
nppc = mob.n_points_per_curve
|
||||
stroke_data["point"] = points
|
||||
stroke_data["prev_point"][:nppc] = points[-nppc:]
|
||||
stroke_data["prev_point"][nppc:] = points[:-nppc]
|
||||
stroke_data["next_point"][:-nppc] = points[nppc:]
|
||||
stroke_data["next_point"][-nppc:] = points[:nppc]
|
||||
stroke_data["color"] = mob.renderer_data.stroke_rgbas
|
||||
stroke_data["stroke_width"] = mob.renderer_data.stroke_widths
|
||||
|
||||
return stroke_data
|
||||
|
||||
def get_fill_shader_data(self, mob: OpenGLVMobject) -> np.ndarray:
|
||||
if not isinstance(mob.renderer_data, GLRenderData):
|
||||
raise TypeError()
|
||||
|
||||
fill_data = np.zeros(len(mob.points), dtype=fill_dtype)
|
||||
fill_data["point"] = mob.points
|
||||
fill_data["color"] = mob.renderer_data.fill_rgbas
|
||||
# fill_data["orientation"] = mob.renderer_data.orientation
|
||||
fill_data["unit_normal"] = mob.renderer_data.normals
|
||||
fill_data["vert_index"] = np.reshape(range(len(mob.points)), (-1, 1))
|
||||
return fill_data
|
||||
|
||||
@override
|
||||
def render_vmobject(self, mob: OpenGLVMobject, camera: OpenGLCameraFrame) -> None:
|
||||
def render_vmobject(self, mob: OpenGLVMobject) -> None:
|
||||
# Setting camera uniforms
|
||||
|
||||
if mob.renderer_data is None:
|
||||
|
|
@ -201,20 +288,25 @@ class OpenGLRenderer(Renderer):
|
|||
logger.debug("Initializing GLRenderData")
|
||||
mob.renderer_data = GLRenderData()
|
||||
# Generate Mesh
|
||||
mob.renderer_data.mesh, mob.renderer_data.orientation = get_triangulation(
|
||||
mob
|
||||
)
|
||||
mesh_length = len(mob.renderer_data.mesh)
|
||||
(
|
||||
mob.renderer_data.vert_indices,
|
||||
mob.renderer_data.orientation,
|
||||
) = get_triangulation(mob)
|
||||
points_length = len(mob.points)
|
||||
|
||||
# Generate Fill Color
|
||||
fill_color = np.array([c._internal_value for c in mob.fill_color])
|
||||
stroke_color = np.array([c._internal_value for c in mob.stroke_color])
|
||||
mob.renderer_data.fill_rgbas = prepare_array(fill_color, mesh_length)
|
||||
mob.renderer_data.stroke_rgbas = prepare_array(stroke_color, mesh_length)
|
||||
mob.renderer_data.fill_rgbas = prepare_array(fill_color, points_length)
|
||||
mob.renderer_data.stroke_rgbas = prepare_array(stroke_color, points_length)
|
||||
mob.renderer_data.stroke_widths = prepare_array(
|
||||
np.asarray(listify(mob.stroke_width)), points_length
|
||||
)
|
||||
mob.renderer_data.normals = np.repeat(
|
||||
[mob.get_unit_normal()], mesh_length, axis=0
|
||||
[mob.get_unit_normal()], points_length, axis=0
|
||||
)
|
||||
mob.renderer_data.bounding_box = compute_bounding_box(mob)
|
||||
print(mob.renderer_data)
|
||||
|
||||
# print(mob.renderer_data.mesh)
|
||||
# print(mob.renderer_data.orientation)
|
||||
|
|
@ -227,6 +319,46 @@ class OpenGLRenderer(Renderer):
|
|||
# if(mob.has_fill()):
|
||||
# mob.renderer_data.mesh = ... # Triangulation todo
|
||||
|
||||
# self.vmobject_fill_program['reflectiveness'].value = mob.reflectiveness
|
||||
self.vmobject_fill_program["gloss"].value = mob.gloss
|
||||
self.vmobject_fill_program["shadow"].value = mob.shadow
|
||||
|
||||
# self.vmobject_stroke_program['reflectiveness'].value = mob.reflectiveness
|
||||
self.vmobject_stroke_program["gloss"].value = mob.gloss
|
||||
self.vmobject_stroke_program["shadow"].value = mob.shadow
|
||||
self.vmobject_stroke_program["joint_type"].value = float(
|
||||
mob.joint_type.value
|
||||
) # TODO: This maybe breaks
|
||||
self.vmobject_stroke_program["flat_stroke"].value = mob.flat_stroke
|
||||
|
||||
def render_shader(prog, mob, data):
|
||||
vbo = self.ctx.buffer(data.tobytes())
|
||||
ibo = self.ctx.buffer(mob.renderer_data.vert_indices)
|
||||
# print(prog,vbo,data)
|
||||
vert_format = gl.detect_format(prog, data.dtype.names)
|
||||
# print(vert_format)
|
||||
vao = self.ctx.vertex_array(
|
||||
program=prog,
|
||||
content=[(vbo, vert_format, *data.dtype.names)],
|
||||
index_buffer=ibo,
|
||||
)
|
||||
self.target_fbo.use()
|
||||
self.target_fbo.clear(*self.background_color)
|
||||
vao.render(gl.TRIANGLES)
|
||||
vbo.release()
|
||||
ibo.release()
|
||||
vao.release()
|
||||
|
||||
# print(self.get_fill_shader_data(mob))
|
||||
self.ctx.enable(gl.BLEND)
|
||||
self.ctx.enable(gl.DEPTH_TEST)
|
||||
|
||||
render_shader(self.vmobject_fill_program, mob, self.get_fill_shader_data(mob))
|
||||
# render_shader(self.vmobject_stroke_program, mob, stroke_dtype, self.get_stroke_shader_data(mob))
|
||||
# print(self.target_fbo.read())
|
||||
self.ctx.copy_framebuffer(self.output_fbo, self.target_fbo)
|
||||
# Image.frombytes('RGBA', self.output_fbo.size, self.output_fbo.read(components=4), 'raw', 'RGBA', 0, -1).show()
|
||||
|
||||
# set shader
|
||||
# use vbo
|
||||
# render fill
|
||||
|
|
|
|||
|
|
@ -75,6 +75,9 @@ def load_shader_program_by_folder(ctx: gl.Context, folder_name: str):
|
|||
vertex_code = get_shader_code_from_file(Path(folder_name + "/vert.glsl"))
|
||||
geometry_code = get_shader_code_from_file(Path(folder_name + "/geom.glsl"))
|
||||
fragment_code = get_shader_code_from_file(Path(folder_name + "/frag.glsl"))
|
||||
# print(folder_name)
|
||||
# for i,l in enumerate(geometry_code.splitlines()):
|
||||
# print(str(i) + ":" + l )
|
||||
if vertex_code is None or fragment_code is None:
|
||||
logger.error(
|
||||
f"Invalid program definition for {folder_name} vertex or fragment shader not present"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject
|
|||
from manim.mobject.types.image_mobject import ImageMobject
|
||||
from manim.mobject.types.vectorized_mobject import VMobject
|
||||
|
||||
Image: TypeAlias = np.ndarray
|
||||
ImageType: TypeAlias = np.ndarray
|
||||
|
||||
|
||||
class RendererData:
|
||||
|
|
@ -21,13 +21,12 @@ class Renderer(ABC):
|
|||
self.fbo = np.zeros((config.height, config.width))
|
||||
self.capabilities = {
|
||||
VMobject: self.render_vmobject,
|
||||
ImageMobject: self.render_imobject,
|
||||
ImageMobject: self.render_image,
|
||||
}
|
||||
|
||||
def render(self, camera, renderables: [VMobject]) -> Image: # Image
|
||||
def render(self, camera, renderables: [VMobject]) -> ImageType: # Image
|
||||
for mob in renderables:
|
||||
if type(mob) in self.capabilities:
|
||||
mob.points
|
||||
self.capabilities[type(mob)](mob)
|
||||
else:
|
||||
print("WARNING: NOT SUPPORTED")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
uniform vec2 frame_shape;
|
||||
uniform vec2 pixel_shape;
|
||||
uniform vec3 camera_offset;
|
||||
uniform vec3 camera_center;
|
||||
uniform mat3 camera_rotation;
|
||||
uniform float is_fixed_in_frame;
|
||||
uniform float is_fixed_orientation;
|
||||
uniform vec3 fixed_orientation_center;
|
||||
uniform float focal_distance;
|
||||
|
|
|
|||
|
|
@ -1,53 +1,51 @@
|
|||
vec3 float_to_color(float value, float min_val, float max_val, vec3[9] colormap_data)
|
||||
{
|
||||
vec3 float_to_color(float value, float min_val, float max_val, vec3[9] colormap_data){
|
||||
float alpha = clamp((value - min_val) / (max_val - min_val), 0.0, 1.0);
|
||||
int disc_alpha = min(int(alpha * 8), 7);
|
||||
return mix(colormap_data[disc_alpha], colormap_data[disc_alpha + 1], 8.0 * alpha - disc_alpha);
|
||||
return mix(
|
||||
colormap_data[disc_alpha],
|
||||
colormap_data[disc_alpha + 1],
|
||||
8.0 * alpha - disc_alpha
|
||||
);
|
||||
}
|
||||
|
||||
vec4 add_light(vec4 color, vec3 point, vec3 unit_normal, vec3 light_coords, vec3 cam_coords, float reflectiveness,
|
||||
float gloss, float shadow)
|
||||
{
|
||||
if (reflectiveness == 0.0 && gloss == 0.0 && shadow == 0.0)
|
||||
return color;
|
||||
|
||||
vec4 result = color;
|
||||
// Assume everything has already been rotated such that camera is in the z-direction
|
||||
// cam_coords = vec3(0, 0, focal_distance);
|
||||
vec3 to_camera = normalize(cam_coords - point);
|
||||
vec3 to_light = normalize(light_coords - point);
|
||||
vec4 add_light(vec4 color,
|
||||
vec3 point,
|
||||
vec3 unit_normal,
|
||||
vec3 light_coords,
|
||||
float gloss,
|
||||
float shadow){
|
||||
if(gloss == 0.0 && shadow == 0.0) return color;
|
||||
|
||||
// Note, this effectively treats surfaces as two-sided
|
||||
// if(dot(to_camera, unit_normal) < 0) unit_normal *= -1;
|
||||
|
||||
float light_to_normal = dot(to_light, unit_normal);
|
||||
// When unit normal points towards light, brighten
|
||||
float bright_factor = max(light_to_normal, 0) * reflectiveness;
|
||||
// For glossy surface, add extra shine if light beam go towards camera
|
||||
vec3 light_reflection = -to_light + 2 * unit_normal * dot(to_light, unit_normal);
|
||||
float light_to_cam = dot(light_reflection, to_camera);
|
||||
float shine = gloss * exp(-3 * pow(1 - light_to_cam, 2));
|
||||
bright_factor += shine;
|
||||
|
||||
result.rgb = mix(result.rgb, vec3(1.0), bright_factor);
|
||||
if (light_to_normal < 0)
|
||||
{
|
||||
// Darken
|
||||
result.rgb = mix(result.rgb, vec3(0.0), -light_to_normal * shadow);
|
||||
// TODO, do we actually want this? It effectively treats surfaces as two-sided
|
||||
if(unit_normal.z < 0){
|
||||
unit_normal *= -1;
|
||||
}
|
||||
// float darkening = mix(1, max(light_to_normal, 0), shadow);
|
||||
// return vec4(
|
||||
// darkening * mix(color.rgb, vec3(1.0), shine),
|
||||
// color.a
|
||||
// );
|
||||
return result;
|
||||
|
||||
// TODO, read this in as a uniform?
|
||||
float camera_distance = 6;
|
||||
// Assume everything has already been rotated such that camera is in the z-direction
|
||||
vec3 to_camera = vec3(0, 0, camera_distance) - point;
|
||||
vec3 to_light = light_coords - point;
|
||||
vec3 light_reflection = -to_light + 2 * unit_normal * dot(to_light, unit_normal);
|
||||
float dot_prod = dot(normalize(light_reflection), normalize(to_camera));
|
||||
float shine = gloss * exp(-3 * pow(1 - dot_prod, 2));
|
||||
float dp2 = dot(normalize(to_light), unit_normal);
|
||||
float darkening = mix(1, max(dp2, 0), shadow);
|
||||
return vec4(
|
||||
darkening * mix(color.rgb, vec3(1.0), shine),
|
||||
color.a
|
||||
);
|
||||
}
|
||||
|
||||
vec4 finalize_color(vec4 color, vec3 point, vec3 unit_normal, vec3 light_coords, vec3 cam_coords, float reflectiveness,
|
||||
float gloss, float shadow)
|
||||
{
|
||||
vec4 finalize_color(vec4 color,
|
||||
vec3 point,
|
||||
vec3 unit_normal,
|
||||
vec3 light_coords,
|
||||
float gloss,
|
||||
float shadow){
|
||||
///// INSERT COLOR FUNCTION HERE /////
|
||||
// The line above may be replaced by arbitrary code snippets, as per
|
||||
// the method Mobject.set_color_by_code
|
||||
return add_light(color, point, unit_normal, light_coords, cam_coords, reflectiveness, gloss, shadow);
|
||||
return add_light(color, point, unit_normal, light_coords, gloss, shadow);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,25 @@
|
|||
// Assumes the following uniforms exist in the surrounding context:
|
||||
// uniform float is_fixed_in_frame;
|
||||
// uniform vec3 camera_offset;
|
||||
// uniform float is_fixed_orientation;
|
||||
// uniform vec3 fixed_orientation_center;
|
||||
// uniform vec3 camera_center;
|
||||
// uniform mat3 camera_rotation;
|
||||
|
||||
vec3 rotate_point_into_frame(vec3 point)
|
||||
{
|
||||
if (bool(is_fixed_in_frame))
|
||||
{
|
||||
vec3 rotate_point_into_frame(vec3 point){
|
||||
if(bool(is_fixed_in_frame)){
|
||||
return point;
|
||||
}
|
||||
return camera_rotation * point;
|
||||
}
|
||||
|
||||
vec3 position_point_into_frame(vec3 point)
|
||||
{
|
||||
if (bool(is_fixed_in_frame))
|
||||
{
|
||||
|
||||
vec3 position_point_into_frame(vec3 point){
|
||||
if(bool(is_fixed_in_frame)){
|
||||
return point;
|
||||
}
|
||||
return rotate_point_into_frame(point - camera_offset);
|
||||
if(bool(is_fixed_orientation)){
|
||||
vec3 new_center = rotate_point_into_frame(fixed_orientation_center);
|
||||
return point + (new_center - fixed_orientation_center);
|
||||
}
|
||||
return rotate_point_into_frame(point - camera_center);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
#version 330
|
||||
|
||||
#include "../include/camera_uniform_declarations.glsl"
|
||||
#include ../include/camera_uniform_declarations.glsl
|
||||
|
||||
in vec4 color;
|
||||
in float fill_all; // Either 0 or 1
|
||||
in float fill_all; // Either 0 or 1e
|
||||
in float uv_anti_alias_width;
|
||||
|
||||
in float orientation;
|
||||
|
|
@ -14,10 +14,8 @@ out vec4 frag_color;
|
|||
|
||||
#define ANTI_ALIASING
|
||||
|
||||
float sdf()
|
||||
{
|
||||
if (bezier_degree < 2)
|
||||
{
|
||||
float sdf(){
|
||||
if(bezier_degree < 2){
|
||||
return abs(uv_coords[1]);
|
||||
}
|
||||
vec2 p = uv_coords;
|
||||
|
|
@ -31,13 +29,11 @@ float sdf()
|
|||
#endif
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
if (color.a == 0)
|
||||
discard;
|
||||
|
||||
void main() {
|
||||
if (color.a == 0) discard;
|
||||
frag_color = color;
|
||||
if (fill_all == 1.0)
|
||||
return;
|
||||
if (fill_all == 1.0) return;
|
||||
#ifdef ANTI_ALIASING
|
||||
frag_color.a *= 0.5 - sdf(); // Anti-aliasing
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,24 +1,23 @@
|
|||
#version 330
|
||||
|
||||
layout(triangles) in;
|
||||
layout(triangle_strip, max_vertices = 5) out;
|
||||
layout (triangles) in;
|
||||
layout (triangle_strip, max_vertices = 5) out;
|
||||
|
||||
uniform float anti_alias_width;
|
||||
|
||||
// Needed for get_gl_Position
|
||||
uniform vec2 frame_shape;
|
||||
uniform vec2 pixel_shape;
|
||||
uniform float focal_distance;
|
||||
uniform float is_fixed_in_frame;
|
||||
uniform float is_fixed_orientation;
|
||||
uniform vec3 fixed_orientation_center;
|
||||
// Needed for finalize_color
|
||||
uniform vec3 light_source_position;
|
||||
uniform vec3 camera_position;
|
||||
uniform float reflectiveness;
|
||||
uniform float gloss;
|
||||
uniform float shadow;
|
||||
|
||||
in vec3 bp[3];
|
||||
in float v_orientation[3];
|
||||
in vec3 v_global_unit_normal[3];
|
||||
in vec4 v_color[3];
|
||||
in float v_vert_index[3];
|
||||
|
||||
|
|
@ -30,14 +29,10 @@ out vec2 uv_coords;
|
|||
out float bezier_degree;
|
||||
|
||||
// Analog of import for manim only
|
||||
// Comment to prevent formatting
|
||||
#include "../include/quadratic_bezier_geometry_functions.glsl"
|
||||
//
|
||||
#include "../include/get_gl_Position.glsl"
|
||||
//
|
||||
#include "../include/get_unit_normal.glsl"
|
||||
//
|
||||
#include "../include/finalize_color.glsl"
|
||||
#include ../include/quadratic_bezier_geometry_functions.glsl
|
||||
#include ../include/get_gl_Position.glsl
|
||||
#include ../include/get_unit_normal.glsl
|
||||
#include ../include/finalize_color.glsl
|
||||
|
||||
const vec2 uv_coords_arr[3] = vec2[3](vec2(0, 0), vec2(0.5, 0), vec2(1, 1));
|
||||
|
||||
|
|
@ -51,31 +46,31 @@ void emit_vertex_wrapper(vec3 point, int index)
|
|||
|
||||
void emit_simple_triangle()
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
emit_vertex_wrapper(bp[i], i);
|
||||
}
|
||||
EndPrimitive();
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
void main(){
|
||||
// If vert indices are sequential, don't fill all
|
||||
fill_all = float((v_vert_index[1] - v_vert_index[0]) != 1.0 || (v_vert_index[2] - v_vert_index[1]) != 1.0);
|
||||
fill_all = float(
|
||||
(v_vert_index[1] - v_vert_index[0]) != 1.0 ||
|
||||
(v_vert_index[2] - v_vert_index[1]) != 1.0
|
||||
);
|
||||
|
||||
if (fill_all == 1.0)
|
||||
{
|
||||
if(fill_all == 1.0){
|
||||
emit_simple_triangle();
|
||||
return;
|
||||
}
|
||||
|
||||
vec3 new_bp[3];
|
||||
bezier_degree = get_reduced_control_points(vec3[3](bp[0], bp[1], bp[2]), new_bp);
|
||||
unit_normal = get_unit_normal(new_bp);
|
||||
orientation = v_orientation[0];
|
||||
vec3 local_unit_normal = get_unit_normal(new_bp);
|
||||
orientation = sign(dot(v_global_unit_normal[0], local_unit_normal));
|
||||
|
||||
if (bezier_degree >= 1)
|
||||
{
|
||||
if(bezier_degree >= 1){
|
||||
emit_simple_triangle();
|
||||
}
|
||||
// Don't emit any vertices for bezier_degree 0
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
#version 330
|
||||
|
||||
#include "../include/camera_uniform_declarations.glsl"
|
||||
#include ../include/camera_uniform_declarations.glsl
|
||||
|
||||
in vec3 point;
|
||||
in float orientation;
|
||||
in vec3 unit_normal;
|
||||
in vec4 color;
|
||||
in float vert_index;
|
||||
|
||||
out vec3 bp; // Bezier control point
|
||||
out float v_orientation;
|
||||
out vec4 v_color;
|
||||
out float v_vert_index;
|
||||
out vec3 v_global_unit_normal;
|
||||
|
||||
// Analog of import for manim only
|
||||
#include "../include/position_point_into_frame.glsl"
|
||||
#include ../include/position_point_into_frame.glsl
|
||||
|
||||
void main()
|
||||
{
|
||||
bp = position_point_into_frame(point);
|
||||
v_orientation = orientation;
|
||||
void main(){
|
||||
bp = position_point_into_frame(point.xyz);
|
||||
v_global_unit_normal = rotate_point_into_frame(unit_normal);
|
||||
v_color = color;
|
||||
v_vert_index = vert_index;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#version 330
|
||||
|
||||
#include "../include/camera_uniform_declarations.glsl"
|
||||
#include ../include/camera_uniform_declarations.glsl
|
||||
|
||||
in vec2 uv_coords;
|
||||
in vec2 uv_b2;
|
||||
|
|
@ -20,25 +20,25 @@ in float bezier_degree;
|
|||
|
||||
out vec4 frag_color;
|
||||
|
||||
float cross2d(vec2 v, vec2 w)
|
||||
{
|
||||
|
||||
float cross2d(vec2 v, vec2 w){
|
||||
return v.x * w.y - w.x * v.y;
|
||||
}
|
||||
|
||||
float modify_distance_for_endpoints(vec2 p, float dist, float t)
|
||||
{
|
||||
|
||||
float modify_distance_for_endpoints(vec2 p, float dist, float t){
|
||||
float buff = 0.5 * uv_stroke_width - uv_anti_alias_width;
|
||||
// Check the beginning of the curve
|
||||
if (t == 0)
|
||||
{
|
||||
if(t == 0){
|
||||
// Clip the start
|
||||
if (has_prev == 0)
|
||||
return max(dist, -p.x + buff);
|
||||
if(has_prev == 0) return max(dist, -p.x + buff);
|
||||
// Bevel start
|
||||
if (bevel_start == 1)
|
||||
{
|
||||
if(bevel_start == 1){
|
||||
float a = angle_from_prev;
|
||||
mat2 rot = mat2(cos(a), sin(a), -sin(a), cos(a));
|
||||
mat2 rot = mat2(
|
||||
cos(a), sin(a),
|
||||
-sin(a), cos(a)
|
||||
);
|
||||
// Dist for intersection of two lines
|
||||
float bevel_d = max(abs(p.y), abs((rot * p).y));
|
||||
// Dist for union of this intersection with the real curve
|
||||
|
|
@ -47,29 +47,30 @@ float modify_distance_for_endpoints(vec2 p, float dist, float t)
|
|||
return max(min(dist, bevel_d), dist / 2);
|
||||
}
|
||||
// Otherwise, start will be rounded off
|
||||
}
|
||||
else if (t == 1)
|
||||
{
|
||||
}else if(t == 1){
|
||||
// Check the end of the curve
|
||||
// TODO, too much code repetition
|
||||
vec2 v21 = (bezier_degree == 2) ? vec2(1, 0) - uv_b2 : vec2(-1, 0);
|
||||
float len_v21 = length(v21);
|
||||
if (len_v21 == 0)
|
||||
{
|
||||
if(len_v21 == 0){
|
||||
v21 = -uv_b2;
|
||||
len_v21 = length(v21);
|
||||
}
|
||||
|
||||
float perp_dist = dot(p - uv_b2, v21) / len_v21;
|
||||
if (has_next == 0)
|
||||
return max(dist, -perp_dist + buff);
|
||||
if(has_next == 0) return max(dist, -perp_dist + buff);
|
||||
// Bevel end
|
||||
if (bevel_end == 1)
|
||||
{
|
||||
if(bevel_end == 1){
|
||||
float a = -angle_to_next;
|
||||
mat2 rot = mat2(cos(a), sin(a), -sin(a), cos(a));
|
||||
mat2 rot = mat2(
|
||||
cos(a), sin(a),
|
||||
-sin(a), cos(a)
|
||||
);
|
||||
vec2 v21_unit = v21 / length(v21);
|
||||
float bevel_d = max(abs(cross2d(p - uv_b2, v21_unit)), abs(cross2d((rot * (p - uv_b2)), v21_unit)));
|
||||
float bevel_d = max(
|
||||
abs(cross2d(p - uv_b2, v21_unit)),
|
||||
abs(cross2d((rot * (p - uv_b2)), v21_unit))
|
||||
);
|
||||
return max(min(dist, bevel_d), dist / 2);
|
||||
}
|
||||
// Otherwise, end will be rounded off
|
||||
|
|
@ -77,12 +78,12 @@ float modify_distance_for_endpoints(vec2 p, float dist, float t)
|
|||
return dist;
|
||||
}
|
||||
|
||||
#include "../include/quadratic_bezier_distance.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
if (uv_stroke_width == 0)
|
||||
discard;
|
||||
#include ../include/quadratic_bezier_distance.glsl
|
||||
|
||||
|
||||
void main() {
|
||||
if (uv_stroke_width == 0) discard;
|
||||
float dist_to_curve = min_dist_to_curve(uv_coords, uv_b2, bezier_degree);
|
||||
// An sdf for the region around the curve we wish to color.
|
||||
float signed_dist = abs(dist_to_curve) - 0.5 * uv_stroke_width;
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
#version 330
|
||||
|
||||
layout(triangles) in;
|
||||
layout(triangle_strip, max_vertices = 5) out;
|
||||
layout (triangles) in;
|
||||
layout (triangle_strip, max_vertices = 5) out;
|
||||
|
||||
// Needed for get_gl_Position
|
||||
uniform vec2 frame_shape;
|
||||
uniform vec2 pixel_shape;
|
||||
uniform float focal_distance;
|
||||
uniform float is_fixed_in_frame;
|
||||
uniform float is_fixed_orientation;
|
||||
uniform vec3 fixed_orientation_center;
|
||||
|
||||
uniform float anti_alias_width;
|
||||
uniform float flat_stroke;
|
||||
|
||||
// Needed for lighting
|
||||
//Needed for lighting
|
||||
uniform vec3 light_source_position;
|
||||
uniform vec3 camera_position;
|
||||
uniform float joint_type;
|
||||
uniform float reflectiveness;
|
||||
uniform float gloss;
|
||||
uniform float shadow;
|
||||
|
||||
in vec3 bp[3];
|
||||
in vec3 prev_bp[3];
|
||||
in vec3 next_bp[3];
|
||||
in vec3 v_global_unit_normal[3];
|
||||
|
||||
in vec4 v_color[3];
|
||||
in float v_stroke_width[3];
|
||||
|
|
@ -43,8 +43,6 @@ out float bezier_degree;
|
|||
out vec2 uv_coords;
|
||||
out vec2 uv_b2;
|
||||
|
||||
vec3 unit_normal;
|
||||
|
||||
// Codes for joint types
|
||||
const float AUTO_JOINT = 0;
|
||||
const float ROUND_JOINT = 1;
|
||||
|
|
@ -52,61 +50,55 @@ const float BEVEL_JOINT = 2;
|
|||
const float MITER_JOINT = 3;
|
||||
const float PI = 3.141592653;
|
||||
|
||||
#include "../include/finalize_color.glsl"
|
||||
#include "../include/get_gl_Position.glsl"
|
||||
#include "../include/get_unit_normal.glsl"
|
||||
#include "../include/quadratic_bezier_geometry_functions.glsl"
|
||||
|
||||
void flatten_points(in vec3[3] points, out vec2[3] flat_points)
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
#include ../include/quadratic_bezier_geometry_functions.glsl
|
||||
#include ../include/get_gl_Position.glsl
|
||||
#include ../include/get_unit_normal.glsl
|
||||
#include ../include/finalize_color.glsl
|
||||
|
||||
|
||||
void flatten_points(in vec3[3] points, out vec2[3] flat_points){
|
||||
for(int i = 0; i < 3; i++){
|
||||
float sf = perspective_scale_factor(points[i].z, focal_distance);
|
||||
flat_points[i] = sf * points[i].xy;
|
||||
}
|
||||
}
|
||||
|
||||
float angle_between_vectors(vec2 v1, vec2 v2)
|
||||
{
|
||||
|
||||
float angle_between_vectors(vec2 v1, vec2 v2){
|
||||
float v1_norm = length(v1);
|
||||
float v2_norm = length(v2);
|
||||
if (v1_norm == 0 || v2_norm == 0)
|
||||
return 0.0;
|
||||
if(v1_norm == 0 || v2_norm == 0) return 0.0;
|
||||
float dp = dot(v1, v2) / (v1_norm * v2_norm);
|
||||
float angle = acos(clamp(dp, -1.0, 1.0));
|
||||
float sn = sign(cross2d(v1, v2));
|
||||
return sn * angle;
|
||||
}
|
||||
|
||||
bool find_intersection(vec2 p0, vec2 v0, vec2 p1, vec2 v1, out vec2 intersection)
|
||||
{
|
||||
|
||||
bool find_intersection(vec2 p0, vec2 v0, vec2 p1, vec2 v1, out vec2 intersection){
|
||||
// Find the intersection of a line passing through
|
||||
// p0 in the direction v0 and one passing through p1 in
|
||||
// the direction p1.
|
||||
// That is, find a solutoin to p0 + v0 * t = p1 + v1 * s
|
||||
float det = -v0.x * v1.y + v1.x * v0.y;
|
||||
if (det == 0)
|
||||
return false;
|
||||
if(det == 0) return false;
|
||||
float t = cross2d(p0 - p1, v1) / det;
|
||||
intersection = p0 + v0 * t;
|
||||
return true;
|
||||
}
|
||||
|
||||
void create_joint(float angle, vec2 unit_tan, float buff, vec2 static_c0, out vec2 changing_c0, vec2 static_c1,
|
||||
out vec2 changing_c1)
|
||||
{
|
||||
|
||||
void create_joint(float angle, vec2 unit_tan, float buff,
|
||||
vec2 static_c0, out vec2 changing_c0,
|
||||
vec2 static_c1, out vec2 changing_c1){
|
||||
float shift;
|
||||
if (abs(angle) < 1e-3)
|
||||
{
|
||||
if(abs(angle) < 1e-3){
|
||||
// No joint
|
||||
shift = 0;
|
||||
}
|
||||
else if (joint_type == MITER_JOINT)
|
||||
{
|
||||
}else if(joint_type == MITER_JOINT){
|
||||
shift = buff * (-1.0 - cos(angle)) / sin(angle);
|
||||
}
|
||||
else
|
||||
{
|
||||
}else{
|
||||
// For a Bevel joint
|
||||
shift = buff * (1.0 - cos(angle)) / sin(angle);
|
||||
}
|
||||
|
|
@ -114,11 +106,11 @@ void create_joint(float angle, vec2 unit_tan, float buff, vec2 static_c0, out ve
|
|||
changing_c1 = static_c1 + shift * unit_tan;
|
||||
}
|
||||
|
||||
|
||||
// This function is responsible for finding the corners of
|
||||
// a bounding region around the bezier curve, which can be
|
||||
// emitted as a triangle fan
|
||||
int get_corners(vec2 controls[3], int degree, float stroke_widths[3], out vec2 corners[5])
|
||||
{
|
||||
int get_corners(vec2 controls[3], int degree, float stroke_widths[3], out vec2 corners[5]){
|
||||
vec2 p0 = controls[0];
|
||||
vec2 p1 = controls[1];
|
||||
vec2 p2 = controls[2];
|
||||
|
|
@ -129,13 +121,13 @@ int get_corners(vec2 controls[3], int degree, float stroke_widths[3], out vec2 c
|
|||
vec2 v01 = -v10;
|
||||
vec2 v21 = -v12;
|
||||
|
||||
vec2 p0_perp = vec2(-v01.y, v01.x); // Pointing to the left of the curve from p0
|
||||
vec2 p2_perp = vec2(-v12.y, v12.x); // Pointing to the left of the curve from p2
|
||||
vec2 p0_perp = vec2(-v01.y, v01.x); // Pointing to the left of the curve from p0
|
||||
vec2 p2_perp = vec2(-v12.y, v12.x); // Pointing to the left of the curve from p2
|
||||
|
||||
// aaw is the added width given around the polygon for antialiasing.
|
||||
// In case the normal is faced away from (0, 0, 1), the vector to the
|
||||
// camera, this is scaled up.
|
||||
float aaw = anti_alias_width * frame_shape.y / pixel_shape.y;
|
||||
float aaw = anti_alias_width;
|
||||
float buff0 = 0.5 * stroke_widths[0] + aaw;
|
||||
float buff2 = 0.5 * stroke_widths[2] + aaw;
|
||||
float aaw0 = (1 - has_prev) * aaw;
|
||||
|
|
@ -147,92 +139,94 @@ int get_corners(vec2 controls[3], int degree, float stroke_widths[3], out vec2 c
|
|||
vec2 c3 = p2 - buff2 * p2_perp + aaw2 * v12;
|
||||
|
||||
// Account for previous and next control points
|
||||
if (has_prev > 0)
|
||||
create_joint(angle_from_prev, v01, buff0, c0, c0, c1, c1);
|
||||
if (has_next > 0)
|
||||
create_joint(angle_to_next, v21, buff2, c3, c3, c2, c2);
|
||||
if(has_prev > 0) create_joint(angle_from_prev, v01, buff0, c0, c0, c1, c1);
|
||||
if(has_next > 0) create_joint(angle_to_next, v21, buff2, c3, c3, c2, c2);
|
||||
|
||||
// Linear case is the simplest
|
||||
if (degree == 1)
|
||||
{
|
||||
// The order of corners should be for a triangle_strip. Last entry is a
|
||||
// dummy
|
||||
if(degree == 1){
|
||||
// The order of corners should be for a triangle_strip. Last entry is a dummy
|
||||
corners = vec2[5](c0, c1, c3, c2, vec2(0.0));
|
||||
return 4;
|
||||
}
|
||||
// Otherwise, form a pentagon around the curve
|
||||
float orientation = sign(cross2d(v01, v12)); // Positive for ccw curves
|
||||
if (orientation > 0)
|
||||
corners = vec2[5](c0, c1, p1, c2, c3);
|
||||
else
|
||||
corners = vec2[5](c1, c0, p1, c3, c2);
|
||||
float orientation = sign(cross2d(v01, v12)); // Positive for ccw curves
|
||||
if(orientation > 0) corners = vec2[5](c0, c1, p1, c2, c3);
|
||||
else corners = vec2[5](c1, c0, p1, c3, c2);
|
||||
// Replace corner[2] with convex hull point accounting for stroke width
|
||||
find_intersection(corners[0], v01, corners[4], v21, corners[2]);
|
||||
return 5;
|
||||
}
|
||||
|
||||
void set_adjascent_info(vec2 c0, vec2 tangent, int degree, vec2 adj[3], out float bevel, out float angle)
|
||||
{
|
||||
|
||||
void set_adjascent_info(vec2 c0, vec2 tangent,
|
||||
int degree,
|
||||
vec2 adj[3],
|
||||
out float bevel,
|
||||
out float angle
|
||||
){
|
||||
bool linear_adj = (angle_between_vectors(adj[1] - adj[0], adj[2] - adj[1]) < 1e-3);
|
||||
angle = angle_between_vectors(c0 - adj[1], tangent);
|
||||
// Decide on joint type
|
||||
bool one_linear = (degree == 1 || linear_adj);
|
||||
bool should_bevel = ((joint_type == AUTO_JOINT && one_linear) || joint_type == BEVEL_JOINT);
|
||||
bool should_bevel = (
|
||||
(joint_type == AUTO_JOINT && one_linear) ||
|
||||
joint_type == BEVEL_JOINT
|
||||
);
|
||||
bevel = should_bevel ? 1.0 : 0.0;
|
||||
}
|
||||
|
||||
void find_joint_info(vec2 controls[3], vec2 prev[3], vec2 next[3], int degree)
|
||||
{
|
||||
|
||||
void find_joint_info(vec2 controls[3], vec2 prev[3], vec2 next[3], int degree){
|
||||
float tol = 1e-6;
|
||||
|
||||
// Made as floats not bools so they can be passed to the frag shader
|
||||
has_prev = float(distance(prev[2], controls[0]) < tol);
|
||||
has_next = float(distance(next[0], controls[2]) < tol);
|
||||
|
||||
if (bool(has_prev))
|
||||
{
|
||||
if(bool(has_prev)){
|
||||
vec2 tangent = controls[1] - controls[0];
|
||||
set_adjascent_info(controls[0], tangent, degree, prev, bevel_start, angle_from_prev);
|
||||
set_adjascent_info(
|
||||
controls[0], tangent, degree, prev,
|
||||
bevel_start, angle_from_prev
|
||||
);
|
||||
}
|
||||
if (bool(has_next))
|
||||
{
|
||||
if(bool(has_next)){
|
||||
vec2 tangent = controls[1] - controls[2];
|
||||
set_adjascent_info(controls[2], tangent, degree, next, bevel_end, angle_to_next);
|
||||
set_adjascent_info(
|
||||
controls[2], tangent, degree, next,
|
||||
bevel_end, angle_to_next
|
||||
);
|
||||
angle_to_next *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
|
||||
void main() {
|
||||
// Convert control points to a standard form if they are linear or null
|
||||
vec3 controls[3];
|
||||
vec3 prev[3];
|
||||
vec3 next[3];
|
||||
unit_normal = get_unit_normal(controls);
|
||||
bezier_degree = get_reduced_control_points(vec3[3](bp[0], bp[1], bp[2]), controls);
|
||||
if (bezier_degree == 0.0)
|
||||
return; // Null curve
|
||||
if(bezier_degree == 0.0) return; // Null curve
|
||||
int degree = int(bezier_degree);
|
||||
get_reduced_control_points(vec3[3](prev_bp[0], prev_bp[1], prev_bp[2]), prev);
|
||||
get_reduced_control_points(vec3[3](next_bp[0], next_bp[1], next_bp[2]), next);
|
||||
|
||||
|
||||
// Adjust stroke width based on distance from the camera
|
||||
float scaled_strokes[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
for(int i = 0; i < 3; i++){
|
||||
float sf = perspective_scale_factor(controls[i].z, focal_distance);
|
||||
if (bool(flat_stroke))
|
||||
{
|
||||
if(bool(flat_stroke)){
|
||||
vec3 to_cam = normalize(vec3(0.0, 0.0, focal_distance) - controls[i]);
|
||||
sf *= abs(dot(unit_normal, to_cam));
|
||||
sf *= abs(dot(v_global_unit_normal[i], to_cam));
|
||||
}
|
||||
scaled_strokes[i] = v_stroke_width[i] * sf;
|
||||
}
|
||||
|
||||
// Control points are projected to the xy plane before drawing, which in turn
|
||||
// gets tranlated to a uv plane. The z-coordinate information will be
|
||||
// remembered by what's sent out to gl_Position, and by how it affects the
|
||||
// lighting and stroke width
|
||||
// gets translated to a uv plane. The z-coordinate information will be remembered
|
||||
// by what's sent out to gl_Position, and by how it affects the lighting and stroke width
|
||||
vec2 flat_controls[3];
|
||||
vec2 flat_prev[3];
|
||||
vec2 flat_next[3];
|
||||
|
|
@ -247,26 +241,33 @@ void main()
|
|||
int n_corners = get_corners(flat_controls, degree, scaled_strokes, corners);
|
||||
|
||||
int index_map[5] = int[5](0, 0, 1, 2, 2);
|
||||
if (n_corners == 4)
|
||||
index_map[2] = 2;
|
||||
if(n_corners == 4) index_map[2] = 2;
|
||||
|
||||
// Find uv conversion matrix
|
||||
mat3 xy_to_uv = get_xy_to_uv(flat_controls[0], flat_controls[1]);
|
||||
float scale_factor = length(flat_controls[1] - flat_controls[0]);
|
||||
uv_anti_alias_width = anti_alias_width * frame_shape.y / pixel_shape.y / scale_factor;
|
||||
uv_anti_alias_width = anti_alias_width / scale_factor;
|
||||
uv_b2 = (xy_to_uv * vec3(flat_controls[2], 1.0)).xy;
|
||||
|
||||
// Emit each corner
|
||||
for (int i = 0; i < n_corners; i++)
|
||||
{
|
||||
for(int i = 0; i < n_corners; i++){
|
||||
uv_coords = (xy_to_uv * vec3(corners[i], 1.0)).xy;
|
||||
uv_stroke_width = scaled_strokes[index_map[i]] / scale_factor;
|
||||
// Apply some lighting to the color before sending out.
|
||||
// vec3 xyz_coords = vec3(corners[i], controls[index_map[i]].z);
|
||||
vec3 xyz_coords = vec3(corners[i], controls[index_map[i]].z);
|
||||
color = finalize_color(v_color[index_map[i]], xyz_coords, unit_normal, light_source_position, camera_position,
|
||||
reflectiveness, gloss, shadow);
|
||||
gl_Position = vec4(get_gl_Position(vec3(corners[i], 0.0)).xy, get_gl_Position(controls[index_map[i]]).zw);
|
||||
color = finalize_color(
|
||||
v_color[index_map[i]],
|
||||
xyz_coords,
|
||||
v_global_unit_normal[index_map[i]],
|
||||
light_source_position,
|
||||
gloss,
|
||||
shadow
|
||||
);
|
||||
gl_Position = vec4(
|
||||
get_gl_Position(vec3(corners[i], 0.0)).xy,
|
||||
get_gl_Position(controls[index_map[i]]).zw
|
||||
);
|
||||
EmitVertex();
|
||||
}
|
||||
EndPrimitive();
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
#version 330
|
||||
|
||||
#include "../include/camera_uniform_declarations.glsl"
|
||||
#include ../include/camera_uniform_declarations.glsl
|
||||
|
||||
in vec3 point;
|
||||
in vec3 prev_point;
|
||||
in vec3 next_point;
|
||||
in vec3 unit_normal;
|
||||
|
||||
in float stroke_width;
|
||||
in vec4 color;
|
||||
|
|
@ -13,20 +14,21 @@ in vec4 color;
|
|||
out vec3 bp;
|
||||
out vec3 prev_bp;
|
||||
out vec3 next_bp;
|
||||
out vec3 v_global_unit_normal;
|
||||
|
||||
out float v_stroke_width;
|
||||
out vec4 v_color;
|
||||
|
||||
const float STROKE_WIDTH_CONVERSION = 0.01;
|
||||
|
||||
#include "../include/position_point_into_frame.glsl"
|
||||
#include ../include/position_point_into_frame.glsl
|
||||
|
||||
void main()
|
||||
{
|
||||
void main(){
|
||||
bp = position_point_into_frame(point);
|
||||
prev_bp = position_point_into_frame(prev_point);
|
||||
next_bp = position_point_into_frame(next_point);
|
||||
v_global_unit_normal = rotate_point_into_frame(unit_normal);
|
||||
|
||||
v_stroke_width = STROKE_WIDTH_CONVERSION * stroke_width * frame_shape[1] / 8.0;
|
||||
v_stroke_width = STROKE_WIDTH_CONVERSION * stroke_width;
|
||||
v_color = color;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue