透视投影矩阵_透视投影矩阵推导知乎

透视投影矩阵_透视投影矩阵推导知乎透视投影矩阵TheOpenGLPerspectiveProjectionMatrix关于透视投影矩阵的使用BuildingaBasicPerspectiveProjectionMatrixTheOpenGLPerspectiveProjectionMatrix首先,重要的是要记住OpenGL中的矩阵是使用列主顺序(而不是行主顺序)定义的。在所有的OpenGL书籍和参考文献中,OpenGL中使用的透视投影矩阵定义为:我们可以简单地转置矩阵,我们可以得到下面的以行向量为顺序的

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

首先,重要的是要记住OpenGL中的矩阵是使用列主顺序(而不是行主顺序)定义的。在所有的OpenGL书籍和参考文献中,OpenGL中使用的透视投影矩阵定义为:

透视投影矩阵
我们可以简单地转置矩阵,就能得到下面的以行向量为顺序的矩阵:
在这里插入图片描述
下面对透视投影矩阵的参数做一些说明:

l, r:立方体的左,右在X轴上的投影
b, t:立方体的下,上在X轴上的投影
n:近平面在Z轴上的投影
f:远平面在Z轴上的投影
在这里插入图片描述
关于OpenGL透视投影矩阵的推导,可参考链接link.

在这里我们推荐另外一种大佬关于投影矩阵的推导方法,是基于计算机图形学投影矩阵的推导,求出来的结果会和OpenGL的透视投影矩阵有差别,但是在推导过程上更加简单,易于理解。可参照以下链接: link.

关于透视投影矩阵的使用

在旧的固定函数渲染管道中,使用两个函数来设置屏幕坐标和投影矩阵,这两个函数分别是gluPerspective(它是glu库的一部分)和glFrustum。

设置投影矩阵 glFrustum()

在OpenGL中设置透视投影矩阵是通过调用glFrustum来完成的。该函数有六个参数:

// 定义
glFrustum(float left, float right, float bottom, float top, float near, float far);

设置屏幕坐标 gluPerspective()

gluPerspective函数用于设置屏幕坐标。它将视角、图像长宽比(图像宽度除以图像高度)和剪切平面作为增广量。

fovy是垂直视角,也就是从近平面的上边的中心和下边的中心分别连一条线到摄像机所成的角度。 aspect是宽高比,aspect= width/height。

// 定义
void gluPerspective(float fovy, float aspect, float near, float far);

思考: 如何利用FOVY和Aspect确定近平面的 l,r,b,t参数?
注:n,f是固定值,不用确定!
在这里插入图片描述
write:


tan(FOVY/2)=opposite/adjacent=BC/AB=t/n

therefore:


t=tan(FOVY/2)∗n

由于相机的下半部分与上半部分是对称的,则:


b=−t

已知点C在Y轴的分量是 t,下方点D在Y轴的分量是 –t,由此我们可以知道平面高度是 2t。同理可知,平面宽度是 2r

所以宽高比:


Aspect = 2r/2t = r/t

则可进一步得知:


r = Aspect * t




l = -r


综上:只要定义视锥的宽高比和视角,即可得
l,r,b,t 参数用于定义正交投影的立方体。

附参考代码:

#include <cstdio> 
#include <cstdlib> 
#include <fstream> 
#include "geometry.h" 
#include "vertexdata.h" 
 
// compute screen coordinates first
void gluPerspective( 
    const float &angleOfView, 
    const float &imageAspectRatio, 
    const float &n, const float &f, 
    float &b, float &t, float &l, float &r) 
{ 
    
    float scale = tan(angleOfView * 0.5 * M_PI / 180) * n; 
    r = imageAspectRatio * scale, l = -r; 
    t = scale, b = -t; 
} 
 
// set the OpenGL perspective projection matrix
void glFrustum( 
    const float &b, const float &t, const float &l, const float &r, 
    const float &n, const float &f, 
    Matrix44f &M) 
{ 
    
    // set OpenGL perspective projection matrix
    M[0][0] = 2 * n / (r - l); 
    M[0][1] = 0; 
    M[0][2] = 0; 
    M[0][3] = 0; 
 
    M[1][0] = 0; 
    M[1][1] = 2 * n / (t - b); 
    M[1][2] = 0; 
    M[1][3] = 0; 
 
    M[2][0] = (r + l) / (r - l); 
    M[2][1] = (t + b) / (t - b); 
    M[2][2] = -(f + n) / (f - n); 
    M[2][3] = -1; 
 
    M[3][0] = 0; 
    M[3][1] = 0; 
    M[3][2] = -2 * f * n / (f - n); 
    M[3][3] = 0; 
} 
 
void multPointMatrix(const Vec3f &in, Vec3f &out, const Matrix44f &M) 
{ 
    
    //out = in * Mproj;
    out.x   = in.x * M[0][0] + in.y * M[1][0] + in.z * M[2][0] + /* in.z = 1 */ M[3][0]; 
    out.y   = in.x * M[0][1] + in.y * M[1][1] + in.z * M[2][1] + /* in.z = 1 */ M[3][1]; 
    out.z   = in.x * M[0][2] + in.y * M[1][2] + in.z * M[2][2] + /* in.z = 1 */ M[3][2]; 
    float w = in.x * M[0][3] + in.y * M[1][3] + in.z * M[2][3] + /* in.z = 1 */ M[3][3]; 
 
    // normalize if w is different than 1 (convert from homogeneous to Cartesian coordinates)
    if (w != 1) { 
    
        out.x /= w; 
        out.y /= w; 
        out.z /= w; 
    } 
} 
 
int main(int argc, char **argv) 
{ 
    
    uint32_t imageWidth = 512, imageHeight = 512; 
    Matrix44f Mproj; 
    Matrix44f worldToCamera; 
    worldToCamera[3][1] = -10; 
    worldToCamera[3][2] = -20; 
    float angleOfView = 90; 
    float near = 0.1; 
    float far = 100; 
    float imageAspectRatio = imageWidth / (float)imageHeight; 
    float b, t, l, r; 
    gluPerspective(angleOfView, imageAspectRatio, near, far, b, t, l, r); 
    glFrustum(b, t, l, r, near, far, Mproj); 
    unsigned char *buffer = new unsigned char[imageWidth * imageHeight]; 
    memset(buffer, 0x0, imageWidth * imageHeight); 
    for (uint32_t i = 0; i < numVertices; ++i) { 
    
        Vec3f vertCamera, projectedVert; 
        multPointMatrix(vertices[i], vertCamera, worldToCamera); 
        multPointMatrix(vertCamera, projectedVert, Mproj); 
        if (projectedVert.x < -imageAspectRatio || projectedVert.x > imageAspectRatio || projectedVert.y < -1 || projectedVert.y > 1) continue; 
        // convert to raster space and mark the position of the vertex in the image with a simple dot
        uint32_t x = std::min(imageWidth - 1, (uint32_t)((projectedVert.x + 1) * 0.5 * imageWidth)); 
        uint32_t y = std::min(imageHeight - 1, (uint32_t)((1 - (projectedVert.y + 1) * 0.5) * imageHeight)); 
        buffer[y * imageWidth + x] = 255; 
        //std::cerr << "here sometmes" << std::endl;
    } 
    // export to image
    std::ofstream ofs; 
    ofs.open("./out.ppm"); 
    ofs << "P5\n" << imageWidth << " " << imageHeight << "\n255\n"; 
    ofs.write((char*)buffer, imageWidth * imageHeight); 
    ofs.close(); 
    delete [] buffer; 
 
    return 0; 
} 

参考链接:

  1. https://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix/opengl-perspective-projection-matrix
  2. https://zhuanlan.zhihu.com/p/144329075
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/185835.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Tomcat之——宕机自动重启和每日定时启动tomcat

    Tomcat之——宕机自动重启和每日定时启动tomcat在项目后期维护中会遇到这样的情况,tomcat在内存溢出的时候就出现死机的情况和遇到长时间不响应,需要人工手动关闭和重启服务,针对这样的突发情况,希望程序能自动处理问题而不需要人工关于,所以才有了目前的需求。一、设置tomcat定时启动1,首先将tomcat注册为服务,先打开tomcat的bin目录下service.bat文件,修改下面的值,这是sevvice的注册名称和显示名称,一般

  • jmeter正则提取器的使用_java正则表达式用法

    jmeter正则提取器的使用_java正则表达式用法一、正则表达式提取器各名词解(1)ApplytoMainsampleandsub-samples(作用于主节点的取样器及对应子节点的取样器)Mainsampleonly(仅作用于主节点的取样器)Sub-samplesonly(仅作用于子节点的取样器)Jmeter-VariableNametouse(作用于jmeter变量(输入框内可输入jmeter的变量名称),从指定变量值中提取需要的值)(2)要检查的响应字段1、主体:响应报文的主体,最常用2、Body(une.

  • 一叶知秋,一个 LED 就能入门 Linux 内核「建议收藏」

    一叶知秋,一个 LED 就能入门 Linux 内核「建议收藏」简单点灯最近项目上需要用到LED子系统,在嵌入式Linux里面点个灯还是比较简单的,只要在某个灯对应的目录里,向相应文件写入特定值,就可以让LED亮/灭/闪烁。#echo1>/sys/class/leds/green/brightness//点亮LED#echo0>/sys/class/leds/green/brightness//熄灭LED#echoheartbeat>/sys/class/leds/green/trigger//

  • RESTful介绍和使用教程

    RESTful介绍和使用教程REST(RepresentationalStateTransfer)表象化状态转变(表述性状态转变),在2000年被提出,基于HTTP、URI、XML、JSON等标准和协议,支持轻量级、跨平台、跨语言的架构设计。是Web服务的一种新的架构风格(一种思想)。…

    2022年10月23日
  • java 构造函数的作用_Java中构造函数的作用(转)

    java 构造函数的作用_Java中构造函数的作用(转)https://blog.csdn.net/vipmao/article/details/51530954,我觉得讲的非常好,学习到了构造函数的最大作用就是创建对象时完成初始化,当我们在new一个对象并传入参数的时候,会自动调用构造函数并完成参数的初始化。如下:publicclassTest{privateStringname;//有参数的构造函数publicTest(Stringn…

  • macpro录屏快捷键_mac屏幕录制设置

    macpro录屏快捷键_mac屏幕录制设置在视频内容越来越丰富的今天,录屏是我们经常会用到的一个功能,不管是录制一些无法下载的视频还是录制屏幕制作自己的视频,都需要用到录屏功能,本文中我们来推荐几款好用的Mac录屏软件Mac系统自带的录屏工具虽然我们可能天天都在使用Mac系统,但是可能很多人都不知道,其实Mac已经自带了一个好用的截图和录屏工具,你可以随时使用Command+Shift+5快捷键调出Mac自带的录屏功…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号