zoukankan      html  css  js  c++  java
  • 完美的视图旋转算法

    这里说的视图旋转不是FPS游戏里面那种第一人称的视图旋转,那个处理起来比较自然。这里说的视图旋转是类似3ds Max, Maya这种3D设计软件里面的第三人称的视图旋转。有的系统里面叫做tumble,有的人习惯叫orbit,有的系统里面干脆就叫rotate。不管怎样,这个看似非常基础的算法,网上居然几乎找不到理想的方法说明,我简直震惊了。好吧,经过一些实验,自己写了一个算法,经测试各种角度的旋转都是完美的,共享出来大家鉴定下。如果有更好的算法,还请留言告诉我。我们先来看看实现:

    eiBool need_init;
    eiNode *cam_inst = ei_edit_node(cam_inst_name, &need_init);
    
    eiIndex xform_pid = ei_node_find_param(cam_inst, "transform");
    eiIndex motion_xform_pid = ei_node_find_param(cam_inst, "motion_transform");
    eiMatrix xform_val = *ei_node_get_matrix(cam_inst, xform_pid);
    
    eiVector up_vector = ei_vector(0.0f, 0.0f, 1.0f);
    eiVector camera_pos = point_transform(ei_vector(0.0f, 0.0f, 0.0f), xform_val);
    eiVector cam_up = normalize(vector_transform(ei_vector(0.0f, 1.0f, 0.0f), xform_val));
    eiVector cam_right = normalize(vector_transform(ei_vector(1.0f, 0.0f, 0.0f), xform_val));
    eiVector cam_dir = normalize(vector_transform(ei_vector(0.0f, 0.0f, -1.0f), xform_val));
    if (!target_set) /* initialize camera to align with original view */
    {
    	eiVector obj_center = ei_vector(0.0f, 0.0f, 0.0f);
    	point_on_plane(camera_target, camera_pos, -cam_dir, obj_center);
    	target_set = EI_TRUE;
    }
    eiVector target_vec = camera_pos - camera_target;
    eiScalar target_dist = normalize_len(target_vec, target_vec);
    /* make horizontal speed slower when approaching up vector */
    eiScalar horiz_speed = 1.0f - 0.7f * absf(dot(up_vector, target_vec));
    target_vec = vector_transform(target_vec, rotate(radians(offset[0] * -0.2f * horiz_speed), cam_up));
    target_vec = vector_transform(target_vec, rotate(radians(offset[1] * -0.2f), cam_right));
    camera_pos = camera_target + target_vec * target_dist;
    eiVector camera_dir_z = target_vec;
    eiVector camera_dir_x = cross(up_vector, camera_dir_z);
    if (absf(dot(up_vector, camera_dir_z)) > 0.99f) /* fix up vector precision issue */
    {
    	eiVector fixed_up = cross(camera_dir_z, cam_right);
    	camera_dir_x = cross(fixed_up, camera_dir_z);
    }
    if (dot(cam_right, camera_dir_x) < 0.0f) /* fix sudden flip when approaching up vector */
    {
    	camera_dir_x = - camera_dir_x;
    }
    eiVector camera_dir_y = cross(camera_dir_z, camera_dir_x);
    
    xform_val = ei_matrix(
    	camera_dir_x.x, camera_dir_x.y, camera_dir_x.z, 0.0f, 
    	camera_dir_y.x, camera_dir_y.y, camera_dir_y.z, 0.0f, 
    	camera_dir_z.x, camera_dir_z.y, camera_dir_z.z, 0.0f, 
    	camera_pos.x, camera_pos.y, camera_pos.z, 1.0f);
    
    ei_node_set_matrix(cam_inst, xform_pid, &xform_val);
    ei_node_set_matrix(cam_inst, motion_xform_pid, &xform_val);
    
    ei_end_edit_node(cam_inst);
    

    实现是基于Elara SDK写的(Elara是一个离线渲染器,详见:www.rendease.com),不过都是基本的数学函数,看起来应该不复杂,我就不改写了。

    说一下比较核心的几个地方:

    1. 一定要基于up vector构造新的摄像机坐标系,否则我发现总会产生转多了视图倾斜的问题。up vector的方向也是不同的系统中使用这个算法需要注意的地方,这里用的是跟3ds Max兼容的up vector即Z轴朝上方向。

    2. 初始化的时候,一定要计算好摄像机target点的位置,否则第一次旋转会产生跳变。

    3. 因为基于up vector构造坐标系,一定要处理下摄像机朝向旋转到与up vector很接近的方向时候的精度问题。

    4. 当旋转过顶部的时候,由于与up vector叉乘产生的向量的方向突变,所以会产生跳变,这里要特殊处理一下。

    5. 摄像机朝向越接up vector的方向,绕up vector旋转的速度应该越慢,这样看起来不会产生跳跃感。

    6. point_on_plane算法中,要取绝对值,不能用signed distance,以保证camera target总在摄像机朝向的前方。

  • 相关阅读:
    侯捷STL课程及源码剖析学习1
    路边拾遗之其他模块(struct/csv/xlwt/smtp)
    面向对象之内置方法(简单)、组合。以及接口归一化设计与抽象类
    面向对象之继承与派生
    面向对象之类属性实例属性及其增删改查
    python中常用模块详解二
    函数之命名空间、作用域、闭包、装饰器四剑客。
    文件操作之1,2,3
    python中常用模块详解一
    python内置常用内置方法详解
  • 原文地址:https://www.cnblogs.com/len3d/p/5530401.html
Copyright © 2011-2022 走看看