纹理映射(Texture Mapping)

无论是在现实生活中还是在游戏虚拟世界中,将任何一个物体进行投影,然后进解析,都会发现它是由若干个多边形所构成,再将多边形进行分解,可以划分为若干个四边形和三角形,再将四边形进行分解,可以发现,四边形又可以划分为2个或以上的三角形构成。
存在纹理贴图
无纹理贴图
因此就是采用这种方法,将图像数据映射到三角形单元中去。
纹理映射技术,是一种将图形绘制(映射)到表面的技术,可以显著地增加所绘制场景的细节和真实感。
如:
这里写图片描述
这里写图片描述


  • 纹理坐标
    纹理实际上是一个二维数组,它的元素是一些颜色值。单个的颜色值被称为纹理元素或纹理像素。每一个纹理像素在纹理中都有一个唯一的地址。这个地址可以被认为是一个列和行的值,它们分别由U和V来表示。纹理坐标位于纹理空间中。也就是说,它们和纹理中的(0,0)位置相对应。当我们将一个纹理应用于一个图元时,它的纹理像素地址必须要映射到对象坐标系中。然后再被平移到屏幕坐标系或像素位置上。
    纹理坐标是一对浮点值,用于访问纹理图像中的信息。在Direct3D中所使用的纹理坐标系由沿水平方向的x轴和沿垂直方向的y轴构成。

    要注意,为了能够处理不同尺度的纹理,Direct3D将纹理坐标做了规范化的处理,使其限定在区间[0,1]内。
    一般对于每个屏幕三角形单元,都可以在纹理中定义一个相应的三角形区域,然后将该三角形区域的纹理映射到屏幕三角形单元中。
    添加一个纹理坐标对以标识纹理中的顶点:
struct Vertex
{
  float _x,_y,_z;
  float _nx,_ny,_nz;
  float _u,_v;
  static const DWORD FVF;
  };
  const DWORD Vertex::FVF=D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1;

值得注意的是:当为一个3D三角形指定相应的纹理三角形时,并不是马上就会进行纹理映射,是直到光栅化的时候,纹理映射才会进行。

  • 创建纹理:
    一般纹理的数据都是从磁盘中的图像文件读入,然后再加载到IDirect3DTexture9对象中;同样该图像文件可以在场景中的多个表面上共享使用。
    Direct3D支持的图像文件格式有:
    .jpg; .bmp; .tga; .png; .dds; .ppm; .dib; .hdr; .pfm;

       从文件中加载纹理并将其加载到内存中,使用D3DXCreateTextureFromFile()函数
    
HRESULT D3DXCreateTextureFromFile(
LPDIRECT3DDEVICE9 	pDevice,          //设备对象
LPCTSTR			  	pSrcFile,         //所需加载的图像文件名
LPDIRECT3DTEXTURE9 *ppTexture         //定义一个指针用来接收所创建的纹理
);

如果想让文件所加载的图像发挥更多的控制作用,可以使用D3DXCreateTextureFromFileEx()函数

HRESULT D3DXCreateTextureFromFileEx(
LPDIRECT3DDEVICE9 	pDevice,            //设备对象
LPCTSTR 		  	pSrcFile,           //所需加载的图像文件名
UINT			  	Width,             //图像宽度
UINT			  	Height,             //图像高度
UINT      		  	MipLevels,          //图片的图层,与图像质量有关
DWORD			  	Usage,             //设定纹理的使用方法
D3DFORMAT 		  	Format,            //每个颜色使用的位数(8,16,24,36)
D3DPOOL			  	Pool,              //纹理对象驻留的内存类别
DWORD			  	Filter,             //处理图像质量的方法
DWORD     		  	MipFilter,          //像素过滤方式
D3DCOLOR          	ColorKey,           //设置透明色
D3DXIMAGE_INFO    	*pSrcInfo,          //记录载入图片信息
PALETTEENTRY      	*pPalette,          //记录调色板信息
LPDIRECT3DTEXTURE9	*ppTexture          //用来接收所创建的纹理
); 

现在已经知道加载纹理的函数了,那怎么使用?
比如现在要加载一个名为“hell.bmp”的图像文件创建纹理:
1、创建设备对象和用来接收所创建纹理的指针

LPDIRECT3DDEVICE9 Device;
IDiretct3DTexture9 *_hell;
D3DXCreateTextureFromFile(Device,
"hell.bmp",         //如果不是放在当前项目下,就加上路径名
&_hell);

2、设置当前纹理,使用SetTexture()函数

//SetTexture()的函数原型
HRESULT SetTexture(
DWORD Sampler,               	   //纹理要施加的采样器阶段,该值在[0,7]
IDirect3DBaseTexture9 *pTexture    //Direct3D9纹理对象
);
Device->SetTexture(0,_hell);        
  • 纹理过滤器
    如前面所说(在纹理中定义一个三角形,然后将三角线区域的纹理映射到屏幕三角形单元中),理论上是这样,但实际上,纹理三角形与屏幕三角形的大小并不一致,当纹理三角形比屏幕三角形小时,为了适应屏幕三角形,只能将纹理三角形进行放大;当纹理三角形比屏幕三角形大时,为了适应屏幕三角形,也只能将纹理三角形进行缩小。放大/缩小的过程其实就是将图像的某些像素数据进行复制/舍弃。因此会使得有些时候图形会变得很畸形,为了防止这类情况Direct3D提供了一种技术——纹理过滤(Texture filtering).
    Direct3D提供了四种类型的纹理过滤方式。
    1、最近点采样(nearest point sampling):该方式处理的速度在3种类型里面是最快的,但效果也是最差的,内存开销小。
