纹理采样和过滤

本文探讨了在OpenGL和DirectX中纹理采样的概念,包括如何使用纹理采样函数获取纹理值。针对纹理坐标转换为数组下标的方法进行了详细说明,并指出了在不同坐标系统之间的差异。此外,还介绍了线性纹理过滤对于改善视觉效果的重要性,以及渲染到纹理的实践应用。最后,提到了调用这些功能的方法。

纹理采样


在opengl和directx里有纹理采样函数vector4 Sampler.texture2d(float s,float t),用它可以取得对应纹理坐标的纹理值;然而,并不知道这个函数的具体实现是怎么样的.事实上从图像读取的纹理就是一个数组,类似这样:

imgData=new unsigned char[width*height*3]; 

纹理坐标可以转换为数组的下标,类似这样:

	float u=(float)(width-1)*s;
	float v=(float)(height-1)*(1.0-t);
	int iu=(int)u;
	int iv=(int)v;

纹理坐标s和t的范围是0到1,这边采用的是opengl的坐标系,y轴从下到上递增,但是图片保存的坐标系y轴是从上到下递增,所以1.0-t做y轴取反以获得opengl坐标系的对应的纹理坐标,这边运算出的u和v的范围是0到纹理宽和0到纹理高,然后将v乘以纹理宽加上u然后乘以3(一个纹理包含rgb分量)即可获得数组下标:

	int imgIndex=3*(iv*width+iu);
最后通过数组下标取得对应的纹理值:

	color.x=(float)imgData[imgIndex]*INV_SCALE;
	color.y=(float)imgData[imgIndex+1]*INV_SCALE;
	color.z=(float)imgData[imgIndex+2]*INV_SCALE;
这边直接把1/255作为一个宏可以加快运算速度:

#define INV_SCALE 0.003921568627451

然后return color;即可获得纹理颜色值,以上就是POINT/NEAREST采样的实现.事实上这是最基本的纹理采样函数实现,这里边还有问题,效果不是很好.


线性纹理过滤


POINT/NEAREST采样固然能够取得纹理的颜色值,但是效果看上去很差,有大块的走样,上面有这样的代码:
	int iu=(int)u;
	int iv=(int)v;
问题就出在这里,(int)这样直接抛弃小数点以后的值导致采样出的相邻纹理并不连续,那么用float采样行吗? 答案是:不行! 这边实现的采样函数是从数组取值,纹理坐标转为数组下标,数组下标不能用float只能用int,那么就没办法了吗? 并不是,可以对周围纹理进行采样然后按照各自比例进行混合,这样能够提高显示效果.原理如下:
例如计算出的u和v类似这样:3.45,4.55 那么u就在3到4之间,v在4到5之间,比例是(1-0.45):0.45和(1-0.55):0.55,那么对(3,4),(4,4),(3,5),(4,5)进行采样,然后乘以各自比例进行颜色混合计算即可得出经过过滤的颜色值.
具体实现如下:
VECTOR4D Sampler::texture2D(float s,float t) {
	VECTOR4D color(1,1,1,1);
	float u=(float)(width-1)*s;
	float v=(float)(height-1)*(1.0-t);
	int iu=(int)u;
	int iv=(int)v;
	int uNext=iu+1<=(width-1)?iu+1:iu;
	int vNext=iv+1<=(height-1)?iv+1:iv;

	float uNextPer=u-iu;
	float vNextPer=v-iv;
	float uPer=1.0-uNextPer;
	float vPer=1.0-vNextPer;

	int imgIndex=3*(iv*width+iu);
	color.x=(float)imgData[imgIndex]*INV_SCALE;
	color.y=(float)imgData[imgIndex+1]*INV_SCALE;
	color.z=(float)imgData[imgIndex+2]*INV_SCALE;
	
	int imgIndexNextU=3*(iv*width+uNext);
	int imgIndexNextV=3*(vNext*width+iu);
	int imgIndexNextUV=3*(vNext*width+uNext);

	VECTOR4D colorNextU(1,1,1,1),colorNextV(1,1,1,1),colorNextUV(1,1,1,1);
	colorNextU.x=(float)imgData[imgIndexNextU]*INV_SCALE;
	colorNextU.y=(float)imgData[imgIndexNextU+1]*INV_SCALE;
	colorNextU.z=(float)imgData[imgIndexNextU+2]*INV_SCALE;
	colorNextV.x=(float)imgData[imgIndexNextV]*INV_SCALE;
	colorNextV.y=(float)imgData[imgIndexNextV+1]*INV_SCALE;
	colorNextV.z=(float)imgData[imgIndexNextV+2]*INV_SCALE;
	colorNextUV.x=(float)imgData[imgIndexNextUV]*INV_SCALE;
	colorNextUV.y=(float)imgData[imgIndexNextUV+1]*INV_SCALE;
	colorNextUV.z=(float)imgData[imgIndexNextUV+2]*INV_SCALE;

	color.x=color.x*uPer*vPer+colorNextU.x*uNextPer*vPer+colorNextV.x*uPer*vNextPer+colorNextUV.x*uNextPer*vNextPer;
	color.y=color.y*uPer*vPer+colorNextU.y*uNextPer*vPer+colorNextV.y*uPer*vNextPer+colorNextUV.y*uNextPer*vNextPer;
	color.z=color.z*uPer*vPer+colorNextU.z*uNextPer*vPer+colorNextV.z*uPer*vNextPer+colorNextUV.z*uNextPer*vNextPer;
	
	return color;
}
这样得出的纹理颜色比之前点采样的看上去好多了.

渲染到纹理


现代opengl和directx都有FrameBuffer/RenderTarget功能,有了这项功能可以进行Render To Texture(渲染到纹理)操作.RTT操作的具体应用有很多,最普遍的应用莫过于Shadow Map技术.事实上RTT的具体实现方法就是把渲染缓冲区的值复制给纹理数组,也并没有多少复杂的,具体实现如下:
void writeFrameBuffer2Sampler(FrameBuffer* fb,Sampler* sampler) {
	for(int i=0;i<fb->height;i++) {
		for(int j=0;j<fb->width;j++) {
			int index=(i*fb->width+j)*3;
			sampler->imgData[((i)*fb->width+j)*3]=fb->colorBuffer[index];
			sampler->imgData[((i)*fb->width+j)*3+1]=fb->colorBuffer[index+1];
			sampler->imgData[((i)*fb->width+j)*3+2]=fb->colorBuffer[index+2];
		}
	}
}
那样,渲染得到的结果就可以作为纹理使用了.

调用方法


类似opengl和directx在shader里调用纹理采样函数,这边模拟了opengl的shader:
void fragmentShader(Fragment input,FragmentOut& output) {
	VECTOR4D texColor(1,1,1,1);
	if(currTexture!=NULL)
		texColor=currTexture->texture2D(input.s,input.t);
	output.r=texColor.x;
	output.g=texColor.y;
	output.b=texColor.z;
	output.a=texColor.w;
}
FragmentShader在光栅化函数rasterize中调用,每生成一个fragment则调用一次FragmentShader.

图片数据加载参见: http://blog.csdn.net/zxx43/article/details/41594871
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值