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.mzph.cn/news/132234.shtml

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

相关文章

使用pandas处理excel文件【Demo】

一、代码示例 import pandas as pd from pandas import Series,DataFrame from pandasql import sqldf import matplotlib.pyplotidInfos DataFrame(pd.read_excel(home_data.xlsx))print(idInfos.head(2))print(idInfos.dtypes)# print(idInfos[:][姓名]) # 自定义一个函数s…

音视频报警可视对讲15.6寸管理机

音视频报警可视对讲15.6寸管理机 一、管理机技术指标&#xff1a; 1、15.6寸原装京东方工业液晶触摸屏&#xff0c;分辨率1920 (H) x 1080 (V)&#xff1b; 2、1000M/100M自适应双网口&#xff1b; 4、按键设置&#xff1a;报警/呼叫按键&#xff0c;通话/挂机按键&#xff…

AGV 导航方法总体设计与分析

导航方案设计 导航方法分为三个部分——路径规划、实时定位、轨迹纠正。 视觉定位模块 主要有三个工作任务&#xff1a;利用二维码定位对 AGV 初始位姿 进行矫正&#xff0c;保证 AGV 初始运动时其运动轨迹能够与道路平行&#xff1b;利用 ORBSLAM 定位能够实时的获得 AGV 在…

WebSocket Day 01:入门案例

前言 欢迎来到WebSocket入门案例系列的第一天&#xff01;在今天的博客中&#xff0c;我们将一起探索WebSocket的基础知识和使用方法。本系列将以一个简单的入门案例为基础&#xff0c;带领您逐步了解WebSocket的原理和用法。 一、什么是 WebSocket ? WebSocket是一种在Web应…

香港金融科技周VERTU CSO Sophie谈Web3.0的下一个风口 手机虚拟货币移动支付

10月31日&#xff0c;香港金融科技周正式拉开帷幕。这项香港金融科技界地年度盛事今年已经踏入了第八届&#xff0c;本届活动吸引超过数百位金融科技专业人士、创业者和行业领袖现场参与&#xff0c;线上参与观众超过10万人次。 在金融科技周的圆桌会议上&#xff0c;VERTU首席…

缓解缓存击穿的大杀器之---singleflight深入浅出

singleflight简单介绍 singlefight直译“单飞”&#xff0c;那顾名思义就是有一堆鸟&#xff0c;但是咱只让一只鸟单飞。。。&#x1f604; singleflight 提供了重复函数调用抑制机制&#xff0c;使用它可以避免同时进行相同的函数调用。第一个调用未完成时后续的重复调用会等…

【Unity细节】为什么UI移动了锚点,中心点和位置,运行的时候还是不在设置的位置当中

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 &#x1f636;‍&#x1f32b;️收录于专栏&#xff1a;unity细节和bug &#x1f636;‍&#x1f32b;️优质专栏 ⭐【…

[MRCTF2020]你传你呢1

提示 只对php以及phtml文件之类的做了防护content-type.htaccess文件 这里就不整那么麻烦直接抓包测试 首先对后缀测试看过滤了哪些 (php php3 pht php5 phtml phps) 全部被ban了 到这里的后续思路通过上传一些配置文件把上传的图片都以php文件执行 尝试上传图片码, 直接上传成…

新大陆NVH200-AP(U)扫码枪在上位机软件开发中的应用

前言: 由于本次使用的是USB接口的扫码枪 1、先安装Nset软件,使用扫码枪扫描“启动设置条码”,然后扫描“USB CDC串口”条码 2、打开NSet软件,点击“刷新按钮” 就能找到扫码枪设备 3、设置条码后缀 点击“高级设置”,然后点击“数据编辑”,在“后缀”那里设置结束符…

逆向学习记录(2)windows常用基本操作及用环境变量配置上多个python版本

1、如何打开cmd 第一种方法&#xff1a;按下winr&#xff0c;运行cmd 第二种方法&#xff1a;进入一个目录&#xff0c;点击路径处&#xff08;显示蓝色背景&#xff09;&#xff0c;然后直接键盘输入cmd&#xff0c;回车&#xff0c;运行cmd并直接进入此目录。 2、命令dir&am…

