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

这节教程是介绍有关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);   //采样方式

//VertexShader
cbuffer 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);
	}




下面给我的源代码:













  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值