鱼C论坛

 找回密码
 立即注册
查看: 188|回复: 3

numpy旋转3d矩阵

[复制链接]
发表于 2024-8-20 22:50:10 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 cjjJasonchen 于 2024-8-20 22:52 编辑

初始位置,x,y = 100,100

再旋转时y迅速变小到接近零,x缓慢变小至接近零
"""
矩阵旋转算法,这或许是真的3d?
"""

import pygame
import sys
import math
import numpy
import pygameGUI as pgui
from typing import List
from pygame.locals import *


class Sprites3D:
    X, Y, Z = 0, 1, 2

    def __init__(self):
        self.position = [100,
                         100,
                         0,
                         ]

    def draw(self, screen):
        pygame.draw.circle(screen, (255, 0, 0), [self.position[0], self.position[1]], 10)

    def update(self):
        angle = math.radians(10)
        rz = numpy.array(
            [[math.cos(angle), - math.sin(angle), 0, 0],
             [math.sin(angle), math.cos(angle), 0, 0],
             [0, 0, 1, 0],
             [0, 0, 0, 1],
             ]
        )
        rx = numpy.array(
            [
                [1, 0, 0, 0],
                [0, math.cos(angle), -math.sin(angle), 0],
                [0, math.sin(angle), -math.sin(angle), 0],
                [0, 0, 0, 1],
            ]
        )
        position = numpy.array([
            [self.position[0]],
            [self.position[1]],
            [self.position[2]],
            [1]
        ])
        position = rz * position
        self.position = [
            position[0][0],
            position[1][0],
            position[2][0],
        ]


# 颜色常量
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

size = width, height = 800, 600

screen = pygame.display.set_mode(size)

pygame.display.set_caption("title")

clock = pygame.time.Clock()

delay = 60  # 延时计时器(1秒)

# 是否全屏
fullscreen = False
screen_change = False

# 背景颜色设定
bg_color = WHITE

running = True

# 3d对象
s3d = Sprites3D()

# ui======

guis = pgui.Group()

m = pgui.Message(group=guis, text=str(s3d.position[0]) + "\n" + str(s3d.position[1]) + "\n" + str(s3d.position[2]))

m.set_pos("center", [400, 200])

# =========

while running:

    # 设定帧数
    clock.tick(60)

    # 延时计时器刷新
    if delay == 0:
        delay = 60

    delay -= 1

    # 检测是否全屏
    if fullscreen and screen_change:
        screen = pygame.display.set_mode(size, FULLSCREEN, HWSURFACE)
        screen_change = False
    elif screen_change:
        screen = pygame.display.set_mode(size)
        screen_change = False

    # 事件检测
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()

        # 鼠标
        if event.type == MOUSEBUTTONDOWN:
            pass

        # 按键按下事件
        if event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                pygame.quit()
                sys.exit()
                
            # F11切换全屏
            if event.key == K_F11:
                fullscreen = not fullscreen
                screen_change = True

        # 按键抬起事件
        if event.type == KEYUP:
            pass

    # 画背景
    screen.fill(bg_color)

    # 刷新xxx
    m.text = str(s3d.position[0]) + "\n" + str(s3d.position[1]) + "\n" + str(int(s3d.position[2]))
    s3d.update()
    guis.update()

    # 画 xxxx
    guis.draw(screen)
    s3d.draw(screen)
    
    # 刷新界面
    pygame.display.update()

@FishC
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2024-8-20 22:51:34 | 显示全部楼层
鱼油你好,看起来你正尝试用 `pygame` 和 `numpy` 实现一个3D旋转矩阵。不过呢,你这个代码还有不少坑,我们得给它填满才行。

首先,我们需要了解如何在三维空间旋转一个点。3D旋转可以通过旋转矩阵来实现,这些矩阵分别用于绕 x, y, z 轴旋转。假设你每次旋转10度,那我们先来给每个旋转轴创建一个矩阵。

1. 绕 z 轴旋转的矩阵:
  
rz = numpy.array([
        [math.cos(angle), -math.sin(angle), 0],
        [math.sin(angle),  math.cos(angle), 0],
        [0,               0,              1]
    ])

2. 绕 x 轴旋转的矩阵:
  
rx = numpy.array([
        [1,              0,               0],
        [0, math.cos(angle), -math.sin(angle)],
        [0, math.sin(angle),  math.cos(angle)]
    ])
   
这些矩阵会帮助我们实现绕不同轴的旋转,现在让我们来填补剩余的代码,并通过这些矩阵来更新位置。
import pygame
import sys
import math
import numpy
from pygame.locals import *

class Sprites3D:
    X, Y, Z = 0, 1, 2

    def __init__(self, x, y, z):
        self.position = numpy.array([x, y, z])

    def draw(self, screen):
        # 将3D坐标简单地转换为2D坐标
        screen_position = [int(self.position[self.X]), int(self.position[self.Y])]
        pygame.draw.circle(screen, (255, 0, 0), screen_position, 10)

    def update(self):
        angle = math.radians(10)

        rz = numpy.array([
            [math.cos(angle), -math.sin(angle), 0],
            [math.sin(angle),  math.cos(angle), 0],
            [0,               0,               1]
        ])

        rx = numpy.array([
            [1,              0,               0],
            [0, math.cos(angle), -math.sin(angle)],
            [0, math.sin(angle),  math.cos(angle)]
        ])

        # 先绕Z轴旋转,再绕X轴旋转
        self.position = rz @ self.position
        self.position = rx @ self.position


