Panda3d 相机控制

Panda3d 相机控制

文章目录

  • Panda3d 相机控制
    • Panda3d中的透视镜头和垂直镜头
      • 透视镜头
      • 垂直镜头
    • Panda3d 中用代码控制相机的移动
      • 用键盘控制相机的移动
      • 用鼠标控制相机的移动

Panda3d 把相机也当做是一个 PandaNode,因此可以向操作其他节点对其进行操作。

真正的相机是在ShowBase类中的一个叫做base.camNodePath,在这个上面还有一个更简单的叫做base.cameraNodePath,一般对相机进行控制的话,是在代码中进行控制。

默认情况下,panda运行一个task使我们可以通过鼠标来移动相机。用户自己写的移动相机的代码将和这个task产生冲突。该task根据鼠标当前的每一帧输入来更新相机的位置。这意味着直接控制相机的代码将不能工作,因为它会跟默认的相机控制任务相冲突。为了处理这种冲突,那么用户可以通过调用以下函数进行:

base.disableMouse()

ShowBase类为用户准备了一些控制相机的方法。useDrive()命令打开键盘和鼠标控制,这两种控制系统都只能在X和Y轴上移动,不能在Z轴上移动。

键盘系统使用方向键,“上”向前移动相机,“下”向后移动。“左”“右”键左右移动相机镜头。
鼠标系统对按下的任何一个键都有反应。若光标向屏幕上方移动,相机向前;若光标向屏幕下方移动,相机向后。如果光标在屏幕两边,相机向那个方向进行旋转。相机的移动速度取决于光标距离中心的远近。另外,Panda还提供一个命令允许使用跟踪球(trackball)鼠标:

base.useDrive() 
base.useTrackball()

ShowBase还提供了oobe()方法,当你的代码在移动相机节点(base.camera)时,你可以用鼠标/跟踪球来控制基相机节点(base.cam)。这对debug非常有用。oobe代表“out-of-body experience”(灵魂出窍),即开发时你可以对程序进行全方位的观察(God’s-eye view)。该方法是一个开关,你要打开oobe模式时调用它,然后再调用一次关闭它:

base.oobe()

oobeCull()是oobe()的一个变形,它们的作用相近,oobeCull()的不同在于,当从新的相机位置重绘场景时,场景仍然会以原先的相机位置进行剔除(cull)。因此,你可以从你“灵魂飞出”地方来观察场景,你可以四处游走,看到物体进入和弹出视区,就像你的视棱台(view frustum)也在移动一样。

Panda3d中的透视镜头和垂直镜头

透视镜头

每个相机都有一个镜头,决定它的成像参数。对于简单的应用程序,你无需考虑镜头问题,默认的镜头参数已经很好了。但是,有时候你想调整一些镜头的参数,如视域。根据对镜头的不同需求,我们提供了几种接口来修改参数。
Panda3d 启动时,它自动为你创建了一个默认的相机和镜头。这个默认相机对象保持在base.cam(从方便的角度,我们应该使用base.camera来移动相机),默认镜头是base.camLens。
默认镜头几乎总是一个透视镜头——即 PerspectiveLens 类的一个实例——除非你换成另一种镜头。到目前为止,透视镜头是一种使用最广泛的镜头,它就像一台真实的相机的镜头,功能与人眼晶状体相同。

透视镜头

上图展示一个常规透镜相机的成像。相机只能看到黑线框里面的物体,这个区域被称为镜头棱台(frustum)。
图中可以看到镜头拍摄的图像(图像是颠倒的,跟真实的物理镜头成像一样)。颠倒的图像只是起到说明作用,它并不是Panda3D相机的一部分,它帮助我们理解Panda3D镜头和真实镜头的关系。
PerspectiveLens有很多参数可以设置,这些参数不都是独立的,设置某些参数将改变另外一些参数的值。

垂直镜头

前面介绍了PerspectiveLens类,以及透视镜头,3D渲染常用的另外一种镜头就是垂直镜头,它没有视域的概念, 如下图所示:

垂直镜头

在垂直镜头里没有透视——穿过镜头的平行光不汇聚,而是保持平行。透视镜头模拟了真实的物理镜头,但现实中不存在垂直镜头。它主要用于特殊效果,比如非自然的景观、模拟即时战略游戏的2.5D场景,或绘制不需要透视的2D物体。事实上,对于 render2d scene graph,默认的相机就是一个OrthographicLens,用于绘制屏幕GUI。

