OpenGL 深度图仅渲染 1 个单位远的对象

问题描述 投票:0回答:0

我编辑了这个问题,因为我对我的代码之前的“错误”有错误的解释。 之前我以为我在 FBO 或 MVP 矩阵中犯了一个错误,因为我的阴影贴图完全是空白的,但实际上我做的一切都是正确的,深度贴图仅从 0-1 的距离渲染事物,关联黑白像素屏幕上。 我真傻。

我缩小了整个场景并重新定位了“太阳”,这样我现在就可以看到阴影了。 我的问题是如何“扩展”此视图,以便不仅可以看到远至 1 个单位的对象,而且可以看到我的视图。

我的阴影贴图是黑白的,与我遵循的教程中的红色和黑色相反。这是怎么回事?

最后,“太阳”的视图不会随着我的移动而更新。预计它会跟随角色,更新视图和可见阴影,但视图在“太阳”的位置保持静止。

这是我的整个 Shadow Box 类,因为这可能是问题所在:

编辑:这可能不是问题,但你仍然可以看看它。

class ShadowBox:
    """
    Represents the 3D cuboidal area of the world in which objects will cast
    shadows (basically represents the orthographic projection area for the shadow
    render pass). It is updated each frame to optimise the area, making it as
    small as possible (to allow for optimal shadow map resolution) while not
    being too small to avoid objects not having shadows when they should.
    Everything inside the cuboidal area represented by this object will be
    rendered to the shadow map in the shadow render pass. Everything outside the
    area won't be.
    """
    __OFFSET = 10
    __UP = vec4(0, 1, 0, 0)
    __FORWARD = vec4(0, 0, -1, 0)
    __SHADOW_DISTANCE = 100

    def __init__(self, light_view_matrix: mat4, camera):
        """
        Creates a new shadow box and calculates some initial values relating to
        the camera's view frustum, namely the width and height of the near plane
        and (possibly adjusted) far plane.

        :param light_view_matrix:
        basically the "view matrix" of the light. Can be used to transform a point from world space into "light" space
        (i.e. changes a point's coordinates from being in relation to the world's axis to being in terms of the light's
        local axis).

        :param camera:
        the in-game camera.
        """

        self.__light_view_matrix = light_view_matrix
        self.__cam = camera

        self.__min_x = None
        self.__max_x = None
        self.__min_y = None
        self.__max_y = None
        self.__min_z = None
        self.__max_z = None

        self.__far_height = None
        self.__far_width = None
        self.__near_height = None
        self.__near_width = None

        self.calculate_widths_and_heights()

    def update(self) -> None:
        """
        Updates the bounds of the shadow box based on the light direction and the
        camera's view frustum, to make sure that the box covers the smallest area
        possible while still ensuring that everything inside the camera's view
        (within a certain range) will cast shadows.
        :return:
        """

        rotation: mat4 = self.calculate_camera_rotation_matrix()
        forward_vector = rotation * self.__FORWARD
        forward_vector = vec3(forward_vector.x, forward_vector.y, forward_vector.z)

        to_far = forward_vector * self.__SHADOW_DISTANCE
        to_near = forward_vector * master_renderer.MasterRenderer.get_near_plane()
        center_near = to_near + vec3(self.__cam.get_position())
        center_far = to_far + vec3(self.__cam.get_position())

        points: list[vec4] = self.calculate_frustum_vertices(rotation, forward_vector, center_near, center_far)

        first = True
        for point in points:
            if first:
                self.__min_x = point.x
                self.__max_x = point.x
                self.__min_y = point.y
                self.__max_y = point.y
                self.__min_z = point.z
                self.__max_z = point.z
                first = False
                continue

            if point.x > self.__max_x:
                self.__max_x = point.x
            elif point.x < self.__min_x:
                self.__min_x = point.x

            if point.y > self.__max_y:
                self.__max_y = point.y
            elif point.y < self.__min_y:
                self.__min_y = point.y

            if point.z > self.__max_z:
                self.__max_z = point.z
            elif point.z < self.__min_z:
                self.__min_z = point.z
        self.__max_z += self.__OFFSET

    def get_center(self) -> vec3:
        """
        Calculates the center of the "view cuboid" in light space first, and then
        Converts this to world space using the inverse light's view matrix.
        :return:
        The center of the "view cuboid" in world space.
        """
        x = (self.__min_x + self.__max_x) / 2.0
        y = (self.__min_y + self.__max_y) / 2.0
        z = (self.__min_z + self.__max_z) / 2.0
        cen = vec4(x, y, z, 1)
        inverted_light = self.__light_view_matrix.inverse()
        center = inverted_light * cen
        return vec3(center.x, center.y, center.z)

    def get_width(self) -> float:
        """
        :return: The width of the "view cuboid" (orthographic projection area).
        """
        return self.__max_x - self.__min_x

    def get_height(self) -> float:
        """
        :return: The height of the "view cuboid" (orthographic projection area).
        """
        return self.__max_y - self.__min_y

    def get_length(self) -> float:
        """
        :return: The height of the "view cuboid" (orthographic projection area).
        """
        return self.__max_z - self.__min_z

    def calculate_frustum_vertices(self, rotation: mat4, forward_vector: vec3, center_near: vec3,
                                   center_far: vec3) -> list[vec4]:
        """
        Calculates the position of the vertex at each corner of the view frustum
        in light space (8 vertices in total, so this returns 8 positions).
        :param rotation: camera's rotation.
        :param forward_vector: the direction that the camera is aiming, and thus the direction of the frustum.
        :param center_near: the center point of the frustum's near plane.
        :param center_far: the center point of the frustum's (possibly adjusted) far plane.
        :return: The positions of the vertices of the frustum in light space.
        """
        up_vector = rotation * self.__UP
        up_vector = vec3(up_vector.x, up_vector.y, up_vector.z)
        right_vector = forward_vector.cross(up_vector)
        down_vector = -up_vector
        left_vector = -right_vector
        far_top = center_far + up_vector * self.__far_height
        far_bottom = center_far + down_vector * self.__far_height
        near_top = center_near + up_vector * self.__near_height
        near_bottom = center_near + down_vector * self.__near_height
        points = [vec4()] * 8
        points[0] = self.calculate_light_space_frustum_corner(far_top, right_vector, self.__far_width)
        points[1] = self.calculate_light_space_frustum_corner(far_top, left_vector, self.__far_width)
        points[2] = self.calculate_light_space_frustum_corner(far_bottom, right_vector, self.__far_width)
        points[3] = self.calculate_light_space_frustum_corner(far_bottom, left_vector, self.__far_width)
        points[4] = self.calculate_light_space_frustum_corner(near_top, right_vector, self.__near_width)
        points[5] = self.calculate_light_space_frustum_corner(near_top, left_vector, self.__near_width)
        points[6] = self.calculate_light_space_frustum_corner(near_bottom, right_vector, self.__near_width)
        points[7] = self.calculate_light_space_frustum_corner(near_bottom, left_vector, self.__near_width)
        return points

    def calculate_light_space_frustum_corner(self, start_point: vec3, direction: vec3, width: float) -> vec4:
        """
        Calculates one of the corner vertices of the view frustum in world space
        and converts it to light space.
        :param start_point: the starting center point on the view frustum.
        :param direction: the direction of the corner from the start point.
        :param width: the distance of the corner from the start point.
        :return: The relevant corner vertex of the view frustum in light space.
        """
        point = start_point + direction * width
        point4f = vec4(point.x, point.y, point.z, 1)
        point4f = self.__light_view_matrix * point4f
        return point4f

    def calculate_camera_rotation_matrix(self) -> mat4:
        """
        :return: The rotation of the camera represented as a matrix.
        """
        rotation = mat4(1)
        rotation = rotation.rotate(radians(-self.__cam.get_yaw()), vec3(0, 1, 0))
        rotation = rotation.rotate(radians(-self.__cam.get_pitch()), vec3(1, 0, 0))
        return rotation

    def calculate_widths_and_heights(self) -> None:
        """
        Calculates the width and height of the near and far planes of the
        camera's view frustum. However, this doesn't have to use the "actual" far
        plane of the view frustum. It can use a shortened view frustum if desired
        by bringing the far-plane closer, which would increase shadow resolution
        but means that distant objects wouldn't cast shadows.
        :return:
        """
        # to prevent circular import
        fov = master_renderer.MasterRenderer.get_fov()
        near_plane = master_renderer.MasterRenderer.get_near_plane()

        self.__far_width = self.__SHADOW_DISTANCE * tan(radians(fov))
        self.__near_width = near_plane * tan(radians(fov))
        self.__far_height = self.__far_width / self.get_aspect_ratio()
        self.__near_height = self.__near_width / self.get_aspect_ratio()

    @staticmethod
    def get_aspect_ratio() -> float:
        """
        :return: The aspect ratio of the display (width:height ratio).
        """
        return DisplayManager.get_width() / DisplayManager.get_height()

