什么是光栅化_为什么要光栅化?怎么实现光栅化方法
光栅化是在计算机上生成图像的重要步骤,但opengl、directx和其他图形界面都封装了光栅化方法。我自己做了一个栅格化器,接下来我就说说如何实现栅格化。
为什么要栅格化?
图形管道的输入是图元的顶点,输出是像素。在这一步中,还有一个中间产物叫做fragment,每个fragment对应一个像素,但是fragment比像素有更多的属性用于计算,比如深度值和法向量。我们可以通过线段计算出最终将要生成的像素的颜色值,我们把由输入顶点计算线段的过程称为光栅化。为什么要栅格化?因为要生成段来计算最终颜色。
栅格化的输入输出有哪些?和普通函数一样,栅格化函数也需要输入和输出。从前面的定义来看,函数的输入是图形元素的顶点结构,输出是片段结构。为什么是结构?因为这些都可以用C语言中的struct来描述。
光栅化发生在哪个步骤?通常在图形界面中会暴露顶点处理程序和片段处理程序(着色器听起来像雾,所以用处理程序代替),但是gpu会进行光栅插值计算,这就是为什么片段处理程序的输入是顶点处理程序输出的插值。由于光栅化是顶点处理后的一个步骤,所以输入的顶点结构是顶点处理后的,也就是mvp变换和与透视矩阵相乘后的顶点。注意:这一步没有经过透视分割,光栅化插值发生在裁剪空间,绝非标准化空间,所以顶点位置是四维齐次坐标,不是三维坐标!
光栅化方法怎么实现?
我们首先可以确定的是光栅化的输入和输出是什么。而且你要知道手头可以用什么数据。
首先对输入的顶点进行处理,转换成屏幕坐标,然后将裁剪空间的顶点坐标转换成标准化空间,就像这样:
ndca . x=clipa . x/clipa . w;
ndca . y=clipa . y/clipa . w;
ndcb . x=clipb . x/clipb . w;
ndcb . y=clipb . y/clipb . w;
ndcc . x=clipc . x/clipc . w;
ndcc . y=clipc . y/clipc . w;
然后在顶点的标准坐标上进行视口变换:“
viewPortTransform(face-》ndcA.x,face-》ndcA.y,fb-》width,fb-》height,scrAX,scrAY);
viewPortTransform(face-》ndcB.x,face-》ndcB.y,fb-》width,fb-》height,scrBX,scrBY);
viewPortTransform(face-》ndcC.x,face-》ndcC.y,fb-》width,fb-》height,scrCX,scrCY);
然后得到三个二维坐标来表示这三个顶点在屏幕上的最终位置。他们可以形成一个二维三角形,并找到三角形的包围盒。"
int minX=max(0,min(scrAX,min(scrBX,scrCX)));
int maxX=min(fb-》width-1,max(scrAX,max(scrBX,scrCX)));
int minY=max(0,min(scrAY,min(scrBY,scrCY)));
int maxY=min(fb-》height-1,max(scrAY,max(scrBY,scrCY)));
注意不要超出屏幕范围,屏幕范围外的点都被切掉。
遍历边界框以获得潜在片段的屏幕位置:
for(int scrX=minX;scrX"=maxX;scrX ) {
for(int scrY=minY;scrY"=maxY;scrY ) {。
}
}
分别计算各线段对应的标准化空间坐标:
invViewPortTransform(scrX,scrY,fb-》宽度,fb-》高度,ndcX,ndcY);
这里用的是逆视口变换,非常方便,缩放平移坐标即可。
然后我们得到可能碎片的标准化空间的x和y坐标。为什么可能是碎片?因为还不能确定这些片段是在要栅格化的三角形的外面还是里面,所以我们只计算三角形里面的片段。
但是知道这些有什么用呢?
这里有一个公式可以计算三个顶点对片段的影响比例,也称为权重:
这个公式的公元前分别代表三角形的三个顶点啊啊啊各自是顶点a在裁剪空间的齐次坐标(是四维的)的x y w值,这边没用到z值,由于z也要通过这个权值进行计算。
这个怎么推导这个公式?
已知待光栅化三角形字母表的三个顶点在裁剪空间的齐次坐标,把权值阿尔法贝塔伽玛设为pa pb pc,可得每一个片段的裁剪空间齐次坐标为:
x=pa*ax pb*bx pc*cx
y=pa*ay pb*by pc*cy
z=pa*az pb*bz pc*cz
w=pa*aw pb*bw pc*cw
然后计算片段在标准化坐标系的坐标值为:
nx=x/w
ny=y/w
nz=z/w
nw=1
能够推得:
x=w*nx
y=w*ny
w=w
由于:
x=pa*ax pb*bx pc*cx
y=pa*ay pb*by pc*cy
w=pa*aw pb*bw pc*cw
转换为3x3矩阵就是
ax bx CX pa w * NX ay by cy * Pb=w * ny aw bw CW PC w
当中(同非消耗性)非消耗品和纽约洋基棒球队就是之前取得的片段在标准化坐标系的x y值;而且因为pa pb pc是比值,所以w能够去除;这样仅仅要求取3x3矩阵的逆就能够取得pa pb pc的值。
可是要注意pa pb pc=1,所以计算出值以后要进行例如以下处理:
浮点求和=pa pb pc
pa/=sum;Pb/=sum;PC/=sum;
然后把有比值小于0的片段抛弃:
如果(pa《0||pb《0||pc《0
继续;
接下来就能够用这三个权值对顶点属性进行插值运算了。
详细的光栅化函数是这样:
void rasterize(FrameBuffer* fb,DepthBuffer* db,FragmentShader fs,Face* face) {
float ndcX=0,ndcY=0,clipW=0;
int scrAX,scrAY,scrBX,scrBY,scrCX,scrCY
viewPortTransform(face-》ndcA.x,face-》ndcA.y,fb-》width,fb-》height,scrAX,scrAY);
viewPortTransform(face-》ndcB.x,face-》ndcB.y,fb-》width,fb-》height,scrBX,scrBY);
viewPortTransform(face-》ndcC.x,face-》ndcC.y,fb-》width,fb-》height,scrCX,scrCY);
int minX=max(0,min(scrAX,min(scrBX,scrCX)));
int maxX=min(fb-》width-1,max(scrAX,max(scrBX,scrCX)));
int minY=max(0,min(scrAY,min(scrBY,scrCY)));
int maxY=min(fb-》height-1,max(scrAY,max(scrBY,scrCY)));
for(int scrX=minX;scrX"=maxXscrX ) {
for(int scrY=minY;scrY"=maxYscrY ) {
invViewPortTransform(scrX,scrY,fb-")宽度,fb-》。高度,ndcX,ndcY);
VECTOR4D ndcPixel(ndcX,ndcY,1,0);
向量4d比例4d=脸-》clipMatrixInv * NDC像素;
向量3d比例片段(比例4d。x,比例4D.y,比例4D。z);
float pa=proportionFragment.x
float pb=proportionFragment.y
float pc=proportionFragment.z
浮点求和=pa pb pc
pa/=sum;Pb/=sum;PC/=sum;
如果(pa《0||pb《0||pc《0
继续;
碎片蓄意杀伤
interpolate3f(pa,pb,pc,face-》clipA.w,face-》clipB.w,face-》clipC.w,clipW);
插值3f(pa,pb,pc,face-》clipA.z,face-》clipB.z,face-》clipC.z,frag。ndcz);
frag.ndcZ/=clipW;
if(frag.ndcZ 《-1||frag.ndcZ》 1)
继续;
如果(db!=NULL) {
float storeZ=readDepth(db,scrX,scrY);
如果(storeZ
插值3f(pa,pb,pc,face-》clipA.x,face-》clipB.x,face-》clipC.x,frag。ndcx);
frag.ndcX/=clipW;
插值3f(pa,pb,pc,face-》clipA.y,face-》clipB.y,face-》clipC.y,frag。ndcy);
frag.ndcY/=clipW;
插值3f(pa,pb,pc,face-》clipA.nx,face-》clipB.nx,face-》clipC.nx,frag。NX);
interpolate3f(pa,pb,pc,face-》clipA.ny,face-》clipB.ny,face-》clipC.ny,frag。ny);
interpolate3f(pa,pb,pc,face-》clipA.nz,face-》clipB.nz,face-》clipC.nz,frag。NZ);
插值3f(pa,pb,pc,face-》clipA.s,face-》clipB.s,face-》clipC.s,frag。s);
插值3f(pa,pb,pc,face-》clipA.t,face-》clipB.t,face-》clipC.t,frag。t);
碎片散开
fs(frag,outFrag);
drawPixel(fb,scrX,scrY,outFrag.r,outFrag.g,outFrag。b);
}
}
}
光栅化完毕了,这下就能自己实现opengl和directx了!
推荐阅读
- 华为鸿蒙系统的升级用户已经超过5000万怎么办_华为鸿蒙系统的升级用户已经超过5000万
- 惠普笔记本电脑怎么样跟联想戴尔比,惠普笔记本电脑怎么样拆机
- 燃气热水器怎么清洗水垢文章,燃气热水器怎么清洗
- Adobe Photoshop CS2中文破解版软件介绍(Adobe Photoshop CS2中文破解版)
- 辽宁人事考试网辽宁2022省考报名入口官网辽宁省考报名什么时候?
- 路由器怎么设置静态ip地址上网功能,路由器怎么设置静态IP地址上网
- 辣木籽食用方法及用量,辣木籽食用方法
- 文章中秋节联欢晚会,看晚会文章,品中秋美食,庆阖家团圆,赢幸运大奖
- pplive网络电视播放器软件介绍(pplive网络电视播放器)
- 个人信用查询,个人征信查询_信用报告查询
- 基本定时器和通用定时器,电子定时器技巧:[2]定时器如何调整时间
- 2022三伏天的正确时间表?(三伏天是哪一天2022)
- 酷狗音乐铃声制作在哪里,怎么用酷狗音乐制作手机铃声
- 怎么查每日用电度数和费用,每日每月电费明细怎查询,微信怎么查每天用电量
- 高速公路为啥会堵车呢?,高速公路为什么会堵车?真相全在这里!
- 疯狂车手最新版游戏介绍(疯狂车手最新版)
- 培养基中丙酮酸钠的作用是什么? 丙酮酸钠价格
- 手机将文章转换为gif,在手机端,如何将文章转为Gif表情包\动画
- 抗美援朝中国牺牲多少志愿军具体人数(抗美援朝中国去了多少人,牺牲多少志愿军)