查看: 2977|回复: 4
打印 上一主题 下一主题

[教程] XNA Billboard(公告板技术)

[复制链接]

1557

主题

1

听众

1万

积分

资深设计师

Rank: 7Rank: 7Rank: 7

纳金币
454
精华
31

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

跳转到指定楼层
楼主
发表于 2013-10-15 15:48:54 |只看该作者 |倒序浏览

公告板技术是3D游戏中用的非常多的一种技术,主要是用于控制场景中的Texture的方向,让他始终以一定的角度对着我们的镜头(一般是垂直于镜头)。
如我们在3D游戏中看到的怪物的蓝、红和怪物名字、一些花草树木等,无论我们在哪个方向看它总是对着我们。
如下图所示:

      

GraphicsDeviceManager graphics;
Texture2D texRedPanda;
//镜头信息参数
Vector3 pos, lookat, up;
//World,View,Project矩阵
Matrix world,view, project;
BasicEffect basicEffect;
//顶点结构
VertexPositionTexture[] vpt;
VertexDeclaration vertexDec;

public GameMain()
{
    graphics = new GraphicsDeviceManager(this);
    Content.RootDirectory = "Content";
}

protected override void Initialize()
{
    //初始化镜头信息
    pos = new Vector3(0, 0,200);
    lookat = Vector3.Zero;
    up = Vector3.Up;

    //初始化变换矩阵
    world = Matrix.Identity;
    bbWorld = Matrix.Identity;
    view = Matrix.CreateLookAt(pos, lookat, up);
    project = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, 800f/600f, 1, 1000);

    vpt = new VertexPositionTexture[6];
    GraphicsDevice.RenderState.CullMode = CullMode.None;
    base.Initialize();
}

protected override void LoadContent()
{

    texRedPanda = Content.Load<Texture2D>("RedPanda");

    basicEffect = new BasicEffect(GraphicsDevice, null);
    vertexDec = new VertexDeclaration(GraphicsDevice, VertexPositionTexture.VertexElements);

    //定义三角形的各顶点坐标和纹理坐标
    vpt[0] = new VertexPositionTexture(new Vector3(-25,-25, 0), new Vector2(0, 1));
    vpt[1] = new VertexPositionTexture(new Vector3(-25, 25, 0), new Vector2(0, 0));
    vpt[2] = new VertexPositionTexture(new Vector3(25,-25, 0), new Vector2(1, 1));
    vpt[3] = new VertexPositionTexture(new Vector3(-25, 25, 0), new Vector2(0, 0));
    vpt[4] = new VertexPositionTexture(new Vector3(25, 25, 0), new Vector2(1, 0));
    vpt[5] = new VertexPositionTexture(new Vector3(25,-25, 0), new Vector2(1, 1));
}

protected override void Update(GameTime gameTime)
{
    // Allows the game to exit
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
        this.Exit();
    base.Update(gameTime);
}

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.Black);
    //设置变换矩阵参数
    basicEffect.World = world;
    basicEffect.View = view;
    basicEffect.Projection = project;
    //设置绘制纹理
    basicEffect.TextureEnabled = true;
    basicEffect.Texture = texRedPanda;
    basicEffect.Begin();
    foreach (var pass in basicEffect.CurrentTechnique.Passes)
    {
        pass.Begin();
        GraphicsDevice.VertexDeclaration = vertexDec;
        GraphicsDevice.DrawUserPrimitives<VertexPositionTexture>(PrimitiveType.TriangleList, vpt,0, 2);
        pass.End();
    }
    basicEffect.End();
    base.Draw(gameTime);
}
       texRedPanda的位置大概是在坐标原点,为了等会便于观察,将在Update加些内容。让镜头的位置通过键盘控制绕原点旋转(这里不是用World变换)。这里是在网上找的公式:
    x1 = x0 * cosB + y0 * sinB
    y1 = -x0 * sinB + y0 * cosB

       x0,y0表示镜头现在的位置,y1,y2表示绕原点旋转B弧度后的坐标。
       现在我们就呆以看到没有使用公告板技术时的效果了。把下面的代码加到Update方法的里:

float x0, z0;
x0=pos.X;
z0=pos.Z;
if (Keyboard.GetState().IsKeyDown(Keys.Left))
{
    pos.X = x0 * (float)Math.Cos(0.1d) + z0 * (float)Math.Sin(0.1d);
    pos.Z = (-x0) * (float)Math.Sin(0.1d) + z0 * (float)Math.Cos(0.1d);
}
if (Keyboard.GetState().IsKeyDown(Keys.Right))
{
    pos.X = x0 * (float)Math.Cos(-0.1d) + z0 * (float)Math.Sin(-0.1d);
    pos.Z = (-x0) * (float)Math.Sin(-0.1d) + z0 * (float)Math.Cos(-0.1d);
}
view = Matrix.CreateLookAt(pos, Vector3.Zero, Vector3.Up);

//将视点 位置显示在标题栏
Window.Title = pos.ToString();
       下面我们加一个用了公告板的World变换。主要代码如下,
    bbWorld = Matrix.CreateBillboard(Vector3.Zero, -pos, Vector3.Up, null);
       现在再来对比下 效果,发现无论我们控制镜头在哪个位置,小熊猫图片的下面始终对着我们。

这里已经实现了一个初级的公告板。当然也可以不用XNA的现有方法,我们可以把代码放到HLSL里面去,这里有个实现,但我帮他是数学原理没有推算过。所以只有套着用了。

float4x4 World;
float4x4 View;
float4x4 Projection;
texture Texture;

sampler textureSampler=sampler_state
{
    texture=<Texture>;
    magfilter = LINEAR;
    minfilter = LINEAR;
    mipfilter=LINEAR;
    AddressU = CLAMP;
    AddressV = CLAMP;
};

struct VertexShaderInput
{
    float3 PosOSITION0;
    float2 TexCoord:TEXCOORD0;
};

struct VertexShaderOutput
{
    float4 Position : POSITION0;
    float2 TexCoord:TEXCOORD0;
};

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
    VertexShaderOutput output;

    float4x4 worldViewMatrix = mul(World, View);   
    float3 positionVS = input.Pos + float3(worldViewMatrix._41, worldViewMatrix._42, worldViewMatrix._43);
    output.Position = mul(float4(positionVS, 1.0f), Projection);
    output.TexCoord=input.TexCoord;

    return output;
}

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
    return tex2D(textureSampler,input.TexCoord);
}

technique BillBoard
{
    pass BillBoard
    {
        VertexShader = compile vs_1_0 VertexShaderFunction();
        PixelShader = compile ps_1_0 PixelShaderFunction();
    }
}  

分享到: QQ好友和群QQ好友和群 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
转播转播0 分享淘帖0 收藏收藏0 支持支持0 反对反对0
回复

使用道具 举报

1557

主题

1

听众

1万

积分

资深设计师

Rank: 7Rank: 7Rank: 7

纳金币
454
精华
31

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

沙发
发表于 2013-10-15 16:10:44 |只看该作者
顶下!!!!
回复

使用道具 举报

9903

主题

126

听众

7万

积分

首席设计师

Rank: 8Rank: 8

纳金币
53488
精华
316

最佳新人 热心会员 灌水之王 活跃会员 突出贡献 荣誉管理 论坛元老

板凳
发表于 2013-10-15 16:25:42 |只看该作者
不错的分享
回复

使用道具 举报

nts    

3

主题

1

听众

743

积分

初级设计师

Rank: 3Rank: 3

纳金币
7
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

地板
发表于 2013-10-16 09:58:18 |只看该作者
我记得用限定物体某个轴向就可以实现的
回复

使用道具 举报

2

主题

1

听众

1143

积分

助理设计师

Rank: 4

纳金币
350
精华
0
5#
发表于 2013-10-28 10:14:59 |只看该作者
感谢楼主分享~
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

手机版|纳金网 ( 闽ICP备2021016425号-2/3

GMT+8, 2024-11-27 12:42 , Processed in 0.106664 second(s), 30 queries .

Powered by Discuz!-创意设计 X2.5

© 2008-2019 Narkii Inc.

回顶部