这也是我的矩阵的创建方式:

    def update_light_view_matrix(self, direction: vec3, center: vec3) -> None:
        """
        Updates the "view" matrix of the light. This creates a view matrix which
        will line up the direction of the "view cuboid" with the direction of the
        light. The light itself has no position, so the "view" matrix is centered
        at the center of the "view cuboid". The created view matrix determines
        where and how the "view cuboid" is positioned in the world. The size of
        the view cuboid, however, is determined by the projection matrix.
        :param direction: the light direction, and therefore the direction that the "view cuboid" should be pointing.
        :param center: the center of the "view cuboid" in world space.
        :return:
        """
        direction = direction.normalize()
        self.__light_view_matrix = mat4().identity()
        pitch = math.acos(math.sqrt(direction.x**2 + direction.y**2))
        self.__light_view_matrix = self.__light_view_matrix.rotate(pitch, vec3(1, 0, 0))
        yaw = math.degrees(math.atan(direction.x / direction.z))
        yaw = yaw - 180 if direction.z > 0 else yaw
        self.__light_view_matrix = self.__light_view_matrix.rotate(-math.radians(yaw), vec3(0, 1, 0))
        self.__light_view_matrix = self.__light_view_matrix.translate(-center)

    def update_ortho_projection_matrix(self, width: float, height: float, length: float) -> None:
        """
        Creates the orthographic projection matrix. This projection matrix
        basically sets the width, length and height of the "view cuboid", based
        on the values that were calculated in the ShadowBox class.
        :param width: shadow box width.
        :param height: shadow box height.
        :param length: shadow box length.
        :return:
        """
        self.__projection_matrix = mat4()
        self.__projection_matrix[(0, 0)] = 2.0 / width
        self.__projection_matrix[(1, 1)] = 2.0 / height
        self.__projection_matrix[(2, 2)] = -2.0 / length
        self.__projection_matrix[(3, 3)] = 1

