Программируем на Monogame без XNA. Часть 4. Простой шейдер

В этой статье мы создадим просто шейдер постобработки и попробуем использовать его для рисования сцены под Android.

Для начала добавим текстуру в проект и нарисуем ее на экране (растянем ее на весь экран).

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace AndroidGame
{
    ///

    /// This is the main type for your game.
    ///
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Texture2D background;

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

            graphics.IsFullScreen = true;
            graphics.PreferredBackBufferWidth = 800;
            graphics.PreferredBackBufferHeight = 480;
            graphics.SupportedOrientations = DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight;
        }

        ///

        /// 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.
        ///
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        ///

        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        ///
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
            background = Content.Load("back");
        }

        ///

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

        ///

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

            // TODO: Add your update logic here

            base.Update(gameTime);
        }

        ///

        /// This is called when the game should draw itself.
        ///
        ///Provides a snapshot of timing values.
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            spriteBatch.Begin();
            spriteBatch.Draw(background, new Rectangle(0,0, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight), Color.White);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

Теперь добавим в наш проект новый шейдер и применим его ко всему экрану.

Добавим Sprite Effect и назовем его shader.fx. Для начала воспользуемся шейдером по умолчанию и убедимся в том, что он ничего не «ломает».

Загрузим шейдер и применим его к сцене.

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace AndroidGame
{
    ///

    /// This is the main type for your game.
    ///
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Texture2D background;
        Effect shader;

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

            graphics.IsFullScreen = true;
            graphics.PreferredBackBufferWidth = 800;
            graphics.PreferredBackBufferHeight = 480;
            graphics.SupportedOrientations = DisplayOrientation.LandscapeLeft | DisplayOrientation.LandscapeRight;
        }

        ///

        /// 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.
        ///
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        ///

        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        ///
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
            background = Content.Load("back");
            shader = Content.Load("shader");
        }

        ///

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

        ///

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

            // TODO: Add your update logic here

            base.Update(gameTime);
        }

        ///

        /// This is called when the game should draw itself.
        ///
        ///Provides a snapshot of timing values.
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            spriteBatch.Begin(SpriteSortMode.Immediate, null, null, null, null,
                shader, null);
            spriteBatch.Draw(background, new Rectangle(0,0, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight), Color.White);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

Ничего не изменилось в сцене, а значит все сделано правильно.

Теперь сделаем какие-нибудь изменения в шейдере, например, обнулим красный канал.

Не забудьте перестроить Pipeline Project (F6).

#if OPENGL
	#define SV_POSITION POSITION
	#define VS_SHADERMODEL vs_3_0
	#define PS_SHADERMODEL ps_3_0
#else
	#define VS_SHADERMODEL vs_4_0_level_9_1
	#define PS_SHADERMODEL ps_4_0_level_9_1
#endif

Texture2D SpriteTexture;

sampler2D SpriteTextureSampler = sampler_state
{
	Texture = ;
};

struct VertexShaderOutput
{
	float4 Position : SV_POSITION;
	float4 Color : COLOR0;
	float2 TextureCoordinates : TEXCOORD0;
};

float4 MainPS(VertexShaderOutput input) : COLOR
{
	float4 color = tex2D(SpriteTextureSampler,input.TextureCoordinates) * input.Color;
	color.r = 0;
	return color;
}

technique SpriteDrawing
{
	pass P0
	{
		PixelShader = compile PS_SHADERMODEL MainPS();
	}
};

Красный канал пропал, а значит шейдер работает.

Теперь сделаем простой шейдер, который переводит цвета на картинке в оттенки серого в зависимости от яркости исходных цветов.

#if OPENGL
	#define SV_POSITION POSITION
	#define VS_SHADERMODEL vs_3_0
	#define PS_SHADERMODEL ps_3_0
#else
	#define VS_SHADERMODEL vs_4_0_level_9_1
	#define PS_SHADERMODEL ps_4_0_level_9_1
#endif

Texture2D SpriteTexture;

sampler2D SpriteTextureSampler = sampler_state
{
	Texture = <SpriteTexture>;
};

struct VertexShaderOutput
{
	float4 Position : SV_POSITION;
	float4 Color : COLOR0;
	float2 TextureCoordinates : TEXCOORD0;
};

float4 MainPS(VertexShaderOutput input) : COLOR
{
	float3 color = tex2D(SpriteTextureSampler, input.TextureCoordinates.xy).rgb;

	color = dot(color, float3(0.3, 0.59, 0.11));
	return float4(color, 1);
}

technique SpriteDrawing
{
	pass P0
	{
		PixelShader = compile PS_SHADERMODEL MainPS();
	}
};

Вот и результат:

_android

Реклама
Запись опубликована в рубрике Компьютерная графика, MonoGame с метками , , , , , , , . Добавьте в закладки постоянную ссылку.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s