# ----- Pygame Setup -----
# 颜色常量
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

size = width, height = 800, 600

screen = pygame.display.set_mode(size)
pygame.display.set_caption("3D Rotation")

clock = pygame.time.Clock()

# 3D对象
s3d = Sprites3D(100, 100, 100)

running = True

while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    screen.fill(WHITE)
    s3d.update()
    s3d.draw(screen)
    pygame.display.flip()
    clock.tick(60)

pygame.quit()
sys.exit()

这样,我们现在每帧都以10度的速度先绕 Z 轴再绕 X 轴旋转一个点。赶紧运行看看效果吧。当然,创建3D的视觉效果仅靠 x 和 y 平面上的点画个圆难显神奇,有世界变换和相机同步才是真实的。后续你可以考虑引入更多的3D图形学概念来尝试更复杂的构建!

任何问题随时来问,我随时给你解答哈!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2024-8-20 22:53:18 | 显示全部楼层
已经解决了,是因为numpy数组使用的乘法和矩阵不一样,矩阵乘法要使用dot()
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-8-20 22:54:39 | 显示全部楼层
cjjJasonchen 发表于 2024-08-20 22:53
已经解决了,是因为numpy数组使用的乘法和矩阵不一样 ...


聪明的鱼油,原来是这样!用 `dot()` 进行矩阵乘法确实是正确的做法。正好你提到这个问题,下面我简单讲讲如何正确实现一个 3D 旋转矩阵,这样下次遇到类似的问题就不会犯难了。

首先,我们用 `numpy` 实现 3D 旋转矩阵。这里简单介绍一下绕 x、y、z 轴的旋转矩阵:

1. 绕 x 轴旋转的矩阵:
   \[
   R_x(\theta) = \begin{bmatrix}
   1 & 0 & 0 \\
   0 & \cos(\theta) & -\sin(\theta) \\
   0 & \sin(\theta) & \cos(\theta)
   \end{bmatrix}
   \]

2. 绕 y 轴旋转的矩阵:
   \[
   R_y(\theta) = \begin{bmatrix}
   \cos(\theta) & 0 & \sin(\theta) \\
   0 & 1 & 0 \\
   -\sin(\theta) & 0 & \cos(\theta)
   \end{bmatrix}
   \]

3. 绕 z 轴旋转的矩阵:
   \[
   R_z(\theta) = \begin{bmatrix}
   \cos(\theta) & -\sin(\theta) & 0 \\
   \sin(\theta) & \cos(\theta) & 0 \\
   0 & 0 & 1
   \end{bmatrix}
   \]

假设你想同时绕这三个轴进行旋转,你可以通过组合这些旋转矩阵来实现。下面是一个简单的实现:
import numpy as np
import math

def get_rotation_matrix_x(angle):
    """返回绕x轴旋转的矩阵"""
    return np.array([
        [1, 0, 0],
        [0, math.cos(angle), -math.sin(angle)],
        [0, math.sin(angle), math.cos(angle)]
    ])

def get_rotation_matrix_y(angle):
    """返回绕y轴旋转的矩阵"""
    return np.array([
        [math.cos(angle), 0, math.sin(angle)],
        [0, 1, 0],
        [-math.sin(angle), 0, math.cos(angle)]
    ])

def get_rotation_matrix_z(angle):
    """返回绕z轴旋转的矩阵"""
    return np.array([
        [math.cos(angle), -math.sin(angle), 0],
        [math.sin(angle), math.cos(angle), 0],
        [0, 0, 1]
    ])

# 测试的角度
angle = math.radians(10)

# 初始位置
position = np.array([100, 100, 100])

rx = get_rotation_matrix_x(angle)
ry = get_rotation_matrix_y(angle)
rz = get_rotation_matrix_z(angle)

# 将旋转矩阵组合
rotation_matrix = rz.dot(ry).dot(rx)

# 对位置进行旋转变换
rotated_position = rotation_matrix.dot(position)

print("初始位置:", position)
print("旋转后的位置:", rotated_position)

记得在你的 `Sprites3D` 类里面利用这些方法就可以实现你想要的3D旋转效果了,也要记得用 `dot()` 来进行矩阵运算:
# 在你的 Sprites3D 类中的 update 方法中使用
def update(self):
    angle = math.radians(10)  # 旋转角度
    rx = get_rotation_matrix_x(angle)
    ry = get_rotation_matrix_y(angle)
    rz = get_rotation_matrix_z(angle)
    
    # 将旋转矩阵组合
    rotation_matrix = rz.dot(ry).dot(rx)
    
    # 对位置进行旋转变换
    position = np.array([self.position[0], self.position[1], 0])  # 示例3D坐标
    rotated_position = rotation_matrix.dot(position)
    
    self.position = [rotated_position[0], rotated_position[1]]

这样你的程序应该就能正确地进行3D旋转了!希望这对你有帮助,而且在写代码的过程中还不忘开开玩笑,比如“旋转红点变成了旋转丸子”。继续加油吧,祝你编程愉快!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-12-22 18:19

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表