LPDIRECT3DDEVICE9 Device;
Device->SetSmaplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_POINT);//放大
Device->SetSmaplerState(0,D3DSAMP_MINFILTER,D3DTEXF_POINT);//缩小
2、线性纹理过滤(linear filtering):该方式可以产生较好的效果,而且速度也是蛮快的,内存开销比较适宜得当。
Device->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);//放大
Device->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);//缩小
3、各向异性纹理过滤(anisotropic filtering):该方式可以产生最好的过滤效果,但处理速度却是最慢的,内存的开销也是最大的。再进行使用各向异性纹理过滤时,必须对D3DSAMP_MAXANISOTROPIC水平值进行设定,该值决定了各向异性过滤的质量水平,值越大,图像效果就越好。不过在使用前先使用GetDeviceCaps()函数进行检测下,看硬件能支持的数值是多少。
Device->SetSmaplerState(0,D3DSAMP_MAXANISOTROPIC,3);//假设值为3
Device->SetSmaplerState(0,D3DSAMP_MAGFILETER,D3DTEXF_ANISOTROPIC);//放大
Device->SetSmaplerState(0,D3DSAMP_MINFILETER,D3DTEXF_ANISOTROPIC);//缩小
4、多级渐进纹理(mipmap):由某一纹理的原式分辨率创建一系列逐渐减小(将像素分别缩小一半)的纹理图像,并且对每种分辨率下的纹理所采用的过滤方式进行定制,以便保留那些比较重要的细节。

这里写图片描述

Device->SetSamplerState(0,D3DSAMP_MIPFILTER,Filter);

Fileter可以取以下值:

  • D3DTEXF_NONE: 禁用多级渐进纹理过滤。

  • D3DTEXF_POINT: 采用最近点采样(nearest point sampling)进行过滤。

  • D3DTEXF_LINEAR: 采用线性纹理(linear filtering)进行过滤。

  • 寻址模式
    上面说过纹理坐标必须限制在[0,1]范围内,如果超出那怎么办,Direct3D提供了四种用来处理纹理坐标值超出[0,1]区间的纹理映射模式。

  • 重复寻址模式(wrap address mode):

这里写图片描述

Device->SetSamplerState(0,D3DSAMP_ADDRESSU,D3DTADDRESS_WRAP);
Device->SetSamplerState(0,D3DSAMP_ADDRESSV,D3DTADDRESS_WRAP);
  • 边界颜色寻址模式(boarder color address mode)

这里写图片描述

Device->SetSamplerState(0,D3DSAMP_ADDRESSU,D3DTADDRESS_BORDER);
Device->SetSamplerState(0,D3DSAMP_ADDRESSV,D3DTADDRESS_BORDER);
Device->SetSmaplerState(0,D3DSAMP_BORDERCOLOR,0x000000ff);
  • 钳位(calmp address mode):

这里写图片描述

Device->SetSmaplerState(0,D3DSAMP_ADDRESSU,D3DTADDRESS_CLAMP);
Device->SetSmaplerState(0,D3DSAMP_ADDRESSV,D3DTADDRESS_CLAMP);
  • 镜像(mirror):

这里写图片描述

Device->SetSamplerState(0,D3DSAMP_ADDRESSU,D3DTADDRESS_MIRROR);
Device->SetSamplerState(0,D3DSAMP_ADDRESSV,D3DTADDRESS_MIRROR);

最后上传源代码(有点小问题,不过无伤大雅)

源码链接

  • 14
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Projective Texture Mapping 是一种常用的 Shader 技术,可以用来在 Unity 中实现纹理投影效果。它的原理是通过将投影纹理映射到场景物体上,并基于物体的表面法线和光照方向来计算出光照效果,从而实现物体表面的投影纹理效果。 以下是一个简单的 Projective Texture Mapping 的 Shader 示例: ```shader Shader "Custom/ProjectiveTextureMapping" { Properties { _MainTex ("Texture", 2D) = "white" {} _ProjTex ("Projection Texture", 2D) = "white" {} _Intensity ("Intensity", Range(0,1)) = 1 } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 projPos : TEXCOORD0; float3 worldNormal : TEXCOORD1; float4 vertex : SV_POSITION; }; sampler2D _MainTex; sampler2D _ProjTex; float _Intensity; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.projPos = ComputeGrabScreenPos(o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { // 计算纹理坐标 float2 projCoord = i.projPos.xy / i.projPos.w; projCoord = 0.5 * (projCoord + 1.0); // 计算投影纹理颜色 fixed4 projColor = tex2D(_ProjTex, projCoord); // 计算漫反射光照 float3 worldNormal = normalize(i.worldNormal); float3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); float intensity = _Intensity * max(0, dot(worldNormal, worldLightDir)); // 计算最终颜色并返回 fixed4 texColor = tex2D(_MainTex, i.uv); fixed4 finalColor = lerp(texColor, projColor, intensity); return finalColor; } ENDCG } } } ``` 这个 Shader 的主要实现思路是,将投影纹理的颜色和物体表面原有的颜色进行插值计算,插值的比例根据光照方向和表面法线来计算。其中,ComputeGrabScreenPos() 函数用于将物体的顶点坐标转换为屏幕坐标,从而计算出纹理坐标。在 Pass 中的 vert 函数中,通过 UnityObjectToWorldNormal() 函数将物体的法线转换为世界坐标系下的法线,从而能够正确计算出光照效果。 使用这个 Shader,将它添加到一个材质中,并将这个材质应用到需要投影纹理的物体上,就可以实现 Projective Texture Mapping 技术了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值