艾巴生活网

您现在的位置是:主页>科技 >内容

科技

什么是光栅化_为什么要光栅化?怎么实现光栅化方法

2024-02-02 11:32:01科技帅气的蚂蚁
光栅化是在计算机上生成图像的重要步骤,但opengl、directx和其他图形界面都封装了光栅化方法。我自己做了一个栅格化器,接下来我就说说如

什么是光栅化_为什么要光栅化?怎么实现光栅化方法

光栅化是在计算机上生成图像的重要步骤,但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了!