模型矩阵绝对正确。

...
self.__projection_view_matrix = self.__projection_matrix * self.__light_view_matrix
...
mvp_matrix = self.__projection_view_matrix * model_matrix

我想我会添加矩阵计算的输出,以便您可以验证它们:

projection_matrix:
[   0.0036,    0.0000,    0.0000,    0.0000]
[   0.0000,    0.0069,    0.0000,    0.0000]
[   0.0000,    0.0000,   -0.0128,    0.0000]
[   0.0000,    0.0000,    0.0000,    1.0000]

light_view_matrix:
[  -0.7071,    0.0000,   -0.7071,  -15.1670]
[  -0.3430,    0.8745,    0.3430,   22.1743]
[   0.6183,    0.4851,   -0.6183,   -5.0441]
[   0.0000,    0.0000,    0.0000,    1.0000]

projection_view_matrix:
[  -0.0026,    0.0000,   -0.0026,   -0.0552]
[  -0.0024,    0.0060,    0.0024,    0.1527]
[  -0.0079,   -0.0062,    0.0079,    0.0643]
[   0.0000,    0.0000,    0.0000,    1.0000]

mvp_matrix:
[   1.0000,    0.0000,    0.0000,  575.0000]
[   0.0000,    1.0000,    0.0000,   -0.8157]
[   0.0000,    0.0000,    1.0000,  792.0000]
[   0.0000,    0.0000,    0.0000,    1.0000]

每个实例的

mvp_matrix
看起来像这样,因为它们没有旋转和缩放,只有一个位置。

python matrix opengl pyopengl
© www.soinside.com 2019 - 2024. All rights reserved.