您的位置:首页 > 其它

纹理采样和过滤

2017-04-15 09:09 169 查看

纹理采样

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

[cpp]
view plain
copy

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

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

[cpp]
view plain
copy

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分量)即可获得数组下标:

[cpp]
view plain
copy

int imgIndex=3*(iv*width+iu);  

最后通过数组下标取得对应的纹理值:

[cpp]
view plain
copy

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作为一个宏可以加快运算速度:

[cpp]
view plain
copy

#define INV_SCALE 0.003921568627451  

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

线性纹理过滤

POINT/NEAREST采样固然能够取得纹理的颜色值,但是效果看上去很差,有大块的走样,上面有这样的代码:

[cpp]
view plain
copy

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)进行采样,然后乘以各自比例进行颜色混合计算即可得出经过过滤的颜色值.
具体实现如下:

[cpp]
view plain
copy

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的具体实现方法就是把渲染缓冲区的值复制给纹理数组,也并没有多少复杂的,具体实现如下:

[cpp]
view plain
copy

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:

[cpp]
view plain
copy

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: