软渲染器(Directx11)三之世界矩阵,相机变换矩阵,透视投影矩阵,透视除法,视口变换矩阵
其实各大矩阵具体的推导过程我就不给出了,我直接给出矩阵具体的形式和实现代码,以及那些大牛推导矩阵详细的文章:
一,世界矩阵(WorldMatrix)
我一般称世界矩阵为SRT矩阵,SRT分别是"Scale","rotate","translate"三个单词的缩写,也就是世界矩阵由缩放矩阵,旋转矩阵,平移矩阵构成的
(1)缩放矩阵(ScaleMatrix)
假设在X轴缩放Sx倍,在Y轴缩放Sy倍,在Z轴缩放Sz倍,缩放如下所示:
点乘以矩阵的公式如下:
\
实现代码:
//构造一个缩放矩阵
Matrix BuildScaleMatrix(float x, float y, float z)
{Matrix ScaleMa;//清除为0ZeroMemory(&ScaleMa, sizeof(ScaleMa));ScaleMa.ma[0][0] = x;ScaleMa.ma[1][1] = y;ScaleMa.ma[2][2] = z;ScaleMa.ma[3][3] = 1.0f;return ScaleMa;
}
(2)旋转矩阵(RotateMatrix)
在说明旋转矩阵之前,我得说明一下,在左手坐标系如何判断哪个方向为顺时针方向旋转。
先来看下面的图(本人画工有点差,请见谅),我们先用左手的拇指朝向Y的正方向,则四指所绕的方向也就是饶Y轴渲染的顺时针方向了,其它轴的顺时针方向同理也就是这样判断。(其他轴的顺时针旋转方向以及右手坐标系的顺时针也是用这样的方法判断,只不过右手坐标系下改为了使用右手)
一,绕X轴顺时针旋转Θ度数
实现代码:
//构造一个绕X轴的旋转矩阵,形参为角度,顺时针
Matrix BuildRotateXMatrix(float x)
{Matrix RotateXMa;//清除为0ZeroMemory(&RotateXMa, sizeof(RotateXMa));//度数转化为弧度,math库的函数的参数都是弧度float angle = (x *MATH_PI) / (180.0f);RotateXMa.ma[0][0] = 1.0f;RotateXMa.ma[3][3] = 1.0f;float cs = (float)cos(angle);float ss = (float)sin(angle);RotateXMa.ma[1][1] = cs;RotateXMa.ma[2][2] = cs;RotateXMa.ma[1][2] = ss;RotateXMa.ma[2][1] = -ss;return RotateXMa;
}
二,绕Y轴顺时针旋转Θ度数
实现代码:
//构造一个绕Y轴的旋转矩阵,形参为角度,顺时针
Matrix BuildRotateYMatrix(float y)
{Matrix RotateYMa;//清除为0ZeroMemory(&RotateYMa, sizeof(RotateYMa));//度数转化为弧度,math库的函数的参数都是弧度float angle = (y *MATH_PI) / (180.0f);RotateYMa.ma[1][1] = 1.0f;RotateYMa.ma[3][3] = 1.0f;float cs = (float)cos(angle);float ss = (float)sin(angle);RotateYMa.ma[0][0] = cs;RotateYMa.ma[2][2] = cs;RotateYMa.ma[0][2] = -ss;RotateYMa.ma[2][0] = ss;return RotateYMa;
}
三,绕Z轴顺时针旋转Θ度数
实现代码:
//构造一个绕Z轴的旋转矩阵,形参为角度,顺时针
Matrix BuildRotateZMatrix(float z)
{Matrix RotateZMa;//清除为0ZeroMemory(&RotateZMa, sizeof(RotateZMa));RotateZMa.ma[2][2] = 1.0f;RotateZMa.ma[3][3] = 1.0f;//度数转化为弧度,math库的函数的参数都是弧度float angle = (z *MATH_PI) / (180.0f);float cs = (float)cos(angle);float ss = (float)sin(angle);RotateZMa.ma[0][0] = cs;RotateZMa.ma[1][1] = cs;RotateZMa.ma[0][1] = ss;RotateZMa.ma[1][0] = -ss;return RotateZMa;
}
(3)移动矩阵(TranslateMatrix)
假设某个顶点在X轴方向移动dx个单位,Y轴移动dy个单位,在Z轴移动dz个单位
实现代码:
//构造一个移动矩阵
Matrix BuildTranslateMatrix(float x,float y,float z)
{Matrix TranslateMa;//清除为0ZeroMemory(&TranslateMa, sizeof(TranslateMa));TranslateMa.ma[0][0] = 1.0f;TranslateMa.ma[1][1] = 1.0f;TranslateMa.ma[2][2] = 1.0f;TranslateMa.ma[3][3] = 1.0f;TranslateMa.ma[3][0] = x;TranslateMa.ma[3][1] = y;TranslateMa.ma[3][2] = z;return TranslateMa;
}
关于缩放矩阵,旋转矩阵,移动矩阵的详细推导见《Introduction+to+3D+Game+Programming+with+DirectX+11》第三章
二,相机变换矩阵(ViewMatrix)
在此之前看看UVN相机模型,如图所示:
注视向量N的方向跟相机空间(ViewSpace)的Z轴方向是一致的,而竖直向量V的方向与相机空间(ViewSpace)的Y轴方向是一致的,右向量U的方向与相机空间(ViewSpace)的X轴方向是一致的。
计算UVN向量的公式如下:
这里的“X”为叉乘的意思,并且一样可以通过左手规则(因为本格软件光栅器用的是模拟的为Directx11,为左手坐标系)得到叉乘向量的方向,也就是用左手从叉乘符号左边那个向量绕向叉乘符号右边的那个向量,拇指所指方向即为叉乘得到向量的方向。
相机矩阵如下所示:
实现代码:
//建立一个左手坐标系的相机变换矩阵
Matrix MatrixLookAtLH(Point* Eye, Point* LookAt, Vector* Up)
{Vector N;Vector U;Vector V;//求出注视向量NN.x = LookAt->x - Eye->x;N.y = LookAt->y - Eye->y;N.z = LookAt->z - Eye->z;N.w = 0.0f;//用叉乘求出右向量U,U=UpxN;U = VectorCrossProduct(Up, &N);//用叉乘求出V,V=NxUV = VectorCrossProduct(&N, &U);//规格化UVN三个向量VectorNormalize(&U);VectorNormalize(&V);VectorNormalize(&N);//求出-EyeU -EyeV -EyeNfloat x1 = -VectorDotProduct(Eye, &U);float y1 = -VectorDotProduct(Eye, &V);float z1 = -VectorDotProduct(Eye, &N);//求出相机变换矩阵Matrix mViewMatrix;ZeroMemory(&mViewMatrix, sizeof(mViewMatrix));mViewMatrix.ma[0][0] = U.x;mViewMatrix.ma[1][0] = U.y;mViewMatrix.ma[2][0] = U.z;mViewMatrix.ma[3][0] = x1;mViewMatrix.ma[0][1] = V.x;mViewMatrix.ma[1][1] = V.y;mViewMatrix.ma[2][1] = V.z;mViewMatrix.ma[3][1] = y1;mViewMatrix.ma[0][2] = N.x;mViewMatrix.ma[1][2] = N.y;mViewMatrix.ma[2][2] = N.z;mViewMatrix.ma[3][2] = z1;mViewMatrix.ma[3][3] = 1.0f;return mViewMatrix;
}
具体推导见文章:推导相机变换矩阵
三,透视投影矩阵(PerspectiveMatrix)
在推导透视投影矩阵前先看看视截体(Frustum)是怎么样的:
我的渲染器,近截面和投影平面是同一个平面,视截体在YZ平面的投影如下面图所示,
n为原点到近截面的距离,f为原点到远截面的距离,α为视截体在YZ平面投影的FOV视角,r为投影平面的宽高比,则透视投影矩阵为:
实现代码:
//建立一个左手坐标系的透视投影矩阵,注意为左手坐标系下的,视平面和近截面为同一个平面,FOV视角为YZ屏幕的
Matrix MatrixPerspectiveFovLH(float FovYZ,float ScreenAspect,float ScreenNear,float ScreenFar)
{Matrix mProjMatrix;//清除为0ZeroMemory(&mProjMatrix, sizeof(mProjMatrix));//度数转化为弧度,math库的函数的参数都是弧度float angle = (FovYZ *MATH_PI)/ (180.0f);//半角angle /= 2.0f;//求出各类参数float s1 = 1 / (ScreenAspect*(float)tan(angle));float s2 = 1 / tan(angle);float a = ScreenFar / (ScreenFar - ScreenNear);float b = -(ScreenNear*ScreenFar) / (ScreenFar - ScreenNear);mProjMatrix.ma[0][0] = s1;mProjMatrix.ma[1][1] = s2;mProjMatrix.ma[2][2] = a;mProjMatrix.ma[3][2] = b;mProjMatrix.ma[2][3] = 1.0f;return mProjMatrix;
}
具体推导参见文章:
深入探索透视投影变换 和 深入探索透视投影变换(续)
四,透视除法:
透视除法的本质:
假设有一个向量为(x,y,z,w),进行透视除法也就是把向量的每个参数除以向量的第四个参数,得到(x/w,y/w,z/w,1)
参见 深入探索透视投影变换 和 深入探索透视投影变换(续)
实现代码:
//对点集进行透视除法,会后变到NDC空间
void PerspectiveDivede(vector<Vertex>& pList)
{for (vector<Vertex>::iterator it = pList.begin(); it != pList.end(); ++it){(*it).x = (*it).x / (*it).w;(*it).y = (*it).y / (*it).w;(*it).z = (*it).z / (*it).w;(*it).w = (*it).w / (*it).w;}
}
五,视口变换矩阵(ViewPortMatrix)
实际上在D3D11中,不需要我们手动进行视口变换的,D3D11给我们提供了一个接口,我们在这个接口填充参数,然后D3D11在内部自动帮我们进行视口变换了,那个接口如下图所示:
生成相应的视口矩阵:
解释一下上面的参数,TopLeftX和TopLeftY为视平面在左上角的坐标点,一般来说都为0。Width为视平面的宽度,Height为视平面的高度,MinDepth为顶点的最小深度(Z缓存),
MaxDepth为顶点的最大深度(Z缓存),一般来说MinDepth为0,MaxDepth为1.
所以一般视口变换矩阵为下面所示:
实现代码:
}//一般而言,视口变换矩阵的TopLeftX=0,TopLeftY=0,MaxDepth=1.0,MinDepth=0.0
Matrix MatrixViewPort(float ScreenWidth, float ScreenHeight, float MaxDepth, float MinDepth, float TopLeftX, float TopLeftY)
{Matrix MatrixViewPort;//清除为0ZeroMemory(&MatrixViewPort, sizeof(MatrixViewPort));MatrixViewPort.ma[0][0] = ScreenWidth / 2.0f;MatrixViewPort.ma[1][1] = -ScreenHeight/ 2.0f;MatrixViewPort.ma[2][2] = MaxDepth - MinDepth;MatrixViewPort.ma[3][0] = TopLeftX + ScreenWidth / 2.0f;MatrixViewPort.ma[3][1] = TopLeftY+ ScreenHeight / 2.0f;MatrixViewPort.ma[3][2] = MinDepth;MatrixViewPort.ma[3][3] = 1.0f;return MatrixViewPort;
}
具体推导参见《Introduction+to+3D+Game+Programming+with+DirectX+11》的第十六章的16.1小节
实现代码:
软渲染器(Directx11)三之世界矩阵,相机变换矩阵,透视投影矩阵,透视除法,视口变换矩阵相关推荐
- SoftRendererRenderPipeline(从迷你光栅化软渲染器的实现看渲染流水线)
简介 这是可能一篇没有什么实际作用的文章,因为没有任何shader效果实现,整篇文章到最后,我只实现了一个旋转的立方体(o(╯□╰)o,好弱),和游戏引擎渲染的万紫千红的3D世界显得有很大落差,仿佛一 ...
- 全网最简单的软渲染器
引言 本文实现了一个包含矩阵变化.光栅化.面剔除.深度测试等功能的软渲染器. 所谓软渲染器就是使用 CPU 渲染 3D 模型的程序. 因此请记住我们的最终目的:将3D模型显示在屏幕上 . 本文分为两个 ...
- 关于在寒假用两周从零手写包含模拟着色器的软渲染器这件事
当你重新踏上旅途之后,一定要记得旅途本身的意义. --巴巴托斯 轮子哥说过,编译原理,操作系统,图形学是程序员的三大浪漫,既然以后想从事游戏方面的工作,造这个轮子是不可避免的.其实早在本科的时候我就有 ...
- 用C# Bitmap作为画布写个3D软渲染器
文章目录 Recoards 记录 图元光栅 Bitmap.SetPixel优化成LockBits/UnlockBits指针操作 Blend Projection 投影 Wireframe 线框 Sci ...
- 设计自己的软渲染器6-纹理映射与背面剔除
纹理映射 纹理映射说白了就是将一幅图像贴在我们所要贴的物体的表面. 为每个多边形顶点附一个纹理坐标,然后再纹理素材上取样贴在一个多边形上,然后达到所要的视觉效果. 如下为一个正方体箱子每个面都贴上纹理 ...
- 设计自己的软渲染器1-准备篇
软渲染器:简单说来就是借助软件将3D模型数据渲染成我们屏幕上的画面内容. 相关知识:线性代数,基本图形学知识,基本操作系统知识. 主要参考:<计算机图形学>第四版<3D游戏编程大师技 ...
- 【十天自制软渲染器】DAY 03:画一个三角形(向量叉乘算法 重心坐标算法)
前面两天画了点和线,今天我们来画一个最简单也是最强大的面--三角形. 本文主要讲解三角形绘制算法的推导和思路(只涉及到一点点的向量知识),最后会给出代码实现,大家放心的看下去就好. 本文源码
- 2000行代码实现软渲染引擎
代码地址:https://github.com/sdlwlxf1/tinyEngine 终于实现了自己的软渲染器,图形学的学习暂时告一段落.代码参考知乎上的韦易笑大神的回答,自己加入了很多功能包括: ...
- output怎么用_如何用 C++ 写一个可编程软件渲染器?
今天你想用最新的 D3D12 画一个三角形,少说也要上千行代码了,对于初学者来讲,这个门槛是非常高的,太多干扰了,而一千多行代码,已经足够你重头实现一个简易版 D3D 了,为什么不呢?比起从图形 AP ...
最新文章
- 微信支付HTTPS服务器证书验证(PHP)
- 事务消息大揭秘!RocketMQ、Kafka、Pulsar全方位对比
- python supper_python supper()函数
- Hadoop精华问答 | 关于Hadoop核心技术的精华问答
- (Docker实战) 第五篇:建立持续集成环境02
- Java实现Redis的订阅发布功能,亲测可以
- 不肯去幼儿园的小盆友
- 使用 jQuery Mobile 和 CSS3 实现响应式设计
- Date类的getYear(),getMonth过时,现在的获取方法
- win10 软路由_「保姆级」万晓博带你用软路由搭建家庭网络中心第1节-准备工作...
- 量子计算机和量子纠缠的关系,科普:什么是量子纠缠和量子计算?
- 利用机器学习分析脑电数据(原理分析+示例代码+快速上手)
- 错误	 CS8107 C# 7.0 中不支持功能“xxxxxx”。请使用 7.1 或更高的语言版本。
- 一个可以截取其他App素材的办法Visual Studio Code
- udaldump数据导入导出工具使用
- SpringBoot+Netty开发IM即时通讯系列(一)
- java 相似度匹配算法
- Oracle之唯一性约束(UNIQUE Constraint)用法详解
- 继电器开关阿里云IOT上云设置操作
- 用 TFserving 部署深度学习模型