SharpGL之透视投影和摄像机
当三维体放在世界坐标系中后,由于显示器只能用二维图像显示三维休,因此必须要依赖投影来把三维体降低维数。
投影变换的目的就是定义了一个视景体,使得视景体外多余的部分不会显示。
投影包括透视投影(perspective projection)和正视投影(orthographic projection)两种。
上图就是透视投影的效果,它符合人的观察经验,即离视点近的物体大,离视点远的物体小,远到极点就消失,成为灭点。就像笔直马路的尽头看上去就成为一个点。
我们看一下代码中有关投影部分的代码, 解释一下相关功能函数的参数意义。

1 private void openGLControl_Resized(object sender, EventArgs e)
2 {
3 // TODO: Set the projection matrix here.
4 OpenGL gl = openGLControl.OpenGL;
5 gl.MatrixMode(OpenGL.GL_PROJECTION);
6 gl.LoadIdentity();
7
8 gl.Perspective(20.0f, (double)Width / (double)Height, 0.01, 100.0);
9 gl.LookAt(-5, 5, -5, 0, 0, 0, 0, 1, 0);
10
11 gl.MatrixMode(OpenGL.GL_MODELVIEW);
12 }

创建透视效果的函数名和原型为:
Perspective(double fovy, double aspect, double zNerar, double zFar);
aspect是窗口的纵横比
zNerar,zFar分别是近处和远处的裁面位置。
fovy是控制视野在XY平面的角度,范围是0--180度。你可以把它想象成是3dsmax中的相机镜头对应的视野参数。
下面的Perspective函数的示意图中, 视角指的是参数fovy, aspect=w/h, zNerar为近平面距离, zFar为远平面距离.

对于比较难以理解的参数fovy, 如果你仍然不是太明白,下面换个方式说明一下.

我们拿3dsmax的摄像机参数来做比喻(意义是类似的),上图是我们使用15mm镜头(视野为100.389度)看到的效果。小镜头看到的视野更大。摄影里面叫这种小镜头叫广角镜头。

现在使用85mm的镜头(视野为23.913度),只可以看到物体的局部了。它的可视范围变窄了。
那么 Perspective()函数的fovy参数的意义就等同于镜头所代表的视野度数。
设置好投影变换后, 接下来还要设置视点变换.
设置视点变换的函数LookAt的原形为:
LookAt(double eyex, double eyey, double eyez, double centerx, double centery, double centerz, double upx, double upy, double upz);
这个函数是做视点变换用的,你可以认为它是设置摄像机的函数。
在opengl中,默认时视点定位于坐标系的原点,初使方向指向Z轴负方向,前面说过,默认的三维模形也是贴着世界坐标系的Z轴0的深度上放置的, 因此,如果不进行视点变换,则相当于摄像机正好贴着物体,什么也看不见,这就是为什么上节的代码里面要有一句代码gl.Translate()用于把把物体往Z轴负方向移动一定距离的原因。
我们还是以3dsmax中的摄像机来类比。下面的示意图中,我们使用的是一种被称为”目标摄像机“的相机,之所以这么称谓,是因为为这个摄像机带一个目标点可以调节。
前三个参数 eyex, eyey, eyez 表示摄像机本身位于世界坐标系中的位置。
在3dsmax中,摄像机的位置可以任意平移的.

上图我们放置的位置是XY轴刚好对着世界坐标系的原点。Z轴朝正方向走了老远。因为摄像机离物体远了,因此可以正常看到物体。

上图中,摄像机的XYZ都远离了物体做了变换,相当于在围绕三维体进行观察。
当中三个参数 centerx, centery, centerz 相当于调整了3dsmax目标摄像机的目标点位置。

如上图所示, 改变目标点的位置也有类似围绕观察对象的效果。并且,这个目标点在Z轴上相对于物体的距离是没有什么意义的,主要意义是在XY上的变化上。这一点读者可以自己去试验。
最后三个参数 upx, upy, upz相当于在旋转3dsmax目标摄像机。
不过这个参数的值并不是角度值,而是向量,数值的大小没意义,正负值才有意义。

在3dsmax中,摄像机是可以任意角度旋转的。
但是在Lookat()函数中这最后三个参数, upx,upy 只能在上下,左右,45度这几个角度中变换,而且upz取值似乎没什么意义。
解释完这两个函数,我们用程序绘制一个三角形来测试一下上述视点变换和投影变换的效果。

上图是视野为20的效果,可以看到三角形看上去大些了。

视野改为60,三角形看上去小些了。
以上结果和上面分析Perspective()函数的fovy参数时结论是一致的。

现在我们改动LookAt的前三个参数,三角形正对着我们显示了。

