官术网_书友最值得收藏!

Implementing lens flare within the HiDef profile

Modern GPUs are very good at determining whether one set of polygons is obscured by another set. We can use this to our advantage when creating lens flares within a HiDef profile.

Getting ready

This recipe assumes that you've already got a scene, rendering correctly, albeit without a lens flare.

How to do it...

To create a lens flare within the HiDef profile:

  1. Create a new class to hold the lens flare behavior:
    class HiDefLensFlare
    {
    
  2. Add some instance-level variables to hold the details of the occlusion test, the lighting, and the glow image:
    SpriteBatch spriteBatch;
    GraphicsDevice graphicsDevice;
    public BasicEffect ShadowCaptureEffect;
    OcclusionQuery occlusionQuery;
    bool occlusionQueryActive;
    float occlusionAlpha;
    const float querySize = 50;
    VertexPositionColor[] queryVertices;
    public Vector3 LightDirection = Vector3.Normalize(new Vector3(0.5f, -0.1f, 0.5f));
    Vector2 lightPosition;
    bool lightBehindCamera;
    Texture2D glow;
    Vector2 glowOrigin;
    float glowScale = 400f;
    
  3. Next, add a constructor to set up the occlusion test prerequisites and load the glow texture:
    public HiDefLensFlare(GraphicsDevice graphicsDevice, ContentManager content)
    {
    this.graphicsDevice = graphicsDevice;
    spriteBatch = new SpriteBatch(graphicsDevice);
    ShadowCaptureEffect = new BasicEffect(graphicsDevice)
    {
    View = Matrix.Identity,
    VertexColorEnabled = true
    };
    occlusionQuery = new OcclusionQuery(graphicsDevice);
    queryVertices = new VertexPositionColor[4];
    queryVertices[0].Position = new Vector3(-querySize / 2, -querySize / 2, -1);
    queryVertices[1].Position = new Vector3(querySize / 2, -querySize / 2, -1);
    queryVertices[2].Position = new Vector3(-querySize / 2, querySize / 2, -1);
    queryVertices[3].Position = new Vector3(querySize / 2, querySize / 2, -1);
    glow = content.Load<Texture2D>(@"lensflare/glow");
    glowOrigin = new Vector2(glow.Width, glow.Height) / 2;
    }
    
  4. Create a new BlendState instance-level variable so the ocular test can proceed without changing the visible image:
    static readonly BlendState ColorWriteDisable = new BlendState
    {
    ColorWriteChannels = ColorWriteChannels.None
    };
    
  5. Add a new method to perform the ocular test:
    public void Measure(Matrix view, Matrix projection)
    {
    
  6. Calculate the position of the lens flare on screen, and exit early if it's behind the player's viewpoint:
    var infiniteView = view;
    infiniteView.Translation = Vector3.Zero;
    var viewport = graphicsDevice.Viewport;
    var projectedPosition = viewport.Project(
    -LightDirection, projection,
    infiniteView, Matrix.Identity);
    if ((projectedPosition.Z < 0) ||
    (projectedPosition.Z > 1))
    {
    lightBehindCamera = true;
    return;
    }
    lightPosition = new Vector2(projectedPosition.X, projectedPosition.Y);
    lightBehindCamera = false;
    
  7. Add the calculation for how much of the lens flare test area is occluded by the scene once the previous occlusion test has completed:
    if (occlusionQueryActive)
    {
    if (!occlusionQuery.IsComplete)
    {
    return;
    }
    const float queryArea = querySize * querySize;
    occlusionAlpha = Math.Min(
    occlusionQuery.PixelCount / queryArea, 1);
    }
    
  8. Set up for the next occlusion query:
    graphicsDevice.BlendState = ColorWriteDisable;
    graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
    ShadowCaptureEffect.World = Matrix.CreateTranslation(
    lightPosition.X,
    lightPosition.Y, 0);
    ShadowCaptureEffect.Projection = Matrix.CreateOrthographicOffCenter(0,
    viewport.Width,
    viewport.Height,
    0, 0, 1);
    ShadowCaptureEffect.CurrentTechnique.Passes[0].Apply();
    
  9. Render the lens flare test vertices inside the occlusion test to determine how many pixels were rendered:
    occlusionQuery.Begin();
    graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, queryVertices, 0, 2);
    occlusionQuery.End();
    occlusionQueryActive = true;
    
  10. Complete the class by adding a Draw() method to render the glow:
    public void Draw()
    {
    if (lightBehindCamera || occlusionAlpha <= 0)
    return;
    Color color = Color.White * occlusionAlpha;
    Vector2 origin = new Vector2(glow.Width, glow.Height) / 2;
    float scale = glowScale * 2 / glow.Width;
    spriteBatch.Begin();
    spriteBatch.Draw(glow, lightPosition, null, color, 0,
    origin, scale, SpriteEffects.None, 0);
    spriteBatch.End();
    }
    

How it works...

XNA and the underlying DirectX infrastructure contain a rather handy little diagnostic tool in the form of the occlusion test. With this test, you can count how many pixels were filled when trying to render a particular portion of a scene.

We utilize this in the lens flare example by attempting to render a small rectangle across the opposite side of the scene from the player's viewpoint, and measuring how much of it is obscured by the scene's meshes. With this number, we adjust the opacity of the lens flare's glow texture up or down to simulate the sun disappearing either partially or completely behind an object.

主站蜘蛛池模板: 雷州市| 广东省| 娄烦县| 泗洪县| 榆社县| 临邑县| 忻城县| 宝兴县| 清涧县| 宝兴县| 兴业县| 东莞市| 新乡市| 蕲春县| 汶上县| 定远县| 双辽市| 宁城县| 安康市| 凤山县| 荆门市| 凤阳县| 同心县| 武鸣县| 云霄县| 长子县| 紫金县| 社会| 汉寿县| 敖汉旗| 嘉善县| 天等县| 边坝县| 儋州市| 五家渠市| 南城县| 福海县| 准格尔旗| 珠海市| 宜丰县| 章丘市|