threejs获取模型坐标_Threejs倒影实现解析

倒影是在自然界中非常常见的一种现象,例如水面倒影、镜子。我们都知道,眼睛之所以能够看到某个物体,是因为物体本身能够发光或者物体能够反射其它的物体所发的光,这些光进入到我们的眼里就形成了该物体影像。倒影形成也是一种光学的现象,其原理是物体发射或者反射的光经过倒影平面的反射后进入到我们的眼里,我们所看到的在倒影平面形成的虚像就是该物体的倒影。

ad605f949ad43d2949fb2866d38750d6.png

52edc6da5ec945b53ad31804d86db82f.png

不管是做数据可视化还是游戏,我们经常需要在3D场景中来实现这种自然现象,给水面、镜面等物体增加倒影的效果,来提高视觉效果。那么在3D渲染中这种效果是怎么实现的那?WebGL的渲染引擎threejs给我们提供了一个很好的倒影实现的封装,通过对threejs提供的代码进行分析,希望能够和大家一起学习一下。

大概的思路是:构建一个虚拟的相机对需要倒影的物体进行渲染,然后将渲染的结果当作纹理映射到倒影平面上,这样就可以实现倒影的效果了。这里面我们需要解决两个问题,第一个是如何构建这个虚拟的相机,第二个是怎么将通过纹理相机渲染出来的结果正确的映射到倒影平面上。

使用threejs来实现倒影我们只需要引入threejs的倒影引擎(该文件位于threejs项目example/js/objects目录下),然后创建一个接受倒影的几何体,其他的交给threejs来完成就可以来,这里我们创建了一个圆形来接收阴影。

<script src="js/objects/Reflector.js"></script>var geometry = new THREE.CircleBufferGeometry(40, 64);
var groundMirror = new THREE.Reflector(geometry, {textureWidth: WIDTH * window.devicePixelRatio,textureHeight: HEIGHT * window.devicePixelRatio
});

a3bb2fdeaf51538978bbf260ef413d46.png

第一步:构建虚拟的相机

得到倒影面和真实相机的位置

reflectorWorldPosition.setFromMatrixPosition( scope.matrixWorld );
cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld ); 

得到反射面的旋转矩阵

rotationMatrix.extractRotation( scope.matrixWorld );

先定义一个默认的法向量,乘以上一步得到的反射面的旋转向量得到反射面现在的法向量

normal.set( 0, 0, 1 );
normal.applyMatrix4( rotationMatrix );

计算相机位置

// 计算相机位置到反射平面位置到向量
view.subVectors( reflectorWorldPosition, cameraWorldPosition );
// 当向量与反射面当法向量夹角说明相机在反射面的背面,则直接返回不进行倒影的渲染
if ( view.dot( normal ) > 0 ) return;
// 得到反射向量的反向量
view.reflect( normal ).negate();
// 投影面位置加上该向量得到虚拟相机的位置
view.add( reflectorWorldPosition );

与计算相机位置类似的步骤计算相机的视点位置

rotationMatrix.extractRotation( camera.matrixWorld );
lookAtPosition.set( 0, 0, - 1 );
lookAtPosition.applyMatrix4( rotationMatrix );
lookAtPosition.add( cameraWorldPosition );target.subVectors( reflectorWorldPosition, lookAtPosition );
target.reflect( normal ).negate();
target.add( reflectorWorldPosition );

根据计算的相机位置和相机视点位置构建虚拟相机

virtualCamera.position.copy( view );
virtualCamera.up.set( 0, 1, 0 );
virtualCamera.up.applyMatrix4( rotationMatrix ); 
virtualCamera.up.reflect( normal );
virtualCamera.lookAt( target );  
virtualCamera.far = camera.far; 
virtualCamera.updateMatrixWorld();
virtualCamera.projectionMatrix.copy( camera.projectionMatrix );

第二步:如何将虚拟相机的渲染结果映射到投影面上

初始化一个默认矩阵,这是初始化的矩阵主要是为了把屏幕坐标和[-1, 1]映射到[0, 1]的纹理坐标

textureMatrix.set(0.5, 0.0, 0.0, 0.5,0.0, 0.5, 0.0, 0.5,0.0, 0.0, 0.5, 0.5,0.0, 0.0, 0.0, 1.0
);

然后将该矩阵乘以模型、视图、投影矩阵,经过模型、视图、投影矩阵变换的坐标为屏幕坐标,再经过上述矩阵后就可以映射为纹理坐标了

textureMatrix.multiply( virtualCamera.projectionMatrix );
textureMatrix.multiply( virtualCamera.matrixWorldInverse );
textureMatrix.multiply( scope.matrixWorld );

将该矩阵在着色器中使用,可以得到倒影面各坐标点对应的纹理坐标,这样就可以把渲染结果正确的映射到投影平面上

vUv = textureMatrix * vec4( position, 1.0 );

进一步的完善