Oracle(12)Managing Indexes

目录 目标&#xff1a; 一、基础知识 1、Classification ofindexes 索引的分类 2、B-Tree vs Bitmap 3、Creating Indexes: Guidelines 创建索引:准则 4、Offline Index Rebuild 脱机索引重建 5、RebuildingIndexes 重建索引 6、Online Index Rebuild 在线索引重建 7…

第22期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练 Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大型语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以…

前端下载后端文件流,文件可以下载,但是打不开,显示“文件已损坏”的问题分析与解决方案

目录 场景还原相关代码开发者工具 - 网络请求记录 问题排查定位改bug 总结 场景还原 我在前端使用axios接收后端xlsx表格文件流并下载&#xff0c;xlsx文件能够下载成功&#xff0c;但是打开却显示文件无法打开 相关代码 请求API封装:Content–Type以及responseType经核对均…

虚拟机Linux-Centos系统网络配置常用命令+Docker 的常用命令

目录 1、虚拟机Linux-Centos系统网络配置常用命令2、Docker 的常用命令2.1 安装docker步骤命令2.2 在docker容器中安装和运行mysql 2、dockerfile关键字区别(ADD/COPY,CMD/ENTRYPOINT) 1、虚拟机Linux-Centos系统网络配置常用命令 进入网络配置文件目录 cd /etc/sysconfig/ne…

【webrtc】 对视频质量的码率控制的测试与探索

目录 环境设置 transport-cc goog-remb (webrtc中的两种码率算法&#xff09; 修改成remb算法 测试 效果 后续 可参考工程 环境设置 要到meshx上操作 telnet 112 然后执行factory_env show |grep meshx_ip 之后telnet meshx_ip 用户名admin 密码****.119 执行一下r…

Leetcode-88 合并两个有序数组

使用内置排序函数&#xff0c;时间复杂度On^2 class Solution {public void merge(int[] nums1, int m, int[] nums2, int n) {int j0,im;while(j<n){nums1[i]nums2[j];}Arrays.sort(nums1);} }新建一个临时数组用于放排序后的元素&#xff0c;再将临时数组赋值给nums1&…

【软件STM32cubeIDE下H73xx配置串口uart1+中断接收/DMA收发+HAL库+简单数据解析-基础样例】

#【软件STM32cubeIDE下H73xx配置串口uart1中断接收/DMA收发HAL库简单数据解析-基础样例】 1、前言2、实验器件3-1、普通收发中断接收实验第一步&#xff1a;代码调试-基本配置&#xff08;1&#xff09;基本配置&#xff08;3&#xff09;时钟配置&#xff08;4&#xff09;保存…

大神接力 | YOLOv4算法超详细解析(包括诞生背景+论文解析+技术原理等)

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。YOLOv4论文的发表背景是在原作者声名放弃更新YOLO算法后&#xff0c;俄罗斯的Alexey大神扛起了YOLOv4的大旗&#xff0c;因此&#xff0c;其诞生背景是为了进一步提高目标检测算法的性能和精度。本篇文章就简单讲述一下YOL…

Java 8 新特性 Stream 的使用场景(不定期更新)

方便在写代码的过程中直接使用&#xff0c;好记性不如好文章&#xff0c;直接 CV 改了直接用。提高 办&#xff08;摸&#xff09;公&#xff08;鱼&#xff09;效&#xff08;时&#xff09;率&#xff08;间&#xff09;&#xff0c; 不然就直接问 GPT 也不是说不行。 只符合…

Mysql学习文档笔记

文章目录 基础篇通用语法及分类DDL&#xff08;数据定义语言&#xff09;数据库操作注意事项 表操作 DML&#xff08;数据操作语言&#xff09;添加数据注意事项 更新和删除数据 DQL&#xff08;数据查询语言&#xff09;基础查询条件查询聚合查询&#xff08;聚合函数&#xf…