D3D11教程三十之ProjectiveTexturing(投影纹理)

来源:互联网 发布:php获取qq信息 编辑:程序博客网 时间:2024/05/18 02:46

这节教程是介绍有关ProjectiveTexturing(投影纹理)技术的,结构如下




一,ProjectiveTexturing(投影纹理)的简介.

ProjectiveTexturing(投影纹理)是实现3D实时渲染的最重要的技术之一,很多高级效果,如SoftShadow(软弱阴影),Water,ProjecvtiveLightMap(投影光照图),Reflection等效果都需要用到投影纹理技术。投影纹理意思就是将一张2D纹理投影到3D物体上或者换一个说法就是用一个特别的相机(建立单独的相机变换矩阵和透视投影矩阵)来获取世界空间3D物体投射在某张2D纹理上的UV坐标值。纹理投影技术经常和RTT(渲染到纹理技术)一起搭配使用,可以看看教程:D3D11教程二十一之Water(水的实现)以及D3D11教程三十一之ShadowMap(阴影贴图)之聚光灯成影,还有 D3D11教程十九之PlannarReflection(基于RTT技术和投影纹理技术)都是投影纹理技术搭配RTT技术使用的典型例子,那两个教程中折射的物体和反射的物体的实现其实是投影纹理.

在来看看一个使用投影纹理的例子:



上面这张有一个骷颅头的2D纹理贴图,利用这张2D纹理贴图我们可以生成下面的图形效果:



看这张图里面的骷髅头实际上仅仅是一张2D纹理贴图的效果,而不是真正地将一个3D骷颅头进行渲染的效果。



为了下面更好地讲述有关概念,我只好自创两个概念, ViewCamera(观察照相机)和ProjectorCamera(投影照相机或者投影幻灯机)

ViewCamera提供了我们在3D程序中的人眼观察的视角。
ProjectorCamera提供了投影机观察3D物体的角度,决定了世界空间(WorldSpace)中每个顶点对应于投影纹理坐标系(ProjectiveTextureCoordinate)相应的2D纹理坐标(U,V).


在世界空间中(WorldSpace),我们是如何得到每个顶点对应于投影纹理坐标系(ProjectiveTextureCoordinate)相应的2D纹理坐标(U,V)呢?

来看一张图:




看图中,这个视截体是ProjectorCamera(投影幻灯机)的视截体,点P是在世界空间的一个点(Xp,Yp,Zp,1.0),如何求出相应的(u,v)坐标呢?

要求投影纹理坐标系(ProjectiveTextureCoordinate)相应的2D纹理坐标(U,V),首先得熟悉D3D11的3D渲染流水线:

我在D3D11教程二十五之DepthBuffer(深度缓存)论述过3D渲染流水线,求纹理坐标系(ProjectiveTextureCoordinate)相应的2D纹理坐标(U,V)分为两步:
(1) 假设在世界空间上,点P坐标为(Xp,Yp,Zp,1.0),乘以ProjectorCamera(注意不是ViewCamera)生成的相机变换矩阵和透视投影矩阵,变换到齐次裁剪空间
在齐次裁剪空P点相应的坐标值为
(Xp,Yp,Zp,Wp)  -Wp=<Xp<=Wp   -Wp=<Yp<=Wp     0=<Zp<=Wp,
(2)由于我们不标记“”SV_POSITION“”,显卡不会自动进行透视除法(当然显卡也不会自动进行裁剪算法),所以我们手动计算 
   -1<=Xp/Wp<=1     -1<=Yp/Wp<=1,  0=<{0.5*(Xp/Wp)+0.5}<=1,   0=<{-0.5*(Yp/Wp)+0.5}<=1   又由于投影纹理坐标系中 0=<U<=1    0=<V<=1,并且U坐标方向和X坐标方向相同,而V坐标方向和Y坐标相反,所以我们得出结果:   U= 0.5*(Xp/Wp)+0.5;      V=-0.5*(Yp/Wp)+0.5;


下面我们放出投影幻灯机的类:

ViewPointClass.h

#pragma once#ifndef _VIEW_POINT_CLASS_H#define _VIEW_POINT_CLASS_H#include<Windows.h>#include<xnamath.h>class ViewPointClass{private:XMFLOAT3 mPostion; //投影相机的位置XMFLOAT3 mLookAt; //投影相机看到的位置XMMATRIX mProjectorViewMatirx, mProjectorProjMatrix; //投影相机的相机变换矩阵和投影矩阵float mFieldOfView; //Fov视角大小float mAspectRatio; //屏幕宽高比float mNearPlane, mFarPlane; //视截体的近截面和远截面public:ViewPointClass();ViewPointClass(const ViewPointClass&);~ViewPointClass();public://Set函数void SetPostion(float,float,float);void SetLookAt(float,float,float);void SetProjectionParameters(float, float, float, float);//生成相机矩阵和投影矩阵void GenerateViewMatrix();void GenerateProjMatrix();//Get函数XMMATRIX GetViewMatrix();XMMATRIX GetProjMatrix();};#endif 


ViewPointClass.CPP

