def test_look_at_right_handed_preset(self): """Tests that look_at_right_handed generates expected results..""" camera_position = ((0.0, 0.0, 0.0), (0.1, 0.2, 0.3)) look_at = ((0.0, 0.0, 1.0), (0.4, 0.5, 0.6)) up_vector = ((0.0, 1.0, 0.0), (0.7, 0.8, 0.9)) pred = glm.look_at_right_handed(camera_position, look_at, up_vector) gt = (((-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)), ((4.08248186e-01, -8.16496551e-01, 4.08248395e-01, -2.98023224e-08), (-7.07106888e-01, 1.19209290e-07, 7.07106769e-01, -1.41421378e-01), (-5.77350318e-01, -5.77350318e-01, -5.77350318e-01, 3.46410215e-01), (0.0, 0.0, 0.0, 1.0))) self.assertAllClose(pred, gt)
def test_rasterize(self): height = 500 width = 500 camera_origin = (0.0, 0.0, 0.0) camera_up = (0.0, 1.0, 0.0) look_at = (0.0, 0.0, 1.0) fov = (60.0 * np.math.pi / 180, ) near_plane = (1.0, ) far_plane = (10.0, ) world_to_camera = glm.look_at_right_handed(camera_origin, look_at, camera_up) perspective_matrix = glm.perspective_right_handed( fov, (float(width) / float(height), ), near_plane, far_plane) view_projection_matrix = tf.matmul(perspective_matrix, world_to_camera) view_projection_matrix = tf.squeeze(view_projection_matrix) for depth in range(2, 5): tris = np.array([[-10.0, 10.0, depth], [10.0, 10.0, depth], [0.0, -10.0, depth]], dtype=np.float32) tris_tf = tf.reshape(tris, [-1]) render_parameters = { "view_projection_matrix": ("mat", view_projection_matrix), "triangular_mesh": ("buffer", tris_tf) } render_parameters = list(six.iteritems(render_parameters)) variable_names = [v[0] for v in render_parameters] variable_kinds = [v[1][0] for v in render_parameters] variable_values = [v[1][1] for v in render_parameters] result = rasterizer.rasterize( num_points=tris.shape[0], variable_names=variable_names, variable_kinds=variable_kinds, variable_values=variable_values, output_resolution=(width, height), vertex_shader=test_vertex_shader, geometry_shader=test_geometry_shader, fragment_shader=test_fragment_shader, ) gt = np.tile((0, depth), (width, height, 1)) self.assertAllClose(result[..., 2:4], gt)
def test_rasterize(self): max_depth = 10 min_depth = 2 height = 480 width = 640 camera_origin = (0.0, 0.0, 0.0) camera_up = (0.0, 1.0, 0.0) look_at = (0.0, 0.0, 1.0) fov = (60.0 * np.math.pi / 180,) near_plane = (1.0,) far_plane = (10.0,) batch_shape = tf.convert_to_tensor( value=(2, (max_depth - min_depth) // 2), dtype=tf.int32) world_to_camera = glm.look_at_right_handed(camera_origin, look_at, camera_up) perspective_matrix = glm.perspective_right_handed( fov, (float(width) / float(height),), near_plane, far_plane) view_projection_matrix = tf.matmul(perspective_matrix, world_to_camera) view_projection_matrix = tf.squeeze(view_projection_matrix) # Generate triangles at different depths and associated ground truth. tris = np.zeros((max_depth - min_depth, 9), dtype=np.float32) gt = np.zeros((max_depth - min_depth, width, height, 2), dtype=np.float32) for idx in range(max_depth - min_depth): tris[idx, :] = (-100.0, 100.0, idx + min_depth, 100.0, 100.0, idx + min_depth, 0.0, -100.0, idx + min_depth) gt[idx, :, :, :] = (0, idx + min_depth) # Broadcast the variables. render_parameters = { "view_projection_matrix": ("mat", tf.broadcast_to( input=view_projection_matrix, shape=tf.concat( values=(batch_shape, tf.shape(input=view_projection_matrix)[-2:]), axis=0))), "triangular_mesh": ("buffer", tf.reshape( tris, shape=tf.concat(values=(batch_shape, (9,)), axis=0))) } # Reshape the ground truth. gt = tf.reshape( gt, shape=tf.concat(values=(batch_shape, (height, width, 2)), axis=0)) render_parameters = list(six.iteritems(render_parameters)) variable_names = [v[0] for v in render_parameters] variable_kinds = [v[1][0] for v in render_parameters] variable_values = [v[1][1] for v in render_parameters] def rasterize(): return rasterizer.rasterize( num_points=3, variable_names=variable_names, variable_kinds=variable_kinds, variable_values=variable_values, output_resolution=(width, height), vertex_shader=test_vertex_shader, geometry_shader=test_geometry_shader, fragment_shader=test_fragment_shader, ) result = rasterize() self.assertAllClose(result[..., 2:4], gt) @tf.function def check_lazy_shape(): # Within @tf.function, the tensor shape is determined by SetShapeFn # callback. Ensure that the shape of non-batch axes matches that of of # the actual tensor evaluated in eager mode above. lazy_shape = rasterize().shape self.assertEqual(lazy_shape[-3:], list(result.shape)[-3:]) check_lazy_shape()
def __init__(self, background_vertices, background_attributes, background_triangles, camera_origin, look_at, camera_up, field_of_view, image_size, near_plane, far_plane, bottom_left=(0.0, 0.0), name=None): """Initializes TriangleRasterizer with OpenGL parameters and the background. Note: In the following, A1 to An are optional batch dimensions. Args: background_vertices: A tensor of shape `[V, 3]` containing `V` 3D vertices. Note that these background vertices will be used in every rasterized image. background_attributes: A tensor of shape `[V, K]` containing `V` vertices associated with K-dimensional attributes. Pixels for which the first visible surface is in the background geometry will make use of `background_attribute` for estimating their own attribute. Note that these background attributes will be use in every rasterized image. background_triangles: An integer tensor of shape `[T, 3]` containing `T` triangles, each associated with 3 vertices from `background_vertices`. Note that these background triangles will be used in every rasterized image. camera_origin: A Tensor of shape `[A1, ..., An, 3]`, where the last axis represents the 3D position of the camera. look_at: A Tensor of shape `[A1, ..., An, 3]`, with the last axis storing the position where the camera is looking at. camera_up: A Tensor of shape `[A1, ..., An, 3]`, where the last axis defines the up vector of the camera. field_of_view: A Tensor of shape `[A1, ..., An, 1]`, where the last axis represents the vertical field of view of the frustum expressed in radians. Note that values for `field_of_view` must be in the range (0, pi). image_size: A tuple (height, width) containing the dimensions in pixels of the rasterized image". near_plane: A Tensor of shape `[A1, ..., An, 1]`, where the last axis captures the distance between the viewer and the near clipping plane. Note that values for `near_plane` must be non-negative. far_plane: A Tensor of shape `[A1, ..., An, 1]`, where the last axis captures the distance between the viewer and the far clipping plane. Note that values for `far_plane` must be non-negative. bottom_left: A Tensor of shape `[A1, ..., An, 2]`, where the last axis captures the position (in pixels) of the lower left corner of the screen. Defaults to (0.0, 0.0). name: A name for this op. Defaults to 'triangle_rasterizer_init'. """ with tf.compat.v1.name_scope( name, "triangle_rasterizer_init", (background_vertices, background_attributes, background_triangles, camera_origin, look_at, camera_up, field_of_view, near_plane, far_plane, bottom_left)): background_vertices = tf.convert_to_tensor(value=background_vertices) background_attributes = tf.convert_to_tensor(value=background_attributes) background_triangles = tf.convert_to_tensor(value=background_triangles) shape.check_static( tensor=background_vertices, tensor_name="background_vertices", has_rank=2, has_dim_equals=(-1, 3)) shape.check_static( tensor=background_attributes, tensor_name="background_attributes", has_rank=2) shape.check_static( tensor=background_triangles, tensor_name="background_triangles", # has_rank=2, has_dim_equals=(-1, 3)) shape.compare_batch_dimensions( tensors=(background_vertices, background_attributes), last_axes=-2, tensor_names=("background_geometry", "background_attribute"), broadcast_compatible=False) background_vertices = tf.expand_dims(background_vertices, axis=0) background_attributes = tf.expand_dims(background_attributes, axis=0) height = float(image_size[0]) width = float(image_size[1]) self._background_geometry = tf.gather( background_vertices, background_triangles, axis=-2) self._background_attribute = tf.gather( background_attributes, background_triangles, axis=-2) self._camera_origin = tf.convert_to_tensor(value=camera_origin) self._look_at = tf.convert_to_tensor(value=look_at) self._camera_up = tf.convert_to_tensor(value=camera_up) self._field_of_view = tf.convert_to_tensor(value=field_of_view) self._image_size_glm = tf.convert_to_tensor(value=(width, height)) self._image_size_int = (int(width), int(height)) self._near_plane = tf.convert_to_tensor(value=near_plane) self._far_plane = tf.convert_to_tensor(value=far_plane) self._bottom_left = tf.convert_to_tensor(value=bottom_left) # Construct the pixel grid. Note that OpenGL uses half-integer pixel # centers. px = tf.linspace(0.5, width - 0.5, num=int(width)) py = tf.linspace(0.5, height - 0.5, num=int(height)) xv, yv = tf.meshgrid(px, py) self._pixel_position = tf.stack((xv, yv), axis=-1) # Construct the view projection matrix. world_to_camera = glm.look_at_right_handed(camera_origin, look_at, camera_up) perspective_matrix = glm.perspective_right_handed(field_of_view, (width / height,), near_plane, far_plane) perspective_matrix = tf.squeeze(perspective_matrix) self._view_projection_matrix = tf.linalg.matmul(perspective_matrix, world_to_camera)