现在的位置: 首页 > 综合 > 正文

在Windows Phone中进行3D开发之八光照

2013年08月22日 ⁄ 综合 ⁄ 共 5648字 ⁄ 字号 评论关闭

        在现实生活中,除了颜色以外,最重要的其实是光照,有了光照,才有了明暗、着色、高光等效果,在继上节的纹理之后,这节来为场景加入光照效果。

        在XNA中,可以简单地把光照分为两种,一种是环境光,一种是有向光。

        环境光是不来自任何特殊方向的光,它有光源,但是被周围环境的多次反射变得没有确定的方向,物体各表面都均等受光。在使用时主要用环境光改变场景的基本光线颜色。

        有向光是来自某个方向被物体表面反射的光,比如射灯的光等。在描述这种光线时,除了描述方向以外,还要描述散射光的颜色和物体表面高光区的颜色等其他属性。

        在使用光线的时候,还有一个额外的参数需要指定,那就是法线方向。不妨作这样的思考,对于任意一个平面,渲染引擎是如何计算入射光和反射光的夹角呢?是如何确定该平面哪个方向为正方向的呢?其实,这些问题的答案就是依靠法线。

        回顾一下法线的定义,简单的说就是垂直于平面的垂线,对于曲面来说,就是垂直于曲面某点切线的线。法线在3D空间的重要作用就是确定面的正方向,通常定义由内部指向外部的方向为正方向,有了这个方向,渲染引擎在做光线运算和纹理贴图时才能正确处理方向,从而达到预期的效果。

        在场景中如果需要光照效果,就不能简单的用VertexPositionTexture类了,XNA提供了VertexPositionNormalTexture类,这个类提供了对法线的表示方法。下面,我们在前面的三角形定义的基础上,改用这个新的类,代码如下:

           triangle = new VertexPositionNormalTexture[]{

               new VertexPositionNormalTexture(new Vector3(0, 1, 0),new Vector3(0,0,1),new Vector2(0.5f,0)),

               new VertexPositionNormalTexture(new Vector3(1, -1, 0),newVector3(0,0,1), new Vector2(1,1)),

               new VertexPositionNormalTexture(new Vector3(-1,-1, 0),newVector3(0,0,1), new Vector2(0,1))

            };

        注意到每个点的法线方向都用了(0,0,1),即全部指向z轴的正方向,从而确定了整个三角形的正方向是从手机屏幕向外指向用户。

        有了这个数据定义,就可以通过BasicEffect对象添加光照效果了。代码如下:

           basicEffect.LightingEnabled = true;

           basicEffect.AmbientLightColor = new Vector3(0.1f, 0.1f, 0.1f);

           basicEffect.DirectionalLight0.DiffuseColor = Color.Red.ToVector3();

           basicEffect.DirectionalLight0.Direction = Vector3.Normalize(newVector3(-0.5f, 0.5f, -1));

           basicEffect.DirectionalLight0.SpecularColor = Color.White.ToVector3();

           basicEffect.DirectionalLight0.Enabled = true;

        通过LightingEnabled=true可以启动光照效果,只有该变量的值为true,后续语句中设置的光照才会得到渲染。

        AmbientLightColor指定的是环境光的颜色。

        通过BasicEffect可以设置环境光、漫反射光、高光以及3个有向光的效果。在上述的代码中,通过对DirectionalLight0的设置,就是确定了一个有向光的属性。这些属性中,DiffuseColor是其漫反射光的颜色,Direction是一个归一化的三维向量,用于指定光线的方向,SpecularColor是定义高光的颜色,Enabled是启用该有向光的效果渲染。

        最终的运行结果如图所示。光源的方向是从右下角到左上角并指向屏幕内部,因此右下角的高光效果较明显。而且整个物体被漫反射光映成了红色,像是被夕阳照射的一样。


        其余的光照效果需要在实践中不断的积累,相信有了上述的学习,在运用时能够达到目标效果。

附本节Game1类的完整源码:

    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        Camera camera;
        Matrix world = Matrix.Identity;
        BasicEffect basicEffect;
        VertexPositionNormalTexture[] triangle;
        Matrix translateMatrix=Matrix.Identity;
        Matrix scaleMatrix = Matrix.Identity;
        Matrix rotateMatrix = Matrix.Identity;
        Texture2D texture;

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

            // Frame rate is 30 fps by default for Windows Phone.
            TargetElapsedTime = TimeSpan.FromTicks(333333);

            // Extend battery life under lock.
            InactiveSleepTime = TimeSpan.FromSeconds(1);
            graphics.IsFullScreen = true;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            camera = new Camera(this, new Vector3(0, 0, 5), Vector3.Zero, Vector3.Up, MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 1.0f, 50.0f);
            Components.Add(camera);
            basicEffect = new BasicEffect(GraphicsDevice);

            triangle = new VertexPositionNormalTexture[]{
                new VertexPositionNormalTexture(new Vector3(0, 1, 0),new Vector3(0,0,1), new Vector2(0.5f,0)),
                new VertexPositionNormalTexture(new Vector3(1, -1, 0),new Vector3(0,0,1), new Vector2(1,1)),
                new VertexPositionNormalTexture(new Vector3(-1,-1, 0),new Vector3(0,0,1), new Vector2(0,1))
            };

            texture = Content.Load<Texture2D>(@"Tulips");
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            TouchPanel.EnabledGestures = GestureType.Tap;
            if (TouchPanel.IsGestureAvailable)
            {
                GestureSample gestureSample = TouchPanel.ReadGesture();
                if (gestureSample.GestureType == GestureType.Tap)
                {
                    translateMatrix *= Matrix.CreateTranslation(0.3f, 0, 0); 
                    //scaleMatrix = Matrix.CreateScale(0.9f);
                    rotateMatrix *= Matrix.CreateRotationY(MathHelper.ToRadians(10));
                }
            }

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            RasterizerState rasterizerState = new RasterizerState();
            rasterizerState.CullMode = CullMode.None;
            GraphicsDevice.RasterizerState = rasterizerState;

            basicEffect.World = scaleMatrix * translateMatrix * rotateMatrix;
            basicEffect.View = camera.view;
            basicEffect.Projection = camera.projection;

            basicEffect.TextureEnabled = true;
            basicEffect.Texture = texture;
            GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;

            basicEffect.LightingEnabled = true;
            basicEffect.AmbientLightColor = new Vector3(0.1f, 0.1f, 0.1f);
            basicEffect.DirectionalLight0.DiffuseColor = Color.Red.ToVector3();
            basicEffect.DirectionalLight0.Direction = Vector3.Normalize(new Vector3(-0.5f, 0.5f, -1));
            basicEffect.DirectionalLight0.SpecularColor = Color.White.ToVector3();
            basicEffect.DirectionalLight0.Enabled = true;
            
            foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
            {
                pass.Apply();
                GraphicsDevice.DrawUserPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleStrip, triangle, 0, 1);
            }

            base.Draw(gameTime);
        }
    }

——欢迎转载,请注明出处 http://blog.csdn.net/caowenbin ——


 

抱歉!评论已关闭.