#include"ViewPointClass.h"ViewPointClass::ViewPointClass(){}ViewPointClass::~ViewPointClass(){}ViewPointClass::ViewPointClass(const ViewPointClass&other){}void ViewPointClass::SetPostion(float x, float y, float z){mPostion = XMFLOAT3(x, y, z);}void ViewPointClass::SetLookAt(float x, float y, float z){mLookAt = XMFLOAT3(x, y, z);}void ViewPointClass::SetProjectionParameters(float FieldOfView, float AspectRatio, float NearPlane, float FarPlane){mFieldOfView = FieldOfView;mAspectRatio = AspectRatio;mNearPlane = NearPlane;mFarPlane = FarPlane;}void ViewPointClass::GenerateViewMatrix(){XMVECTOR EyePostion = XMLoadFloat3(&mPostion);XMVECTOR LookAt = XMLoadFloat3(&mLookAt);XMVECTOR Up = XMVectorSet(0.0f, 1.0f, 0.0f,0.0f);mProjectorViewMatirx = XMMatrixLookAtLH(EyePostion,LookAt,Up);}//近平面不变的条件下,远平面只影响远处看到物体的范围void ViewPointClass::GenerateProjMatrix(){mProjectorProjMatrix = XMMatrixPerspectiveFovLH(mFieldOfView,mAspectRatio,mNearPlane,mFarPlane);}XMMATRIX ViewPointClass::GetViewMatrix(){return mProjectorViewMatirx;}XMMATRIX ViewPointClass::GetProjMatrix(){return mProjectorProjMatrix;        }



ColorShader.fx

Texture2D BaseTexture:register(t0);  //基础纹理Texture2D ProjectiveTexture:register(t1);  //投影纹理SamplerState SampleType:register(s0);   //采样方式//VertexShadercbuffer CBMatrix:register(b0){matrix World;matrix View;matrix Proj;matrix WorldInvTranspose;matrix ProjectorView;matrix ProjectorProj;};cbuffer CBLight:register(b1){float4 DiffuseColor;float3 LightDirection;float pad;}struct VertexIn{float3 Pos:POSITION;float2 Tex:TEXCOORD0;  //多重纹理可以用其它数字float3 Normal:NORMAL;};struct VertexOut{float4 Pos:SV_POSITION;float4 ProjPos:POSITION; //投影坐标,投影在齐次裁剪空间float2 Tex:TEXCOORD0;float3 W_Normal:NORMAL;  //世界空间的法线};VertexOut VS(VertexIn ina){VertexOut outa;//将坐标变换到观察相机下的齐次裁剪空间outa.Pos = mul(float4(ina.Pos,1.0f), World);outa.Pos = mul(outa.Pos, View);outa.Pos = mul(outa.Pos, Proj);//将顶点法向量由局部坐标变换到世界坐标outa.W_Normal = mul(ina.Normal, (float3x3)WorldInvTranspose);  //此事世界逆转置矩阵的第四行本来就没啥用//对世界空间的顶点法向量进行规格化outa.W_Normal = normalize(outa.W_Normal);//获取纹理坐标outa.Tex= ina.Tex;//将坐标变换到投影相机下的齐次裁剪空间outa.ProjPos= mul(float4(ina.Pos, 1.0f), World);outa.ProjPos = mul(outa.ProjPos, ProjectorView);outa.ProjPos = mul(outa.ProjPos, ProjectorProj);return outa;}float4 PS(VertexOut outa) : SV_Target{float4 TexColor; //采集基础纹理颜色    float2 ProjTex;   //投影纹理坐标float4 color = {0.0f,0.0f,0.0f,0.0f}; //最终输出的颜色//第一,获取采样颜色    TexColor = BaseTexture.Sample(SampleType, outa.Tex);//第二,获取投影相机下的投影纹理空间的坐标值[0.0,1.0]  u=0.5*x+0.5;   v=-0.5*y+0.5;   -w<=x<=w  -w<=y<=w  ProjTex.x =(outa.ProjPos.x/outa.ProjPos.w)*0.5f + 0.5f;ProjTex.y= (outa.ProjPos.y/outa.ProjPos.w)*(-0.5f) + 0.5f;//第三,由于3D模型可能超出投影相机下的视截体,其投影纹理可能不在[0.0,1.0],所以得进行判定这个3D物体投影的部分是否在视截体内     (没SV_POSITION签名 显卡不会进行裁剪)if (saturate(ProjTex.x) == ProjTex.x&&saturate(ProjTex.y) == ProjTex.y){TexColor= ProjectiveTexture.Sample(SampleType, ProjTex);}color = TexColor;return color;}



下面是我将用到的投射到3D物体上的2D纹理:






这是没用投射纹理技术的







这是我们用了投影纹理技术的:观察相机位置A(0.0f, 6.0f, -12.0f)    投影相机(投影机)的位置B(2.0f, 5.0f, -2.0f),好吧,




在下图我做出辅助线来,观察相机A点的视截体我就不作辅助线了,作投影相机B的辅助线,红色的点为投影相机的视截体和平面的交点,画画水平有点糟糕,请见谅:







最后注意的是,在Shader可以看到我们的判定:由于我们  float4 ProjPos:POSITION  没标记“”POSTION“” 而不是“”SV_POSITION“”,显卡不会进行视截体内外面部分的裁剪,所以求得的UV坐标值可能超过[0,1]范围,所以有下面的判定
//第三,由于3D模型可能超出投影相机下的视截体,其投影纹理可能不在[0.0,1.0],所以得进行判定这个3D物体投影的部分是否在视截体内     (没SV_POSITION签名 显卡不会进行裁剪)if (saturate(ProjTex.x) == ProjTex.x&&saturate(ProjTex.y) == ProjTex.y){TexColor= ProjectiveTexture.Sample(SampleType, ProjTex);}




下面给我的源代码:
http://download.csdn.net/detail/qq_29523119/9670555













0 0
原创粉丝点击