调整虚拟相机渲染时的投影矩阵,将相机的近裁剪面重置为投影面,避免对倒影面下方对物体进行投影,clipBias参数是对裁剪面进行了一个偏移,具体对算法请参考:

Oblique View Frustum Near-Plane Clipping

http://www.terathon.com/lengyel/Lengyel-Oblique.pdf

reflectorPlane.setFromNormalAndCoplanarPoint( normal, reflectorWorldPosition );  
reflectorPlane.applyMatrix4( virtualCamera.matrixWorldInverse );clipPlane.set( reflectorPlane.normal.x, reflectorPlane.normal.y, reflectorPlane.normal.z, reflectorPlane.constant );var projectionMatrix = virtualCamera.projectionMatrix;q.x = ( Math.sign( clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ];
q.y = ( Math.sign( clipPlane.y ) + projectionMatrix.elements[ 9 ] ) / projectionMatrix.elements[ 5 ];
q.z = - 1.0;
q.w = ( 1.0 + projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ];clipPlane.multiplyScalar( 2.0 / clipPlane.dot( q ) );projectionMatrix.elements[ 2 ] = clipPlane.x;
projectionMatrix.elements[ 6 ] = clipPlane.y;
projectionMatrix.elements[ 10 ] = clipPlane.z + 1.0 - clipBias;
projectionMatrix.elements[ 14 ] = clipPlane.w;

最主要对几个步骤在已经完成了,下面进行对就是通过构建对虚拟相机和新的投影矩阵对整个场景进行渲染,将渲染结果当作纹理映射到投影平面上就完成了。

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

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

相关文章

aws java mysql_java - AWS EC2 / MySql - spring boot无法从datasource确定jdbc url - 堆栈内存溢出...

我有一个简单的Spring Boot应用程序&#xff0c;我试图在基于Amazon AMI的Amazon EC2实例上部署。 它使用MySql数据库(版本8.0.15)。我在AWS上创建了数据库&#xff0c;当我从本地系统指向它的配置时&#xff0c;它可以工作。 我也可以从MySql Workbench访问它。但是当我将Spri…

debian 删除mysql数据库_Debian中完全卸载MySQL的方法

作者&#xff1a; 字体&#xff1a;[增加 减小] 类型&#xff1a;转载这篇文章主要介绍了Debian中完全卸载MySQL的方法,同时介绍了清理方法,可以做到彻底卸载mysql,需要的朋友可以参考下之前服务器上配置测试用的服务环境&#xff0c;我偷懒顺手用网上现成的脚本进行安装&#…

python123外汇兑换计算器_Python 3.x--使用re模块,实现计算器运算实例

1 importre23 #乘除运算处理&#xff0c;可以处理不含括号的加减和乘除函数(只处理乘除)4 defmulti_and_divi(arg):5 #传入参数为列表&#xff0c;如&#xff1a;[3*2-1*9/3,0]6 val arg[0]7 #对字符串进行乘除匹配&#xff1a;如3*2-1*9/3&#xff0c;就匹配&#xff1a;3*28 …

python修改文件名字数字_python实现多进程按序号批量修改文件名的方法示例

本文实例讲述了python实现多进程按序号批量修改文件名的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;说明文件名命名方式如图&#xff0c;是数字序号开头&#xff0c;但是中间有些文件删掉了&#xff0c;序号不连续&#xff0c;这里将序号连续起来&#xff0c;…

遗传算法求函数最大值实验_小知识:什么是遗传算法

1 什么是遗传算法遗传算法(GeneticAlgorithm, GA)是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型&#xff0c;是一种通过模拟自然进化过程搜索最优解的方法。其主要特点是直接对结构对象进行操作&#xff0c;不存在求导和函数连续性的限定&#xff1b;…

$dbms=mysql_Oracle dbms

Oracle dbms_random包的用法 Oracle dbms_random包的用法 1.dbms_random.value方法 dbms_random是一个可以生成随机数或者字符串的程序包。这个包有initialize()、seed()、terminate()、value()、normal()、random()、string()等几个函数&#xff0c;但value()是最常用Oracle d…

js定位div坐标存入mysql_JavaScript与Div 对层定位和移动获得坐标

1:移动图层 获得点的x轴y轴坐标&#xff0c;从而进行绝对定位(注意&#xff1a;竖拉框会影响 x 轴 y 轴坐标值)var x,y,z,downfalse,objfunction init(){objevent.srcElement //事件触发对象obj.setCapture() //设置属于当前对象的鼠标捕捉zobj.style.zIndex …

mysql 不指定 长度吗_mysql中整数类型后面的数字,是不是指定这个字段的长度?比如int(11),11代表11个字节吗?...

原先对mysql不太理解&#xff0c;但也没有报错。但理解的不够深入。这次补上。原来以为int(11)是指11个字节&#xff0c;int(10)就是10个字节。我错了。http://zhidao.baidu.com/link?urlpuYWaGBQNKNHgffO5kdvXshF3KmX8OuB4Mor3HXapbNHa8m1CdlF8PJTqVuKa1eKcEd6Bv2NKUr3I-KJr5…

python绘制折线图显示数据_漂亮图表也可用python信手拈来!一文教你学会用Python绘制堆积折线图...

今天&#xff0c;和大家聊聊关于Python绘图相关的东东哦&#xff0c;还是和大家继续深耕Python经典的matplotlib库哦&#xff01;好啦&#xff0c;咱们就开始吧&#xff01;首先&#xff0c;咱们聊聊如何在Python中去绘制经典的堆积折线图到这可能有些朋友可能会问了&#xff1…

linux下使用odbc连接mysql_Linux环境下通过ODBC访问MSSql Server

为了解决Linux系统连接MSSql Server的问题&#xff0c;微软为Linux系统提供了连接MSSql Server的ODBC官方驱动。通过官方驱动&#xff0c;Linux程序可以方便地对MSSql Server进行访问。官网提供了三个版本的驱动&#xff0c;分别用于以下发行版的Linux系统&#xff1a;64bit Re…

python画图代码对比_Python实现代码差异对比分析

在写代码过程&#xff0c;有时需要对比查看两个代码文件的不同&#xff0c;肉眼查看费事费力&#xff0c;很难进行对比找出不同。例如&#xff0c;程序运行报错时&#xff0c;会对比自己先前写的代码或者参考别人代码&#xff0c;有哪些地方不对&#xff0c;此时便可以通过该程…

insert into user mysql root_跳过授权表登录后使用insert into创建root权限用户

起因&#xff1a;刚刚搭建好的mysql数据库&#xff0c;做基础优化时&#xff0c;不小心把所有用户都删除了&#xff0c;并且退出了。没办法&#xff0c;只好跳过授权表登录&#xff0c;新建root用户。过程如下&#xff1a;一、停掉mysql&#xff0c;跳过授权登录[rootexplnk-za…

matplotlib 设置标注方向_Python 使用matplotlib画图添加标注、及移动坐标轴位置

import matplotlib.pyplot as pltimport matplotlibimport numpy as np#解决中文乱码问题&#xff0c;引入windows字体库myfont matplotlib.font_manager.FontProperties(fnamerC:/Windows/Fonts/msyh.ttf)x np.linspace(-3,3,50)y 2*x 1plt.plot(x,y)plt.figure(1,figsize…

flyway配置mysql_Flyway快速上手教程

一、Flyway是什么官网解释地非常全面&#xff0c;可先大致阅读一下。简单地说&#xff0c;flyway是一个能对数据库变更做版本控制的工具。二、为什么要用Flyway在多人开发的项目中&#xff0c;我们都习惯了使用SVN或者Git来对代码做版本控制&#xff0c;主要的目的就是为了解决…

mysql主从 单点_MySQL主从复制虽好,能完美解决数据库单点问题吗?

一、单个数据库 服务器 的缺点数据库服务器存在单点问题&#xff1b;数据库服务器资源无法满足增长的读写请求&#xff1b;高峰时数据库连接数经常超过上限。二、如何解决单点问题增加额外的数据库服务器&#xff0c;组建数据库集群&#xff1b;同一集群中的数据库服务器需要具…

java包含关系图_Java——Spring框架完整依赖关系图!再复习了解加工一下吧?

因为spring-core依赖了commons-logging&#xff0c;而其他模块都依赖了spring-core&#xff0c;所以整个spring框架都依赖了commons-logging&#xff0c;如果有自己的日志实现如log4j&#xff0c;可以排除对commons-logging的依赖&#xff0c;没有日志实现而排除了commons-logg…

java ora 28040_Oracle 11g与112c中ORA-28040 错误解决

今天遇到一个连接192.168.56.102这个库报错Java.sql.SQLException: ORA-28040: No matching authentication protocolOracle 12C下查询此报错oralce:/oracle/db/app/12.1.0/db/network/admin> oerr ora 2804028040, 0000, "No matching authentication protocol"/…

java中有序数组比无序数值好_java面向对象的有序数组和无序数组的比较

packageaa;classArray{//定义一个有序数组private long[] a;//定义数组长度private intnElems;//构造函数初始化public Array(intmax){a new long[max];nElems 0;}//size函数public intsize(){returnnElems;}//定义添加函数public void insert(longvalue){//将value赋值给数组成…

java成员的访问权限_Java成员的访问权限

类中某成员(方法或属性,method or field)的访问权限:1、package(或称为friendly&#xff0c;但不用写出&#xff0c;friendly也不是关键字)权限&#xff0c;在同一包中的类和继承该类的子类可以访问&#xff1b;2、public权限&#xff0c;任何类都可以访问&#xff1b;3、priva…

java jvm 加载类的顺序_java JVM-类加载静态初始化块调用顺序

测试类加载的全过程public class Have {static {System.out.println("加载Have");//先加载Have再调用main方法}public static void main(String[] args) throws Exception {System.out.println("main方法"); //先调main方法A anew A(); //先初始化父类&…