纹理操作
DirectX9固定管线渲染的纹理操作中包含了大量的默认值和参数,令人十分头痛。总结了几种个人比较常用的纹理操作如下:
1.纹理颜色值与纹理颜色值的混合操作
选择两张图像分别作为背景(Background)和前景(Foreground),采用多重渲染,将两站图像的像素相乘。
g_pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0); // 第0层纹理状态采用顶点数组中的第一组顶点
g_pd3dDevice->SetTexture(0, g_pBackground); // 第0层使用背景图片作为纹理操作对象
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 第0层纹理状态的参数1使用当前设置的纹理操作对象(即是g_pBackground)
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); // 第0层纹理状态的纹理颜色操作运算:直接将参数1作为结果返回
/*
上面的代码意思是:直接将g_pBackground作为第0层纹理状态的渲染结果传入下一层(第1层)
*/
g_pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0); // 第1层纹理状态也采用顶点数组中的第一组顶点
g_pd3dDevice->SetTexture(1, g_pForeground); // 第1层使用前景图片作为纹理操作对象
g_pd3dDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 第1层纹理状态的参数1使用当前设置的纹理操作对象(即是g_pForeground)
g_pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); // 第1层纹理状态的参数2使用当前帧缓存(Frame Buffer)中的纹理,也就是上一层纹理状态的操作结果(也即是第0层的纹理渲染结果g_pBackground)
g_pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE); // 指定参数1和参数2的纹理颜色操作运算是乘法D3DTOP_MODULATE
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);混合操作
g_pd3dDevice->SetTexture(0, g_pBackground); // 第0层使用背景图片作为纹理操作对象
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 第0层纹理状态的参数1使用当前设置的纹理操作对象(即是g_pBackground)
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1); // 第0层纹理状态的纹理颜色操作运算:直接将参数1作为结果返回
/*
上面的代码意思是:直接将g_pBackground作为第0层纹理状态的渲染结果传入下一层(第1层)
*/
g_pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0); // 第1层纹理状态也采用顶点数组中的第一组顶点
g_pd3dDevice->SetTexture(1, g_pForeground); // 第1层使用前景图片作为纹理操作对象
g_pd3dDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE); // 第1层纹理状态的参数1使用当前设置的纹理操作对象(即是g_pForeground)
g_pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); // 第1层纹理状态的参数2使用当前帧缓存(Frame Buffer)中的纹理,也就是上一层纹理状态的操作结果(也即是第0层的纹理渲染结果g_pBackground)
g_pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE); // 指定参数1和参数2的纹理颜色操作运算是乘法D3DTOP_MODULATE
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);混合操作
2.纹理颜色值与指定的颜色值混合操作
g_pd3dDevice->SetTexture(0, g_pBackground);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
DWORD dwColor = 0x000000FF;
g_pd3dDevice->SetRenderState(D3DRS_TEXTUREFACTOR, dwColor); // 指定当前渲染状态使用的固定颜色值是dwColor,但是具体要不要使用这个固定颜色值,还要由下面的纹理颜色操作对象来决定
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TFACTOR); // 第0层的纹理状态的参数1是使用固定颜色值
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE); // 第0层的纹理状态的参数2是当前纹理操作对象(g_pBackground)
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_ADD); // 第0层的纹理颜色操作运算是:参数1+参数2
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
DWORD dwColor = 0x000000FF;
g_pd3dDevice->SetRenderState(D3DRS_TEXTUREFACTOR, dwColor); // 指定当前渲染状态使用的固定颜色值是dwColor,但是具体要不要使用这个固定颜色值,还要由下面的纹理颜色操作对象来决定
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TFACTOR); // 第0层的纹理状态的参数1是使用固定颜色值
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE); // 第0层的纹理状态的参数2是当前纹理操作对象(g_pBackground)
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_ADD); // 第0层的纹理颜色操作运算是:参数1+参数2
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
3.带有Alpha的透明混合
3.1采用多次渲染的方式(即多次调用DrawXXXX函数)
g_pd3dDevice->SetTexture(0, g_pBackground);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
/*
上述代码完成了一次渲染(调用了一次DrawPrimitive),结果是当前的FrameBuffer中存放的是g_pBackground
下面的代码将开始第二次渲染,用于将前景g_Foreground贴在g_pBackground上(带透明度的合成)
*/
g_pd3dDevice->SetTexture(0, g_pForeground); // 设置第0层的纹理操作对象是g_pForeground(此纹理图像必须是带Alpha通道的32位图)
// 指示第0层纹理操作是直接将g_pForeground返回(可以理解为此时g_pForeground放在一个临时的Buffer中,这个Buffer还需要与前面的存有背景的FrameBuffer进行合成)
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
// 开启Alpha合成(当前层与FrameBuffer中的结果进行合成)并设置Alpha合成公式
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); // 源纹理(当前层的纹理,那个临时的buffer,即g_pForceground)的合成因子(Blend factor)是源纹理的Alpha数值
g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); // 目标纹理(前面合成好的FrameBuffer中的背景g_pBackground)的合成因子(Blend factor)是源纹理的(1-Alpha)
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); // 开启Alpha合成
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); // 关闭Alpha合成,这是默认值
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
/*
上述代码完成了一次渲染(调用了一次DrawPrimitive),结果是当前的FrameBuffer中存放的是g_pBackground
下面的代码将开始第二次渲染,用于将前景g_Foreground贴在g_pBackground上(带透明度的合成)
*/
g_pd3dDevice->SetTexture(0, g_pForeground); // 设置第0层的纹理操作对象是g_pForeground(此纹理图像必须是带Alpha通道的32位图)
// 指示第0层纹理操作是直接将g_pForeground返回(可以理解为此时g_pForeground放在一个临时的Buffer中,这个Buffer还需要与前面的存有背景的FrameBuffer进行合成)
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
// 开启Alpha合成(当前层与FrameBuffer中的结果进行合成)并设置Alpha合成公式
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); // 源纹理(当前层的纹理,那个临时的buffer,即g_pForceground)的合成因子(Blend factor)是源纹理的Alpha数值
g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); // 目标纹理(前面合成好的FrameBuffer中的背景g_pBackground)的合成因子(Blend factor)是源纹理的(1-Alpha)
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); // 开启Alpha合成
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); // 关闭Alpha合成,这是默认值
3.2多重渲染(只调用一次DrawXXXX)
g_pd3dDevice->SetTexture(0, g_pBackground);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
/*
上述代码只是设置了第0层的纹理操作对象,并没有进行实际的渲染
*/
g_pd3dDevice->SetTexture(1, g_pForeground);
g_pd3dDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
// 设置第1层的纹理状态操作的Alpha值来自本层设置的纹理操作对象(即g_pForeground中的Alpha通道)
// 而对于DirectX9中第0层的SetTextureStageState的D3DTSS_ALPHAARG1和D3DTSS_ALPHAOP数值默认即为D3DTA_TEXTURE和D3DTOP_SELECTARG1,所以下面这两句代码对第0层实际上没有添加的必要,除非之前修改过这两个参数的数值
// g_pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); // 第1层的纹理状态的Alpha参数1是来自本层的纹理操作对象,即g_pForeground的Alpha通道
// g_pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); // 第1层的纹理状态的Alpha操作运算是将Alpha参数1直接返回,那么本层后续的纹理颜色运算如果使用了Alpha合成的话,就会采用这里Alpha运算的结果了(实际就是g_pForeground的Alpha通道)
g_pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
g_pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
g_pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA); // 使用本层纹理中的Alpha通道做透明度合成,使用这个标志的时候,并不需要g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
/*
既然D3DTOP_BLENDTEXTUREALPHA可以完成从当前层的Texture中提取出Alpha通道,那么前面的D3DTSS_ALPHAOP操作是否是没有意义的?这个暂时还不明白
*/
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
/*
上述代码只是设置了第0层的纹理操作对象,并没有进行实际的渲染
*/
g_pd3dDevice->SetTexture(1, g_pForeground);
g_pd3dDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
// 设置第1层的纹理状态操作的Alpha值来自本层设置的纹理操作对象(即g_pForeground中的Alpha通道)
// 而对于DirectX9中第0层的SetTextureStageState的D3DTSS_ALPHAARG1和D3DTSS_ALPHAOP数值默认即为D3DTA_TEXTURE和D3DTOP_SELECTARG1,所以下面这两句代码对第0层实际上没有添加的必要,除非之前修改过这两个参数的数值
// g_pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); // 第1层的纹理状态的Alpha参数1是来自本层的纹理操作对象,即g_pForeground的Alpha通道
// g_pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); // 第1层的纹理状态的Alpha操作运算是将Alpha参数1直接返回,那么本层后续的纹理颜色运算如果使用了Alpha合成的话,就会采用这里Alpha运算的结果了(实际就是g_pForeground的Alpha通道)
g_pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
g_pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
g_pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA); // 使用本层纹理中的Alpha通道做透明度合成,使用这个标志的时候,并不需要g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
/*
既然D3DTOP_BLENDTEXTUREALPHA可以完成从当前层的Texture中提取出Alpha通道,那么前面的D3DTSS_ALPHAOP操作是否是没有意义的?这个暂时还不明白
*/
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);