既然垂直镜头没有视域角度,lens.setFov()方法就不起作用。为了调节垂直镜头的范围,你需要调整它的胶片规格。与PerspectiveLens不同,对OrthographicLens来说胶片规格的单位不是任意的,应该用空间单位,与场景建模时使用的单位一致。例如,上图OrthographicLens的胶片规格被设为lens.setFilmSize(20, 15), 20 英尺 x 15 英尺 ——因为场景建模以英尺为单位,一只大熊猫大概有 12 英尺 高。

垂直镜头的另一个方便的参数是近距离,它的值不必一定是正数。实际上可以是负数——可以把近平面放在相机平面之后,也就是说相机可以看到它身后的物体。为render2d准备的OrthographicLens被设成setNearFar(-1000, 1000),将绘制所有Z值在-1000到1000之间的物体。(当然,在render2d中,几乎全部物体的Z值都为0,因此不会有什么问题)
如果需要,你可以把默认的相机换成一个垂直镜头:

lens = OrthographicLens() 
lens.setFilmSize(20, 15) # 根据你的场景来选取适当的值
base.cam.node().setLens(lens)

注意,使用垂直镜头可能让人失去空间感——比如,物体不因为你靠近它而变大,也不因为你远离它而变小——因此你可能不知道相机在移动。

Panda3d 中用代码控制相机的移动

下面两个代码就需要禁用Panda3d中默认的鼠标控制相机任务,否则不能达到用户预期的目的。

用键盘控制相机的移动

最终的代码如下所示:

from direct.actor.Actor import Actor
from panda3d.core import loadPrcFileData
from direct.showbase.ShowBase import ShowBase #基本显示模块# 画面显示配置,设置窗口大小,窗口名称、显示帧率
confVar = """
win-size 1280 720
window-title example
show-frame-rate-meter True
"""loadPrcFileData("", confVar)class MyApp(ShowBase):def __init__(self):#场景初始化super(MyApp, self).__init__()base.disableMouse()self.person = base.loader.loadModel('smiley')self.person.reparentTo(self.render)# 循环一个动作# self.person.loop('run')self.cam.setPos(0, -10, 0)self.keyMap = {'up':False,'down':False,'left':False,'right':False,'go':False,'back':False,'rotate':False,}self.speed = 4self.angle = 0# self.accept(<event-name>,<function name>)# self.accept(<event-name>,<function name>, <parameters-list>)self.accept('arrow_up', self.updateKeyMap, ['up', True])self.accept('arrow_up-up', self.updateKeyMap, ['up', False])self.accept('arrow_down', self.updateKeyMap, ['down', True])self.accept('arrow_down-up', self.updateKeyMap, ['down', False])self.accept('arrow_left', self.updateKeyMap, ['left', True])self.accept('arrow_left-up', self.updateKeyMap, ['left', False])self.accept('arrow_right', self.updateKeyMap, ['right', True])self.accept('arrow_right-up', self.updateKeyMap, ['right', False])self.accept('w', self.updateKeyMap, ['go', True])self.accept('w-up', self.updateKeyMap, ['go', False])self.accept('s', self.updateKeyMap, ['back', True])self.accept('s-up', self.updateKeyMap, ['back', False])self.accept('space-up', self.updateKeyMap, ['rotate', True])self.accept('apace-up-up', self.updateKeyMap, ['rotate', False])self.taskMgr.add(self.update,"update")def updateKeyMap(self, key, state):self.keyMap[key] = statedef update(self, task):# 或者这个函数的运行时间,或者说是更新的帧率dt = globalClock.getDt()pos = self.person.getPos()if self.keyMap['up']:pos.z += self.speed * dtif self.keyMap['down']:pos.z -= self.speed * dtif self.keyMap['left']:pos.x -= self.speed * dtif self.keyMap['right']:pos.x += self.speed * dtif self.keyMap['go']:pos.y -= self.speed * dtif self.keyMap['back']:pos.y += self.speed * dtif self.keyMap['rotate']:self.angle += 1if self.angle == 360:self.angle = 0self.person.setH(self.angle)self.person.setPos(pos)return task.contapp = MyApp()
app.run()
run()

用鼠标控制相机的移动

Panda3d 中默认的鼠标操作定义如下:

  • 鼠标左键:按住左键再移动光标可以控制画面左右旋摆。
  • 鼠标右键:按住右键键再移动光标可以控制画面的远近。
  • 鼠标滚轮:按住滚轮键键再移动光标可以控制角度上下左右的角度旋转(盘旋)。
  • 鼠标右键+滚轮:按住右键+滚轮键键再移动光标绕垂直电脑屏幕的轴旋转。

