光栅化方法优化

之前用包围盒和重心坐标法做过光栅化实现,但是那个方法存在问题,这次要实现一个更高效的光栅化方法.

包围盒的问题

之前使用的包围盒方法确定光栅化范围,但是包围盒确定的范围是矩形,要光栅化的是三角形,那么每次都会有不需要的像素参加运算,一个面当然没啥问题,但是如果是100个面问题就大了.

改进方法

在 <3d游戏编程大师技巧> 中作者使用了三角形扫描线插值的方法,效率不错,实现方法类似下图:

通过计算三角形三条边的方程和每条扫描线的两个交点以确定光栅化x值的范围,然后就能够准确地进行光栅化计算,这样就不用考虑不可能到达的像素了.

实现方法

空间中直线有如下参数方程:
p=p0+(p1-p0)*t  p是直线上的某一点,p0是直线向量起点,p1是直线向量的终点,如果t=0则点在p0上而t=1则点在p1上,所以如果要让目标点在线段p0p1上则t的取值范围应该是0到1.

扫描线是从三角形顶遍历到三角形底,从y0到y1,那么有类似如下方程
y=n n的取值范围从y0到y1.三条边的参数方程和扫描线方程联立方程组可以求出三个t,扫描线的左右边界应该处于线段之中,所以排除掉t在0到1范围之外的那个,留下其余两个作为左右边界点的t,代入直线方程求出x就可以确定扫描线的左右范围了.这是一般情况,还有平顶平底情况没有讨论,碰到这种情况不要求和扫描线平行的那个边的直线方程,求出另外两条边的直线方程,然后求出两个t和两个x就可以了,下面是具体实现:

	if(scrAY==scrBY&&scrAY==scrCY) //屏幕坐标所有y都相等,直线不进行光栅化
		return;
	int minY=max(0,min(scrAY,min(scrBY,scrCY))); //顶上的y0
	int maxY=min(fb->height-1,max(scrAY,max(scrBY,scrCY))); //底部的y1
	for(int scrY=minY;scrY<=maxY;scrY++) {
		float x1,x2;
		if(scrAY==scrBY) { //平顶平底情况
			float paramAC=((float)scrY-scrAY)/(scrCY-scrAY);
			float paramBC=((float)scrY-scrBY)/(scrCY-scrBY);
			x1=(scrCX-scrAX)*paramAC+scrAX;
			x2=(scrCX-scrBX)*paramBC+scrBX;
		} else if(scrAY==scrCY) { //平顶平底情况
			float paramAB=((float)scrY-scrAY)/(scrBY-scrAY);
			float paramBC=((float)scrY-scrBY)/(scrCY-scrBY);
			x1=(scrBX-scrAX)*paramAB+scrAX;
			x2=(scrCX-scrBX)*paramBC+scrBX;
		} else if(scrBY==scrCY) { //平顶平底情况
			float paramAB=((float)scrY-scrAY)/(scrBY-scrAY);
			float paramAC=((float)scrY-scrAY)/(scrCY-scrAY);
			x1=(scrBX-scrAX)*paramAB+scrAX;
			x2=(scrCX-scrAX)*paramAC+scrAX;
		} else { //正常情况
			float paramAB=((float)scrY-scrAY)/(scrBY-scrAY);
			float paramAC=((float)scrY-scrAY)/(scrCY-scrAY);
			float paramBC=((float)scrY-scrBY)/(scrCY-scrBY);
			bool ab=(paramAB<=1.0&¶mAB>=0.0)?true:false;
			bool ac=(paramAC<=1.0&¶mAC>=0.0)?true:false;
			bool bc=(paramBC<=1.0&¶mBC>=0.0)?true:false;
			float xAB=(scrBX-scrAX)*paramAB+scrAX;
			float xAC=(scrCX-scrAX)*paramAC+scrAX;
			float xBC=(scrCX-scrBX)*paramBC+scrBX;
			if(!ab) {
				x1=xAC;
				x2=xBC;
			} else if(!ac) {
				x1=xAB;
				x2=xBC;
			} else if(!bc) {
				x1=xAB;
				x2=xAC;
			} else {
				x1=xAC;
				x2=xBC;
				if(x1==x2) //排除两线交点的情况
					x2=xAB;
			}
		}
		int minX=max(0,min(x1,x2)-0.5);  //扫描线左边的x值
		int maxX=min(fb->width-1,max(x1,x2)+0.5); //扫描线右边的x值

接下来的光栅化步骤和之前一样用的重心坐标法:

		for(int scrX=minX;scrX<=maxX;scrX++) {
			invViewPortTransform(scrX,scrY,fb->width,fb->height,ndcX,ndcY);
			VECTOR4D ndcPixel(ndcX,ndcY,1,0);
			VECTOR4D proportion4D=face->clipMatrixInv*ndcPixel;
			VECTOR3D proportionFragment(proportion4D.x,proportion4D.y,proportion4D.z);
			float pa=proportionFragment.x;
			float pb=proportionFragment.y;
			float pc=proportionFragment.z;
			if(pa<0||pb<0||pc<0)
				continue;
			float sum=pa+pb+pc;
			pa/=sum; pb/=sum; pc/=sum;

			Fragment frag;
			interpolate3f(pa,pb,pc,face->clipA.w,face->clipB.w,face->clipC.w,clipW);
			interpolate3f(pa,pb,pc,face->clipA.z,face->clipB.z,face->clipC.z,frag.ndcZ);
			frag.ndcZ/=clipW;
			if(frag.ndcZ<-1||frag.ndcZ>1)
				continue;
			if(db!=NULL) {
				float storeZ=readDepth(db,scrX,scrY);
				if(storeZ<frag.ndcZ)
					continue;
				writeDepth(db,scrX,scrY,frag.ndcZ);
			}

			interpolate3f(pa,pb,pc,face->clipA.x,face->clipB.x,face->clipC.x,frag.ndcX);
			frag.ndcX/=clipW;
			interpolate3f(pa,pb,pc,face->clipA.y,face->clipB.y,face->clipC.y,frag.ndcY);
			frag.ndcY/=clipW;

			interpolate3f(pa,pb,pc,face->clipA.wx,face->clipB.wx,face->clipC.wx,frag.wx);
			interpolate3f(pa,pb,pc,face->clipA.wy,face->clipB.wy,face->clipC.wy,frag.wy);
			interpolate3f(pa,pb,pc,face->clipA.wz,face->clipB.wz,face->clipC.wz,frag.wz);
			interpolate3f(pa,pb,pc,face->clipA.ww,face->clipB.ww,face->clipC.ww,frag.ww);

			interpolate3f(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);

			interpolate3f(pa,pb,pc,face->clipA.s,face->clipB.s,face->clipC.s,frag.s);
			interpolate3f(pa,pb,pc,face->clipA.t,face->clipB.t,face->clipC.t,frag.t);

			FragmentOut outFrag;
			fs(frag,outFrag);
			drawPixel(fb,scrX,scrY,outFrag.r,outFrag.g,outFrag.b);
		}

使用这个方法可以避免进行不必要的光栅化运算,比之前的包围盒方法效率更高.

参考: <3d游戏编程大师技巧>

http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-837-computer-graphics-fall-2012/lecture-notes/MIT6_837F12_Lec21.pdf

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-23 04:23:54

光栅化方法优化的相关文章

Azure 动手演示之一:采用定制化方法在 Azure 中创建虚拟机

也许大家看过很多关于 Windows Azure (Microsoft Azure) 介绍或如何使用的文章,但即使看过很多图片或 PPT,也难以真正理解这一全球领先的公有云平台. 鉴于此,我制作了一系列如何使用 Azure 的视频录像,主要是实时录制我在屏幕上对 Azure 的操作.希望能对大家深入的了解 Windows Azure (Microsoft Azure) 有所帮助. 本演示看上去非常简单,目的是如何在 Azure 上创建虚拟机,但事实上,它是如何在 Azure 上创建整个企业部署的

图像处理之常见二值化方法汇总

图像处理之常见二值化方法汇总 图像二值化是图像分析与处理中最常见最重要的处理手段,二值处理方法也非常多.越 精准的方法计算量也越大.本文主要介绍四种常见的二值处理方法,通常情况下可以满 足大多数图像处理的需要.主要本文讨论的方法仅针对RGB色彩空间. 方法一: 该方法非常简单,对RGB彩色图像灰度化以后,扫描图像的每个像素值,值小于127的 将像素值设为0(黑色),值大于等于127的像素值设为255(白色).该方法的好处是计算 量少速度快.缺点更多首先阈值为127没有任何理由可以解释,其次完全不

【转】图像灰度化方法总结及其VC实现

转载自:  http://blog.csdn.net/likezhaobin/article/details/6915754 最近一段时间作者开始进行运动目标识别定位系统设计,本文以及后续的几篇文章都是从一个图像处理初学者的角度来总结目标检测定位过程中所应用到的各种常 见的算法,尤其是解决算法实现过程中由于粗心大意或者C编程基本功不扎实所引起的各种问题.本文主要对彩色图片灰度化的方法及其实现过程进行总结,最终给 出实现的C代码. 在进行视频流目标识别与跟踪时,通常第一个步骤就是对采集到的彩色图像

深度学习全优化方法总结比较(转)

https://zhuanlan.zhihu.com/p/22252270 https://zhuanlan.zhihu.com/p/27449596 http://cs231n.github.io/neural-networks-3/#sgd 前言 (标题不能再中二了)本文仅对一些常见的优化方法进行直观介绍和简单的比较,各种优化方法的详细内容及公式只好去认真啃论文了,在此我就不赘述了. SGD 此处的SGD指mini-batch gradient descent,关于batch gradien

二分查找法优化插入排序

通俗的插排是对一个整型数组进行升序排序,可以看出每个元素插入到队列中经过两个步骤:先是挨个比较,找到自己所在的位置:然后把后面的数据全部移位,然后把元素插入. 要把数据插入,移位是必不可少了.那么,挨个比较倒是可以优化,因为要插入的队列已经是排序好的,我们可以使用二分法来减少比较的次数. 二分法的时间复杂度为O(log 2 n),而线性查找的复杂度为O(n). 在对50000个随机数进行测试比较中,发现加了二分查找的插排比普通的插排速度快了将近一倍!(通俗插排7888ms,优化插排4852ms)

php页面静态化方法小结

1.首先说说页面静态化的优点: ①静态化html页面提高网站访问速度: ②有利于SEO收索引擎优化,百度谷歌等收索引擎优先收录静态页面,收录的更全: ③提高安全性,静态化页面减少sql注入的攻击:而且不用承担php页面的漏洞危险: ④不用每次打开页面都请求系统数据库,提升速度并且减轻系统负担 2.页面静态实现方法: 首先写一个静态模板页面temp.php:里面代码如下: <?php include 'content.php';?><html><head><meta

Atom windows 版不完全汉化方法 附代码

刚刚发布的 Atom 1.0 . 看到的第一眼就喜欢上了它. 不太喜欢 vs code 的庞大. 可惜没中文, 于是找啊找.终于找到个半吊子的方法来汉化 - - 不喜勿喷 查看的官方开发文档  https://atom.io/docs/api/v1.0.0/MenuManager 以及git仓库 https://github.com/atom/atom 由于只有添加的方法,没有消除的方法,所以就这个样子了,没法删除原来的菜单. 诸君也可以自己试试看. 而且以下汉化用代码是根据 https://g

池化方法总结(Pooling)

在卷积神经网络中,我们经常会碰到池化操作,而池化层往往在卷积层后面,通过池化来降低卷积层输出的特征向量,同时改善结果(不易出现过拟合). 为什么可以通过降低维度呢? 因为图像具有一种"静态性"的属性,这也就意味着在一个图像区域有用的特征极有可能在另一个区域同样适用.因此,为了描述大的图像,一个很自然的想法就是对不同位置的特征进行聚合统计,例如,人们可以计算图像一个区域上的某个特定特征的平均值 (或最大值)来代表这个区域的特征.[1] 1.  一般池化(General Pooling)

二值化方法总结

1.im2bw(Image, threshold_value) 2.最大类件方差(ostu): matlab:im2bw(image, graythresh(image)) 3.bernsen算法 较原始的Bernsen: 这个算法的中心思想是:设当前像素为P,计算以P为中心的大小为(2w+1)*(2w+1)窗口内的所有像素的最大值M与最小值N,两者的均值T, if(M-N)> S  则当前点P的阈值为T. else 当前窗口所在区域的灰度级差别较小,那么窗口在目标区或在背景区,若T>T'则当