现在我们改变LookAt的最后三个参数,Y向量改为-1,相当于摄像机旋转向下180度,结果三角形朝下了。
SharpGL之透视投影和摄像机的更多相关文章
- glOrtho、glFrustum && glPerspective
glOrtho :正交投影,摄像机可以位于裁剪体内,所以near和far可以取两个正值或者一正一负 glFrustum :透视投影,摄像机不可以位于裁剪体内,所以near和fa ...
- JavaFX 简单3D演示样例
从Java8開始,在JavaFX中便添加了3D部分的内容,包含Camera,Material,Light,Shape3D等基础内容. 当然,JavaFX 3D应该是OpenJFX里眼下正在补充和完好的 ...
- Godot-3D教程-01.介绍3D
创建一个3D游戏将是个挑战,额外增加的Z坐标将使许多用于2D游戏的通用技术不再有用.为了帮助变换(transition),值得一提的是Godot将使用十分相似的API用于2D和3D. 目前许多节点是公 ...
- 可编程渲染管线(Scriptable Render Pipeline, SRP)
原文链接 可编程渲染管线处理数据的流程可分为以下3大阶段 1. 应用阶段 这个阶段大概会由CPU处理4件事情.首先会对模型数据进行可见性判断.模型数据由顶点位置.法线方向.顶点颜色.纹理坐标等构成.然 ...
- SharpGL学习笔记(十九) 摄像机漫游
所谓的摄像机漫游,就是可以在场景中来回走动. 现实中,我们通过眼睛观察东西,身体移动带动眼睛移动观察身边的事物,这也是在漫游. 在OpenGL中我们使用函数LookAt()来操作摄像机在三维场景中进行 ...
- three.js之正投影摄像机与透视投影摄像机的区别
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- SharpGL学习笔记(七) OpenGL的变换总结
笔者接触OpenGL最大的困难是: 经常调试一份代码时, 屏幕漆黑一片, 也不知道结果对不对,不知道如何是好! 这其实就是关于OpenGL"变换"的基础概念没有掌握好, 以至于对& ...
- SharpGL学习笔记(六) 裁剪变换
在OpenGL中,除了视景体定义的6个裁剪平面(上下左右前后)外, 用户还可以定义一个或者多个附加的裁剪平面,以去掉场景中无关的目标. 附加平面裁剪函数原型如下: ClipPlane(OpenGL.G ...
- SharpGL学习笔记(三) 投影变换和视点变换
从本节开始,我们使用SharpGL带的VS2010扩展,来直接生成SharpGL工程. 如果你新建项目时,没有看到下面的SharpGL项目,那么请事先在SharpGL源代码中找到一个叫 ”SharpG ...
随机推荐
- Discuz 数据库各表的作用
pre_common_addon 插件扩展中心服务商表 pre_common_admincp_cmenu 后台管理面板,自定义常用菜单表 pre_common_admincp_group 后台团队职务 ...
- ABP进阶教程9 - CSV导出中文乱码
点这里进入ABP进阶教程目录 问题描述 功能按钮 - 导出CSV,中文信息导出为乱码. 解决方案 打开展示层(即JD.CRS.Web.Mvc)的\wwwroot\view-resources\View ...
- 5个 JS 解构有趣的用途
摘要: 玩转ES6解构赋值. 原文:5个 JS 解构有趣的用途 译者:前端小智 1. 交换变量 通常交换两个变量的方法需要一个额外的临时变量,来看看例子: let a = 1; let b = 2; ...
- Linux的启动过程的分析
Linux的启动过程 Linux系统从启动大哦提供服务的基本过程为:首先机器家电,然后通过MBR或者UEFI装载GRUB,再启动内核,再由内核启动服务,最后开始对外服务 CentOS7要经历四个主要阶 ...
- vue组件name的作用小结
我们在写vue项目的时候会遇到给组件命名 这里的name非必选项,看起来好像没啥用处,但是实际上这里用处还挺多的 ? 1 2 3 export default { name:'xxx' } 1. ...
- python的wraps函数
当使用@修饰符修饰函数时,会存在这样一个问题:被修饰的函数会消失(这是因为修饰函数没有设置返回值,如果设置了返回值,则就把返回值赋给被修饰函数,比如,test1函数的返回值设置为 return 6, ...
- 批量文件B中选出部分文件(与A文件夹数量相同),放到C中
import glob import os,sys import shutil fileDir = 'F:/project/Breast/InBreast/INBreast/outimgpatch/n ...
- C++ class 内的 [] 重载示例。
#include <iostream> // overloading "operator [] " inside class ///////////////////// ...
- Shiro内置过滤器
Shiro内置过滤器 DefaultFilter 枚举类定义了shiro所有的默认过滤器. package org.apache.shiro.web.filter.mgt; public enum D ...
- LG3389 「模板」高斯消元法 高斯消元
问题描述 LG3389 题解 高斯消元,是用来解\(n\)元一次方程组的算法,时间复杂度\(O(n^3)\) 这样就构造出了这个方程组的矩阵 目标就是把这个矩阵左边\(n \times n\)消为单位 ...