下面代码中的鼠标操作定义如下:

  • 鼠标左键:按住左键再移动光标可以控制画面左右上下移动。

  • 鼠标右键:按住右键键再移动光标可以控制画面的左右上下旋转。

  • 鼠标滚轮:按住滚轮键可以控制画面的前后移动,也就是画面的放大和缩小。

鼠标事件定义

  • mouse1 是鼠标左键
  • mouse2 是鼠标中键
  • mouse3 是鼠标右键

代码如下:


from direct.actor.Actor import Actor
from panda3d.core import loadPrcFileData
from direct.showbase.ShowBase import ShowBase #基本显示模块
from direct.showbase.ShowBase import (Filename, LVecBase3f, NodePath, Task)
from direct.interval.IntervalGlobal import LerpPosInterval
from IPython import embed# 画面显示配置,设置窗口大小,窗口名称、显示帧率
confVar = """
win-size 1280 720
window-title example
show-frame-rate-meter True
"""loadPrcFileData("", confVar)class MyApp(ShowBase):def __init__(self):#场景初始化super(MyApp, self).__init__()base.disableMouse()self.person = base.loader.loadModel('smiley')self.person.reparentTo(self.render)# 循环一个动作# self.person.loop('run')self.cam.setPos(0, -10, 0)self.keyMap = {'up':False,'down':False,'left':False,'right':False,'go':False,'back':False,'rotate':False,}self.speed = 4self.angle = 0self.mouse_map = {}self.Cursor2D_X = 0.0self.Cursor2D_Y = 0.0self.Cursor2D_X_pre = 0.0self.Cursor2D_Y_pre = 0.0self.Cursor2D_X_direction = 0.0self.Cursor2D_Y_direction = 0.0# 可以取消原来默认的鼠标点击事件,可以用户自定义鼠标控制事件self.accept("mouse1", self.SetMouse, ["mouse1_event", True])self.accept("mouse1-up", self.SetMouse, ["mouse1_event", False])# mouse2 是鼠标中键# 鼠标的右键self.accept("mouse3", self.SetMouse, ["mouse3_event", True])self.accept("mouse3-up", self.SetMouse, ["mouse3_event", False])# self.mouseWatcherNode.set_modifier_buttons(ModifierButtons())# self.buttonThrowers[0].node().set_modifier_buttons(ModifierButtons())self.accept("mouse3-up_mouse1-up", self.SetMouse, ["mouse31_event", False])# 鼠标中键向上滚动self.accept("wheel_up", self.cameraZoom,[-1])# 鼠标中键向下滚动self.accept("wheel_down", self.cameraZoom,[1])self.taskMgr.add(self.UpdateMouseCameraTask, "UpdateMouseCameraTask")def SetMouse(self, mouse, val):self.mouse_map[mouse] = valdef UpdateMouseCameraTask(self, task):# time since last framedt = globalClock.getDt()step = 90if base.mouseWatcherNode.hasMouse():# 两条指令值等价的,都是得到当前鼠标的位置self.Cursor2D_X = base.mouseWatcherNode.getMouseX()self.Cursor2D_Y = base.mouseWatcherNode.getMouseY()if(self.Cursor2D_X - self.Cursor2D_X_pre > 1e-4):self.Cursor2D_X_direction = 0.1elif (self.Cursor2D_X - self.Cursor2D_X_pre < -1e-4):self.Cursor2D_X_direction = -0.1else:self.Cursor2D_X_direction = 0.0if(self.Cursor2D_Y - self.Cursor2D_Y_pre > 1e-4):self.Cursor2D_Y_direction = 0.1elif (self.Cursor2D_Y - self.Cursor2D_Y_pre < -1e-4):self.Cursor2D_Y_direction = -0.1else:self.Cursor2D_Y_direction = 0.0if self.mouse_map.get("mouse1_event") == True and (self.mouse_map.get("mouse3_event") == False or  self.mouse_map.get("mouse3_event") == None):self.camera.setPos(LVecBase3f(self.camera.getX()-(2*self.Cursor2D_X_direction), self.camera.getY(), self.camera.getZ()-2*self.Cursor2D_Y_direction))if self.mouse_map.get("mouse3_event") == True and (self.mouse_map.get("mouse1_event") == False or self.mouse_map.get("mouse1_event") == None):self.camera.setHpr(self.camera.getH()+(2*self.Cursor2D_X_direction), self.camera.getP()+(2*self.Cursor2D_Y_direction), self.camera.getR())# # 以下两个指令用来获取得到当前窗口的大小# print(base.win.getProperties().getXSize())# print(base.win.getProperties().getYSize())self.Cursor2D_X_pre = self.Cursor2D_Xself.Cursor2D_Y_pre = self.Cursor2D_Yreturn Task.cont# 进行相机视野的放大和缩小def cameraZoom(self,dir):self.camera.setPos(LVecBase3f(self.camera.getX(), self.camera.getY()-(2*dir), self.camera.getZ()+(2*dir)))# self.camZoom = LerpPosInterval(self.camera, self.speed, LVecBase3f(self.camera.getX(), self.camera.getY()-(2*dir), self.camera.getZ()+(2*dir)))# self.camZoom.start()app = MyApp()
app.run()
run()

上述UpdateMouseCameraTask 主要是通过不断的比较当前鼠标光标的位置来调整相机的位置,按下鼠标的左键主要是控制相机的位置移动, 而按下右键主要是控制相机的姿态旋转。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/180754.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(自适应手机端)响应式新闻博客知识类pbootcms网站模板 自媒体运营博客网站源码下载

(自适应手机端)响应式新闻博客知识类pbootcms网站模板 自媒体运营博客网站源码下载 带后台系统PbootCMS内核开发的网站模板&#xff0c;该模板适用于新闻博客网站、自媒体运营网站等企业&#xff0c;当然其他行业也可以做&#xff0c;只需要把文字图片换成其他行业的即可&#…

怎样做好金融投资翻译

我们知道&#xff0c; 金融投资翻译所需的译文往往是会议文献、年终报表、信贷审批等重要企业金融资料&#xff0c;其准确性事关整个企业在今后一段时期内的发展战略与经营成效。尤其像年报&#xff0c;对于上市公司来说更是至关重要的。那么&#xff0c;怎样做好金融投资翻译&…

Linux shell编程学习笔记21:用select in循环语句打造菜单

一、select in循环语句的功能 Linux shell脚本编程提供了select in语句&#xff0c;这是 Shell 独有的一种循环语句&#xff0c;非常适合终端&#xff08;Terminal&#xff09;这样的交互场景&#xff0c;它可以根据用户的设置显示出带编号的菜单&#xff0c;用户通过输入不同…

七月论文审稿GPT第二版:从Meta Nougat、GPT4审稿到Mistral、LLaMA LongLora

前言 如此前这篇文章《学术论文GPT的源码解读与微调&#xff1a;从chatpaper、gpt_academic到七月论文审稿GPT》中的第三部分所述&#xff0c;对于论文的摘要/总结、对话、翻译、语法检查而言&#xff0c;市面上的学术论文GPT的效果虽暂未有多好&#xff0c;可至少还过得去&am…

vue中的rules表单校验规则使用方法 :rules=“rules“

一、el-form里面必写属性值 :ref"dataForm" // 提交表单时进行校验 :rules"rules" // return 下的校验规则 :model"userForm" // 绑定表单的值 <el-formref"dataForm" // 必写属性值:rules"rules"…

服务器搭建:从零开始创建自己的Spring Boot应用【含登录、注册功能】

当然&#xff0c;你可以先按照IDEA搭建SSM框架【配置类、新手向】完成基础框架的搭建 步骤 1&#xff1a;设计并实现服务器端的用户数据库 在这个示例中&#xff0c;我们将使用MySQL数据库。首先&#xff0c;你需要安装MySQL并创建一个数据库以存储用户信息。以下是一些基本步…

creating server tcp listening socket 127.0.0.1:6379: bind No error

window下启动redis服务报错&#xff1a; creating server tcp listening socket 127.0.0.1:6379: bind No error 解决方案如下按顺序输入如下命令即可连接成功 redis-cli.exeshutdownexit运行&#xff1a;redis-server.exe redis.windows.conf shutdown出现以下错误&#xff…

正点原子嵌入式linux驱动开发——Linux USB驱动

USB是很常用的接口&#xff0c;目前大多数的设备都是USB接口的&#xff0c;比如鼠标、键盘、USB摄像 头等&#xff0c;在实际开发中也常常遇到USB接口的设备&#xff0c;本章就来学习一下如何使能Linux内核自带的USB驱动。这里不会具体学习USB的驱动开发。 USB接口简介 什么是…

2023-11-04 LeetCode每日一题(数组中两个数的最大异或值)

2023-11-04每日一题 一、题目编号 421. 数组中两个数的最大异或值二、题目链接 点击跳转到题目位置 三、题目描述 给你一个整数数组 nums &#xff0c;返回 nums[i] XOR nums[j] 的最大运算结果&#xff0c;其中 0 ≤ i ≤ j < n 。 示例 1&#xff1a; 示例 2&…

【笔记】单片机卡死的八大原因和解决方法

在微控制器上&#xff0c;程序卡住&#xff08;即停止执行&#xff09;可能有多种原因。下面我将列举一些常见的原因&#xff0c;并提供一些可能导致程序卡住的示例情况。请注意&#xff0c;这里只是一些示例&#xff0c;并不能穷尽所有可能的情况。 1. 死循环&#xff08;Infi…

Nignx安装负载均衡动静分离以及Linux前端项目部署将域名映射到特定IP地址

目录 一、nginx简介 1.1 定义 1.2 背景 1.3 作用 二、nginx搭载负载均衡提供前后分离后台接口数据 2.1 nginx安装 2.1.1 下载依赖 2.1.2 下载并解压安装包 2.1.3 安装nginx 2.1.4 启动nginx服务 2.2 tomcat负载均衡 2.2.1 负载均衡所需服务器准备 2.2.2 配置修改 …

2022年12月 Python(三级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试&#xff08;1~6级&#xff09;全部真题・点这里 一、单选题&#xff08;共25题&#xff0c;每题2分&#xff0c;共50分&#xff09; 第1题 列表L1中全是整数&#xff0c;小明想将其中所有奇数都增加1&#xff0c;偶数不变&#xff0c;于是编写了如下图所示的代…

驱动开发11-2 编写SPI驱动程序-点亮数码管

驱动程序 #include <linux/init.h> #include <linux/module.h> #include <linux/spi/spi.h>int m74hc595_probe(struct spi_device *spi) {printk("%s:%d\n",__FILE__,__LINE__);char buf[]{0XF,0X6D};spi_write(spi,buf,sizeof(buf));return 0; …

阿里云推出AI编程工具“通义灵码“;生成式 AI 入门教程 2

&#x1f989; AI新闻 &#x1f680; 阿里云推出AI编程工具"通义灵码"&#xff0c;支持多种语言及实时续写功能 摘要&#xff1a;阿里云推出了一款名为"通义灵码"的AI编程工具&#xff0c;支持多种主流编程语言&#xff0c;包括Java、Python、Go等。该工…

Redis Sentinel 哨兵模式

Sentinel 哨兵模式 Redis Sentinel 官网 Redis 的 Sentinel 文档 -- Redis中国用户组&#xff08;CRUG&#xff09; Sentinel Redis 命令参考&#xff08;红色&#xff09; Sentinel 通过监控的方式获取主机的工作状态是否正常&#xff0c;当主机发生故障时&#xff0c; Senti…

十年JAVA搬砖路——Linux搭建Ldap服务器。

1.安装命令 yum -y install openldap compat-openldap openldap-clients openldap-servers openldap-servers-sql openldap-devel2.启动ldap systemctl start slapd systemctl enable slapd3.修改密码 slappasswd Aa123456获得返回的密码加密密码串&#xff1a; {SSHA}DkSw0…

RPC 原理详解

文章目录 什么是 RPCRPC 基本原理RPC核心功能服务寻址数据编解码网络传输一次RPC的调用过程 实践基于HTTP协议的RPC基于TCP协议的RPC 什么是 RPC RPC&#xff08;Remote Procedure Call&#xff09;&#xff0c;即远程过程调用&#xff0c;它允许像调用本地服务一样调用远程服…

Python基础入门例程37-NP37 不低于与不超过(运算符)

最近的博文&#xff1a; Python基础入门例程36-NP36 谁的数字大&#xff08;运算符&#xff09;-CSDN博客 Python基础入门例程35-NP35 朋友的年龄是否相等&#xff08;运算符&#xff09;-CSDN博客 Python基础入门例程34-NP34 除法与取模运算&#xff08;运算符&#xff09;…

07.Diffusion Model概述

文章目录 Diffusion Model原理Reverse ProcessDenoise模块Forward Process(Diffusion Process) 文字生成图片by Diffusion Model文字生成图像的常见套路Text EncoderFrchet Inception DistanceContrastive Language-Image Pre-Training(CLIP) DecoderGeneration Model 部分截图…

Fourier分析导论——第3章——Fourier级数的收敛性(E.M. Stein R. Shakarchi)

第 3 章 Fourier级数的收敛性(Convergence of Fourier Series) The sine and cosine series, by which one can represent an arbitrary function in a given interval, enjoy among other remarkable properties that of being convergent. This property did not escape…